Android手机“核心科技”---Vibrator(马达)驱动分析

前言

我的博客已经断更很久了,因为疫情的原因没能开学,也无法去公司报道实习,在家十分的颓废,学习的时间零零散散,更是没有什么时间来写博客的@@(铁废物)。最近疫情好转,自己也来深圳这边某知名IC厂商(同样也让人喊YES的那个)实习,在手机部门做一些驱动相关内容的工作,以前有做过Linux驱动开发,安卓以前没有接触过,在公司就慢慢开始学习,自己下班回家后后也再分析分析代码,写写博客。
因为自己也是一个初学者,在分析上有什么不正确的地方也希望大家可以指正
注意:这些博客没有任何从公司电脑上拷贝的资料,参照的代码为Github的开源代码或者AOSP的代码,求生欲满满-.-

Vibrator简单介绍

Vibrator是振动器的意思,也可以被叫做马达,马达旋转或者做直线运动会产生振动。由于小米的科普(此处应该有些掌声)我们了解了手机马达的几种类别:普通转子马达(一般用于低端机)、币型转子马达(被小米嘲讽的嗡嗡嗡)、Y轴线性马达(形状类似币型转子但是它是做Y轴的直线运动)、X轴线性马达(苹果魅族等厂商的,做X轴线性运动)

马达对手机的使用体验确实有所提升,好的振动反馈是顶级手机必备特性。
对于我们底层驱动开发人员来说,马达却是一个特别好的入门模块,因为马达的控制一般分为两类:PWM控制、ldo输出控制,后者所占的比例较多,一般只是由PMIC输出电压即可,而内核驱动部分、HAL部分、JNI部分、Application Framework部分相对很标准,十分适合用来理解Android驱动框架,体会如何在APP使用JAVA调用底层用C甚至汇编编写的驱动程序。

0: 驱动框架的整体介绍

代码版本、出处及讲解平台

Kernel:mtk-t-alps-release-q0-kernel-4.9 Github地址:点我
Android:Android 10.0.0_r30 android code search地址:点我
平台:MTK6779

相关文件

Application Framework

  • frameworks/base/core/java/android/os/SystemVibrator.java
  • frameworks/base/core/java/android/os/Vibrator.java
  • frameworks/base/services/core/java/com/android/server/VibratorService.java
  • frameworks/base/core/java/android/os/IVibratorService.aidl
  • frameworks/base/services/java/com/android/server/SystemServer.java

JNI

  • frameworks/base/services/core/jni/com_android_server_VibratorService.cpp

HAL

  • hardware/interfaces/vibrator/1.0/default/Vibrator.cpp
  • hardware/interfaces/vibrator/1.0/default/Vibrator.h
  • hardware/libhardware/modules/vibrator/vibrator.c
  • hardware/libhardware/include/hardware/vibrator.h

Kernel

  • \drivers\misc\mediatek\vibrator\mt6779\vibrator.c
  • \drivers\misc\mediatek\vibrator\mt6779\vibrator.h
  • \drivers\misc\mediatek\vibrator\vibrator_drv.c

DTS

  • \arch\arm64\boot\dts\mediatek\k79v1_64.dts

流程分析

在这里插入图片描述
大体的框架如上图所示,当应用操作马达时,它会调用Application Framework层实现的马达调用类,并且这些调用是通过马达服务的(可以理解为一个进程),而马达服务又需要通过aidl实现,aidl是Android进程间的通信方式。在服务中对底层的操控需要使用一些与HAL层CPP函数相映射的JAVA函数,JNI层就是完成这个映射工作的。HAL硬件描述层是在用户空间对内核空间中驱动的使用,通过对Sysfs中的设备节点的读取和写入来完成马达状态的读取和控制。内核的驱动程序中完成对设备的操作,当驱动检测到设备时会在用户空间生成设备节点,定义对设备节点中属性文件的读写操作,同时也会根据设备树中的信息完成对设备的初始化。设备树是对底层硬件的描述,通过定义一个vibrator节点,在节点中定义电压等级、最短震动时间等属性,以供驱动的读取。硬件部分使用SPI与PMIC通信,通过对PMIC的读写就可以实现输出LDO控制vibrator以及接受vibrator的过流中断等。
这是对自顶向下对驱动框架的简单介绍,接下来我们自底向上逐步分析vibrator的驱动框架

1: 硬件部分

PMIC与AP使用SPI连接,马达连接在PMIC上,马达由一个LDO输出控制,输出与地之间接一个滤波电容,去除尖峰电压

2: 设备树部分

设备树中的vibrator节点代码如下

	vibrator0:vibrator@0 {
		compatible = "mediatek,vibrator";
		vib_timer = <25>;
		vib_limit = <9>;
		vib_vol= <9>;
	};

这个包含用于与驱动匹配的compatible,以及电压等级、震动的时间限制等

3: kernel Driver部分

kernel Driver部分包含两部分,与底层芯片相关的放在以AP命名的文件夹中,与底层实现不相关的不同平台是共用的,降低了代码维护的难度。
首先与硬件不相关的代码如下

硬件不相关

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/leds.h>
#include <linux/hrtimer.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/jiffies.h>
#include <linux/timer.h>

#include <vibrator.h>
#include <vibrator_hal.h>
#include <mt-plat/upmu_common.h>

#define VIB_DEVICE				"mtk_vibrator"
#define VIB_TAG                                 "[vibrator]"

struct mt_vibr {
	struct workqueue_struct *vibr_queue;
	struct work_struct vibr_work;
	struct hrtimer vibr_timer;
	int ldo_state;
	int shutdown_flag;
	atomic_t vibr_dur;
	spinlock_t vibr_lock;
	atomic_t vibr_state;
};

static struct mt_vibr *g_mt_vib;

static int vibr_Enable(void)
{
	if (!g_mt_vib->ldo_state) {
		vibr_Enable_HW();
		g_mt_vib->ldo_state = 1;
	}
	return 0;
}

static int vibr_Disable(void)
{
	if (g_mt_vib->ldo_state) {
		vibr_Disable_HW();
		g_mt_vib->ldo_state = 0;
	}
	return 0;
}

static void update_vibrator(struct work_struct *work)
{
	struct mt_vibr *vibr = container_of(work, struct mt_vibr, vibr_work);

	if (atomic_read(&vibr->vibr_state) == 0)
		vibr_Disable();
	else
		vibr_Enable();
}

static void vibrator_enable(unsigned int dur, unsigned int activate)
{
	unsigned long flags;
	struct vibrator_hw *hw = mt_get_cust_vibrator_hw();

	spin_lock_irqsave(&g_mt_vib->vibr_lock, flags);
	hrtimer_cancel(&g_mt_vib->vibr_timer);
	pr_debug(VIB_TAG "cancel hrtimer, cust:%dms, value:%u, shutdown:%d\n",
			hw->vib_timer, dur, g_mt_vib->shutdown_flag);

	if (activate == 0 || g_mt_vib->shutdown_flag == 1) {
		atomic_set(&g_mt_vib->vibr_state, 0);
	} else {
#ifdef CUST_VIBR_LIMIT
		if (dur > hw->vib_limit && dur < hw->vib_timer)
#else
		if (dur >= 10 && dur < hw->vib_timer)
#endif
			dur = hw->vib_timer;

		dur = (dur > 15000 ? 15000 : dur);
		atomic_set(&g_mt_vib->vibr_state, 1);
		hrtimer_start(&g_mt_vib->vibr_timer,
			      ktime_set(dur / 1000, (dur % 1000) * 1000000),
			      HRTIMER_MODE_REL);
	}
	spin_unlock_irqrestore(&g_mt_vib->vibr_lock, flags);
	queue_work(g_mt_vib->vibr_queue, &g_mt_vib->vibr_work);
}

static void vibrator_oc_handler(void)
{
	pr_debug(VIB_TAG "vibrator_oc_handler: disable vibr due to oc intr happened\n");
	vibrator_enable(0, 0);
}

static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer)
{
	struct mt_vibr *vibr = container_of(timer, struct mt_vibr, vibr_timer);

	atomic_set(&vibr->vibr_state, 0);
	queue_work(vibr->vibr_queue, &vibr->vibr_work);
	return HRTIMER_NORESTART;
}

static const struct of_device_id vibr_of_ids[] = {
	{ .compatible = "mediatek,vibrator", },
	{}
};

static atomic_t vib_state;

static ssize_t vibr_activate_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "%d\n", atomic_read(&g_mt_vib->vibr_state));
}

static ssize_t vibr_activate_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	unsigned int activate, dur;
	ssize_t ret;

	ret = kstrtouint(buf, 10, &activate);
	if (ret) {
		pr_err(VIB_TAG "set activate fail\n");
		return ret;
	}
	dur = atomic_read(&g_mt_vib->vibr_dur);
	vibrator_enable(dur, activate);
	ret = size;
	return ret;
}

static ssize_t vibr_state_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "%d\n", atomic_read(&vib_state));
}

static ssize_t vibr_state_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	unsigned int state;
	ssize_t ret;

	ret = kstrtouint(buf, 10, &state);
	if (ret) {
		pr_err(VIB_TAG "set state fail\n");
		return ret;
	}
	atomic_set(&vib_state, state);

	ret = size;
	return ret;
}
static ssize_t vibr_duration_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	unsigned int duration;
	ssize_t ret;

	ret = kstrtouint(buf, 10, &duration);
	if (ret) {
		pr_err(VIB_TAG "set duration fail\n");
		return ret;
	}

	atomic_set(&g_mt_vib->vibr_dur, duration);
	ret = size;
	return ret;
}

static DEVICE_ATTR(activate, 0644, vibr_activate_show, vibr_activate_store);
static DEVICE_ATTR(state, 0644, vibr_state_show, vibr_state_store);
static DEVICE_ATTR(duration, 0644, NULL, vibr_duration_store);

static struct attribute *activate_attrs[] = {
	&dev_attr_activate.attr,
	NULL,
};

static struct attribute *state_attrs[] = {
	&dev_attr_state.attr,
	NULL,
};

static struct attribute *duration_attrs[] = {
	&dev_attr_duration.attr,
	NULL,
};

static struct attribute_group activate_group = {
	.attrs = activate_attrs,
};

static struct attribute_group state_group = {
	.attrs = state_attrs,
};

static struct attribute_group duration_group = {
	.attrs = duration_attrs,
};

static const struct attribute_group *vibr_group[] = {
	&activate_group,
	&state_group,
	&duration_group,
	NULL
};

static struct led_classdev led_vibr = {
	.name		= "vibrator",
	.groups		= vibr_group,
};

static int vib_probe(struct platform_device *pdev)
{
	int ret = 0;
	struct mt_vibr *vibr;

	init_vibr_oc_handler(vibrator_oc_handler);

	vibr = devm_kzalloc(&pdev->dev, sizeof(*vibr), GFP_KERNEL);
	if (!vibr)
		return -ENOMEM;

	ret = devm_led_classdev_register(&pdev->dev, &led_vibr);
	if (ret < 0) {
		pr_err(VIB_TAG "led class register fail\n");
		return ret;
	}

	vibr->vibr_queue = create_singlethread_workqueue(VIB_DEVICE);
	if (!vibr->vibr_queue) {
		pr_err(VIB_TAG "unable to create workqueue\n");
		return -ENODATA;
	}

	INIT_WORK(&vibr->vibr_work, update_vibrator);
	spin_lock_init(&vibr->vibr_lock);
	vibr->shutdown_flag = 0;
	atomic_set(&vibr->vibr_state, 0);
	hrtimer_init(&vibr->vibr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	vibr->vibr_timer.function = vibrator_timer_func;

	dev_set_drvdata(&pdev->dev, vibr);
	g_mt_vib = vibr;
	init_cust_vibrator_dtsi(pdev);
	vibr_power_set();

	pr_debug(VIB_TAG "probe done\n");

	return 0;
}

static int vib_remove(struct platform_device *pdev)
{
	struct mt_vibr *vibr = dev_get_drvdata(&pdev->dev);

	cancel_work_sync(&vibr->vibr_work);
	hrtimer_cancel(&vibr->vibr_timer);
	devm_led_classdev_unregister(&pdev->dev, &led_vibr);

	return 0;
}

static void vib_shutdown(struct platform_device *pdev)
{
	unsigned long flags;
	struct mt_vibr *vibr = dev_get_drvdata(&pdev->dev);

	pr_debug(VIB_TAG "shutdown: enter!\n");
	spin_lock_irqsave(&vibr->vibr_lock, flags);
	vibr->shutdown_flag = 1;
	if (atomic_read(&vibr->vibr_state)) {
		pr_debug(VIB_TAG "vib_shutdown: vibrator will disable\n");
		atomic_set(&vibr->vibr_state, 0);
		spin_unlock_irqrestore(&vibr->vibr_lock, flags);
		vibr_Disable();
		return;
	}
	spin_unlock_irqrestore(&vibr->vibr_lock, flags);
}


static struct platform_driver vibrator_driver = {
	.probe = vib_probe,
	.remove = vib_remove,
	.shutdown = vib_shutdown,
	.driver = {
			.name = VIB_DEVICE,
			.owner = THIS_MODULE,
#ifdef CONFIG_OF
			.of_match_table = vibr_of_ids,
#endif
		   },
};

static int vib_mod_init(void)
{
	s32 ret;

	ret = platform_driver_register(&vibrator_driver);
	if (ret) {
		pr_err(VIB_TAG "Unable to register driver (%d)\n", ret);
		return ret;
	}
	pr_debug(VIB_TAG "init Done\n");

	return 0;
}

static void vib_mod_exit(void)
{
	pr_debug(VIB_TAG "vib_mod_exit Done\n");
}

module_init(vib_mod_init);
module_exit(vib_mod_exit);
MODULE_AUTHOR("MediaTek Inc.");
MODULE_DESCRIPTION("MTK Vibrator Driver (VIB)");
MODULE_LICENSE("GPL");

这个代码还是挺长的,逐语句分析太麻烦也没有必要,代码分为以下几个重要部分:

  • 驱动初始化,完成platform驱动的注册
  • 驱动、设备匹配后执行probe函数(此处是重点,后面会着重分析)
  • 设备属性文件的生成,读写函数的实现与绑定
  • 与控制vibrator相关的工作队列的任务函数,定时器回调函数
  • 驱动的remove、shutdown函数

我们首先分析probe函数,它主要工作为

  • 定义过流中断服务函数(该中断会关闭vibrator)
  • 为私有结构体分配内存
  • 注册一个leds类的设备(原因是控制类似,归属一类设备,可以省去很多相似的定义,生成的设备节点会在/sys/class/leds下)
  • 定义工作队列和工作队列的任务
  • 初始化自旋锁和原子量
  • 初始化hrtimer(内核提供的高精度定时器)并设置定时器回调函数
  • 将驱动的私有结构体设置到设备中
  • 读取设备树数据初始化PMIC

接着我们分析一下设备节点属性文件相关内容

  • 使用DEVICE_ATTR来定义属性文件并设置权限、读写函数
  • 定义attribute_group并绑定attribute_group和attribute数组
  • 定义led_classdev类型的led_vibr,并设置它的名字和属性集(该设备将在probe函数的leds类设备注册中使用)

当系统RUN起来后,会在/sys/class/leds/vibrator 下生成三个设备节点(其实不止三个,leds类还有一些设备节点但是对于我们vibrator来说只关注这三个即可),他们的含义如下

名称读操作写操作
activate获取马达状态激活马达
state获取马达状态未定义
duration未定义设置震动时长

通过对这三个属性文件的读写就可获取马达状态并且完成马达的控制

我们再分析一下驱动的锁机制

  • 驱动使用了自旋锁和原子量来保证程序的互斥
  • 原子量是Vibrator的状态变量
  • 自旋锁的上锁与解锁使用spin_lock_irqsave和spin_unlock_irqrestore,会在加锁的时候保护当前终端的状态

hrtimer的分析:
对于这个内核模块我会再写一篇博客来分析,简单描述下,这是一个使用红黑树来管理的直接操作硬件定时器中断的可以实现ns级定时的高精度定时器,定时控制精准

关于马达的控制流程分析:

  • 马达的控制函数为vibrator_enable函数传入震动时长和是否震动
  • 首先要加锁并且取消现有的hrtimer定时器
  • 对传入的参数进行检测并对震动时长进行限幅
  • 设置一个震动时长的定时器并开启定时器,开启震动(通过将马达状态切换任务加入任务队列)
  • 当定时结束后会执行回调函数,在回调函数中会关闭震动(同上)

为什么使用工作队列

  • 工作队列是一种异步执行机制,会在合适的时间执行队列中的任务,只需要将任务加入到队列即可,不会长时间持有CPU,工作队列(workqueue)在驱动开发中经常使用
  • 在打开马达时有一个3ms的延时操作,所以需要使用工作队列异步执行

remove和shutdown分析

  • 区别在于remove需要对资源进行释放和shutdown只是关闭马达
  • 原因在于这两个函数的执行时机,remove在设备拔出或者取消匹配时调用,shutdown在系统重启或者关闭的时候调用

硬件相关代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#ifdef CONFIG_MTK_PMIC_NEW_ARCH
#include <mt-plat/upmu_common.h>
#endif
#include "vibrator.h"

#define T    "vibrator"
struct vibrator_hw *pvib_cust;

#define OC_INTR_INIT_DELAY      (3)
void vibr_Enable_HW(void)
{
#ifdef CONFIG_MTK_PMIC_NEW_ARCH
	pmic_set_register_value(PMIC_RG_LDO_VIBR_EN, 1);
	mdelay(OC_INTR_INIT_DELAY);
	pmic_enable_interrupt(INT_VIBR_OC, 1, "vibr");
#endif
}

void vibr_Disable_HW(void)
{
#ifdef CONFIG_MTK_PMIC_NEW_ARCH
	pmic_enable_interrupt(INT_VIBR_OC, 0, "vibr");
	pmic_set_register_value(PMIC_RG_LDO_VIBR_EN, 0);
#endif
}

void init_vibr_oc_handler(void (*vibr_oc_func)(void))
{
#ifdef CONFIG_MTK_PMIC_NEW_ARCH
	pmic_register_interrupt_callback(INT_VIBR_OC, vibr_oc_func);
#endif
}

/******************************************
 * Set RG_VIBR_VOSEL	 Output voltage select
 *  hw->vib_vol:  Voltage selection
 * 4'b0000 :1.2V
 * 4'b0001 :1.3V
 * 4'b0010 :1.5V
 * 4'b0100 :1.8V
 * 4'b0101 :2.0V
 * 4'b1000 :2.7V
 * 4'b1001 :2.8V
 * 4'b1011 :3.0V
 * 4'b1101 :3.3V
 * ****************************************/
void init_cust_vibrator_dtsi(struct platform_device *pdev)
{
	int ret;

	if (pvib_cust == NULL) {
		pvib_cust = kmalloc(sizeof(struct vibrator_hw), GFP_KERNEL);
		if (pvib_cust == NULL) {
			pr_debug(T "%s kmalloc fail\n", __func__);
			return;
		}
		ret = of_property_read_u32(pdev->dev.of_node, "vib_timer",
			&(pvib_cust->vib_timer));
		if (!ret)
			pr_debug(T "vib_timer:%d\n", pvib_cust->vib_timer);
		else
			pvib_cust->vib_timer = 25;
#ifdef CUST_VIBR_LIMIT
		ret = of_property_read_u32(pdev->dev.of_node, "vib_limit",
			&(pvib_cust->vib_limit));
		if (!ret)
			pr_debug(T "vib_limit : %d\n", pvib_cust->vib_limit);
		else
			pvib_cust->vib_limit = 9;
#endif

#ifdef CUST_VIBR_VOL
		ret = of_property_read_u32(pdev->dev.of_node, "vib_vol",
			&(pvib_cust->vib_vol));
		if (!ret)
			pr_debug(T "vib_vol: %d\n", pvib_cust->vib_vol);
		else
			pvib_cust->vib_vol = 0x05;
#endif
		pr_debug(T "pvib_cust = %d, %d, %d\n",
			pvib_cust->vib_timer, pvib_cust->vib_limit,
					pvib_cust->vib_vol);
	}
}

struct vibrator_hw *get_cust_vibrator_dtsi(void)
{
	if (pvib_cust == NULL)
		pr_debug(T "%s fail, pvib_cust is NULL\n", __func__);
	return pvib_cust;
}

void vibr_power_set(void)
{
#ifdef CUST_VIBR_VOL
	struct vibrator_hw *hw = get_cust_vibrator_dtsi();

	if (hw != NULL) {
		pr_debug(T "vibr_init: set voltage = %d\n", hw->vib_vol);
#ifdef CONFIG_MTK_PMIC_NEW_ARCH
		pmic_set_register_value(PMIC_RG_VIBR_VOSEL, hw->vib_vol);
#endif
	} else {
		pr_debug("vibr_init: can not get  dtsi!\n");
	}
#endif
}

struct vibrator_hw *mt_get_cust_vibrator_hw(void)
{
	struct vibrator_hw *hw = get_cust_vibrator_dtsi();
	return hw;
}

硬件相关的代码主要包含几个部分

  • 设备树读取设置函数init_cust_vibrator_dtsi
  • 马达开关函数vibr_Enable_HW和vibr_Disable_HW
  • 输出电压设置函数
  • 过流中断设置函数

这个代码很简单不用做过多分析,vibr_Enable_HW中有一个3MS的延时,这就是为什么在驱动中需要使用工作队列

4: HAL 部分

HAL (Hardware Abstraction Layer), 又称为“硬件抽象层”。在Linux驱动中,我们已经将马达设为映射为文件了;而该HAL层的存在的意义,就是“对设备文件进行操作,从而相当于硬件进行操作”。HAL层的作用,一是操作硬件设备,二是操作接口封装,外界能方便的使用HAL提供的接口直接操作硬件设备。
代码在hardware/libhardware/modules/vibrator/vibrator.c

#include <hardware/hardware.h>
#include <hardware/vibrator.h>

#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <log/log.h>

#define TIMEOUT_STR_LEN         20

static const char THE_DEVICE[] = "/sys/class/timed_output/vibrator/enable";

static bool device_exists(const char *file) {
    int fd;

    fd = TEMP_FAILURE_RETRY(open(file, O_RDWR));
    if(fd < 0) {
        return false;
    }

    close(fd);
    return true;
}

static bool vibra_exists() {
    return device_exists(THE_DEVICE);
}

static int write_value(const char *file, const char *value)
{
    int to_write, written, ret, fd;

    fd = TEMP_FAILURE_RETRY(open(file, O_WRONLY));
    if (fd < 0) {
        return -errno;
    }

    to_write = strlen(value) + 1;
    written = TEMP_FAILURE_RETRY(write(fd, value, to_write));
    if (written == -1) {
        ret = -errno;
    } else if (written != to_write) {
        /* even though EAGAIN is an errno value that could be set
           by write() in some cases, none of them apply here.  So, this return
           value can be clearly identified when debugging and suggests the
           caller that it may try to call vibrator_on() again */
        ret = -EAGAIN;
    } else {
        ret = 0;
    }

    errno = 0;
    close(fd);

    return ret;
}

static int sendit(unsigned int timeout_ms)
{
    char value[TIMEOUT_STR_LEN]; /* large enough for millions of years */

    snprintf(value, sizeof(value), "%u", timeout_ms);
    return write_value(THE_DEVICE, value);
}

static int vibra_on(vibrator_device_t* vibradev __unused, unsigned int timeout_ms)
{
    /* constant on, up to maximum allowed time */
    return sendit(timeout_ms);
}

static int vibra_off(vibrator_device_t* vibradev __unused)
{
    return sendit(0);
}

static const char LED_DEVICE[] = "/sys/class/leds/vibrator";

static int write_led_file(const char *file, const char *value)
{
    char file_str[50];

    snprintf(file_str, sizeof(file_str), "%s/%s", LED_DEVICE, file);
    return write_value(file_str, value);
}

static bool vibra_led_exists()
{
    char file_str[50];

    snprintf(file_str, sizeof(file_str), "%s/%s", LED_DEVICE, "activate");
    return device_exists(file_str);
}

static int vibra_led_on(vibrator_device_t* vibradev __unused, unsigned int timeout_ms)
{
    int ret;
    char value[TIMEOUT_STR_LEN]; /* large enough for millions of years */

    ret = write_led_file("state", "1");
    if (ret)
        return ret;

    snprintf(value, sizeof(value), "%u\n", timeout_ms);
    ret = write_led_file("duration", value);
    if (ret)
        return ret;

    return write_led_file("activate", "1");
}

static int vibra_led_off(vibrator_device_t* vibradev __unused)
{
    return write_led_file("activate", "0");
}

static int vibra_close(hw_device_t *device)
{
    free(device);
    return 0;
}

static int vibra_open(const hw_module_t* module, const char* id __unused,
                      hw_device_t** device __unused) {
    bool use_led;

    if (vibra_exists()) {
        ALOGD("Vibrator using timed_output");
        use_led = false;
    } else if (vibra_led_exists()) {
        ALOGD("Vibrator using LED trigger");
        use_led = true;
    } else {
        ALOGE("Vibrator device does not exist. Cannot start vibrator");
        return -ENODEV;
    }

    vibrator_device_t *vibradev = calloc(1, sizeof(vibrator_device_t));

    if (!vibradev) {
        ALOGE("Can not allocate memory for the vibrator device");
        return -ENOMEM;
    }

    vibradev->common.tag = HARDWARE_DEVICE_TAG;
    vibradev->common.module = (hw_module_t *) module;
    vibradev->common.version = HARDWARE_DEVICE_API_VERSION(1,0);
    vibradev->common.close = vibra_close;

    if (use_led) {
        vibradev->vibrator_on = vibra_led_on;
        vibradev->vibrator_off = vibra_led_off;
    } else {
        vibradev->vibrator_on = vibra_on;
        vibradev->vibrator_off = vibra_off;
    }

    *device = (hw_device_t *) vibradev;

    return 0;
}

/*===========================================================================*/
/* Default vibrator HW module interface definition                           */
/*===========================================================================*/

static struct hw_module_methods_t vibrator_module_methods = {
    .open = vibra_open,
};

struct hw_module_t HAL_MODULE_INFO_SYM = {
    .tag = HARDWARE_MODULE_TAG,
    .module_api_version = VIBRATOR_API_VERSION,
    .hal_api_version = HARDWARE_HAL_API_VERSION,
    .id = VIBRATOR_HARDWARE_MODULE_ID,
    .name = "Default vibrator HAL",
    .author = "The Android Open Source Project",
    .methods = &vibrator_module_methods,
};

可以看出HAL层的代码有两种控制Vibrator的方式

  • 读写"/sys/class/timed_output/vibrator/enable"
  • 读写"/sys/class/leds/vibrator"下的属性文件

很明显我们选择了后者
这个文件中定义了控制Vibrator的函数,open、close、on、off等,和我们在做Linux驱动时的用户空间程序没什么区别

完成了HAL,接下来就是连接C和JAVA的JNI层了

5: JNI部分

JNI(Java Native Interface),中文是“Java本地接口”。

JNI是Java中一种技术,它存在的意义,是保证本地代码(C/C++代码)能在任何Java虚拟机下工作。简单点说,Java通过JNI接口,能够调用到C/C++代码。

JNI的代码在frameworks/base/services/core/jni/com_android_server_VibratorService.cpp

#define LOG_TAG "VibratorService"

#include <android/hardware/vibrator/1.3/IVibrator.h>
#include <android/hardware/vibrator/BnVibratorCallback.h>
#include <android/hardware/vibrator/IVibrator.h>
#include <binder/IServiceManager.h>

#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include "android_runtime/AndroidRuntime.h"
#include "core_jni_helpers.h"

#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/vibrator.h>

#include <inttypes.h>
#include <stdio.h>

using android::hardware::Return;
using android::hardware::Void;
using android::hardware::vibrator::V1_0::EffectStrength;
using android::hardware::vibrator::V1_0::Status;
using android::hardware::vibrator::V1_1::Effect_1_1;

namespace V1_0 = android::hardware::vibrator::V1_0;
namespace V1_1 = android::hardware::vibrator::V1_1;
namespace V1_2 = android::hardware::vibrator::V1_2;
namespace V1_3 = android::hardware::vibrator::V1_3;
namespace aidl = android::hardware::vibrator;

namespace android {

static jmethodID sMethodIdOnComplete;

static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) ==
                static_cast<uint8_t>(aidl::EffectStrength::LIGHT));
static_assert(static_cast<uint8_t>(V1_0::EffectStrength::MEDIUM) ==
                static_cast<uint8_t>(aidl::EffectStrength::MEDIUM));
static_assert(static_cast<uint8_t>(V1_0::EffectStrength::STRONG) ==
                static_cast<uint8_t>(aidl::EffectStrength::STRONG));

static_assert(static_cast<uint8_t>(V1_3::Effect::CLICK) ==
                static_cast<uint8_t>(aidl::Effect::CLICK));
static_assert(static_cast<uint8_t>(V1_3::Effect::DOUBLE_CLICK) ==
                static_cast<uint8_t>(aidl::Effect::DOUBLE_CLICK));
static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) ==
                static_cast<uint8_t>(aidl::Effect::TICK));
static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) ==
                static_cast<uint8_t>(aidl::Effect::THUD));
static_assert(static_cast<uint8_t>(V1_3::Effect::POP) ==
                static_cast<uint8_t>(aidl::Effect::POP));
static_assert(static_cast<uint8_t>(V1_3::Effect::HEAVY_CLICK) ==
                static_cast<uint8_t>(aidl::Effect::HEAVY_CLICK));
static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_1) ==
                static_cast<uint8_t>(aidl::Effect::RINGTONE_1));
static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_2) ==
                static_cast<uint8_t>(aidl::Effect::RINGTONE_2));
static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) ==
                static_cast<uint8_t>(aidl::Effect::RINGTONE_15));
static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
                static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));

class VibratorCallback {
    public:
        VibratorCallback(JNIEnv *env, jobject vibration) :
        mVibration(MakeGlobalRefOrDie(env, vibration)) {}

        ~VibratorCallback() {
            JNIEnv *env = AndroidRuntime::getJNIEnv();
            env->DeleteGlobalRef(mVibration);
        }

        void onComplete() {
            auto env = AndroidRuntime::getJNIEnv();
            env->CallVoidMethod(mVibration, sMethodIdOnComplete);
        }

    private:
        jobject mVibration;
};

class AidlVibratorCallback : public aidl::BnVibratorCallback {
  public:
    AidlVibratorCallback(JNIEnv *env, jobject vibration) :
    mCb(env, vibration) {}

    binder::Status onComplete() override {
        mCb.onComplete();
        return binder::Status::ok(); // oneway, local call
    }

  private:
    VibratorCallback mCb;
};

static constexpr int NUM_TRIES = 2;

template<class R>
inline R NoneStatus() {
    using ::android::hardware::Status;
    return Status::fromExceptionCode(Status::EX_NONE);
}

template<>
inline binder::Status NoneStatus() {
    using binder::Status;
    return Status::fromExceptionCode(Status::EX_NONE);
}

// Creates a Return<R> with STATUS::EX_NULL_POINTER.
template<class R>
inline R NullptrStatus() {
    using ::android::hardware::Status;
    return Status::fromExceptionCode(Status::EX_NULL_POINTER);
}

template<>
inline binder::Status NullptrStatus() {
    using binder::Status;
    return Status::fromExceptionCode(Status::EX_NULL_POINTER);
}

template <typename I>
sp<I> getService() {
    return I::getService();
}

template <>
sp<aidl::IVibrator> getService() {
    return waitForVintfService<aidl::IVibrator>();
}

template <typename I>
sp<I> tryGetService() {
    return I::tryGetService();
}

template <>
sp<aidl::IVibrator> tryGetService() {
    return checkVintfService<aidl::IVibrator>();
}

template <typename I>
class HalWrapper {
  public:
    static std::unique_ptr<HalWrapper> Create() {
        // Assume that if getService returns a nullptr, HAL is not available on the
        // device.
        auto hal = getService<I>();
        return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr;
    }

    // Helper used to transparently deal with the vibrator HAL becoming unavailable.
    template<class R, class... Args0, class... Args1>
    R call(R (I::* fn)(Args0...), Args1&&... args1) {
        // Return<R> doesn't have a default constructor, so make a Return<R> with
        // STATUS::EX_NONE.
        R ret{NoneStatus<R>()};

        // Note that ret is guaranteed to be changed after this loop.
        for (int i = 0; i < NUM_TRIES; ++i) {
            ret = (mHal == nullptr) ? NullptrStatus<R>()
                    : (*mHal.*fn)(std::forward<Args1>(args1)...);

            if (ret.isOk()) {
                break;
            }

            ALOGE("Failed to issue command to vibrator HAL. Retrying.");

            // Restoring connection to the HAL.
            mHal = tryGetService<I>();
        }
        return ret;
    }

  private:
    HalWrapper(sp<I> &&hal) : mHal(std::move(hal)) {}

  private:
    sp<I> mHal;
};

template <typename I>
static auto getHal() {
    static auto sHalWrapper = HalWrapper<I>::Create();
    return sHalWrapper.get();
}

template<class R, class I, class... Args0, class... Args1>
R halCall(R (I::* fn)(Args0...), Args1&&... args1) {
    auto hal = getHal<I>();
    return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>();
}

template<class R>
bool isValidEffect(jlong effect) {
    if (effect < 0) {
        return false;
    }
    R val = static_cast<R>(effect);
    auto iter = hardware::hidl_enum_range<R>();
    return val >= *iter.begin() && val <= *std::prev(iter.end());
}

static void vibratorInit(JNIEnv *env, jclass clazz)
{
    if (auto hal = getHal<aidl::IVibrator>()) {
        // IBinder::pingBinder isn't accessible as a pointer function
        // but getCapabilities can serve the same purpose
        int32_t cap;
        hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk();
    } else {
        halCall(&V1_0::IVibrator::ping).isOk();
    }
}

static jboolean vibratorExists(JNIEnv* /* env */, jclass /* clazz */)
{
    bool ok;

    if (auto hal = getHal<aidl::IVibrator>()) {
        // IBinder::pingBinder isn't accessible as a pointer function
        // but getCapabilities can serve the same purpose
        int32_t cap;
        ok = hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk();
    } else {
        ok = halCall(&V1_0::IVibrator::ping).isOk();
    }
    return ok ? JNI_TRUE : JNI_FALSE;
}

static void vibratorOn(JNIEnv* /* env */, jclass /* clazz */, jlong timeout_ms)
{
    if (auto hal = getHal<aidl::IVibrator>()) {
        auto status = hal->call(&aidl::IVibrator::on, timeout_ms, nullptr);
        if (!status.isOk()) {
            ALOGE("vibratorOn command failed: %s", status.toString8().string());
        }
    } else {
        Status retStatus = halCall(&V1_0::IVibrator::on, timeout_ms).withDefault(Status::UNKNOWN_ERROR);
        if (retStatus != Status::OK) {
            ALOGE("vibratorOn command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));
        }
    }
}

static void vibratorOff(JNIEnv* /* env */, jclass /* clazz */)
{
    if (auto hal = getHal<aidl::IVibrator>()) {
        auto status = hal->call(&aidl::IVibrator::off);
        if (!status.isOk()) {
            ALOGE("vibratorOff command failed: %s", status.toString8().string());
        }
    } else {
        Status retStatus = halCall(&V1_0::IVibrator::off).withDefault(Status::UNKNOWN_ERROR);
        if (retStatus != Status::OK) {
            ALOGE("vibratorOff command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));
        }
    }
}

static jlong vibratorSupportsAmplitudeControl(JNIEnv*, jclass) {
    if (auto hal = getHal<aidl::IVibrator>()) {
        int32_t cap = 0;
        if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) {
            return false;
        }
        return (cap & aidl::IVibrator::CAP_AMPLITUDE_CONTROL) > 0;
    } else {
        return halCall(&V1_0::IVibrator::supportsAmplitudeControl).withDefault(false);
    }
}

static void vibratorSetAmplitude(JNIEnv*, jclass, jint amplitude) {
    if (auto hal = getHal<aidl::IVibrator>()) {
        auto status = hal->call(&aidl::IVibrator::IVibrator::setAmplitude, static_cast<float>(amplitude) / UINT8_MAX);
        if (!status.isOk()) {
            ALOGE("Failed to set vibrator amplitude: %s", status.toString8().string());
        }
    } else {
        Status status = halCall(&V1_0::IVibrator::setAmplitude, static_cast<uint32_t>(amplitude))
            .withDefault(Status::UNKNOWN_ERROR);
        if (status != Status::OK) {
            ALOGE("Failed to set vibrator amplitude (%" PRIu32 ").",
                  static_cast<uint32_t>(status));
        }
    }
}

static jboolean vibratorSupportsExternalControl(JNIEnv*, jclass) {
    if (auto hal = getHal<aidl::IVibrator>()) {
        int32_t cap = 0;
        if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) {
            return false;
        }
        return (cap & aidl::IVibrator::CAP_EXTERNAL_CONTROL) > 0;
    } else {
        return halCall(&V1_3::IVibrator::supportsExternalControl).withDefault(false);
    }
}

static void vibratorSetExternalControl(JNIEnv*, jclass, jboolean enabled) {
    if (auto hal = getHal<aidl::IVibrator>()) {
        auto status = hal->call(&aidl::IVibrator::IVibrator::setExternalControl, enabled);
        if (!status.isOk()) {
            ALOGE("Failed to set vibrator external control: %s", status.toString8().string());
        }
    } else {
        Status status = halCall(&V1_3::IVibrator::setExternalControl, static_cast<uint32_t>(enabled))
            .withDefault(Status::UNKNOWN_ERROR);
        if (status != Status::OK) {
            ALOGE("Failed to set vibrator external control (%" PRIu32 ").",
                static_cast<uint32_t>(status));
        }
    }
}

static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong strength,
                                   jobject vibration) {
    if (auto hal = getHal<aidl::IVibrator>()) {
        int32_t lengthMs;
        sp<AidlVibratorCallback> effectCallback = new AidlVibratorCallback(env, vibration);
        aidl::Effect effectType(static_cast<aidl::Effect>(effect));
        aidl::EffectStrength effectStrength(static_cast<aidl::EffectStrength>(strength));

        auto status = hal->call(&aidl::IVibrator::perform, effectType, effectStrength, effectCallback, &lengthMs);
        if (!status.isOk()) {
            if (status.exceptionCode() != binder::Status::EX_UNSUPPORTED_OPERATION) {
                ALOGE("Failed to perform haptic effect: effect=%" PRId64 ", strength=%" PRId32
                        ": %s", static_cast<int64_t>(effect), static_cast<int32_t>(strength), status.toString8().string());
            }
            return -1;
        }
        return lengthMs;
    } else {
        Status status;
        uint32_t lengthMs;
        auto callback = [&status, &lengthMs](Status retStatus, uint32_t retLengthMs) {
            status = retStatus;
            lengthMs = retLengthMs;
        };
        EffectStrength effectStrength(static_cast<EffectStrength>(strength));

        Return<void> ret;
        if (isValidEffect<V1_0::Effect>(effect)) {
            ret = halCall(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(effect),
                    effectStrength, callback);
        } else if (isValidEffect<Effect_1_1>(effect)) {
            ret = halCall(&V1_1::IVibrator::perform_1_1, static_cast<Effect_1_1>(effect),
                            effectStrength, callback);
        } else if (isValidEffect<V1_2::Effect>(effect)) {
            ret = halCall(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(effect),
                            effectStrength, callback);
        } else if (isValidEffect<V1_3::Effect>(effect)) {
            ret = halCall(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(effect),
                            effectStrength, callback);
        } else {
            ALOGW("Unable to perform haptic effect, invalid effect ID (%" PRId32 ")",
                    static_cast<int32_t>(effect));
            return -1;
        }

        if (!ret.isOk()) {
            ALOGW("Failed to perform effect (%" PRId32 ")", static_cast<int32_t>(effect));
            return -1;
        }

        if (status == Status::OK) {
            return lengthMs;
        } else if (status != Status::UNSUPPORTED_OPERATION) {
            // Don't warn on UNSUPPORTED_OPERATION, that's a normal event and just means the motor
            // doesn't have a pre-defined waveform to perform for it, so we should just give the
            // opportunity to fall back to the framework waveforms.
            ALOGE("Failed to perform haptic effect: effect=%" PRId64 ", strength=%" PRId32
                    ", error=%" PRIu32 ").", static_cast<int64_t>(effect),
                    static_cast<int32_t>(strength), static_cast<uint32_t>(status));
        }
    }

    return -1;
}

static jlong vibratorGetCapabilities(JNIEnv*, jclass) {
    if (auto hal = getHal<aidl::IVibrator>()) {
        int32_t cap = 0;
        if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) {
            return 0;
        }
        return cap;
    }

    return 0;
}

static void vibratorAlwaysOnEnable(JNIEnv* env, jclass, jlong id, jlong effect, jlong strength) {
    auto status = halCall(&aidl::IVibrator::alwaysOnEnable, id,
            static_cast<aidl::Effect>(effect), static_cast<aidl::EffectStrength>(strength));
    if (!status.isOk()) {
        ALOGE("vibratortAlwaysOnEnable command failed (%s).", status.toString8().string());
    }
}

static void vibratorAlwaysOnDisable(JNIEnv* env, jclass, jlong id) {
    auto status = halCall(&aidl::IVibrator::alwaysOnDisable, id);
    if (!status.isOk()) {
        ALOGE("vibratorAlwaysOnDisable command failed (%s).", status.toString8().string());
    }
}

static const JNINativeMethod method_table[] = {
    { "vibratorExists", "()Z", (void*)vibratorExists },
    { "vibratorInit", "()V", (void*)vibratorInit },
    { "vibratorOn", "(J)V", (void*)vibratorOn },
    { "vibratorOff", "()V", (void*)vibratorOff },
    { "vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},
    { "vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},
    { "vibratorPerformEffect", "(JJLcom/android/server/VibratorService$Vibration;)J",
        (void*)vibratorPerformEffect},
    { "vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl},
    { "vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl},
    { "vibratorGetCapabilities", "()J", (void*)vibratorGetCapabilities},
    { "vibratorAlwaysOnEnable", "(JJJ)V", (void*)vibratorAlwaysOnEnable},
    { "vibratorAlwaysOnDisable", "(J)V", (void*)vibratorAlwaysOnDisable},
};

int register_android_server_VibratorService(JNIEnv *env)
{
    sMethodIdOnComplete = GetMethodIDOrDie(env,
            FindClassOrDie(env, "com/android/server/VibratorService$Vibration"),
            "onComplete", "()V");
    return jniRegisterNativeMethods(env, "com/android/server/VibratorService",
            method_table, NELEM(method_table));
}

};

这个代码干了以下几件事

  • 对HAL的接口再进行了一些封装
  • 使用JNINativeMethod列表将Java层和HAL层的代码联系起来

关于JNINativeMethod
结构体

typedef struct {  
    const char* name;  
    const char* signature;  
    void* fnPtr;  
} JNINativeMethod;

第一个变量name是Java中函数的名字。
第二个变量signature,用字符串是描述了Java中函数的参数和返回值
第三个变量fnPtr是函数指针,指向native函数。前面都要接 (void *)
第一个变量与第三个变量是对应的,一个是java层方法名,对应着第三个参数的native方法名字

参数和返回值还有一些简写对应方式,这里就不做介绍了

JNI层提供JAVA接口以后就是Application Framework的任务了

6: Application Framework部分

应用层操作马达,是通过马达服务进行操作的。而马达服务是通过aidl实现的,aidl是Android进程间的通信方式
主要涉及的文件有

frameworks/base/core/java/android/os/SystemVibrator.java
frameworks/base/core/java/android/os/Vibrator.java
frameworks/base/services/core/java/com/android/server/VibratorService.java
frameworks/base/core/java/android/os/IVibratorService.aidl
frameworks/base/services/java/com/android/server/SystemServer.java

这里的代码较多,就不贴代码了,介绍一下各个文件的作用以及大致的工作流程

  • SystemServer.java
    它是系统服务,作用是启动、管理系统服务,包括“马达服务、Wifi服务、Activity管理服务”等等。
    SystemServer是通过Zygote启动的,而Zygote又是在init中启动的,init则是kernel加载完毕之后启动的第一个进程。在这里,我们只需要知道“SystemServer是用来启动/管理马达服务即可。
  • IVibratorService.aidl
    它是马达服务对应的aidl配置文件。我们在aidl中定义了其它进程可以访问的外部接口;然后再通过VibratorService.java实现这些接口。
  • VibratorService.java
    它是马达服务对应的aidl接口的实现程序。它实现IVibratorService.aidl的接口,从而实现马达服务;它的函数接口,是通过调用JNI层对应的马达控制函数来实现的。
  • Vibrator.java
    它是马达服务开放给应用层的调用类。理论上讲,我们完全可以通过aidl直接调用马达服务,而不需要Vibrator.java类。但是!既然它存在,就肯定有它的理由。事实的确如此,Google之所以这么做。有以下几个原因:
    第一,提供统一而且方便的服务调用方式。这里的“统一”,是指和所有其它的系统服务一样,我们调用服务时,需先通过getSystemService()获取服务,然后再调用服务的函数接口。这里的“方便”,是指若我们直接通过aidl调用,操作比较繁琐(若你用过aidl就会知道,需要先实现ServiceConnection接口以获取IBinder对象,然后再通过IBinder对象调用aidl的接口); 而Vibrator.java封装之后的接口,将许多细节都隐藏了,非常便于应用者调用!
    第二,基于安全的考虑。Vibrator.java封装隐藏了许多细节,而这些都是应用开发者不必要知道的。
    第三,Vibrator是抽象类。它便于我们支持不同类型的马达:包括“将马达直接映射到文件”以及“将马达注册到输入子系统”中。
  • SystemVibrator.java
    它是Vibrator.java的子类,实现了马达的服务接口。

以上就是这些文件的作用,当然只看这些还是远远不够的,还是得“Read The Fucking Source Code”
这些只是总体介绍总结的作用

7: 应用部分

马达权限

调用马达服务,需要在manifest中添加相应的权限

<!-- 震动马达权限 -->
<uses-permission android:name="android.permission.VIBRATE"/>

我自己还没有写过JAVA APP,这是找的别人的代码来分析的

package com.test;

import android.app.Activity;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ToggleButton;
import android.util.Log;

public class VibratorTest extends Activity {
    private static final String TAG = "skywang-->VibratorTest";

    private Vibrator mVibrator;
    private Button mOnce = null;
    private ToggleButton mEndless = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // 获取震动马达服务
        mVibrator= (Vibrator) getSystemService(VIBRATOR_SERVICE);

        mOnce = (Button) findViewById(R.id.vib_once);
        mOnce.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                //震动指定时间
                mVibrator.vibrate(100);
            }
        });

        mEndless = (ToggleButton) findViewById(R.id.vib_endless);
        mEndless.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mEndless.isChecked()) {
                    //等待100ms后,按数组所给数值间隔震动;其后为重复次数,-1为不重复,0一直震动
                    mVibrator.vibrate(new long[]{100,20,100,40,100,60}, 0);
                } else {
                    // 取消震动
                    mVibrator.cancel();
                }
            }
        });

    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mVibrator != null)
            mVibrator= null;
    }
}

马达有两种控制模式

  • mVibrator.vibrate(100)直接指定震动时间
  • mVibrator.vibrate(new long[]{100,20,100,40,100,60}, 0)按照传入的数组做开启和关闭

总结

以上就是“核心科技”Vibrator从硬件到APP的全部内容,我对Android部分的理解还远远不够了,仅是管中窥豹而已,要理解整个体系还需要下很多功夫。
Android这一整套架构其实还是很强大的,其实我们可以看到,从HAL开始的所有代码甚至是可以不修改的,AOSP已经设计好了整个架构,常用的模块已经写好了HAL、JNI、Application Framework层所有的东西,如果你想做自己的手机,要做的就是内核驱动部分,按照HAL提供的框架来实现底层接口,可以说已经大大减少了工作量,只有当有特殊的要求时才会去修改那些,好像这样子想起来做一个手机并没有多难嘛,设计PCB,然后照着示例的内核把驱动实现以下就可以,如果愿意可以自己定制UI,如果懒得定制的话直接使用原生UI。但是当前的市场却不是这样的,慢慢的小公司都走向了衰落,只有大的公司才能生存下去,魅族、锤子这些小众的品牌也曾有很不错的软硬件体验但是却越来越没有市场,这些倒是令人想不到的,可能做手机的难点并不在系统而在于供应链、市场等等技术以外的东西吧。

  • 17
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值