android qcom Lights框架以及开发外部应用调用思路
近期项目开发中遇到需要增加手电筒控制的接口(不访问camera)。此类问题实际上实现起来非常简单。本着学习的精神以及不愿意简陋的实现该功能,因此把Lights框架查看了一遍,并增加新灯的控制方式。
先从调用处开始查看:
frameworks/base/services/core/java/com/android/server/BatteryService.java
//调用处,获取LightsManager的操作句柄
mLed = new Led(context, getLocalService(LightsManager.class));
private final class Led {
private final Light mBatteryLight;
…………………………………………//省略无关代码
public Led(Context context, LightsManager lights) {
//获取灯光操作句柄的方法
mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY);
……………………………………//省略无关代码
mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED,mBatteryLedOn, mBatteryLedOff);//红灯闪
mBatteryLight.setColor(mBatteryFullARGB);//绿灯亮
mBatteryLight.turnOff();//充电灯关
}
}
注意此处代码获取LightsManager类为getlocalService方式,而非ServiceManager.getService的方式获取。因此无法用进程间通信。这里给应用开发人员出了难题。我们此次会将这个问题处理使得应用开发人员可以操作到LightsManager的句柄。
继续分析代码
frameworks/base/services/core/java/com/android/server/lights/LightsManager.java
public abstract class LightsManager {
public static final int LIGHT_ID_BACKLIGHT = 0;//背光灯
public static final int LIGHT_ID_KEYBOARD = 1;//键盘灯
public static final int LIGHT_ID_BUTTONS = 2;//Home Menu,Back键灯
public static final int LIGHT_ID_BATTERY = 3; //充电灯
public static final int LIGHT_ID_NOTIFICATIONS = 4;//通知灯
public static final int LIGHT_ID_ATTENTION = 5; //重要灯
public static final int LIGHT_ID_BLUETOOTH = 6;//蓝牙
public static final int LIGHT_ID_WIFI = 7;//WIFI灯
public static final int LIGHT_ID_TORCH = 8;//新增手电筒灯
public static final int LIGHT_ID_COUNT = 9;//修改原来个数为8
public abstract Light getLight(int id);
}
分析代码可得,LightsManager实际仅仅只是通过ID号获取对应灯光句柄。因此。我们这里新添加手电筒类型,并且将计数值增加1.我们再来看看灯光句柄的代码
frameworks/base/services/core/java/com/android/server/lights/Light.java
public abstract class Light {
public static final int LIGHT_FLASH_NONE = 0;
public static final int LIGHT_FLASH_TIMED = 1;
public static final int LIGHT_FLASH_HARDWARE = 2;
/**
* Light brightness is managed by a user setting.
*/
public static final int BRIGHTNESS_MODE_USER = 0;
/**
* Light brightness is managed by a light sensor.
*/
public static final int BRIGHTNESS_MODE_SENSOR = 1;
public abstract void setBrightness(int brightness);
public abstract void setBrightness(int brightness, int brightnessMode);
public abstract void setColor(int color);
public abstract void setFlashing(int color, int mode, int onMS, int offMS);
public abstract void pulse();
public abstract void pulse(int color, int onMS);
public abstract void turnOff();
}
是一个抽象类。这两个文件的具体实现都在
frameworks/base/services/core/java/com/android/server/lights/LightsService.java
public class LightsService extends SystemService {
……………………//省略
//灯光操作句柄实现处
private final class LightImpl extends Light {
private LightImpl(int id) {
mId = id;
}
@Override
public void setBrightness(int brightness) {
setBrightness(brightness, BRIGHTNESS_MODE_USER);
}
@Override
public void setBrightness(int brightness, int brightnessMode) {
synchronized (this) {
int color = brightness & 0x000000ff;
color = 0xff000000 | (color << 16) | (color << 8) | color;
//最终这个函数也会调用到一个jni方法setLight_native
setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
}
}
……………………//省略
}
…………………………//省略
@Override
public void onStart() {
……………………//省略
//此行代码表明为本地服务。app无法直接调用。
publishLocalService(LightsManager.class, mService);
}
//LightsManager具体实现主要作用就是获取对应id的灯光句柄
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;
}
}
};
……………………………………//省略
public LightsService(Context context) {
//构造函数中初始化走向一个jni方法
mNativePointer = init_native();
}
}
我们来看两个关键jni方法
frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp
enum {
LIGHT_INDEX_BACKLIGHT = 0,
LIGHT_INDEX_KEYBOARD = 1,
LIGHT_INDEX_BUTTONS = 2,
LIGHT_INDEX_BATTERY = 3,
LIGHT_INDEX_NOTIFICATIONS = 4,
LIGHT_INDEX_ATTENTION = 5,
LIGHT_INDEX_BLUETOOTH = 6,
LIGHT_INDEX_WIFI = 7,
LIGHT_INDEX_TORCH = 8, //新增索引
LIGHT_COUNT
};
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);
//新增手电筒索引LIGHT_INDEX_TORCH以及
//手电筒对象名称LIGHT_ID_TORCH 在头文件lights.h中定义.
//get_deivce实现的就是名称匹配获取hw对象,
//此处不再赘述,熟悉hw中间层的应该了解。可以仔细看源码。
devices->lights[LIGHT_INDEX_TORCH]
= get_device(module, LIGHT_ID_TORCH);
} else {
memset(devices, 0, sizeof(Devices));
}
return (jlong)devices;
}
static void setLight_native(JNIEnv *env, jobject clazz, jlong ptr,
jint light, jint colorARGB, jint flashMode, jint onMS, jint offMS, jint brightnessM
ode)
{
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);//最终调用hw中间层的set_light函数指针所指向的方法。
}
}
新增内容以及关键点已在代码中注释,现转到hardware层
hardware/qcom/display/liblight/lights.c
//上个代码get_device所对应调用函数
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))
…………………………//省略
else if (0 == strcmp(LIGHT_ID_TORCH, name))
//上个代码中get_device传入的名字以手电筒为例匹配对应然后将对应set_light操作赋值
set_light = set_light_torch;
else
return -EINVAL;
…………………………//省略
struct light_device_t *dev = malloc(sizeof(struct light_device_t));
…………………………//省略
dev->set_light = set_light;//将对应的set_light赋予Devi设备的操作。
*device = (struct hw_device_t*)dev;
return 0;
}
//上个代码set_light最终调用函数以手电筒为例
static int
set_light_torch(struct light_device_t* dev,
struct light_state_t const* state)
{
int err = 0;
int brightness = rgb_to_brightness(state);
if(!dev) {
return -1;
}
pthread_mutex_lock(&g_lock);
//TORCH_FILE为新增
//char const*const TORCH_FILE ="/sys/class/leds/torch-light0/brightness"
//实际上最终也是写文件实现灯的亮灭。
err = write_int(TORCH_FILE, brightness);
pthread_mutex_unlock(&g_lock);
return err;
}
分析整个框架。基本上新灯添加完毕,原生框架代码也很清晰,只是因为是localservice。外部无法调用只能systemservice自己玩。因此考虑包一层bindservice这样既不影响原来的localservice又能实现应用调用,这包一层的方法自然想到aidl。
interface IUserLightsManager {
void setLight(int light_id);
void setBrightness(int brightness);
}
实现它!!盘它!!
//注意此处需要继承SystemService才能getLocalService
public class UserLightsManager extends SystemService {
private final String Tag = "UserLightsManager";
private Light mLight;
private LightsManager mLightsManager;
public void onStart() {
//获得原生框架LightsManager句柄
mLightsManager = getLocalService(LightsManager.class);
//将mService实际上是aidl的实现作为bindservice。
//此函数最终实现ServiceManager.addService类似。
publishBinderService("UserLightsManager", mService);
}
public UserLightsManager(Context context) {
super(context);
}
//实现aidl 通过此aidl间接调用LightsManager
private final IBinder mService = new IUserLightsManager.Stub() {
//light_id
//see frameworks/base/services/core/java/com/android/server/lights/LightsMa
public void setLight(int light_id ) {
//mLightsManager = getLocalService(LightsManager.class);
//LightsManager.LIGHT_ID_xxxx
Log.d(Tag,"setLight");
mLight = mLightsManager.getLight(light_id);
Log.d(Tag,"setLight mLight" + mLight);
}
public void setBrightness(int brightness) {
Log.d(Tag,"setBrightness mLight" + mLight);
if(mLight != null) {
mLight.setBrightness(brightness);
}
}
};
}
还需要将这个服务启动一下
frameworks/base/services/java/com/android/server/SystemServer.java
mSystemServiceManager.startService(UserLightsManager.class);
最后应用层调用方法
IBinder b = ServiceManager.getService("UserLightsManager");
if(b != null ) {
mIUserLightsManager = IUserLightsManager.Stub.asInterface(b);
}
mIUserLightsManager.setLight(8);
mIUserLightsManager.setBrightness(255);
以上,如有不足之处欢迎来讨论优化