Tiny4412——Android灯光系统
CSDN仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。
本博文对应地址: https://hceng.cn/2019/05/09/Tiny4412——Android灯光系统/#more
继续学习Android驱动,Android灯光系统。
1.思路分析
前面对3.2 Android硬件访问服务进行了详细的分析,知道了Android大致可以分为四层:APP
、JNI
、HAL
、Driver
。
回顾下前面写的点灯程序过程:APP
调用JNI
提供的本地接口方法;JNI
除了向上提供接口,还要调用HAL
提供的硬件操作函数;HAL
操作Linux用户态的LED设备节点;Driver
操作硬件,提供设备节点。
在编写JNI
时,我们自己创建的com_android_server_LedService.cpp
,这样就会导致提供的接口,只有我们才知道,在APP
不通用。因此,更好的做法是使用Android提供的灯光系统,即使用自带的com_android_server_lights_LightsService.cpp
,这样LED的接口就可以通用,APP
也就可以实现通用。相应的,我们要做的就是编写HAL
层代码,提供JNI
所需要的函数,以及编写Driver
提供给HAL
。
首先分析JNI
层的frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp
。
找到JNINativeMethod
,里面建立Java本地方法与C函数名的映射关系,可以看到有三个函数:
{% codeblock lang:cpp %}
static JNINativeMethod method_table[] = {
{ “init_native”, “()J”, (void*)init_native },
{ “finalize_native”, “(J)V”, (void*)finalize_native },
{ “setLight_native”, “(JIIIIII)V”, (void*)setLight_native },
};
{% endcodeblock %}
先看init_native()
:
a.使用hw_get_module()
,获得hw_module_t
结构体;
b.根据传入的不同name
,使用module->methods->open()
,获得不同的light_device_t
结构体;
{% codeblock lang:cpp %}
static light_device_t* get_device(hw_module_t* module, char const* name)
{
int err;
hw_device_t* device;
err = module->methods->open(module, name, &device);
if (err == 0) {
return (light_device_t*)device;
} else {
return NULL;
}
}
static jlong init_native(JNIEnv env, jobject clazz)
{
int err;
hw_module_t module;
Devices* devices;
devices = (Devices*)malloc(sizeof(Devices));
err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
if (err == 0) {
devices->lights[LIGHT_INDEX_BACKLIGHT]
= get_device(module, LIGHT_ID_BACKLIGHT);
devices->lights[LIGHT_INDEX_KEYBOARD]
= get_device(module, LIGHT_ID_KEYBOARD);
devices->lights[LIGHT_INDEX_BUTTONS]
= get_device(module, LIGHT_ID_BUTTONS);
devices->lights[LIGHT_INDEX_BATTERY]
= get_device(module, LIGHT_ID_BATTERY);
devices->lights[LIGHT_INDEX_NOTIFICATIONS]
= get_device(module, LIGHT_ID_NOTIFICATIONS);
devices->lights[LIGHT_INDEX_ATTENTION]
= get_device(module, LIGHT_ID_ATTENTION);
devices->lights[LIGHT_INDEX_BLUETOOTH]
= get_device(module, LIGHT_ID_BLUETOOTH);
devices->lights[LIGHT_INDEX_WIFI]
= get_device(module, LIGHT_ID_WIFI);
} else {
memset(devices, 0, sizeof(Devices));
}
return (jlong)devices;
}
{% endcodeblock %}
再来看setLight_native()
:
主要就是根据APP
传入的参数,调用前面light_device_t
结构体里的set_light()
。
{% codeblock lang:cpp %}
static void setLight_native(JNIEnv env, jobject clazz, jlong ptr,
jint light, jint colorARGB, jint flashMode, jint onMS, jint offMS, jint brightnessMode)
{
Devices devices = (Devices*)ptr;
light_state_t state;
if (light < 0 || light >= LIGHT_COUNT || devices->lights[light] == NULL) {
return ;
}
memset(&state, 0, sizeof(light_state_t));
state.color = colorARGB;
state.flashMode = flashMode;
state.flashOnMS = onMS;
state.flashOffMS = offMS;
state.brightnessMode = brightnessMode;
{
ALOGD_IF_SLOW(50, "Excessive delay setting light");
devices->lights[light]->set_light(devices->lights[light], &state);
}
}
{% endcodeblock %}
最后finalize_native()
做一些清理操作。
理清楚了JNI
层的操作,对应的HAL
层需要提供什么,也基本清晰了:
a.实现hw_module_t
结构体;
b.实现open()
,根据传入的name
,返回不同的light_device_t
结构体;
c.针对功能需求,实现对应light_device_t
结构体里的set_light()
:set_light_battery()
、set_light_notifications()
、set_light_backlight()
;
根据HAL
层的需求,Driver
层要做的就是:
a.对于set_light_battery()
、set_light_notifications()
,实现控制R、G、B三个LED的亮、灭、闪烁;
b.对于set_light_backlight()
,设置PWM
实现亮度控制;
2.编写驱动
驱动需要两个,一个是用于电源灯/通知灯的,一个是用于LCD背光的。
用于电源灯/通知灯的,需要实现控制LED亮灭、LED灯定时闪烁;用于LCD背光的需要PWM
操作。
2.1 LED驱动
先说用于电源灯/通知灯的驱动,之前在Android访问硬件的方法:3.1.1编写LED驱动写过一次,但当时的驱动只支持LED的亮灭,不支持定时闪烁。我们完全可以添加个ioctl()
操作,利用定时器实现闪烁功能,但没有必要,可以直接利用Linux的LED子系统。关于LED子系统,在以前写AM437x——LED驱动已经详细介绍过了,这里直接采用LED子系统的方式编写LED驱动程序。
-
编写步骤:
a.分配led_classde
结构体;
b.设置led_classdev
结构体:
max_brightness = LED_FULL;
brightness_set = tiny4412_brightness_set;
flags = LED_CORE_SUSPENDRESUME;
brightness = LED_OFF;
name = led_gpios[i].name;
default_trigger = "none";
c.硬件相关初始化(设置GPIO,关闭LED等);
d.使用led_classdev_register()
注册led_classde
;
e.实现tiny4412_brightness_set()
(GPIO不支持亮度设置,这里仅支持亮灭); -
参考代码:
{% codeblock lang:c [tiny4412_leds.c] https://github.com/hceng/learn/blob/master/android/02_灯光系统/driver/leds/tiny4412_leds.c %}
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/leds.h>
#include <plat/gpio-cfg.h>
struct led_desc {
int gpio;
char *name;
};
static struct led_desc led_gpios[] = {
{EXYNOS4212_GPM4(0), “led1”},
{EXYNOS4212_GPM4(1), “led2”},
{EXYNOS4212_GPM4(2), “led3”},
{EXYNOS4212_GPM4(3), “led4”},
};
static int pin_num = sizeof(led_gpios)/sizeof(led_gpios[0]);
struct tiny4412_led_data {
struct led_classdev led_dev;
int gpio;
struct mutex lock;
};
static struct tiny4412_led_data *led_devs;
void tiny4412_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct tiny4412_led_data *dev = (struct tiny4412_led_data *)led_cdev;
printk(KERN_DEBUG "Passed %s %d \n",__FUNCTION__,__LINE__);
led_cdev->brightness = brightness;
if (brightness != LED_OFF)
gpio_set_value(dev->gpio, 0);
else
gpio_set_value(dev->gpio, 1);
}
static int leds_drv_init(void)
{
int i, ret;
printk(KERN_DEBUG "Passed %s %d \n",__FUNCTION__,__LINE__);
//1. alloc led_classdev
led_devs = kzalloc(sizeof(struct tiny4412_led_data) * pin_num, GFP_KERNEL);
if (led_devs == NULL) {
printk(KERN_ERR "No memory for device\n");
return -ENOMEM;
}
for (i = 0; i < pin_num; i++)
{
mutex_init(&led_devs[i].lock);
mutex_lock(&led_devs[i].lock);
//2. set led_classdev
led_devs[i].led_dev.max_brightness = LED_FULL; //255
led_devs[i].led_dev.brightness_set = tiny4412_brightness_set;
led_devs[i].led_dev.flags = LED_CORE_SUSPENDRESUME;
led_devs[i].led_dev.brightness = LED_OFF;
led_devs[i].led_dev.name = led_gpios[i].name;
led_devs[i].led_dev.default_trigger = "none";
led_devs[i].gpio = led_gpios[i].gpio;
//3. Hardware setup(Default led off)
s3c_gpio_cfgpin(led_gpios[i].gpio, S3C_GPIO_OUTPUT);
if (led_devs[i].led_dev.brightness == LED_OFF)
gpio_set_value(led_gpios[i].gpio, 1);
else
gpio_set_value(led_gpios[i].gpio, 0);
//4. led_classdev_register
ret = led_classdev_register(NULL, &led_devs[i].led_dev);
if (ret) {
i--;
while (i >= 0) {
led_classdev_unregister(&led_devs[i].led_dev);
i--;
}
mutex_unlock(&led_devs[i].lock);
kfree(led_devs);
return -EIO;
}
mutex_unlock(&led_devs[i].lock);
}
return 0;
}
static void leds_drv_exit(void)
{
int i;
printk(KERN_DEBUG "Passed %s %d \n",__FUNCTION__,__LINE__);
for (i = 0; i < pin_num; i++)
{
mutex_lock(&led_devs[i].lock);
led_classdev_unregister(&led_devs[i].led_dev);
mutex_unlock(&led_devs[i].lock);
}
kfree(led_devs);
}
module_init(leds_drv_init);
module_exit(leds_drv_exit);
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“hceng [email protected]”);
MODULE_DESCRIPTION(“Tiny4412 leds driver.”);
MODULE_VERSION(“v1.0”);
{% endcodeblock %}
- 编译进内核:
首先让内核支持LED子系统,且支持Timer
触发,在内核根目录执行make menuconfig
,勾选上下面选项:
[*] LED Support --->
--- LED Support
[*] LED Class Support
……
[*] LED Trigger support
*** LED Triggers ***
<*> LED Timer Trigger
接着将tiny4412_leds.c
放在drivers/leds/
里,并在该路径下的Makefile
添加:
obj-y += tiny4412_leds.o
最后执行make zImage
重新编译内核,并重新烧写内核。
- 应用层测试:
进入系统,可以看到已经生成了LED
的节点:
aaa@qq.com:/ # ls /sys/class/leds/ -l
lrwxrwxrwx root root 2019-05-10 09:58 led1 -> ../../devices/virtual/leds/led1
lrwxrwxrwx root root 2019-05-10 09:58 led2 -> ../../devices/virtual/leds/led2
lrwxrwxrwx root root 2019-05-10 09:58 led3 -> ../../devices/virtual/leds/led3
lrwxrwxrwx root root 2019-05-10 09:58 led4 -> ../../devices/virtual/leds/led4
lrwxrwxrwx root root 2019-05-10 10:05 mmc1:: -> ../../devices/platform/s3c-sdhci.2/leds/mmc1::
lrwxrwxrwx root root 2019-05-10 10:05 mmc2:: -> ../../devices/platform/s3c-sdhci.3/leds/mmc2::
执行su
切换到root
用户,再执如下命令即可控制对应LED亮灭:
aaa@qq.com:/ # echo 255 > /sys/class/leds/led1/brightness
aaa@qq.com:/ # echo 0 > /sys/class/leds/led1/brightness
执行以下命令,修改触发模式为timer
,即可实现LED闪烁:
aaa@qq.com:/ # echo timer > /sys/class/leds/led1/trigger
此时/sys/class/leds/led1/
会生成delay_off
和delay_on
,可修改这两个值,控制闪烁亮灭时间。
2.2 backligth驱动
在之前的博客Exynos4412——LCD之backligth里,已经对Tiny4412的背光“一线触摸”有了研究,它采用的并不是PWM控制背光调试,而是背光部分交给了一个屏幕上的MCU处理,Tiny4412与MCU通过一个GPIO进行数据传输。这个传输协议,友善之臂并没有公开,且只适用它们家的屏幕,也没什么研究价值,因此我们只需知道如何使用该驱动即可。
在友善提供的Android源码android-5.0.2/vendor/friendly-arm/tiny4412/proprietary/
路径下可以看到如下文件:
fa_codec_ctrl lights.tiny4412.so sensors.tiny4412.so
其中就有控制亮度lights.tiny4412.so
,这就是友善提供的HAL
,不开源的部分。
我们可以尝试以下思路进行分析:
a.lights.tiny4412.so
肯定会操作某个Linux设备节点,从而控制亮度,因此使用下面的命令,找到是操作的哪个节点:
{% codeblock lang:shell %}
aaa@qq.com:/work$ hexdump -C lights.tiny4412.so | grep “dev” -A2
00000a50 3a 25 64 0a 00 2f 64 65 76 2f 62 61 63 6b 6c 69 |:%d…/dev/backli|
00000a60 67 68 74 2d 31 77 69 72 65 00 25 64 0a 00 6c 69 |ght-1wire.%d…li|
00000a70 67 68 74 73 20 4d 6f 64 75 6c 65 00 47 6f 6f 67 |ghts Module.Goog
{% endcodeblock %}
可以看出是操作的/dev/backlight-1wire
节点。
b.接着去Tiny4412上验证下是否有该节点:
aaa@qq.com:/ # ls /dev/backlight-1wire
/dev/backlight-1wire
确实有该节点。
c.接着去内核的驱动目录,搜索节点名字:
{% codeblock lang:shell %}
aaa@qq.com:/work/linux_source/linux-3.0.86/drivers$ grep “backlight-1wire” -nr
Binary file built-in.o matches
Binary file input/built-in.o matches
input/touchscreen/tiny4412_1wire_host.c:84:#define BACKLIGHT_DEVICE_NAME “backlight-1wire”
Binary file input/touchscreen/built-in.o matches
Binary file input/touchscreen/tiny4412_1wire_host.o matches
{% endcodeblock %}
可以看到驱动的源码在drivers/input/touchscreen/tiny4412_1wire_host.c
。
d.最后打开该驱动源码,看是否能直接使用,运气好的是,tiny4412_1wire_host.c
里面有个bl_write()
操作函数,传入参数就可以控制亮度。即分析得到:向/dev/backlight-1wire
,write()
值即可实现背光控制。
另外,从if (v > 127) v = 127;
可知,传入的值范围为0~127。
后面我们写HAL
层代码时,就可以直接写操作/dev/backlight-1wire
控制亮度,该部分驱动无需再写。
3.编写HAL
编写完驱动后,得到了/sys/class/leds/led*
和/dev/backlight-1wire
节点,接下来就是编写HAL
代码操作这两个节点,同时向JNI
提供接口。
3.1编写步骤
编写步骤如下:
a.创建一个名为HMI
(HAL_MODULE_INFO_SYM
)的hw_module_t
结构体,该结构体有一个hw_module_methods_t
结构体成员;
b.hw_module_methods_t
结构体里创建一个open()
函数成员;
c.实现open()
函数,分配一个light_device_t
结构体,根据传入的名字不同,设置不同的操作函数作为light_device_t
的成员,最后返回该结构体;
d.操作设备节点,实现需要提供的不同操作函数;
3.2参考代码及分析
{% codeblock lang:c [lights.c] https://github.com/hceng/learn/blob/master/android/02_灯光系统/hal/lights.c %}
#define LOG_NDEBUG 0
#define LOG_TAG “lights”
#include <cutils/log.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <hardware/lights.h>
char constconst RED_LED_FILE = “/sys/class/leds/led1/brightness”;
char constconst GREEN_LED_FILE = “/sys/class/leds/led2/brightness”;
char constconst BLUE_LED_FILE = “/sys/class/leds/led3/brightness”;
char constconst RED_LED_FILE_TRIGGER = “/sys/class/leds/led1/trigger”;
char constconst GREEN_LED_FILE_TRIGGER = “/sys/class/leds/led2/trigger”;
char constconst BLUE_LED_FILE_TRIGGER = “/sys/class/leds/led3/trigger”;
char constconst RED_LED_FILE_DELAYON = “/sys/class/leds/led1/delay_on”;
char constconst GREEN_LED_FILE_DELAYON = “/sys/class/leds/led2/delay_on”;
char constconst BLUE_LED_FILE_DELAYON = “/sys/class/leds/led3/delay_on”;
char constconst RED_LED_FILE_DELAYOFF = “/sys/class/leds/led1/delay_off”;
char constconst GREEN_LED_FILE_DELAYOFF= “/sys/class/leds/led2/delay_off”;
char constconst BLUE_LED_FILE_DELAYOFF = “/sys/class/leds/led3/delay_off”;
char const*const LCD_BACKLIGHT_FILE = “/dev/backlight-1wire”;
/* Synchronization primities */
static pthread_once_t g_init = PTHREAD_ONCE_INIT;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
/* LED state machine */
static struct light_state_t g_notification;
static struct light_state_t g_battery;
/* Write node function */
static int write_int (const char *path, int value)
{
int fd;
static int already_warned = 0;
fd = open(path, O_RDWR);
if (fd < 0) {
if (already_warned == 0) {
ALOGE(“write_int failed to open %s\n”, path);
already_warned = 1;
}
return -errno;
}
char buffer[20];
int bytes = snprintf(buffer, sizeof(buffer), “%d\n”, value);
int written = write (fd, buffer, bytes);
close(fd);
return written == -1 ? -errno : 0;
}
static int write_string (const char *path, const char *value)
{
int fd;
static int already_warned = 0;
fd = open(path, O_RDWR);
if (fd < 0) {
if (already_warned == 0) {
ALOGE(“write_string failed to open %s\n”, path);
already_warned = 1;
}
return -errno;
}
char buffer[20];
int bytes = snprintf(buffer, sizeof(buffer), “%s\n”, value);
int written = write (fd, buffer, bytes);
close(fd);
return written == -1 ? -errno : 0;
}
/* Color tools /
static int is_lit (struct light_state_t const state)
{
return state->color & 0x00ffffff;
}
static int rgb_to_brightness (struct light_state_t const* state)
{
int color = state->color & 0x00ffffff;
return ((77*((color>>16)&0x00ff))
+ (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
}
/* The actual lights controlling section */
static int set_light_backlight (struct light_device_t *dev, struct light_state_t const *state)
{
int brightness = rgb_to_brightness(state);
ALOGV("%s brightness=%d color=0x%08x", func,brightness,state->color);
pthread_mutex_lock(&g_lock);
//brightness range: 0-255, but LCD_BACKLIGHT_FILE range:0-127
write_int (LCD_BACKLIGHT_FILE, brightness/2);
pthread_mutex_unlock(&g_lock);
return 0;
}
static void set_shared_light_locked (struct light_device_t *dev, struct light_state_t *state)
{
int r, g, b;
int delayOn,delayOff;
r = (state->color >> 16) & 0xFF;
g = (state->color >> 8) & 0xFF;
b = (state->color) & 0xFF;
delayOn = state->flashOnMS;
delayOff = state->flashOffMS;
if (state->flashMode != LIGHT_FLASH_NONE) {
write_string (RED_LED_FILE_TRIGGER, “timer”);
write_string (GREEN_LED_FILE_TRIGGER, “timer”);
write_string (BLUE_LED_FILE_TRIGGER, “timer”);
write_int (RED_LED_FILE_DELAYON, delayOn);
write_int (GREEN_LED_FILE_DELAYON, delayOn);
write_int (BLUE_LED_FILE_DELAYON, delayOn);
write_int (RED_LED_FILE_DELAYOFF, delayOff);
write_int (GREEN_LED_FILE_DELAYOFF, delayOff);
write_int (BLUE_LED_FILE_DELAYOFF, delayOff);
} else {
write_string (RED_LED_FILE_TRIGGER, “none”);
write_string (GREEN_LED_FILE_TRIGGER, “none”);
write_string (BLUE_LED_FILE_TRIGGER, “none”);
}
write_int (RED_LED_FILE, r);
write_int (GREEN_LED_FILE, g);
write_int (BLUE_LED_FILE, b);
}
static void handle_shared_battery_locked (struct light_device_t *dev)
{
if (is_lit (&g_notification))
set_shared_light_locked (dev, &g_notification);
else
set_shared_light_locked (dev, &g_battery);
}
static int set_light_battery (struct light_device_t dev, struct light_state_t const state)
{
ALOGV("%s flashMode=%d onMS = %d offMS = %d color=0x%08x",
func,state->flashMode,state->flashOnMS,state->flashOffMS,state->color);
pthread_mutex_lock (&g_lock);
g_battery = *state;
handle_shared_battery_locked(dev);
pthread_mutex_unlock (&g_lock);
return 0;
}
static int set_light_notifications (struct light_device_t dev, struct light_state_t const state)
{
ALOGV("%s flashMode=%d onMS = %d offMS = %d color=0x%08x",
func,state->flashMode,state->flashOnMS,state->flashOffMS,state->color);
pthread_mutex_lock (&g_lock);
g_notification = *state;
handle_shared_battery_locked(dev);
pthread_mutex_unlock (&g_lock);
return 0;
}
void init_globals ()
{
pthread_mutex_init (&g_lock, NULL); //init the mutex
}
static int close_lights (struct light_device_t *dev)
{
if (dev)
free(dev);
return 0;
}
// hardware/libhardware/include/hardware/lights.h
static int open_lights (const struct hw_module_t* module, char const* name,
struct hw_device_t** device)
{
int (set_light)(struct light_device_t dev,
struct light_state_t const *state);
if (0 == strcmp(LIGHT_ID_BACKLIGHT, name))
set_light = set_light_backlight;
else if (0 == strcmp(LIGHT_ID_BATTERY, name))
set_light = set_light_battery;
else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
set_light = set_light_notifications;
else
return -EINVAL;
pthread_once(&g_init, init_globals);
struct light_device_t *dev = malloc(sizeof(struct light_device_t));
memset(dev, 0, sizeof(*dev));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (struct hw_module_t*)module;
dev->common.close = (int (*)(struct hw_device_t*))close_lights;
dev->set_light = set_light;
*device = (struct hw_device_t*)dev;
return 0;
}
//2.create open(), return light_device_t by name
static struct hw_module_methods_t lights_module_methods = {
.open = open_lights,
};
//1.create hw_module_t
struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = LIGHTS_HARDWARE_MODULE_ID,
.name = “Tiny4412 lights module”,
.author = “hceng [email protected]”,
.methods = &lights_module_methods,
};
{% endcodeblock %}
代码还算比较简单,简单分析下。
框架部分如前面所介绍的步骤一样,没什么特殊的,这里重点分析需实现的各个操作函数。
首先是write_int()
和write_string()
,实现向指定节点写数字和字符串。
接着是颜色相关的is_lit()
,用于判断颜色是否为黑色(即0x00000000
,没有打开任一RGB灯),rgb_to_brightness()
用于将RGB
颜色换算成亮度,用于背光。
接着再是set_light_backlight()
,用于设置背光,注意的是brightness
访问为0-255,但设备节点/dev/backlight-1wire
的支持范围是0-127
,需要转换下。
再来看set_light_battery()
和set_light_notifications()
,它们的内容差不多,都调用handle_shared_battery_locked()
,handle_shared_battery_locked()
的作用是让通知灯优先,这里先判断当前是否存在通知灯信号,如果存在,就调用set_shared_light_locked (dev, &g_notification);
设置RGB亮通知灯,否则设置电源灯。
最后,set_shared_light_locked()
,根据传入的light_state_t
结构体,根据颜色RGB、是否闪烁、闪烁时间来写/sys/class/leds/led*
节点。
light_state_t
结构体定义在hardware/libhardware/include/hardware/lights.h
:
{% codeblock lang:c %}
/**
-
The parameters that can be set for a given light.
-
Not all lights must support all parameters. If you
-
can do something backward-compatible, you should.
/
struct light_state_t {
/*- The color of the LED in ARGB.
- Do your best here.
-
- If your light can only do red or green, if they ask for blue,
-
you should do green.
-
- If you can only do a brightness ramp, then use this formula:
-
unsigned char brightness = ((77*((color>>16)&0x00ff))
-
+ (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
-
- If you can only do on or off, 0 is off, anything else is on.
- The high byte should be ignored. Callers will set it to 0xff (which
- would correspond to 255 alpha).
*/
unsigned int color;
/**
- See the LIGHT_FLASH_* constants
*/
int flashMode;
int flashOnMS;
int flashOffMS;
/**
- Policy used by the framework to manage the light’s brightness.
- Currently the values are BRIGHTNESS_MODE_USER and BRIGHTNESS_MODE_SENSOR.
*/
int brightnessMode;
};
{% endcodeblock %}
包含灯颜色color
、灯模式flashMode
(LIGHT_FLASH_NONE
、LIGHT_FLASH_TIMED
、LIGHT_FLASH_HARDWARE
)、灯闪烁时亮灭时间flashOnMS
和flashOffMS
、亮度模式brightnessMode
(BRIGHTNESS_MODE_USER
、BRIGHTNESS_MODE_SENSOR
)。
3.3编译、烧写、测试
a.在Android源码根目录下,创建hardware/libhardware/modules/lights/
,将前面编写的lights.c
放在该文件夹,并创建Android.mk
,内容如下:
{% codeblock lang:mk [Android.mk] https://github.com/hceng/learn/blob/master/android/02_灯光系统/hal/Android.mk%}
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := lights.default
LOCAL_MODULE := lights.tiny4412
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_C_INCLUDES := hardware/libhardware
LOCAL_SRC_FILES := lights.c
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_MODULE_TAGS := eng
include $(BUILD_SHARED_LIBRARY)
{% endcodeblock %}
b.去掉Android源码中,友善之臂提供的灯光HAL
层。
编辑vendor/friendly-arm/tiny4412/device-tiny4412.mk
,注释掉灯光部分:
ifeq ($(BOARD_USES_PWMLIGHTS),false)
#PRODUCT_COPY_FILES += \
# $(VENDOR_PATH)/proprietary/lights.tiny4412.so:system/lib/hw/lights.tiny4412.so
endif
c.执行以下命令:
source build/envsetup.sh
mmm hardware/libhardware/modules/lights -B //-B参数强制编译
diff vendor/friendly-arm/tiny4412/proprietary/lights.tiny4412.so out/target/product/tiny4412/system/lib/hw/lights.tiny4412.so //判断是否编译了新的lights.tiny4412.so
make snod
./gen-img.sh
将生成的system.img重新烧写。
d.修改内核的文件权限
驱动里会操作/sys/class/leds/led*/
里的brightness
、trigger
、delay_on
、delay_off
,在驱动里原权限是0644
。
--- -> 0 (no excute , no write ,no read)
--x -> 1 excute, (no write, no read)
-w- -> 2 write
-wx -> 3 write, excute
r-- -> 4 read
r-x -> 5 read, excute
rw- -> 6 read, write ,
rwx -> 7 read, write , excute
即用户有读写权限,组用户和其它用户,只有读权限,这会导致应用层写不了,这里简单处理,直接在驱动里改为0666
。
修改Linux内核源码,drivers/leds/led-class.c
里的brightness
和trigger
:
{% codeblock lang:c %}
static struct device_attribute led_class_attrs[] = {
__ATTR(brightness, 0666, led_brightness_show, led_brightness_store),
__ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
#ifdef CONFIG_LEDS_TRIGGERS
__ATTR(trigger, 0666, led_trigger_show, led_trigger_store),
#endif
__ATTR_NULL,
};
{% endcodeblock %}
drivers/leds/ledtrig-timer.c
里的delay_on
和delay_off
::
{% codeblock lang:c %}
#if defined(CONFIG_MACH_IPCAM)
static DEVICE_ATTR(delay_on, 0666, led_delay_on_show, led_delay_on_store);
static DEVICE_ATTR(delay_off, 0666, led_delay_off_show, led_delay_off_store);
#else
static DEVICE_ATTR(delay_on, 0666, led_delay_on_show, led_delay_on_store);
static DEVICE_ATTR(delay_off, 0666, led_delay_off_show, led_delay_off_store);
#endif
{% endcodeblock %}
执行make zImage
编译内核,烧写内核。
e.效果
重新启动后,执行logcat lights:V *:S
,打开APP设置,显示,设置亮度,可以看到如下打印信息:
aaa@qq.com:/ $ logcat lights:V *:S
--------- beginning of main
--------- beginning of system
V/lights ( 1973): set_light_battery flashMode=1 onMS = 125 offMS = 2875 color=0xffff0000
V/lights ( 1973): set_light_backlight brightness=255 color=0xffffffff
V/lights ( 1983): set_light_backlight brightness=10 color=0xff0a0a0a
V/lights ( 1983): set_light_backlight brightness=11 color=0xff0b0b0b
V/lights ( 1983): set_light_backlight brightness=18 color=0xff121212
V/lights ( 1983): set_light_backlight brightness=22 color=0xff161616
……
V/lights ( 1983): set_light_backlight brightness=228 color=0xffe4e4e4
V/lights ( 1983): set_light_backlight brightness=238 color=0xffeeeeee
V/lights ( 1983): set_light_backlight brightness=247 color=0xfff7f7f7
V/lights ( 1983): set_light_backlight brightness=255 color=0xffffffff
可以看到调用了set_light_battery()
,模式为1(LIGHT_FLASH_TIMED
)闪烁,闪烁时间,颜色为0xff0000
红色。因为此时板子连接着电源,且没有电池,认为是充满电状态。
接着是set_light_backlight()
,将亮度条从最暗拖到最亮,LCD也逐渐变亮,brightness
也从10~255变化。
4.Android源码调用分析
在进行各种LED调用分析之前,先了解一些Android的基础知识。
4.1 服务Server
4.1.1 基本概念
Service
是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。
服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。
此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。
Service
分为本地服务(LocalService
)和远程服务(RemoteService
):LocalService
依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外本地服务因为是在同一进程因此不需要IPC,也不需要AIDL。相应bindService
会方便很多。主进程被Kill
后,服务便会终止。RemoteService
为独立的进程,对应进程名格式为所在包名加上你指定的android:process
字符串。由于是独立的进程,因此在Activity
所在进程被Kill
的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。
System Server
是Android
基本服务的提供者,是Android
系统运行的最基本需求。
所有Server
运行在一个叫system_process
的进程中,system_process
进程是Android
Java虚拟机跑的第一个进程。
可以说,整个Android
系统的业务都是围绕System Server
而展开,所以,当system_process
死掉了,手机必须重启。
4.1.2 系统服务源码分析
在Zygote
创建system_process
进程时,实例化了类SystemServer
。
下面对frameworks/base/services/java/com/android/server/SystemServer.java
源码进行分析。
Java语法里,如果运行某个Java程序,先执行它的main()
方法:
{% codeblock lang:java %}
public static void main(String[] args) {
new SystemServer().run();
}
{% endcodeblock %}
可以看到它先实例化new
一个类SystemServer
,再调用它的run()
方法。
类SystemServer
的构造方法如下:
{% codeblock lang:java %}
public SystemServer() {
// Check for factory test mode.
mFactoryTestMode = FactoryTest.getMode();
}
{% endcodeblock %}
只是检查了工厂测试模式,暂不用管。接着看run()
方法源码:
{% codeblock lang:java %}
private void run() {
// If a device’s clock is before 1970 (before 0), a lot of
// APIs crash dealing with negative numbers, notably
// java.io.File#setLastModified, so instead we fake it and
// hope that time from cell towers or NTP fixes it shortly.
if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {
Slog.w(TAG, “System clock is before 1970; setting to 1970.”);
SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);
}
// Here we go!
Slog.i(TAG, "Entered the Android system server!");
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, SystemClock.uptimeMillis());
// In case the runtime switched since last boot (such as when
// the old runtime was removed in an OTA), set the system
// property so that it is in sync. We can't do this in
// libnativehelper's JniInvocation::Init code where we already
// had to fallback to a different runtime because it is
// running as root and we need to be the system user to set
// the property. http://b/11463182
SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary());
// Enable the sampling profiler.
if (SamplingProfilerIntegration.isEnabled()) {
SamplingProfilerIntegration.start();
mProfilerSnapshotTimer = new Timer();
mProfilerSnapshotTimer.schedule(new TimerTask() {
@Override
public void run() {
SamplingProfilerIntegration.writeSnapshot("system_server", null);
}
}, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);
}
// Mmmmmm... more memory!
VMRuntime.getRuntime().clearGrowthLimit();
// The system server has to run all of the time, so it needs to be
// as efficient as possible with its memory usage.
VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
// Some devices rely on runtime fingerprint generation, so make sure
// we've defined it before booting further.
Build.ensureFingerprintProperty();
// Within the system server, it is an error to access Environment paths without
// explicitly specifying a user.
Environment.setUserRequired(true);
// Ensure binder calls into the system always run at foreground priority.
BinderInternal.disableBackgroundScheduling(true);
// Prepare the main looper thread (this thread).
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_FOREGROUND);
android.os.Process.setCanSelfBackground(false);
Looper.prepareMainLooper();
// Initialize native services.
System.loadLibrary("android_servers");
nativeInit();
// Check whether we failed to shut down last time we tried.
// This call may not return.
performPendingShutdown();
// Initialize the system context.
createSystemContext();
// Create the system service manager.
mSystemServiceManager = new SystemServiceManager(mSystemContext);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
// Start services.
try {
startBootstrapServices();
startCoreServices();
startOtherServices();
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
}
// For debug builds, log event loop stalls to dropbox for analysis.
if (StrictMode.conditionallyEnableDebugLogging()) {
Slog.i(TAG, "Enabled StrictMode for system server main thread.");
}
// Loop forever.
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
{% endcodeblock %}
可以看到使用createSystemContext();
创建了一个上下文对象mSystemContext
。
接着创建一个SystemServiceManager
的实例,管理所有的SystemService
。接着使用LocalServices.addService()
将该对象加到LocalService
中。
之后就是依次启动三种类型的SystemService
:
{% codeblock lang:java %}
startBootstrapServices();
startCoreServices();
startOtherServices();
{% endcodeblock %}
先看startBootstrapServices()
启动了哪些系统服务:
{% codeblock lang:java %}
/**
* Starts the small tangle of critical services that are needed to get
* the system off the ground. These services have complex mutual dependencies
* which is why we initialize them all in one place here. Unless your service
* is also entwined in these dependencies, it should be initialized in one of
* the other functions.
*/
private void startBootstrapServices() {
// Wait for installd to finish starting up so that it has a chance to
// create critical directories such as /data/user with the appropriate
// permissions. We need this to complete before we initialize other services.
mInstaller = mSystemServiceManager.startService(Installer.class);
// Activity manager runs the show.
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
// Power manager needs to be started early because other services need it.
// Native daemons may be watching for it to be registered so it must be ready
// to handle incoming binder calls immediately (including being able to verify
// the permissions for those calls).
mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
// Now that the power manager has been started, let the activity manager
// initialize power management features.
mActivityManagerService.initPowerManagement();
// Display manager is needed to provide display metrics before package manager
// starts up.
mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
// We need the default display before we can initialize the package manager.
mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
// Only run "core" apps if we're encrypting the device.
String cryptState = SystemProperties.get("vold.decrypt");
if (ENCRYPTING_STATE.equals(cryptState)) {
Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
mOnlyCore = true;
} else if (ENCRYPTED_STATE.equals(cryptState)) {
Slog.w(TAG, "Device encrypted - only parsing core apps");
mOnlyCore = true;
}
// Start the package manager.
Slog.i(TAG, "Package Manager");
mPackageManagerService = PackageManagerService.main(mSystemContext, mInstaller,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
Slog.i(TAG, "User Service");
ServiceManager.addService(Context.USER_SERVICE, UserManagerService.getInstance());
// Initialize attribute cache used to cache resources from packages.
AttributeCache.init(mSystemContext);
// Set up the Application instance for the system process and get started.
mActivityManagerService.setSystemProcess();
}
{% endcodeblock %}
有Installer
、ActivityManagerService.Lifecycle
、PowerManagerService
、DisplayManagerService
、UserManagerService
。
基本都是通过SystemServiceManager
的startService()
的方法启动,启动完成后会回调SystemService
的onStart()
方法。
继续看startCoreServices()
启动了哪些系统服务:
{% codeblock lang:java %}
/**
* Starts some essential services that are not tangled up in the bootstrap process.
*/
private void startCoreServices() {
// Manages LEDs and display backlight.
mSystemServiceManager.startService(LightsService.class);
// Tracks the battery level. Requires LightService.
mSystemServiceManager.startService(BatteryService.class);
// Tracks application usage stats.
mSystemServiceManager.startService(UsageStatsService.class);
mActivityManagerService.setUsageStatsManager(
LocalServices.getService(UsageStatsManagerInternal.class));
// Tracks whether the updatable WebView is in a ready state and watches for update installs.
mSystemServiceManager.startService(WebViewUpdateService.class);
}
{% endcodeblock %}
有LightsService
、BatteryService
、UsageStatsService
、WebViewUpdateService
。
也是通过SystemServiceManager
的startService()
的方法启动。
最后来看看startOtherServices()
启动了哪些系统服务:
{% codeblock lang:java %}
/**
* Starts a miscellaneous grab bag of stuff that has yet to be refactored
* and organized.
*/
private void startOtherServices() {
final Context context = mSystemContext;
AccountManagerService accountManager = null;
ContentService contentService = null;
VibratorService vibrator = null;
IAlarmManager alarm = null;
MountService mountService = null;
NetworkManagementService networkManagement = null;
NetworkStatsService networkStats = null;
NetworkPolicyManagerService networkPolicy = null;
ConnectivityService connectivity = null;
NetworkScoreService networkScore = null;
NsdService serviceDiscovery= null;
WindowManagerService wm = null;
BluetoothManagerService bluetooth = null;
UsbService usb = null;
SerialService serial = null;
NetworkTimeUpdateService networkTimeUpdater = null;
CommonTimeManagementService commonTimeMgmtService = null;
InputManagerService inputManager = null;
TelephonyRegistry telephonyRegistry = null;
ConsumerIrService consumerIr = null;
AudioService audioService = null;
MmsServiceBroker mmsService = null;
boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
boolean disableMedia = SystemProperties.getBoolean("config.disable_media", false);
boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);
boolean disableTelephony = SystemProperties.getBoolean("config.disable_telephony", false);
boolean disableLocation = SystemProperties.getBoolean("config.disable_location", false);
boolean disableSystemUI = SystemProperties.getBoolean("config.disable_systemui", false);
boolean disableNonCoreServices = SystemProperties.getBoolean("config.disable_noncore", false);
boolean disableNetwork = SystemProperties.getBoolean("config.disable_network", false);
boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
try {
Slog.i(TAG, "Reading configuration...");
SystemConfig.getInstance();
Slog.i(TAG, "Scheduling Policy");
ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
Slog.i(TAG, "Telephony Registry");
telephonyRegistry = new TelephonyRegistry(context);
ServiceManager.addService("telephony.registry", telephonyRegistry);
Slog.i(TAG, "Entropy Mixer");
ServiceManager.addService("entropy", new EntropyMixer(context));
mContentResolver = context.getContentResolver();
// The AccountManager must come before the ContentService
try {
// TODO: seems like this should be disable-able, but req'd by ContentService
Slog.i(TAG, "Account Manager");
accountManager = new AccountManagerService(context);
ServiceManager.addService(Context.ACCOUNT_SERVICE, accountManager);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Account Manager", e);
}
Slog.i(TAG, "Content Manager");
contentService = ContentService.main(context,
mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL);
Slog.i(TAG, "System Content Providers");
mActivityManagerService.installSystemProviders();
Slog.i(TAG, "Vibrator Service");
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
Slog.i(TAG, "Consumer IR Service");
consumerIr = new ConsumerIrService(context);
ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr);
mSystemServiceManager.startService(AlarmManagerService.class);
alarm = IAlarmManager.Stub.asInterface(
ServiceManager.getService(Context.ALARM_SERVICE));
Slog.i(TAG, "Init Watchdog");
final Watchdog watchdog = Watchdog.getInstance();
watchdog.init(context, mActivityManagerService);
Slog.i(TAG, "HDMI Setting");
try {
mHdmiService = new HdmiService();
final Integer tvmode = Settings.System.getInt(mContentResolver, TV_MODE, 0);
mHdmiService.setHdmiMode(tvmode);
final Integer resolution = Settings.System.getInt(mContentResolver, TV_RESOLUTION, 1080960);
mHdmiService.setHdmiResolution(resolution);
final Integer tv_hdcp = Settings.System.getInt(mContentResolver, TV_HDCP, 0);
mHdmiService.setHdmiHdcp(tv_hdcp);
Slog.i(TAG, "\tHDMI Service started with following settings:\n"
+ "\t\ttvmode=" + String.valueOf(tvmode) + "\n"
+ "\t\tresolution=" + String.valueOf(resolution) + "\n"
+ "\t\ttv_hdcp=" + String.valueOf(tv_hdcp) + "\n"
);
Slog.i(TAG, "\tbefore: read HDMI OverScan setting");
mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml"));
InputStream is;
try {
is = mAtomicFile.openRead();
XmlPullParser parser;
try {
parser = Xml.newPullParser();
parser.setInput(new BufferedInputStream(is), null);
int gotit = 0;
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_DOCUMENT:
Slog.i(TAG, "\tHDMI OverScan Xml: START_DOCUMENT");
break;
case XmlPullParser.START_TAG:
Slog.i(TAG, "\tHDMI OverScan Xml: START_TAG - " + parser.getName());
if (parser.getName().equals("local-display")) {
int xOverScan = Integer.parseInt(parser.getAttributeValue(null, "xOverScan"));
int yOverScan = Integer.parseInt(parser.getAttributeValue(null, "yOverScan"));
// int keepRate = Integer.parseInt(parser.getAttributeValue(null, "keepRate"));
// int resolution = Integer.parseInt(parser.getAttributeValue(null, "resolution"));
// int rotangle = Integer.parseInt(parser.getAttributeValue(null, "rotangle"));
// int navbarvisible = Integer.parseInt(parser.getAttributeValue(null, "navbarvisible"));
// String deviceName = parser.getAttributeValue(null, "deviceName");
Slog.i(TAG, "\tHDMI OverScan as following settings:\n"
+ "\t\txOverScan=" + xOverScan + "\n"
+ "\t\tyOverScan=" + yOverScan + "\n"
);
gotit = 1;
if (xOverScan < 0) {
xOverScan = 0;
}
if (yOverScan < 0) {
yOverScan = 0;
}
mHdmiService.setHdmiOverScan(xOverScan,yOverScan,-1,-1);
}
break;
case XmlPullParser.END_TAG:
Slog.i(TAG, "\tHDMI OverScan Xml: END_TAG - " + parser.getName());
break;
}
if (gotit == 1) {
break;
}
eventType = parser.next();
}
} catch (IOException ex) {
Slog.w(TAG, "\tHDMI OverScan exception: Failed to load display manager persistent store data.", ex);
} catch (XmlPullParserException ex) {
Slog.w(TAG, "\tHDMI OverScan exception: Failed to load display manager persistent store data.", ex);
} finally {
Slog.w(TAG, "\tHDMI OverScan enter try-finally");
IoUtils.closeQuietly(is);
}
} catch (FileNotFoundException ex) {
Slog.i(TAG, "\tHDMI OverScan exception: mAtomicFile.openRead()");
}
} catch (Throwable e) {
Slog.e(TAG, "Failure starting HdmiService", e);
}
Slog.i(TAG, "Input Manager");
inputManager = new InputManagerService(context);
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
mActivityManagerService.setWindowManager(wm);
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
// TODO: Use service dependencies instead.
mDisplayManagerService.windowManagerAndInputReady();
// Skip Bluetooth if we have an emulator kernel
// TODO: Use a more reliable check to see if this product should
// support Bluetooth - see bug 988521
if (isEmulator) {
Slog.i(TAG, "No Bluetooh Service (emulator)");
} else if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
Slog.i(TAG, "No Bluetooth Service (factory test)");
} else if (!context.getPackageManager().hasSystemFeature
(PackageManager.FEATURE_BLUETOOTH)) {
Slog.i(TAG, "No Bluetooth Service (Bluetooth Hardware Not Present)");
} else if (disableBluetooth) {
Slog.i(TAG, "Bluetooth Service disabled by config");
} else {
Slog.i(TAG, "Bluetooth Manager Service");
bluetooth = new BluetoothManagerService(context);
ServiceManager.addService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, bluetooth);
}
} catch (RuntimeException e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting core service", e);
}
StatusBarManagerService statusBar = null;
INotificationManager notification = null;
InputMethodManagerService imm = null;
WallpaperManagerService wallpaper = null;
LocationManagerService location = null;
CountryDetectorService countryDetector = null;
TextServicesManagerService tsms = null;
LockSettingsService lockSettings = null;
AssetAtlasService atlas = null;
MediaRouterService mediaRouter = null;
// Bring up services needed for UI.
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
//if (!disableNonCoreServices) { // TODO: View depends on these; mock them?
if (true) {
try {
Slog.i(TAG, "Input Method Service");
imm = new InputMethodManagerService(context, wm);
ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm);
} catch (Throwable e) {
reportWtf("starting Input Manager Service", e);
}
try {
Slog.i(TAG, "Accessibility Manager");
ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
new AccessibilityManagerService(context));
} catch (Throwable e) {
reportWtf("starting Accessibility Manager", e);
}
}
}
try {
wm.displayReady();
} catch (Throwable e) {
reportWtf("making display ready", e);
}
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
if (!disableStorage &&
!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
try {
/*
* NotificationManagerService is dependant on MountService,
* (for media / usb notifications) so we must start MountService first.
*/
Slog.i(TAG, "Mount Service");
mountService = new MountService(context);
ServiceManager.addService("mount", mountService);
} catch (Throwable e) {
reportWtf("starting Mount Service", e);
}
}
}
try {
mPackageManagerService.performBootDexOpt();
} catch (Throwable e) {
reportWtf("performing boot dexopt", e);
}
try {
ActivityManagerNative.getDefault().showBootMessage(
context.getResources().getText(
com.android.internal.R.string.android_upgrading_starting_apps),
false);
} catch (RemoteException e) {
}
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
if (!disableNonCoreServices) {
try {
Slog.i(TAG, "LockSettingsService");
lockSettings = new LockSettingsService(context);
ServiceManager.addService("lock_settings", lockSettings);
} catch (Throwable e) {
reportWtf("starting LockSettingsService service", e);
}
if (!SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("")) {
mSystemServiceManager.startService(PersistentDataBlockService.class);
}
// Always start the Device Policy Manager, so that the API is compatible with
// API8.
mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
}
if (!disableSystemUI) {
try {
Slog.i(TAG, "Status Bar");
statusBar = new StatusBarManagerService(context, wm);
ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
} catch (Throwable e) {
reportWtf("starting StatusBarManagerService", e);
}
}
if (!disableNonCoreServices) {
try {
Slog.i(TAG, "Clipboard Service");
ServiceManager.addService(Context.CLIPBOARD_SERVICE,
new ClipboardService(context));
} catch (Throwable e) {
reportWtf("starting Clipboard Service", e);
}
}
if (!disableNetwork) {
try {
Slog.i(TAG, "NetworkManagement Service");
networkManagement = NetworkManagementService.create(context);
ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
} catch (Throwable e) {
reportWtf("starting NetworkManagement Service", e);
}
}
if (!disableNonCoreServices) {
try {
Slog.i(TAG, "Text Service Manager Service");
tsms = new TextServicesManagerService(context);
ServiceManager.addService(Context.TEXT_SERVICES_MANAGER_SERVICE, tsms);
} catch (Throwable e) {
reportWtf("starting Text Service Manager Service", e);
}
}
if (!disableNetwork) {
try {
Slog.i(TAG, "Network Score Service");
networkScore = new NetworkScoreService(context);
ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore);
} catch (Throwable e) {
reportWtf("starting Network Score Service", e);
}
try {
Slog.i(TAG, "NetworkStats Service");
networkStats = new NetworkStatsService(context, networkManagement, alarm);
ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
} catch (Throwable e) {
reportWtf("starting NetworkStats Service", e);
}
try {
Slog.i(TAG, "NetworkPolicy Service");
networkPolicy = new NetworkPolicyManagerService(
context, mActivityManagerService,
(IPowerManager)ServiceManager.getService(Context.POWER_SERVICE),
networkStats, networkManagement);
ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
} catch (Throwable e) {
reportWtf("starting NetworkPolicy Service", e);
}
mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
mSystemServiceManager.startService(
"com.android.server.wifi.WifiScanningService");
mSystemServiceManager.startService("com.android.server.wifi.RttService");
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET)) {
mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
}
try {
Slog.i(TAG, "Connectivity Service");
connectivity = new ConnectivityService(
context, networkManagement, networkStats, networkPolicy);
ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
networkStats.bindConnectivityManager(connectivity);
networkPolicy.bindConnectivityManager(connectivity);
} catch (Throwable e) {
reportWtf("starting Connectivity Service", e);
}
try {
Slog.i(TAG, "Network Service Discovery Service");
serviceDiscovery = NsdService.create(context);
ServiceManager.addService(
Context.NSD_SERVICE, serviceDiscovery);
} catch (Throwable e) {
reportWtf("starting Service Discovery Service", e);
}
}
if (!disableNonCoreServices) {
try {
Slog.i(TAG, "UpdateLock Service");
ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
new UpdateLockService(context));
} catch (Throwable e) {
reportWtf("starting UpdateLockService", e);
}
}
/*
* MountService has a few dependencies: Notification Manager and
* AppWidget Provider. Make sure MountService is completely started
* first before continuing.
*/
if (mountService != null && !mOnlyCore) {
mountService.waitForAsecScan();
}
try {
if (accountManager != null)
accountManager.systemReady();
} catch (Throwable e) {
reportWtf("making Account Manager Service ready", e);
}
try {
if (contentService != null)
contentService.systemReady();
} catch (Throwable e) {
reportWtf("making Content Service ready", e);
}
mSystemServiceManager.startService(NotificationManagerService.class);
notification = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
networkPolicy.bindNotificationManager(notification);
mSystemServiceManager.startService(DeviceStorageMonitorService.class);
if (!disableLocation) {
try {
Slog.i(TAG, "Location Manager");
location = new LocationManagerService(context);
ServiceManager.addService(Context.LOCATION_SERVICE, location);
} catch (Throwable e) {
reportWtf("starting Location Manager", e);
}
try {
Slog.i(TAG, "Country Detector");
countryDetector = new CountryDetectorService(context);
ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
} catch (Throwable e) {
reportWtf("starting Country Detector", e);
}
}
if (!disableNonCoreServices) {
try {
Slog.i(TAG, "Search Service");
ServiceManager.addService(Context.SEARCH_SERVICE,
new SearchManagerService(context));
} catch (Throwable e) {
reportWtf("starting Search Service", e);
}
}
try {
Slog.i(TAG, "DropBox Service");
ServiceManager.addService(Context.DROPBOX_SERVICE,
new DropBoxManagerService(context, new File("/data/system/dropbox")));
} catch (Throwable e) {
reportWtf("starting DropBoxManagerService", e);
}
if (!disableNonCoreServices && context.getResources().getBoolean(
R.bool.config_enableWallpaperService)) {
try {
Slog.i(TAG, "Wallpaper Service");
wallpaper = new WallpaperManagerService(context);
ServiceManager.addService(Context.WALLPAPER_SERVICE, wallpaper);
} catch (Throwable e) {
reportWtf("starting Wallpaper Service", e);
}
}
if (!disableMedia && !"0".equals(SystemProperties.get("system_init.startaudioservice"))) {
try {
Slog.i(TAG, "Audio Service");
audioService = new AudioService(context);
ServiceManager.addService(Context.AUDIO_SERVICE, audioService);
} catch (Throwable e) {
reportWtf("starting Audio Service", e);
}
}
if (!disableNonCoreServices) {
mSystemServiceManager.startService(DockObserver.class);
}
if (!disableMedia) {
try {
Slog.i(TAG, "Wired Accessory Manager");
// Listen for wired headset changes
inputManager.setWiredAccessoryCallbacks(
new WiredAccessoryManager(context, inputManager));
} catch (Throwable e) {
reportWtf("starting WiredAccessoryManager", e);
}
}
if (!disableNonCoreServices) {
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
|| mPackageManager.hasSystemFeature(
PackageManager.FEATURE_USB_ACCESSORY)) {
// Manage USB host and device support
mSystemServiceManager.startService(USB_SERVICE_CLASS);
}
try {
Slog.i(TAG, "Serial Service");
// Serial port support
serial = new SerialService(context);
ServiceManager.addService(Context.SERIAL_SERVICE, serial);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting SerialService", e);
}
}
mSystemServiceManager.startService(TwilightService.class);
mSystemServiceManager.startService(UiModeManagerService.class);
mSystemServiceManager.startService(JobSchedulerService.class);
if (!disableNonCoreServices) {
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
}
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
}
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
}
}
try {
Slog.i(TAG, "DiskStats Service");
ServiceManager.addService("diskstats", new DiskStatsService(context));
} catch (Throwable e) {
reportWtf("starting DiskStats Service", e);
}
try {
// need to add this service even if SamplingProfilerIntegration.isEnabled()
// is false, because it is this service that detects system property change and
// turns on SamplingProfilerIntegration. Plus, when sampling profiler doesn't work,
// there is little overhead for running this service.
Slog.i(TAG, "SamplingProfiler Service");
ServiceManager.addService("samplingprofiler",
new SamplingProfilerService(context));
} catch (Throwable e) {
reportWtf("starting SamplingProfiler Service", e);
}
if (!disableNetwork) {
try {
Slog.i(TAG, "NetworkTimeUpdateService");
networkTimeUpdater = new NetworkTimeUpdateService(context);
} catch (Throwable e) {
reportWtf("starting NetworkTimeUpdate service", e);
}
}
if (!disableMedia) {
try {
Slog.i(TAG, "CommonTimeManagementService");
commonTimeMgmtService = new CommonTimeManagementService(context);
ServiceManager.addService("commontime_management", commonTimeMgmtService);
} catch (Throwable e) {
reportWtf("starting CommonTimeManagementService service", e);
}
}
if (!disableNetwork) {
try {
Slog.i(TAG, "CertBlacklister");
CertBlacklister blacklister = new CertBlacklister(context);
} catch (Throwable e) {
reportWtf("starting CertBlacklister", e);
}
}
if (!disableNonCoreServices) {
// Dreams (interactive idle-time views, a/k/a screen savers, and doze mode)
mSystemServiceManager.startService(DreamManagerService.class);
}
if (!disableNonCoreServices) {
try {
Slog.i(TAG, "Assets Atlas Service");
atlas = new AssetAtlasService(context);
ServiceManager.addService(AssetAtlasService.ASSET_ATLAS_SERVICE, atlas);
} catch (Throwable e) {
reportWtf("starting AssetAtlasService", e);
}
}
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
}
mSystemServiceManager.startService(RestrictionsManagerService.class);
mSystemServiceManager.startService(MediaSessionService.class);
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
mSystemServiceManager.startService(HdmiControlService.class);
}
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_LIVE_TV)) {
mSystemServiceManager.startService(TvInputManagerService.class);
}
if (!disableNonCoreServices) {
try {
Slog.i(TAG, "Media Router Service");
mediaRouter = new MediaRouterService(context);
ServiceManager.addService(Context.MEDIA_ROUTER_SERVICE, mediaRouter);
} catch (Throwable e) {
reportWtf("starting MediaRouterService", e);
}
mSystemServiceManager.startService(TrustManagerService.class);
mSystemServiceManager.startService(FingerprintService.class);
try {
Slog.i(TAG, "BackgroundDexOptService");
BackgroundDexOptService.schedule(context);
} catch (Throwable e) {
reportWtf("starting BackgroundDexOptService", e);
}
}
mSystemServiceManager.startService(LauncherAppsService.class);
}
if (!disableNonCoreServices) {
mSystemServiceManager.startService(MediaProjectionManagerService.class);
}
// Before things start rolling, be sure we have decided whether
// we are in safe mode.
final boolean safeMode = wm.detectSafeMode();
if (safeMode) {
mActivityManagerService.enterSafeMode();
// Disable the JIT for the system_server process
VMRuntime.getRuntime().disableJitCompilation();
} else {
// Enable the JIT for the system_server process
VMRuntime.getRuntime().startJitCompilation();
}
// MMS service broker
mmsService = mSystemServiceManager.startService(MmsServiceBroker.class);
// It is now time to start up the app processes...
try {
vibrator.systemReady();
} catch (Throwable e) {
reportWtf("making Vibrator Service ready", e);
}
if (lockSettings != null) {
try {
lockSettings.systemReady();
} catch (Throwable e) {
reportWtf("making Lock Settings Service ready", e);
}
}
// Needed by DevicePolicyManager for initialization
mSystemServiceManager.startBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY);
mSystemServiceManager.startBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
try {
wm.systemReady();
} catch (Throwable e) {
reportWtf("making Window Manager Service ready", e);
}
if (safeMode) {
mActivityManagerService.showSafeModeOverlay();
}
// Update the configuration for this context by hand, because we're going
// to start using it before the config change done in wm.systemReady() will
// propagate to it.
Configuration config = wm.computeNewConfiguration();
DisplayMetrics metrics = new DisplayMetrics();
WindowManager w = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
w.getDefaultDisplay().getMetrics(metrics);
context.getResources().updateConfiguration(config, metrics);
try {
// TODO: use boot phase
mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
} catch (Throwable e) {
reportWtf("making Power Manager Service ready", e);
}
try {
mPackageManagerService.systemReady();
} catch (Throwable e) {
reportWtf("making Package Manager Service ready", e);
}
try {
// TODO: use boot phase and communicate these flags some other way
mDisplayManagerService.systemReady(safeMode, mOnlyCore);
} catch (Throwable e) {
reportWtf("making Display Manager Service ready", e);
}
// These are needed to propagate to the runnable below.
final MountService mountServiceF = mountService;
final NetworkManagementService networkManagementF = networkManagement;
final NetworkStatsService networkStatsF = networkStats;
final NetworkPolicyManagerService networkPolicyF = networkPolicy;
final ConnectivityService connectivityF = connectivity;
final NetworkScoreService networkScoreF = networkScore;
final WallpaperManagerService wallpaperF = wallpaper;
final InputMethodManagerService immF = imm;
final LocationManagerService locationF = location;
final CountryDetectorService countryDetectorF = countryDetector;
final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
final CommonTimeManagementService commonTimeMgmtServiceF = commonTimeMgmtService;
final TextServicesManagerService textServiceManagerServiceF = tsms;
final StatusBarManagerService statusBarF = statusBar;
final AssetAtlasService atlasF = atlas;
final InputManagerService inputManagerF = inputManager;
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
final MediaRouterService mediaRouterF = mediaRouter;
final AudioService audioServiceF = audioService;
final MmsServiceBroker mmsServiceF = mmsService;
// We now tell the activity manager it is okay to run third party
// code. It will call back into us once it has gotten to the state
// where third party code can really run (but before it has actually
// started launching the initial applications), for us to complete our
// initialization.
mActivityManagerService.systemReady(new Runnable() {
@Override
public void run() {
Slog.i(TAG, "Making services ready");
mSystemServiceManager.startBootPhase(
SystemService.PHASE_ACTIVITY_MANAGER_READY);
try {
mActivityManagerService.startObservingNativeCrashes();
} catch (Throwable e) {
reportWtf("observing native crashes", e);
}
Slog.i(TAG, "WebViewFactory preparation");
WebViewFactory.prepareWebViewInSystemServer();
try {
startSystemUi(context);
} catch (Throwable e) {
reportWtf("starting System UI", e);
}
try {
if (mountServiceF != null) mountServiceF.systemReady();
} catch (Throwable e) {
reportWtf("making Mount Service ready", e);
}
try {
if (networkScoreF != null) networkScoreF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Score Service ready", e);
}
try {
if (networkManagementF != null) networkManagementF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Managment Service ready", e);
}
try {
if (networkStatsF != null) networkStatsF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Stats Service ready", e);
}
try {
if (networkPolicyF != null) networkPolicyF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Policy Service ready", e);
}
try {
if (connectivityF != null) connectivityF.systemReady();
} catch (Throwable e) {
reportWtf("making Connectivity Service ready", e);
}
try {
if (audioServiceF != null) audioServiceF.systemReady();
} catch (Throwable e) {
reportWtf("Notifying AudioService running", e);
}
Watchdog.getInstance().start();
// It is now okay to let the various system services start their
// third party code...
mSystemServiceManager.startBootPhase(
SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
try {
if (wallpaperF != null) wallpaperF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying WallpaperService running", e);
}
try {
if (immF != null) immF.systemRunning(statusBarF);
} catch (Throwable e) {
reportWtf("Notifying InputMethodService running", e);
}
try {
if (locationF != null) locationF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying Location Service running", e);
}
try {
if (countryDetectorF != null) countryDetectorF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying CountryDetectorService running", e);
}
try {
if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying NetworkTimeService running", e);
}
try {
if (commonTimeMgmtServiceF != null) {
commonTimeMgmtServiceF.systemRunning();
}
} catch (Throwable e) {
reportWtf("Notifying CommonTimeManagementService running", e);
}
try {
if (textServiceManagerServiceF != null)
textServiceManagerServiceF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying TextServicesManagerService running", e);
}
try {
if (atlasF != null) atlasF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying AssetAtlasService running", e);
}
try {
// TODO(BT) Pass parameter to input manager
if (inputManagerF != null) inputManagerF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying InputManagerService running", e);
}
try {
if (telephonyRegistryF != null) telephonyRegistryF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying TelephonyRegistry running", e);
}
try {
if (mediaRouterF != null) mediaRouterF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying MediaRouterService running", e);
}
try {
if (mmsServiceF != null) mmsServiceF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying MmsService running", e);
}
}
});
}
{% endcodeblock %}
可以看到比较夸张,包含了SchedulingPolicyService
、TelephonyRegistry
、EntropyMixer
、AccountManagerService
、VibratorService
、ConsumerIrService
、AlarmManagerService
、InputManagerService
、WindowManagerService
、BluetoothManagerService
、InputMethodManagerService
、AccessibilityManagerService
、MountService
、LockSettingsService
、PersistentDataBlockService
、DevicePolicyManagerService
、StatusBarManagerService
、ClipboardService
、NetworkManagementService
、TextServicesManagerService
、NetworkScoreService
、NotificationManagerService
、DeviceStorageMonitorService
、NetworkStatsService
、NetworkPolicyManagerService
、ConnectivityService
、NsdService
、UpdateLockService
、LocationManagerService
、CountryDetectorService
、SearchManagerService
、DropBoxManagerService
、WallpaperManagerService
、AudioService
、DockObserver
、SerialService
、TwilightService
、UiModeManagerService
、JobSchedulerService
、DiskStatsService
、SamplingProfilerService
、CommonTimeManagementService
、AssetAtlasService
、MediaRouterService
、DreamManagerService
、RestrictionsManagerService
、MediaSessionService
、HdmiControlService
、TvInputManagerService
、TrustManagerService
、FingerprintService
、LauncherAppsService
、MediaProjectionManagerService
、MmsServiceBroker
等。
以上服务不一定都会启动,系统会根据配置,选择性的启动某些服务,比如Android TV就会启动TvInputManagerService
,而Android手机则不会。
在前面三次启动各种SystemService
里,出现了三个不同的启动方法:SystemServiceManager.startService()
、LocalServices.addService()
、ServiceManager.addService()
。
1、先看SystemServiceManager.startService()
和LocalServices.addService()
,启动系统服务都是SystemService
的子类,启动这些服务后会回调SystemService
的onStart()
方法。
2、常用的是SystemServiceManager.startService()
和ServiceManager.addService()
:
方式 | 功能 | 特点 |
---|---|---|
ServiceManager.addService | 向 ServiceManager 注册该服务 | 服务往往直接或间接继承于Binder服务 |
SystemServiceManager.startService | 1.创建服务对象 2.回调该服务的 onStart() 方法;该方法会执行上面的 ServiceManager.addService() 3.根据启动到不同的阶段会回调 mServices 中注册 service 的 onBootPhase() 方法 |
1.service 往往自身或者内部类(如 ActivityManagerService)继承于SystemService 2.在开机过程中,service 除了注册到 ServiceManager 中,还需要做一些其他的处理,如根据系统启动阶段(phase)的不同,做一些不同的处理 |
全部启动所需要的服务以后,SystemServer
便要进入了接受Message
的循环中,等待Message
的事件。
参考博客:Android系统之System Server大纲
4.2 广播Broadcasrs
前面讲Server
有点多,这里就简单说下广播Broadcasrs
,给后面分析电源灯铺垫下基础。
4.2.1 基本概念
广播是Android系统中一种组件间的通信方式,Android中的每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序就只会接收到自己所关心的广播内容,这些广播可能是来自于系统的,也可能是来自于其它的应用程序。
发送广播借助于Intent
,而接收广播则需要广播接收器BroadcastReceiver
。
广播的总体实现流程
1、广播接收者BroadcastReceiver
通过Binder
机制向AMS(Activity Manager Service
)进行注册;
2、广播发送者通过Binder
机制向AMS发送广播;
3、AMS查找符合相应条件(IntentFilter
/Permission
等)的BroadcastReceiver
,将广播发送到BroadcastReceiver
(一般情况下是Activity
)相应的消息循环队列中;
4、消息循环执行拿到此广播,回调BroadcastReceiver
中的onReceive()
方法。
4.2.2 广播使用示例
-
动态注册广播接收器
1、创建该自定义广播接收器的实例;
2、创建一个IntentFilter
实例,调用addAction()
方法配置具体要接收什么广播消息;
3、调用registerReceiver()
方法关联intentFilter
和自定义广播接收器,完成注册;
4、在onDestroy()
方法调用unRegisterReceiver()
解除注册;
{% codeblock lang:java %}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter();
networkChangeReceiver = new NetworkChangeReceiver();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
registerReceiver(networkChangeReceiver, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver( networkChangeReceiver );
}
class NetworkChangeReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context,Intent intent){
//这里写接收到广播后执行的操作
}
}
}
{% endcodeblock %}
-
发送自定义广播
1、new
一个Intent
,构造函数可以传入自定义的action
属性,或者调用Intent
的setAction()
方法,也可以调用Intent
的putExtra()
传递数据;
2、若要发送标准广播就调用sendBroadcast(intent)
,而要发送有序广播就调用sendOrderedBroadcast(intent,null)
。
{% codeblock lang:java %}
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
intent.putExtra(“name”, “hceng”);
sendBroadcast(intent);
{% endcodeblock %}
4.3 电源灯
4.3.1 整体分析
电源灯主要涉及三个SystemServer
:PowerManagerService
、LightsService
、BatteryService
。
PowerManagerService
会先注册广播接收器,BatteryService
会监听底层驱动电池驱动,当电池发生事件时,BatteryService
会发出电池事件广播,同时获取LightsService
改变LED状态,PowerManagerService
收到广播再做一些其它操作,比如低电量弹窗提醒。
UML时序图参考如下:
- Setp1-14:参考前面的
SystemService
的内容,主要就是注册本地服务;注意Step12注册了监听,用于监听电池驱动的状态; - Step15-17:做了获取电池灯的操作,后面再详细解释;
- Step18-20:动态注册广播接收器,用于接收电池广播;
- Step21-28:假设电池驱动上报了变化,将会导致Step21调用,通过Step24发送广播,通过Step27改变LED状态;
4.3.2 电池灯调用分析
从上面整个流程可以发现,LightsService
先加入本地服务,BatteryService
再获取该服务,从而操作LED,再详细分析下该过程。
1、最开始,通过startService()
启动了LightsService
服务(Step6);
2、接着回调onStart()
(Step7),使用publishLocalService()
将mService
添加到本地服务(Step8)。
这个mService
是一个LightsManager
的实例,里面实现了一个getLight()
方法,返回mLights[]
数组的对应项。
{% codeblock lang:java %}
private final LightsManager mService = new LightsManager() {
@Override
public com.android.server.lights.Light getLight(int id) {
if (id < LIGHT_ID_COUNT) {
return mLights[id];
} else {
return null;
}
}
};
{% endcodeblock %}
mLights[]
数组的每项保存的是类LightImpl
传入的不同参数的对象。
{% codeblock lang:java %}
final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT];
……
public LightsService(Context context) {
super(context);
mNativePointer = init_native();
for (int i = 0; i < LightsManager.LIGHT_ID_COUNT; i++) {
mLights[i] = new LightImpl(i);
}
}
{% endcodeblock %}LightImpl
类继承于Light
,实现了操作LED的各种方法,最后都调用到setLight_native()
,也就是JNI里提供的本地接口方法。
经过前面的一系列操作,现在mService
通过自己的getLight(LIGHT_ID_BATTERY)
就可以找到对应mLights[LIGHT_ID_BATTERY]
,再通过mLights[LIGHT_ID_BATTERY]
的LightImpl
操作setLight_native(LIGHT_ID_BATTERY)
,实现对JNI本地接口方法的调用,且通过publishLocalService()
将mService
添加到本地服务。
3、在BatteryService
里,先通过getLocalService()
获取前面的mService
,再通过getLight()
得到
{% codeblock lang:java %}
public BatteryService(Context context) {
……
mLed = new Led(context, getLocalService(LightsManager.class));
……
}
……
public Led(Context context, LightsManager lights) {
mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY);
……
}
{% endcodeblock %}
此时,mBatteryLight
就相当于前面LightImpl
(都继承于Light
),里面包含各种操作LED的各种方法。
4、当电池驱动发生变化,导致监听函数调用,最后调用到updateLightsLocked()
(Step21-27),在updateLightsLocked()
调用setColor()
、setFlashing()
等操作LED灯。
{% codeblock lang:java %}
/**
* Synchronize on BatteryService.
*/
public void updateLightsLocked() {
final int level = mBatteryProps.batteryLevel;
final int status = mBatteryProps.batteryStatus;
if (level < mLowBatteryWarningLevel) {
if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
// Solid red when battery is charging
mBatteryLight.setColor(mBatteryLowARGB);
} else {
// Flash red when battery is low and not charging
mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED,
mBatteryLedOn, mBatteryLedOff);
}
} else if (status == BatteryManager.BATTERY_STATUS_CHARGING
|| status == BatteryManager.BATTERY_STATUS_FULL) {
if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
// Solid green when full or charging and nearly full
mBatteryLight.setColor(mBatteryFullARGB);
} else {
// Solid orange when charging and halfway full
mBatteryLight.setColor(mBatteryMediumARGB);
}
} else {
// No lights if not charging and not low
mBatteryLight.turnOff();
}
}
{% endcodeblock %}
看看里面的判断逻辑:
先判断电量是否低于mLowBatteryWarningLevel
,是的话再判断是否是充电状态,如果是充电状态,则设置颜色为mBatteryLowARGB
(在前面通过getResources
获取,定义在frameworks/base/core/res/res/values/config.xml
),反之就闪烁。
如果电池是在充电或满电状态,电量大于90则显示mBatteryFullARGB
,反之显示mBatteryMediumARGB
,都不是前面的情况,则turnOff()
关闭。
完全和我们常识相符。
4.4 通知灯
前面电池灯不好通过编写APP来控制,基本都是系统服务去控制的,这里的通知灯则可以通过APP控制。
先编写APP,通过按键延时发出通知,让通知灯亮,再分析调用过程,延时原因是让屏幕息屏再发出通知,通知灯才会亮。
4.4.1 APP使用通知灯
APP主要有两个文件,布局文件和代码操作文件。
布局文件比较简单,主要是一个按键:
{% codeblock lang:xml [activity_main.xml] https://github.com/hceng/learn/blob/master/android/02_灯光系统/app/notification/layout/activity_main.xml %}
<android.support.constraint.ConstraintLayout xmlns:android=“http://schemas.android.com/apk/res/android”
xmlns:app=“http://schemas.android.com/apk/res-auto”
xmlns:tools=“http://schemas.android.com/tools”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/TEXT"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="60dp"
android:gravity="center"
android:text="Lights demo!"
android:textColor="#008577"
android:textSize="25sp"
android:textStyle="bold" />
<Button
android:id="@+id/BUTTON"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="30dp"
android:textAllCaps="false"
android:text="Flashing Light after 20s" />
</LinearLayout>
</android.support.constraint.ConstraintLayout>
{% endcodeblock %}
代码文件如下:
{% codeblock lang:java [MainActivity.java] https://github.com/hceng/learn/blob/master/android/02_灯光系统/app/notification/lights/MainActivity.java %}
package cn.hceng.lights;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.support.v4.app.NotificationCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private Button button = null;
boolean flashing = false;
final private int LED_NOTIFICATION_ID = 12;
private Handler mHander = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
button = (Button) findViewById(R.id.BUTTON);
button.setOnClickListener(new MyButtonListener());
}
class MyButtonListener implements View.OnClickListener {
@Override
public void onClick(View v) {
flashing = !flashing;
if (flashing) {
button.setText("Stop Flashing the Light");
} else {
button.setText("Flashing Light after 20S");
}
mHander.postDelayed(new Runnable() {
@Override
public void run() {
if (flashing)
FlashingLight();
else
StopFlashingLight();
}
}, 20000);
}
}
private void FlashingLight() {
Intent mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.hceng.cn"));
PendingIntent mPendingIntent = PendingIntent.getActivity(this, 0, mIntent, 0);
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { //Android O之上,需要渠道
NotificationChannel notificationChannel =
new NotificationChannel("channelid1", "channelname", NotificationManager.IMPORTANCE_HIGH);//如果用IMPORTANCE_NOENE需要在系统的设置里面开启渠道,通知才能正常弹出
mNotificationManager.createNotificationChannel(notificationChannel);
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(MainActivity.this, "channelid1");
mBuilder.setContentTitle("Notification") //设置通知栏标题
.setContentText("Lights demo") //设置通知栏显示内容
.setSmallIcon(R.mipmap.ic_launcher) //设置通知ICON
.setContentIntent(mPendingIntent) //设置点击通知事件
.setAutoCancel(true) //设置点击通知后自动取消
.setDefaults(Notification.FLAG_SHOW_LIGHTS) //Notification.FLAG_SHOW_LIGHTS Notification.DEFAULT_SOUND(需要权限)
.setLights(Color.BLUE, 1000, 1000); //ledARGB ledOnMS ledOffMS
Notification mNotification = mBuilder.build();
//设置lights参数的另一种方式
/*
mNotification.flags |= Notification.FLAG_SHOW_LIGHTS;
mNotification.ledARGB = 0xFF0000FF; //Color.BLUE
mNotification.ledOnMS = 1000;
mNotification.ledOffMS = 100;
*/
mNotificationManager.notify(LED_NOTIFICATION_ID, mNotification); //发出通知
}
private void StopFlashingLight() {
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNotificationManager.cancel(LED_NOTIFICATION_ID); //取消通知
}
}
{% endcodeblock %}
简单说下思路:
1、获取Button
的ID
,绑定监听方法MyButtonListener()
;
2、实现监听方法MyButtonListener()
,延时调用FlashingLight()
发送通知或StopFlashingLight()
停止通知;
3、这里使用Handler
的postDelayed()
方法实现延迟操作;
4、发送通知方法FlashingLight()
里:
a.获取NotificationManager
实例;
b.实例化NotificationCompat.Builder
并设置相关属性;(注意Android O以上版本,需要"channelid
)
c.通过builder.build()
方法生成Notification
对象;并发送通知;
编译APP,连接Tiny4412开发板,首先在系统设置->显示->休眠,把息屏时间改为15秒。
选择在Tiny4412开发板运行APP,点击APP上的Flashing Light after 20S
按钮,在串口输入logcat lights:V *:S
,可以看到如下打印:
V/lights ( 1981): set_light_notifications flashMode=1 onMS = 1000 offMS = 1000 color=0xff0000ff
操作通知灯的模式、时间、颜色与APP的设置相同,说明HAL层的lights.c
已经成功被调用,接下来分析调用流程。
4.4.2 通知灯调用分析
UML时序图参考如下:
Setp1-5:和前面的电源灯一样,先启动LightsService
,创建LightsManager
;
Step6-10:启动NotificationManagerService
,在启动过程中获取LightsManager
,具备了操作LED的能力;
Step11-12:ContextImpl
在调用过程中会registerService
,HashMap
将String
和ServiceFetcher
绑定,通过String
就能找到对应的ServiceFetcher
,这里就是可以通过NOTIFICATION_SERVICE
得到对应的实例化对象。;
Step13-14:在APP里,通过getSystemService
传入NOTIFICATION_SERVICE
获得通知服务,再构造通知,调用notify
发送通知;
Step15-20:notify
里会获取NotificationManagerService
,进而调用到HAL层;
4.5 背光灯
4.5.1 APP设置背光
首先还是编写一个APP来感受下APP如何设置背光亮度。
这次涉及到背光权限,因此会涉及三个文件:布局文件、代码操作文件、涉及权限的AndroidManifest
文件。
布局文件比较简单,主要是一个SeekBar
滑动条:
{% codeblock lang:xml [activity_main.xml] https://github.com/hceng/learn/blob/master/android/02_灯光系统/app/brightness/layout/activity_main.xml %}
<android.support.constraint.ConstraintLayout xmlns:android=“http://schemas.android.com/apk/res/android”
xmlns:app=“http://schemas.android.com/apk/res-auto”
xmlns:tools=“http://schemas.android.com/tools”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/TEXT"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="60dp"
android:gravity="center"
android:text="Lights demo!"
android:textColor="#008577"
android:textSize="25sp"
android:textStyle="bold" />
<SeekBar
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="60dp"
android:id="@+id/SEEKBAR"
android:max="100"
android:progress="50" />
</LinearLayout>
</android.support.constraint.ConstraintLayout>
{% endcodeblock %}
代码文件如下:
{% codeblock lang:java [MainActivity.java] https://github.com/hceng/learn/blob/master/android/02_灯光系统/app/brightness/lights/MainActivity.java %}
package cn.hceng.lights;
import android.provider.Settings;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.SeekBar;
public class MainActivity extends AppCompatActivity {
private SeekBar mBacklightSeekBar = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mBacklightSeekBar = (SeekBar) findViewById(R.id.SEEKBAR);
//关闭自动背光,以便后面手动设置亮度
Settings.System.putInt(getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
//获取当前背光,自动设置当前SeekBar位置
try {
int brightness = android.provider.Settings.System.getInt(getContentResolver(),
android.provider.Settings.System.SCREEN_BRIGHTNESS);
mBacklightSeekBar.setProgress(brightness * 100 / 255);
} catch (Settings.SettingNotFoundException e) {
e.printStackTrace();
}
//监听SeekBar改变,对应设置背光
mBacklightSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
android.provider.Settings.System.putInt(getContentResolver(),
android.provider.Settings.System.SCREEN_BRIGHTNESS,
mBacklightSeekBar.getProgress() * 255 / 100);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
}
{% endcodeblock %}
内容还是比较简单,先获取SeekBar
,接着使用Settings.System.putInt()
关闭自动背光,以便后面手动设置亮度。再使用android.provider.Settings.System.getInt()
获取当前亮度,以变自动设置当前SeekBar位置。最后监听SeekBar改变
,覆写匿名类,调用android.provider.Settings.System.putInt()
设置背光。
可以看到,APP设置背光就是调用android.provider.Settings.System.putInt()
。
最后在AndroidManifest.xml
里添加写系统设置权限WRITE_SETTINGS
。
{% codeblock lang:xml [AndroidManifest.xml] https://github.com/hceng/learn/blob/master/android/02_灯光系统/app/brightness/AndroidManifest.xml %}
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
{% endcodeblock %}
现在编译APP,安装,便可以设置屏幕背光亮度了。
4.5.2 背光灯调用分析
UML时序图参考如下:
Android提供了一个叫ContentObserver
的机制,目的是观察特定Uri
引起的数据库的变化,继而做一些相应的处理,当ContentObserver
所观察的Uri
发生变化时,便会触发它。
对于背光,APP修改亮度值写到数据库,对应的内容观察者监测到该数据库变化,就会设置背光。
Setp1-14:启动DisplayManagerService
、PowerManagerService
、LightsService
;
Setp15-19:创建线程PhotonicModulator
,该线程通常情况是wait()
,当收到通知后将调用setBrightness()
设置背光;
Setp20-25:注册了一个ContentObserver
,之后就对SCREEN_BRIGHTNESS
进行观察;
Setp26-52:APP发来SCREEN_BRIGHTNESS
,将导致notifyAll()
发出通知,让前面的线程PhotonicModulator
得以调用setBrightness()
设置背光;