系统介绍

首先别人的APP要能直接访问到你写的硬件或者不经过任何修改的APP能直接使用你的驱动,就需要使用系统自带的JNI,所以我们需要写出符合系统自带JNI文件的HAL文件和驱动程序,下面具体分析一个这个HAL和驱动程序需要实现什么。


灯光系统由以下四个部分组成

APP:电池灯APP,通知灯APP,背光灯APP
JNI :com_android_server_lights_LightsService.cpp
HAL:我们自己实现,实现一个lights.c
驱动:leds_nanopi3.c(JNI需要什么这里就给它提供什么,这里我们实现亮灭闪烁和亮度


灯光系统的主要功能:

硬件上是属于同一个的有(包括颜色和闪烁):
电池灯:电池电量发生变化时颜色会发生变化
通知灯:有通知会闪烁,比如未接电话或短信

调节LCD亮度:
背光灯


驱动部分:

JNI:
com_android_server_lights_LightsService.cpp(不需要我们实现,但我们需要给它提供接口)

里面需要获得hw_module_t结构体,所以我们的HAL需要实现这个结构体

JNI里面的get_device会根据不同的名字调用module->methods->open返回不同的light_device_t结构体,这个结构体里面带有set_light,即不同灯的控制函数。

*********************lights.h*********************#define LIGHT_ID_BACKLIGHT          "backlight"
#define LIGHT_ID_KEYBOARD           "keyboard"
#define LIGHT_ID_BUTTONS            "buttons"
#define LIGHT_ID_BATTERY            "battery"
#define LIGHT_ID_NOTIFICATIONS      "notifications"
#define LIGHT_ID_ATTENTION          "attention"

这里我们只实现LIGHT_ID_BACKLIGHT(背光)、LIGHT_ID_BATTERY(电池)、LIGHT_ID_NOTIFICATIONS(通知)。

在HAL中的步骤是:
把硬件LED再次划分为不同的逻辑灯

  1. 实现hw_module_t结构体
  2. 实现open函数,它会根据name返回不同的light_device_t结构体
  3. 针对不同的灯(背光/电池/通知),实现对应的set_light函数
struct light_device_t {struct hw_device_t common;int (*set_light)(struct light_device_t* dev,struct light_state_t const* state);
};

这里需实现三个set_light函数

set_light_battery (struct light_device_t *dev, struct light_state_t const* state)
set_light_notifications (struct light_device_t *dev, struct light_state_t const* state)
set_light_backlight (struct light_device_t *dev, struct light_state_t const *state)

通过这些函数里面操作驱动程序。

led_class驱动

1.对灯光的要求

(1)亮度可调节,如背光灯
(2)颜色可变化,如电池电量指示灯
(3)能闪烁,如通知灯

2.Linux内核对led的支持

在Linux内核中已经实现了一个led class, 它里面已经实现了brightness的调节和blink,文件为:drivers/leds/led-class.c
在ledinit()中的led_class_attrs在/sys/class/leds下创建了brightness max_brightness trigger文件。
可以通过以下命令行:
echo 255 > /sys/class/leds/led1/brightness
cat /sys/class/leds/led1/brightness
cat /sys/class/leds/led1/max_brightness

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/leds.h>#include <linux/gpio.h>
#include <mach/gpio.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"}
};struct led_classdev_4412 {struct led_classdev cdev;int gpio;
};static struct led_classdev_4412 *led_devs;
static void brightness_set_4412(struct led_classdev *led_cdev,enum led_brightness brightness)
{struct led_classdev_4412 *dev = (struct led_classdev_4412*)led_cdev;led_cdev->brightness = brightness;if (brightness != LED_OFF){gpio_set_value(dev->gpio, 0);} else {gpio_set_value(dev->gpio, 1);}
}
int leds_init(void)
{int i;int ret;/* 1.每一个灯分配一个 led_classdev 结构体 *//* 2.每一个灯set *//* 3.每一个灯 led_classdev_register */led_devs = kzalloc(sizeof(struct led_classdev_4412) * sizeof(led_gpios)/sizeof(led_gpios[0]), GFP_KERNEL);if (led_devs == NULL) {printk("NO memory for device\n");return -ENOMEM;}for (i = 0; i < sizeof(led_gpios)/sizeof(led_gpios[0]); i ++) {/* 配置成输出引脚        */s3c_gpio_cfgpin(led_gpios[i].gpio, S3C_GPIO_OUTPUT);/* 先熄灭它 */gpio_set_value(led_gpios[i].gpio, 1);led_devs[i].cdev.max_brightness = LED_FULL;led_devs[i].cdev.brightness_set = brightness_set_4412;led_devs[i].cdev.flags = LED_CORE_SUSPENDRESUME;led_devs[i].cdev.brightness = LED_OFF;led_devs[i].cdev.name = led_gpios[i].name;led_devs[i].gpio = led_gpios[i].gpio; //设置gpioret = led_classdev_register(NULL, &led_devs[i].cdev);if (ret) {i--; //把已经注册成功的要释放掉while(i >= 0) {led_classdev_unregister(&led_devs[i].cdev);}kfree(led_devs);return -EIO;}}return 0;
}void leds_exit(void)
{int i;for (i = 0; i < sizeof(led_gpios)/sizeof(led_gpios[0]); i++) {led_classdev_unregister(&led_devs[i]);}kfree(led_gpios);
}module_init(leds_init);
module_exit(leds_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Wqq Inc.");

将文件放到linux-3.0.86/drivers/leds,并修改linux-3.0.86/drivers/leds/Makefile文件

(2)配置内核

make menuconfig

图形界面如下:

选中
-> Device Drivers
-> LED Support
[] LED Class Support
[
] LED Trigger support
<*> LED Timer Trigger
(3)开始编译

make zImage

将镜像烧在开发板中,启动后可以看到,会在/sys/class/leds下看到led1——led4

4.应用程序设置方法
应用程序是通过sysfs文件来控制led的。

echo 255 > /sys/class/leds/led1/brightness 点亮
echo 0 > /sys/class/leds/led1/brightness 熄灭
echo timmer > /sys/class/leds/led1/trigger ,之后此目录下会多出delay_on/delay_off两个文件
配置它两个可以配置闪烁行为。

闪烁
echo timer > /sys/class/leds/led1/trigger
echo 100 > /sys/class/leds/led1/delay_on //亮100ms
echo 200 > /sys/class/leds/led1/delay_off //灭100ms

分析:
echo timer > /sys/class/leds/led1/trigger
这个会导致led_trigger_store函数调用

//位于\linux-3.0.86-20150324\linux-3.0.86\drivers\leds\led-class.c
static struct device_attribute led_class_attrs[] = {__ATTR(brightness, 0644, led_brightness_show, led_brightness_store),__ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
#ifdef CONFIG_LEDS_TRIGGERS__ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
#endif__ATTR_NULL,
};
led_trigger_store // 1. 从trigger_list找出名为"timer"的triggerlist_for_each_entry(trig, &trigger_list, next_trig) {if (!strcmp(trigger_name, trig->name)) {// 2. 调用led_trigger_set(led_cdev, trig);// 3. 把trigger放入led_classdev的trig_list链表里list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);led_cdev->trigger = trigger;// 4. trigger->activate(led_cdev);// 5. 对于"timer"timer_trig_activate// 6. 创建2个文件: delay_on, delay_offdevice_create_filedevice_create_fileled_blink_set // 让LED闪烁led_set_software_blink}}// echo 100 > /sys/class/leds/led1/delay_on
led_delay_on_store //位于linux-3.0.86-20150324\linux-3.0.86\drivers\leds\ledtrig-timer.cstate = simple_strtoul(buf, &after, 10);led_blink_set  // // 让LED闪烁led_cdev->blink_delay_on = state;//echo 200 > /sys/class/leds/led1/delay_off //位于linux-3.0.86-20150324\linux-3.0.86\drivers\leds\ledtrig-timer.c
led_delay_off_storestate = simple_strtoul(buf, &after, 10);led_blink_set // 让LED闪烁led_cdev->blink_delay_off = state;

编写HAL_lights.c

当有通知或者电量低的时候,灯光会闪烁,要把自己LED驱动结合到安卓系统里
app访问c函数的流程

JNI文件使用HAL

怎么写LIGHTS HAL
a. 实现一个名为HMI的hw_module_t结构体
b. 实现一个open函数, 它会根据name返回一个light_device_t结构体
c. 实现多个light_device_t结构体,每一个对应一个DEVICE
light_device_t结构体里第1个成员是hw_device_t结构体, 紧接着一个set_light函数

参考android-5.0.2/hardware/qcom/display/msm8226/liblight/lights.c
1.新建lights.c文件如下:

/** Copyright (C) 2008 The Android Open Source Project* Copyright (C) 2011 Diogo Ferreira <defer@cyanogenmod.com>* Copyright (C) 2012 Andreas Makris <andreas.makris@gmail.com>* Copyright (C) 2012 The CyanogenMod Project <http://www.cyanogenmod.com>** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/#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>/*以下就是我们linux驱动映射的文件,可以通过将数字写入对应的设备节点,调节亮度和闪烁模式*/
char const*const RED_LED_FILE           = "/sys/class/leds/led1/brightness";
char const*const GREEN_LED_FILE         = "/sys/class/leds/led2/brightness";
char const*const BLUE_LED_FILE          = "/sys/class/leds/led3/brightness";
char const*const RED_LED_FILE_TRIGGER   = "/sys/class/leds/led1/trigger";
char const*const GREEN_LED_FILE_TRIGGER = "/sys/class/leds/led2/trigger";
char const*const BLUE_LED_FILE_TRIGGER  = "/sys/class/leds/led3/trigger";
char const*const RED_LED_FILE_DELAYON   = "/sys/class/leds/led1/delay_on";
char const*const GREEN_LED_FILE_DELAYON = "/sys/class/leds/led2/delay_on";
char const*const BLUE_LED_FILE_DELAYON  = "/sys/class/leds/led3/delay_on";
char const*const RED_LED_FILE_DELAYOFF  = "/sys/class/leds/led1/delay_off";
char const*const GREEN_LED_FILE_DELAYOFF= "/sys/class/leds/led2/delay_off";
char const*const 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;
/* Mini-led state machine */
static struct light_state_t g_notification;
static struct light_state_t g_battery;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;
}
//根据RGB值设置成一个亮度值
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 */
====================================================
/*
struct light_state_t {unsigned int color;//表示把灯设置成什么颜色或把LCD的亮度设为什么int flashMode;//是否闪烁,LIGHT_FLASH_NONE表示不闪int flashOnMS;// 亮的时间int flashOffMS;// 灭的时间int brightnessMode;// 表示LCD的背光亮度模式,有用户模式和感光模式
};
*/
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 0-255 *//* LCD_BACKLIGHT_FILE�ܽ�����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;
}
/* Initializations */
void init_globals () {pthread_mutex_init (&g_lock, NULL);
}
/* Glueing boilerplate */
static int close_lights (struct light_device_t *dev) {if (dev)free(dev);return 0;
}
//根据name匹配相应的set_light方法,并返回hw_device_t结构体
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;
}
static struct hw_module_methods_t lights_module_methods = {.open = open_lights,
};
struct hw_module_t HAL_MODULE_INFO_SYM = {.tag = HARDWARE_MODULE_TAG,.version_major = 1,.version_minor = 0,.id = LIGHTS_HARDWARE_MODULE_ID,.name = "Sony lights module",.author = "Diogo Ferreira <defer@cyanogenmod.com>, Andreas Makris <Andreas.Makris@gmail.com>",.methods = &lights_module_methods,
};

2.创建andriod.mk

把新文件上传到服务器, 所在目录:
hardware/libhardware/modules/lights/lights.c
hardware/libhardware/modules/lights/Android.mk
3.编译

lunch
mmm frameworks/base/services
mmm hardware/libhardware/modules/lights
make snod
./gen-img.sh

framework 灯光系统 – 电池灯源码分析

灯光系统如何调用灯光服务、JNI、HAL等从而操控底层Linux驱动控制的led电池灯去闪烁或者亮不同颜色的灯来通知用户。
不扯别的,直接上图,简单描述整个调用过程:

以下是详细调用过程:

  1. 首先Android系统加载后会执行init进程,然后加载SystemService.java系统服务。
    \frameworks\base\services\java\com\android\server\SystemServer.java
//位于\frameworks\base\services\java\com\android\server\SystemServer.java
private void startCoreServices() {// Manages LEDs and display backlight.mSystemServiceManager.startService(LightsService.class);// Tracks the battery level.  Requires LightService.mSystemServiceManager.startService(BatteryService.class);
}private void startBootstrapServices() {/* 加载电源管理服务,灯光系统属于电源管理的一部分 */mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
}

分析mSystemServiceManager.startService方法:

//位于frameworks\base\services\core\java\com\android\server\SystemServiceManager.java
public <T extends SystemService> T startService(Class<T> serviceClass) {final String name = serviceClass.getName();Slog.i(TAG, "Starting " + name);// Create the service.if (!SystemService.class.isAssignableFrom(serviceClass)) {throw new RuntimeException("Failed to create " + name+ ": service must extend " + SystemService.class.getName());}final T service;try {Constructor<T> constructor = serviceClass.getConstructor(Context.class);service = constructor.newInstance(mContext);//构造了一个服务对象} catch (InstantiationException ex) {...}// Register it.//private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();mServices.add(service);//这里将构造的service,添加到ArrayList<SystemService>中// Start it.try {service.onStart();//调用服务的onStart方法} catch (RuntimeException ex) {throw new RuntimeException("Failed to start service " + name+ ": onStart threw an exception", ex);}return service;}

2 . mSystemServiceManager.startService(LightsService.class);
调用LightsService.java的onStart方法,开启一个LightsManager服务。
LightsManager服务用来管理所有的灯光,包括电池灯、通知灯、背光灯。当需要控制这些灯的时候就需要getLocalService获取这个服务,然后调用这个服务的getLight方法,传入对应的灯的id(参考lighs.h)就可以获取对应的灯的实例化对象,从而可以通过这个对象获取对象里面的操作各种灯的方法。

//\frameworks\base\services\core\java\com\android\server\lights\LightsService.java
public class LightsService extends SystemService {static final String TAG = "LightsService";static final boolean DEBUG = false;//实例化多个LightImpl对象,分别对应电池灯、通知灯、背光灯,通过getLight方法传入不同的id就可以获取对应的对象final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT];//LightImpl继承了Light这个抽象类,并实现了抽象类的setBrightness  setColor  setFlashing等抽象方法,这些方法最终都调用了setLightLocked方法,而setLightLocked方法最终有调用了JNI提供的接口setLight_nativeprivate final class LightImpl extends Light {private LightImpl(int id) {mId = id;}@Overridepublic void setBrightness(int brightness) {setBrightness(brightness, BRIGHTNESS_MODE_USER);}@Overridepublic void setBrightness(int brightness, int brightnessMode) {synchronized (this) {int color = brightness & 0x000000ff;color = 0xff000000 | (color << 16) | (color << 8) | color;setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);}}@Overridepublic void setColor(int color) {synchronized (this) {setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, 0);}}@Overridepublic void setFlashing(int color, int mode, int onMS, int offMS) {synchronized (this) {setLightLocked(color, mode, onMS, offMS, BRIGHTNESS_MODE_USER);}}@Overridepublic void pulse() {pulse(0x00ffffff, 7);}@Overridepublic void pulse(int color, int onMS) {synchronized (this) {if (mColor == 0 && !mFlashing) {setLightLocked(color, LIGHT_FLASH_HARDWARE, onMS, 1000, BRIGHTNESS_MODE_USER);mColor = 0;mH.sendMessageDelayed(Message.obtain(mH, 1, this), onMS);}}}@Overridepublic void turnOff() {synchronized (this) {setLightLocked(0, LIGHT_FLASH_NONE, 0, 0, 0);}}private void stopFlashing() {synchronized (this) {setLightLocked(mColor, LIGHT_FLASH_NONE, 0, 0, BRIGHTNESS_MODE_USER);}}private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) {if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"+ Integer.toHexString(color));mColor = color;mMode = mode;mOnMS = onMS;mOffMS = offMS;Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", 0x"+ Integer.toHexString(color) + ")");try {setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);} finally {Trace.traceEnd(Trace.TRACE_TAG_POWER);}}}private int mId;private int mColor;private int mMode;private int mOnMS;private int mOffMS;private boolean mFlashing;}public LightsService(Context context) {super(context);mNativePointer = init_native();for (int i = 0; i < LightsManager.LIGHT_ID_COUNT; i++) {mLights[i] = new LightImpl(i);}}@Overridepublic void onStart() {publishLocalService(LightsManager.class, mService);}private final LightsManager mService = new LightsManager() {@Overridepublic Light getLight(int id) {if (id < LIGHT_ID_COUNT) {return mLights[id];} else {return null;}}};@Overrideprotected void finalize() throws Throwable {finalize_native(mNativePointer);super.finalize();}private Handler mH = new Handler() {@Overridepublic void handleMessage(Message msg) {LightImpl light = (LightImpl)msg.obj;light.stopFlashing();}};private static native long init_native();private static native void finalize_native(long ptr);static native void setLight_native(long ptr, int light, int color, int mode,int onMS, int offMS, int brightnessMode);private long mNativePointer;
}

3.mSystemServiceManager.startService(BatteryService.class);
此时,会调用BatteryService.java的onStart方法。

//位于\frameworks\base\services\core\java\com\android\server\BatteryService.java
BatteryService的构造方法:
public BatteryService(Context context) {mHandler = new Handler(true /*async*/);/* 获取第2章分析注册的publishLocalService(LightsManager.class, mService);服务 */mLed = new Led(context, getLocalService(LightsManager.class));
}public Led(Context context, LightsManager lights) {/* 获取一个电池灯,后面对电池灯的所以操作都是通过mBatteryLight来进行的 */mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY);
}
public void onStart() {IBinder b = ServiceManager.getService("batteryproperties");final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =  IBatteryPropertiesRegistrar.Stub.asInterface(b);/* 注册一个监听器监听电量的变化,电池的Linux驱动上报电池事件时调用batteryPropertiesChanged更新电池灯 */batteryPropertiesRegistrar.registerListener(new BatteryListener());publishBinderService("battery", new BinderService());publishLocalService(BatteryManagerInternal.class, new LocalService());
}private final class BatteryListener extends IBatteryPropertiesListener.Stub {@Overridepublic void batteryPropertiesChanged(BatteryProperties props) {final long identity = Binder.clearCallingIdentity();BatteryService.this.update(props);Binder.restoreCallingIdentity(identity);}
}private void update(BatteryProperties props) {processValuesLocked(false);
}private void processValuesLocked(boolean force) {/* 发送广播ACTION_BATTERY_CHANGED,通知关心电池状态的进程,低电量提示,关机提示,状态栏图标等* 接收广播请看 PowerManagerService.java的BatteryReceiver方法*/sendIntentLocked();/* 同时更新电池灯的状态  --  根据电量和是否在充电状态从而使闪灯或者红灯亮或黄灯亮等 */mLed.updateLightsLocked();
}private void sendIntentLocked() {//  Pack up the values and broadcast them to everyonefinal Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);mHandler.post(new Runnable() {@Overridepublic void run() {ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);}});
}public void updateLightsLocked() {final int level = mBatteryProps.batteryLevel;final int status = mBatteryProps.batteryStatus;/* 如果电量低于警告值的话 */if (level < mLowBatteryWarningLevel) {/* 如果电量低于警告值,并且正在充电,则将电池灯设置为让低电量颜色的灯亮起,如果没有在充电,则让低电量的灯闪烁 */if (status == BatteryManager.BATTERY_STATUS_CHARGING) { mBatteryLight.setColor(mBatteryLowARGB);} else {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) {mBatteryLight.setColor(mBatteryFullARGB);} else {mBatteryLight.setColor(mBatteryMediumARGB);}/* 如果以上状态都不是,则关闭电池灯 */} else {mBatteryLight.turnOff();}
}

3.1 BatteryPropertiesRegistrar服务
我们看看BatteryPropertiesRegistrar.h的定义:
//位于\system\core\healthd\BatteryPropertiesRegistrar.h


.......
namespace android {class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar,public IBinder::DeathRecipient {public:void publish(const sp<BatteryPropertiesRegistrar>& service);void notifyListeners(struct BatteryProperties props);
............
}};  // namespace android

再看看BatteryPropertiesRegistrar.cpp中我们目前关注的函数:
位于\system\core\healthd\BatteryPropertiesRegistrar.cpp

namespace android {void BatteryPropertiesRegistrar::publish(const sp<BatteryPropertiesRegistrar>& service) {//注册服务到service manager进程defaultServiceManager()->addService(String16("batteryproperties"), service);
}void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) {Mutex::Autolock _l(mRegistrationLock);for (size_t i = 0; i < mListeners.size(); i++) {//通知观察者mListeners[i]->batteryPropertiesChanged(props);}
}void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesListener>& listener) {{if (listener == NULL)return;Mutex::Autolock _l(mRegistrationLock);// check whether this is a duplicatefor (size_t i = 0; i < mListeners.size(); i++) {if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) {return;}}//存储观察者mListeners.add(listener);IInterface::asBinder(listener)->linkToDeath(this);}//更新底层的电源状态,最终将调用到BatteryMonitor.cpp的update函数//也是BatteryService第一次启动,注册Listener后,立马就会更新一次电源信息healthd_battery_update();
}
..........
}

我们见过太多对等的Binder通信结构,即双方的主体均在native层,或都在Java层。
这是第一次看到客户端在Java层,服务端在native层。

其实仔细想想,这是完全符合Binder通信架构的。
对于客户端而言,它只需要从service manager进程获取到服务端的BpBinder值,然后利用这个BpBinder值逐步构成Java层的BinderProxy对象(会进一步转化为基于业务的服务代理)。
客户端根本就不要求服务端具有Java层的结构。在实际的通信过程中,客户端数据通过Binder驱动,传到服务端的native层。若native层就能够完成处理过程,就不需要往Java层作进一步的传输,直接返回结果即可。整个处理过程,对于客户端而言,完全是透明的。
3.2 回调接口的作用
我们回过头来看一下BatteryListener的回调接口:

//位于\frameworks\base\services\core\java\com\android\server\BatteryService.java
private final class BatteryListener extends IBatteryPropertiesListener.Stub {@Override public void batteryPropertiesChanged(BatteryProperties props) {final long identity = Binder.clearCallingIdentity();try {//电源属性发生变化后,回调BatteryService的update函数BatteryService.this.update(props);} finally {Binder.restoreCallingIdentity(identity);}}
}private void update(BatteryProperties props) {synchronized (mLock) {if (!mUpdatesStopped) {mBatteryProps = props;// Process the new values.processValuesLocked(false);} else {mLastBatteryProps.set(props);}}}private void processValuesLocked(boolean force) {/* 发送广播ACTION_BATTERY_CHANGED,通知关心电池状态的进程,低电量提示,关机提示,状态栏图标等* 接收广播请看 PowerManagerService.java的BatteryReceiver方法*/sendIntentLocked();/* 同时更新电池灯的状态  --  根据电量和是否在充电状态从而使闪灯或者红灯亮或黄灯亮等 */mLed.updateLightsLocked();
}private void sendIntentLocked() {//  Pack up the values and broadcast them to everyonefinal Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);mHandler.post(new Runnable() {@Overridepublic void run() {ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);}});
}public void updateLightsLocked() {final int level = mBatteryProps.batteryLevel;final int status = mBatteryProps.batteryStatus;/* 如果电量低于警告值的话 */if (level < mLowBatteryWarningLevel) {/* 如果电量低于警告值,并且正在充电,则将电池灯设置为让低电量颜色的灯亮起,如果没有在充电,则让低电量的灯闪烁 */if (status == BatteryManager.BATTERY_STATUS_CHARGING) { mBatteryLight.setColor(mBatteryLowARGB);} else {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) {mBatteryLight.setColor(mBatteryFullARGB);} else {mBatteryLight.setColor(mBatteryMediumARGB);}/* 如果以上状态都不是,则关闭电池灯 */} else {mBatteryLight.turnOff();}
}

看一下广播的接收者,如下:

//位于\frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java// Register for broadcasts from other components of the system.IntentFilter filter = new IntentFilter();filter.addAction(Intent.ACTION_BATTERY_CHANGED);filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);mContext.registerReceiver(new BatteryReceiver(), filter, null, mHandler);private final class BatteryReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {synchronized (mLock) {handleBatteryStateChangedLocked();}}}private void handleBatteryStateChangedLocked() {mDirty |= DIRTY_BATTERY_STATE;updatePowerStateLocked();}

通知灯

我们知道,当手机接收到短信的时候,他会发出声音,并且这个通知灯会亮起,那么我们怎么实现呢?一般步骤如下:

1.  getSystemService("NOTIFICATION_SERVICE")
2. 构造notfification(1)类别:该次实现类别为通知等(2)其他:颜色,OnMS,OffMS。
3. 发出通知

代码如下:

private void FlashingLight(){NotificationManager nm = ( NotificationManager ) getSystemService( NOTIFICATION_SERVICE );
//构造通知
Notification notif = new Notification();notif.flags = Notification.FLAG_SHOW_LIGHTS;notif.ledARGB = 0xFF0000ff;notif.ledOnMS = 100;notif.ledOffMS = 100;
//发出通知nm.notify(LED_NOTIFICATION_ID, notif);}

下面我们先分析一下系统中的源码
源码分析
我们打开源码中lights.h文件,可以看到如下:

#define LIGHT_ID_BACKLIGHT          "backlight"
#define LIGHT_ID_KEYBOARD           "keyboard"
#define LIGHT_ID_BUTTONS            "buttons"
#define LIGHT_ID_BATTERY            "battery"
#define LIGHT_ID_NOTIFICATIONS      "notifications"
#define LIGHT_ID_ATTENTION          "attention"
#define LIGHT_ID_BLUETOOTH          "bluetooth"
#define LIGHT_ID_WIFI               "wifi"

然后在源码中搜索LIGHT_ID_NOTIFICATIONS(通知灯),然后找到文件NotificationManagerService.java

//位于\frameworks\base\services\core\java\com\android\server\notification\NotificationManagerService.java
public void onStart() {...
final LightsManager lights = getLocalService(LightsManager.class);
mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
...
}

和电池灯类似,获得一个对应的Light类型对象,之后所有对通知灯的操作,都是通过mNotificationLight实现,我们还能再改文件中找到一个updateLightsLocked()方法,名字和电池灯中的是一样的,当然内容是不一样的,但是对于通知灯的所有操作都是通过updateLightsLocked()方法实现的,现在我们想知道updateLightsLocked()的调用过程,直接从updateLightsLocked着手是比较困难的。前面提到过,我们编写APP程序时会调用getSystemService(“NOTIFICATION_SERVICE”),那么我们在源码中搜索一下NOTIFICATION_SERVICE,既然有get那么肯定存在类似set的方法,最终我们锁定文件SystemServiceRegistry.java,调用了,以下是一个静态代码块。

//位于frameworks\base\core\java\android\app\SystemServiceRegistry.java
public final class SystemServiceRegistry {static {registerService(Context.NOTIFICATION_SERVICE, NotificationManager.class,new CachedServiceFetcher<NotificationManager>() {@Overridepublic NotificationManager createService(ContextImpl ctx) {final Context outerContext = ctx.getOuterContext();return new NotificationManager(new ContextThemeWrapper(outerContext,Resources.selectSystemTheme(0,outerContext.getApplicationInfo().targetSdkVersion,com.android.internal.R.style.Theme_Dialog,com.android.internal.R.style.Theme_Holo_Dialog,com.android.internal.R.style.Theme_DeviceDefault_Dialog,com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog)),ctx.mMainThread.getHandler());}});}
}private static <T> void registerService(@NonNull String serviceName,@NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName());}
//在这里三个map
// private static final Map<Class<?>, String> SYSTEM_SERVICE_NAMES =new ArrayMap<Class<?>, String>();
//    private static final Map<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =new ArrayMap<String, ServiceFetcher<?>>();
//    private static final Map<String, String> SYSTEM_SERVICE_CLASS_NAMES = new ArrayMap<>();

在看看getSystemService方法

public static Object getSystemService(ContextImpl ctx, String name) {if (name == null) {return null;}final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);if (fetcher == null) {if (sEnableServiceNotFoundWtf) {Slog.wtf(TAG, "Unknown manager requested: " + name);}return null;}final Object ret = fetcher.getService(ctx);if (sEnableServiceNotFoundWtf && ret == null) {// Some services do return null in certain situations, so don't do WTF for them.switch (name) {case Context.CONTENT_CAPTURE_MANAGER_SERVICE:case Context.APP_PREDICTION_SERVICE:case Context.INCREMENTAL_SERVICE:return null;}Slog.wtf(TAG, "Manager wrapper not available: " + name);return null;}return ret;}//fetcher.getService(ctx)后
static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {private final int mCacheIndex;CachedServiceFetcher() {// Note this class must be instantiated only by the static initializer of the// outer class (SystemServiceRegistry), which already does the synchronization,// so bare access to sServiceCacheSize is okay here.mCacheIndex = sServiceCacheSize++;}@Override@SuppressWarnings("unchecked")public final T getService(ContextImpl ctx) {final Object[] cache = ctx.mServiceCache;final int[] gates = ctx.mServiceInitializationStateArray;boolean interrupted = false;T ret = null;for (;;) {boolean doInitialize = false;synchronized (cache) {// Return it if we already have a cached instance.T service = (T) cache[mCacheIndex];if (service != null || gates[mCacheIndex] == ContextImpl.STATE_NOT_FOUND) {ret = service;break; // exit the for (;;)}// At this point, the gate must be either UNINITIALIZED or INITIALIZING.if (gates[mCacheIndex] == ContextImpl.STATE_UNINITIALIZED) {doInitialize = true;gates[mCacheIndex] = ContextImpl.STATE_INITIALIZING;}}if (doInitialize) {// Only the first thread gets here.T service = null;@ServiceInitializationState int newState = ContextImpl.STATE_NOT_FOUND;try {// This thread is the first one to get here. Instantiate the service// *without* the cache lock held.service = createService(ctx);//这里创建servicenewState = ContextImpl.STATE_READY;} catch (ServiceNotFoundException e) {onServiceNotFound(e);} finally {synchronized (cache) {cache[mCacheIndex] = service;gates[mCacheIndex] = newState;cache.notifyAll();}}ret = service;break; // exit the for (;;)}return ret;}...public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;}

当调用frameworks\base\core\java\android\app\ContextImpl.java中的如下方法时

final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();

就会触发上述静态块的执行。
综合以上,所以getSystemService返回的是以下registerService方法中的createService中的返回值,如下即new NotificationManager。在使用getSystemService(“NOTIFICATION_SERVICE”),我们获得的是一个NotificationManager实例化的对象。

static {registerService(Context.NOTIFICATION_SERVICE, NotificationManager.class,new CachedServiceFetcher<NotificationManager>() {@Overridepublic NotificationManager createService(ContextImpl ctx) {final Context outerContext = ctx.getOuterContext();return new NotificationManager(new ContextThemeWrapper(outerContext,Resources.selectSystemTheme(0,outerContext.getApplicationInfo().targetSdkVersion,com.android.internal.R.style.Theme_Dialog,com.android.internal.R.style.Theme_Holo_Dialog,com.android.internal.R.style.Theme_DeviceDefault_Dialog,com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog)),ctx.mMainThread.getHandler());}});}

既然我们获得了NotificationManager的实例化对象,那么我们应该怎么使用呢?我们打开NotificationManager.java文件,
我们找到notify方法,可知最终调用notifyAsUse方法:

//位于frameworks\base\core\java\android\app\NotificationManager.javapublic void notify(String tag, int id, Notification notification){int[] idOut = new int[1];INotificationManager service = getService();String pkg = mContext.getPackageName();...if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");Notification stripped = notification.clone();Builder.stripForDelivery(stripped);try {service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,...} catch (RemoteException e) {}}
//获取一个binder
static public INotificationManager getService(){if (sService != null) {return sService;}IBinder b = ServiceManager.getService("notification");sService = INotificationManager.Stub.asInterface(b);return sService;}

那么系统中肯定有其他的地方注册了"notification",我们之前提到过SystemServer.java会注册很多的注册方法,我们在其中搜索notification,可以找到

mSystemServiceManager.startService(NotificationManagerService.class);//startService会执行mServices.add(service)、service.onStart();

我们查看NotificationManagerService.java文件,当这个类被创建的时候,其中onStart()方法会被调用,该方法最后会调用publishBinderService(Context.NOTIFICATION_SERVICE, mService)。

protected final void publishBinderService(String name, IBinder service,boolean allowIsolated) {ServiceManager.addService(name, service, allowIsolated);}

根据其传入的参数NOTIFICATION_SERVICE = "notification"进行注册。就可以通过使用binder拿到服务对象。
service.enqueueNotificationWithTag(),现在我们回过头来看看enqueueNotificationWithTag()方法:

enqueueNotificationWithTag()enqueueNotificationInternal()mHandler.post(new EnqueueNotificationRunnable(userId, r));EnqueueNotificationRunnable()run()buzzBeepBlinkLocked(r);

最后调用了方法buzzBeepBlinkLocked(),这个方法通过参数r分辨通知是震动,还是声音,或者闪光。在该方法内我们可以找到:

if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold&& ((record.getSuppressedVisualEffects()& NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {mLights.add(key);updateLightsLocked();if (mUseAttentionLight) {mAttentionLight.pulse();}blink = true;}

判断通知是否为闪灯,如果为闪灯,则调用 updateLightsLocked(),这样我们分析的就和之前的连系到一起了。

//位于\frameworks\base\services\core\java\com\android\server\notification\NotificationManagerService.java
void updateLightsLocked(){// handle notification lightsNotificationRecord ledNotification = null;while (ledNotification == null && !mLights.isEmpty()) {final String owner = mLights.get(mLights.size() - 1);ledNotification = mNotificationsByKey.get(owner);if (ledNotification == null) {Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);mLights.remove(owner);}}// Don't flash while we are in a call or screen is onif (ledNotification == null || mInCall || mScreenOn) {mNotificationLight.turnOff();mStatusBar.notificationLightOff();} else {final Notification ledno = ledNotification.sbn.getNotification();int ledARGB = ledno.ledARGB;int ledOnMS = ledno.ledOnMS;int ledOffMS = ledno.ledOffMS;if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {ledARGB = mDefaultNotificationColor;ledOnMS = mDefaultNotificationLedOn;ledOffMS = mDefaultNotificationLedOff;}if (mNotificationPulseEnabled) {// pulse repeatedlymNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,ledOnMS, ledOffMS);}// let SystemUI make an independent decisionmStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);}}

应用程序编写
首先我们打开MainActivity.java文件,可以看到onCreate()方法,该方法会在APP启动的时候调用,那么我们在 class MainActivity中定义一个button如下:

private  Button mLightButton = null;

然后在onCreate()方法中进行实例化,与界面设计的button进行绑定,并且编写其点击方法:

 mLightButton = (Button)findViewById(R.id.button);mLightButton.setOnClickListener(new View.OnClickListener() {@SuppressLint("SetTextI18n")public void onClick(View v) {// Perform action on clickflashing = !flashing;if (flashing){mLightButton.setText("Stop Flashing the Light");}else {mLightButton.setText("Flashing Light at 20S");}mLightHander.postDelayed(mLightRunnable, 20000);}});

可以看到我们定义了一个标志位flashing,用来记录是否闪烁的状态,每次点击按钮之后,都会根据标志位的不同,然后显示器对应的字符串。在最后我们开启定时器,那么定时器在java中是怎么使用的呢?我们需要定义一个class Handle。这个handler中并没有绑定新线程,所以还是次线程处理消息事件,即2s后处理。

private Handler mLightHander = new Handler();
private LightRunnable mLightRunnable = new LightRunnable();
class LightRunnable implements Runnable {@Overridepublic void run() {if(flashing) {FlashingLight();}else{ClearLED();}}}

该接口需要实现一个方法,该方法就是定时时间到,然后执行的函数,可以看到在其中我们调用了两个方法,分别为 FlashingLight(),ClearLED()。其中FlashingLight()的实现如下:

private void FlashingLight(){NotificationManager nm = ( NotificationManager ) getSystemService( NOTIFICATION_SERVICE );Notification notif = new Notification();notif.flags = Notification.FLAG_SHOW_LIGHTS;notif.ledARGB = 0xFF0000ff;notif.ledOnMS = 100;notif.ledOffMS = 100;nm.notify(LED_NOTIFICATION_ID, notif);}private void ClearLED(){NotificationManager nm = ( NotificationManager ) getSystemService( NOTIFICATION_SERVICE );nm.cancel(LED_NOTIFICATION_ID);}

Android灯光系统(电池灯/通知灯)相关推荐

  1. 第四章:Android灯光系统(5)-通知灯

    上小节我们分析了电池灯的源码,这小节我们将编写通知灯的使用过程. 我们知道,当手机接收到短信的时候,他会发出声音,并且这个通知灯会亮起,那么我们怎么实现呢?一般步骤如下: 1. getSystemSe ...

  2. 基于android模拟led发光,LED通知灯模拟app

    LED通知灯模拟app是一款对OLED屏幕能进行模拟的软件,软件中用OLED灯的屏幕为手机带来通知的功能,让那些因为屏幕而不得不舍弃通知灯功能的用户可以重新使用这个功能,有需要的用户快来下载吧! LE ...

  3. android 通知灯 测试,Android灯光系统通知灯【转】

    标签: 一.通知灯应用程序的编写 1.首先实现一个按钮功能 xmlns:tools="http://schemas.android.com/tools" android:layou ...

  4. 第四章:Android灯光系统(3)-编写HAL

    在编写HAL代码之前,我们先回顾一下之前的硬件访问服务,安卓的应用程序是用java语音写的,如果想访问硬件,必须调用C函数,他怎么调用C函数呢? 1.loadLibrary( 加载C库),在C库中,他 ...

  5. Tiny4412——Android灯光系统

    CSDN仅用于增加百度收录权重,排版未优化,日常不维护.请访问:www.hceng.cn 查看.评论. 本博文对应地址: https://hceng.cn/2019/05/09/Tiny4412--A ...

  6. Android灯光系统框架分析

    首先别人的APP要能直接访问到你写的硬件或者不经过任何修改的APP能直接使用你的驱动,就需要使用系统自带的JNI,所以我们需要写出符合系统自带JNI文件的HAL文件和驱动程序,下面具体分析一个这个HA ...

  7. 第四章:Android灯光系统(6)-背光灯

    上小节我们实现了对通知灯控制,该小节我们讲解怎么实现对背光灯的控制,一般来说,操作是比较简单的,我们只需要往数据库中写入亮度就可以了,那么为什么会这么简单,当然是有其他的程序(ContentObser ...

  8. android 背光灯分析,Android灯光系统--深入理解背光灯

    Android灯光系统--深入理解背光灯 一.怎么控制背光灯(简述) APP将亮度值写入数据库 线程检测数据库的值是否发生变化 这种机制成为"内容观察者"--contentObse ...

  9. Android开发笔记——Android 9发送通知

    Android 9 发送通知 发送通知 布局文件 程序代码 运行截图 发送通知 布局文件 <?xml version="1.0" encoding="utf-8&q ...

最新文章

  1. 是时候不把智能手机叫做电话了:移动AI时代来临!
  2. 网络推广专员浅析网络推广中如何分析网站建设水平孰优孰劣?
  3. 自绘制View---------左右滑动刻度调频View
  4. 台式计算机的配置清单(硬件和软件),台式电脑的配置清单和产品推荐
  5. 黄聪:visual studio 2017编译运行出现脚本发生错误等问题如何解决?
  6. 229. Majority Element II
  7. 第06讲:多路加速,了解多进程基本原理
  8. 编程笔记:python 中的 OrderedDict
  9. 关于CSS的一些基础内容
  10. python读写pdf_Python读写PDF
  11. 统计信号处理基础-估计与检测理论的学习过程
  12. yaw公式_3D 视角旋转矩阵 yaw pitch roll (pan, tilt)的数学计算
  13. 企业微信群机器人入门
  14. Codeforces 1010D Mars rover
  15. conver Json to map by fastJson
  16. 小米手机v3.exo 合并_eXo Platform 3.0访谈
  17. NCEP再分析数据(FNL)Python下载
  18. vb.net图书管理系统
  19. ubuntu 软件推荐
  20. 王者荣耀服务器维修多久,王者荣耀服务器维护多久?苹果更新后进不去是正常的吗?...

热门文章

  1. 微信小程序关注公众号
  2. 软件配置 | ios系统Clion下载、安装、配置环境
  3. ORA-39405: Oracle Data Pump does not support importing from a source database with TSTZ version 36
  4. PTA航空公司VIP客户查询c++版——山东科技大学
  5. ios王者荣耀服务器维护31号,王者荣耀3月31日IOS无法更新什么情况?王者荣耀ios更新特别慢解决方法...
  6. 求一个数各个位数之和
  7. Android App耗电分析
  8. oj 2143 图结构练习——最短路径的几种算法
  9. 十进制转换成二进制java
  10. 历史上十大著名思想实验