0%

理解Android灯光系统

基于Android P源码分析,深入理解Android 灯光系统框架。

前言

灯光系统相对比较简单,但涉及多种不同的灯光类型,包括背光灯、电池灯、呼吸灯等。

Java服务

SystemServer.java

zygote 进程启动时会加载 SystemServer.main() 方法,启动系统服务,其中就包括 LightsService 和 startHidlServices。

frameworks/base/services/java/com/android/server/SystemServer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import com.android.server.lights.LightsService;

public final class SystemServer {
private static final String TAG = "SystemServer";

private void startBootstrapServices() {
// Manages LEDs and display backlight so we need it to bring up the display.
traceBeginAndSlog("StartLightsService");
mSystemServiceManager.startService(LightsService.class);
traceEnd();
}

private void run() {
...
// Start services.
try {
traceBeginAndSlog("StartServices");
startBootstrapServices();
startCoreServices();
startOtherServices();
SystemServerInitThreadPool.shutdown();
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
traceEnd();
}
}

private static native void startHidlServices();

private void startOtherServices() {
...
startHidlServices();
...
}

/**
* The main entry point from zygote.
*/
public static void main(String[] args) {
new SystemServer().run();
}

}

LightsService.java

灯光服务类的实现,包括 native 本地方法

frameworks/base/services/core/java/com/android/server/lights/LightsService.java

1
2
3
4
5
6
7
8
9
10
11
12
public class LightsService extends SystemService {
static final String TAG = "LightsService";
final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT];

private final class LightImpl extends Light {
...
}

// native jni方法
static native void setLight_native(int light, int color, int mode,
int onMS, int offMS, int brightnessMode);
}

JNI实现

setLight_native 会对应用传入的参数进行有效性检测,然后调用 hal->setLight() 方法。

frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
static void setLight_native(
JNIEnv* /* env */,
jobject /* clazz */,
jint light,
jint colorARGB,
jint flashMode,
jint onMS,
jint offMS,
jint brightnessMode) {

if (!validate(light, flashMode, brightnessMode)) {
return;
}

sp<ILight> hal = LightHal::associate();

if (hal == nullptr) {
return;
}

Type type = static_cast<Type>(light);
LightState state = constructState(
colorARGB, flashMode, onMS, offMS, brightnessMode);

{
android::base::Timer t;
Return<Status> ret = hal->setLight(type, state);
processReturn(ret, type, state);
if (t.duration() > 50ms) ALOGD("Excessive delay setting light");
}
}

static const JNINativeMethod method_table[] = {
{ "setLight_native", "(IIIIII)V", (void*)setLight_native },
};

int register_android_server_LightsService(JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/lights/LightsService",
method_table, NELEM(method_table));
}

HIDL客户端

ILight.hal 定义了 ILight hidl 接口方法,而 types.hal 定义了相应的类型和结构体。

hardware/interfaces/light/2.0/ILight.hal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package android.hardware.light@2.0;

interface ILight {

/**
* Set the provided lights to the provided values.
*
* @param type logical light to set
* @param state describes what the light should look like.
* @return status result of applying state transformation.
*/
setLight(Type type, LightState state) generates (Status status);

/**
* Discover what indicator lights are available.
*
* @return types list of available lights
*/
getSupportedTypes() generates (vec<Type> types);

};

经过编译会生成对应 C++/Java 源码和相关的库文件。

out/soong/.intermediates/hardware/interfaces/light/2.0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
$ ls 
total 24
drwxrwxr-x 6 xujinlong xujinlong 4096 Nov 25 11:49 android.hardware.light@2.0
drwxrwxr-x 3 xujinlong xujinlong 4096 Nov 25 11:31 android.hardware.light@2.0_genc++
drwxrwxr-x 3 xujinlong xujinlong 4096 Nov 25 11:31 android.hardware.light@2.0_genc++_headers
drwxrwxr-x 3 xujinlong xujinlong 4096 Nov 25 11:46 android.hardware.light-V2.0-java
drwxrwxr-x 3 xujinlong xujinlong 4096 Nov 25 11:31 android.hardware.light-V2.0-java_gen_java
drwxrwxr-x 4 xujinlong xujinlong 4096 Nov 25 11:45 default
$
$ tree android.hardware.light@2.0_genc++_headers/
android.hardware.light@2.0_genc++_headers/
└── gen
└── android
└── hardware
└── light
└── 2.0
├── BnHwLight.h
├── BpHwLight.h
├── BsLight.h
├── hwtypes.h
├── IHwLight.h
├── ILight.h
├── ILight.h.d
└── types.h

5 directories, 8 files
$ tree android.hardware.light-V2.0-java_gen_java/
android.hardware.light-V2.0-java_gen_java/
└── gen
└── android
└── hardware
└── light
└── V2_0
├── Brightness.java
├── Flash.java
├── ILight.java
├── ILight.java.d
├── LightState.java
├── Status.java
└── Type.java

5 directories, 7 files
$ tree android.hardware.light@2.0_genc++
android.hardware.light@2.0_genc++
└── gen
└── android
└── hardware
└── light
└── 2.0
├── LightAll.cpp
├── LightAll.cpp.d
└── types.cpp

5 directories, 3 files
  • default 目录存放生成的 android.hardware.light@2.0-service 执行程序和相应的依赖库 android.hardware.light@2.0-impl.so
  • android.hardware.light@2.0 目录存放生成的 hidl 库 android.hardware.light@2.0.so
  • android.hardware.light@2.0_genc++_headers 目录存放的是生成的头文件
  • android.hardware.light@2.0_genc++ 目录存放的是生成的 C++ 源码
  • android.hardware.light-V2.0-java_gen_java 目录存放的是生成的 Java 源码
  • android.hardware.light-V2.0-java 目录存放的是生成的 jar

生成的 LightAll.cpp 源码中实现了 BnHwLight 和 BpHwLight 接口方法,重点关注 setLight 方法的实现。

1
2
3
4
5
6
// Methods from ::android::hardware::light::V2_0::ILight follow.
::android::hardware::Return<::android::hardware::light::V2_0::Status> BpHwLight::setLight(::android::hardware::light::V2_0::Type type, const ::android::hardware::light::V2_0::LightState& state){
::android::hardware::Return<::android::hardware::light::V2_0::Status> _hidl_out = ::android::hardware::light::V2_0::BpHwLight::_hidl_setLight(this, this, type, state);

return _hidl_out;
}

BpHwLight::setLight 调用 BpHwLight::_hidl_setLight 方法,然后通过 Binder 机制调用到 BnHwLight::_hidl_setLight 方法,最终调用 HIDL 服务的 setLight 方法。

1
2
3
4
5
6
7
BpHwLight::setLight
BpHwLight::_hidl_setLight
transact(1 /* setLight */, _hidl_data, &_hidl_reply);

BnHwLight::onTransact
BnHwLight::_hidl_setLight
static_cast<ILight*>(_hidl_this->getImpl().get())->setLight(type, *state);

HIDL服务

查看当前系统的light相关的进程,结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# ps -ef | grep light
system 3115 1 0 10:50:44 ? 00:00:00 android.hardware.light@2.0-service
root 6239 6155 4 13:39:05 pts/1 00:00:00 grep light

# pmap 3115
3115: /vendor/bin/hw/android.hardware.light@2.0-service
aeb04000 4K r-x-- /vendor/bin/hw/android.hardware.light@2.0-service
aeb05000 4K r---- /vendor/bin/hw/android.hardware.light@2.0-service
aeb06000 4K rw--- [anon]
f1d9e000 68K r-x-- /vendor/lib/libusb.so
f1daf000 4K r---- /vendor/lib/libusb.so
f1db0000 4K rw--- /vendor/lib/libusb.so
f1ddc000 16K r-x-- /vendor/lib/hw/lights.default.so
f1de0000 4K r---- /vendor/lib/hw/lights.default.so
f1de1000 4K rw--- /vendor/lib/hw/lights.default.so
f1e2e000 12K r-x-- /vendor/lib/hw/android.hardware.light@2.0-impl.so
f1e31000 4K r---- /vendor/lib/hw/android.hardware.light@2.0-impl.so
f1e32000 4K rw--- /vendor/lib/hw/android.hardware.light@2.0-impl.so
...

对应的启动脚本位于 vendor/etc/init/android.hardware.light@2.0-service.rc,内容如下:

1
2
3
4
5
6
7
service vendor.light-hal-2-0 /vendor/bin/hw/android.hardware.light@2.0-service
interface android.hardware.light@2.0::ILight default
class hal
user system
group system
# shutting off lights while powering-off
shutdown critical

参考Android.bp,确定android.hardware.light@2.0-service 进程对应的源码是 service.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cc_binary {
name: "android.hardware.light@2.0-service",
relative_install_path: "hw",
defaults: ["hidl_defaults"],
vendor: true,
init_rc: ["android.hardware.light@2.0-service.rc"],
srcs: ["service.cpp"],

shared_libs: [
"liblog",
"libbase",
"libdl",
"libutils",
"libhardware",
"libhidlbase",
"libhidltransport",
"android.hardware.light@2.0",
],
}

这里使用 HIDL Passthrough 接口方法,具体如下:

hardware/interfaces/light/2.0/default/service.cpp

1
2
3
int main() {
return defaultPassthroughServiceImplementation<ILight>();
}

system/libhidl/transport/include/hidl/LegacySupport.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
template<class Interface>
__attribute__((warn_unused_result))
status_t defaultPassthroughServiceImplementation(std::string name,
size_t maxThreads = 1) {
configureRpcThreadpool(maxThreads, true);
status_t result = registerPassthroughServiceImplementation<Interface>(name);

if (result != OK) {
return result;
}

joinRpcThreadpool();
return UNKNOWN_ERROR;
}
template<class Interface>
__attribute__((warn_unused_result))
status_t defaultPassthroughServiceImplementation(size_t maxThreads = 1) {
return defaultPassthroughServiceImplementation<Interface>("default", maxThreads);
}

template<class Interface>
__attribute__((warn_unused_result))
status_t registerPassthroughServiceImplementation(
std::string name = "default") {
sp<Interface> service = Interface::getService(name, true /* getStub */);

if (service == nullptr) {
ALOGE("Could not get passthrough implementation for %s/%s.",
Interface::descriptor, name.c_str());
return EXIT_FAILURE;
}

LOG_FATAL_IF(service->isRemote(), "Implementation of %s/%s is remote!",
Interface::descriptor, name.c_str());

status_t status = service->registerAsService(name);

if (status == OK) {
ALOGI("Registration complete for %s/%s.",
Interface::descriptor, name.c_str());
} else {
ALOGE("Could not register service %s/%s (%d).",
Interface::descriptor, name.c_str(), status);
}

return status;
}

而使用 Passthrough 会加载传统的 HAL 库,相关的代码如下:

hardware/interfaces/light/2.0/default/Light.h

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Light : public ILight {
Light(std::map<Type, light_device_t*> &&lights);

Return<Status> setLight(Type type, const LightState& state) override;
Return<void> getSupportedTypes(getSupportedTypes_cb _hidl_cb) override;

Return<void> debug(const hidl_handle& handle, const hidl_vec<hidl_string>& options) override;

private:
std::map<Type, light_device_t*> mLights;
};

extern "C" ILight* HIDL_FETCH_ILight(const char* name);

hardware/interfaces/light/2.0/default/Light.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
Return<Status> Light::setLight(Type type, const LightState& state)  {
auto it = mLights.find(type);

if (it == mLights.end()) {
return Status::LIGHT_NOT_SUPPORTED;
}

light_device_t* hwLight = it->second;

light_state_t legacyState {
.color = state.color,
.flashMode = static_cast<int>(state.flashMode),
.flashOnMS = state.flashOnMs,
.flashOffMS = state.flashOffMs,
.brightnessMode = static_cast<int>(state.brightnessMode),
};

int ret = hwLight->set_light(hwLight, &legacyState);

switch (ret) {
case -ENOSYS:
return Status::BRIGHTNESS_NOT_SUPPORTED;
case 0:
return Status::SUCCESS;
default:
return Status::UNKNOWN;
}
}

light_device_t* getLightDevice(const char* name) {
light_device_t* lightDevice;
const hw_module_t* hwModule = NULL;

int ret = hw_get_module (LIGHTS_HARDWARE_MODULE_ID, &hwModule);
if (ret == 0) {
ret = hwModule->methods->open(hwModule, name,
reinterpret_cast<hw_device_t**>(&lightDevice));
if (ret != 0) {
ALOGE("light_open %s %s failed: %d", LIGHTS_HARDWARE_MODULE_ID, name, ret);
}
} else {
ALOGE("hw_get_module %s %s failed: %d", LIGHTS_HARDWARE_MODULE_ID, name, ret);
}

if (ret == 0) {
return lightDevice;
} else {
ALOGE("Light passthrough failed to load legacy HAL.");
return nullptr;
}
}

ILight* HIDL_FETCH_ILight(const char* /* name */) {
std::map<Type, light_device_t*> lights;

for(auto const &pair : kLogicalLights) {
Type type = pair.first;
const char* name = pair.second;

light_device_t* light = getLightDevice(name);

if (light != nullptr) {
lights[type] = light;
}
}

if (lights.size() == 0) {
// Log information, but still return new Light.
// Some devices may not have any lights.
ALOGI("Could not open any lights.");
}

return new Light(std::move(lights));
}

HAL

这里根据操作设备名称的差异,设置不同的操作方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = LIGHTS_HARDWARE_MODULE_ID,
.name = "Rokid lights Module",
.author = "<jinlong.xu@rokid.com>",
.methods = &lights_module_methods,
};

static struct hw_module_methods_t lights_module_methods = {
.open = open_lights,
};

static int
open_lights( const struct hw_module_t* module, char const *name,
struct hw_device_t **device )
{
void* set_light;

if (0 == strcmp( LIGHT_ID_BACKLIGHT, name )) {
set_light = set_light_backlight;
} else if (0 == strcmp( LIGHT_ID_KEYBOARD, name )) {
set_light = set_light_keyboard;
} else if (0 == strcmp( LIGHT_ID_BUTTONS, name )) {
set_light = set_light_buttons;
} 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 if (0 == strcmp( LIGHT_ID_ATTENTION, name )) {
set_light = set_light_attention;
} else {
D( "%s: %s light isn't supported yet.", __FUNCTION__, name );
return -EINVAL;
}

struct light_device_t *dev = malloc( sizeof(struct light_device_t) );
if (dev == NULL) {
return -EINVAL;
}
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;
}

以 LIGHT_ID_BATTERY 为例,这里操作的是 /sys/devices/platform/sysled/sysled_status_switch 设备节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#define BATTERY "/sys/devices/platform/sysled/sysled_status_switch"

static int
set_light_battery( struct light_device_t* dev, struct light_state_t const* state )
{
int fd,nwr;
int ret = -1;
char wbuf[20] = {0};
int light_level;

light_level = state->color;
D("Set_light_battery:%d\n",light_level);
if(!dev)
return -1;

fd = open(BATTERY, O_RDWR);
if (fd > 0){
nwr = sprintf(wbuf, "%d", light_level);
ret = write(fd, wbuf, nwr);
close(fd);
}
return 0;
}

Driver

设备树节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
sysled {
compatible = "amlogic, sysled";
dev_name = "sysled";
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pwm_ao_c_pins1 &pwm_c_pins1 &pwm_f_pins1>;
pwm_config = <&sysled_pwm_conf>;
};

sysled_pwm_conf:sysled_pwm_conf{
red_led_conf{
pwms = <&pwm_AO_cd MESON_PWM_0 200000 0>;
};
green_led_conf{
pwms = <&pwm_ef MESON_PWM_1 200000 0>;
};
blue_led_conf{
pwms = <&pwm_cd MESON_PWM_0 200000 0>;
};
};

drivers/amlogic/led/led_sys.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
static int aml_sysled_probe(struct platform_device *pdev)
{
struct aml_sysled_dev *ldev;
int ret;

ldev = kzalloc(sizeof(struct aml_sysled_dev), GFP_KERNEL);

/* set driver data */
platform_set_drvdata(pdev, ldev);

/* parse dt param */
ret = aml_sysled_dt_parse(pdev);
if (ret)
return ret;

local_dev = ldev;
ldev->dev = &pdev->dev;
mutex_init(&ldev->lock);
ldev->sysled_wq = create_workqueue("sysled_wq");
INIT_DELAYED_WORK(&ldev->work, sysled_work_func);
queue_delayed_work(ldev->sysled_wq, &ldev->work, 0);

ret = device_create_file(&pdev->dev, &dev_attr_sysled_status_switch);
if (ret != 0) {
dev_err(&pdev->dev,
"Failed to create sysfile sysled_system_status.\n");
return ret;
}

ret = device_create_file(&pdev->dev, &dev_attr_sysled_led_test);
if (ret != 0) {
dev_err(&pdev->dev,
"Failed to create sysfile sysled_led_test.\n");
return ret;
}

dev_info(&pdev->dev, "Amlogic Sys-Led Probe Success\n");
return 0;
}

static const struct of_device_id aml_sysled_dt_match[] = {
{
.compatible = "amlogic, sysled",
},
{},
};

static struct platform_driver aml_sysled_driver = {
.driver = {
.name = AML_DEV_NAME,
.owner = THIS_MODULE,
.of_match_table = aml_sysled_dt_match,
},
.probe = aml_sysled_probe,
.remove = __exit_p(aml_sysled_remove),
.shutdown = aml_sysled_shutdown,
#ifdef CONFIG_PM
.suspend = aml_sysled_suspend,
.resume = aml_sysled_resume,
#endif
};

static int __init aml_sysled_init(void)
{
pr_info("module init\n");
if (platform_driver_register(&aml_sysled_driver)) {
pr_err("failed to register driver\n");
return -ENODEV;
}

return 0;
}

添加 sysfs 方式修改的驱动接口,这里就不贴代码实现细节了。

1
2
3
static DEVICE_ATTR(sysled_status_switch, 0664, sysled_status_show, sysled_status_store);

static DEVICE_ATTR(sysled_led_test, 0664, sysled_test_show, sysled_test_store);

总结

通过阅读源码,简单梳理了 Android 灯光系统的流程。Android P 引入了 HIDL,使用单独的服务进程操作 HAL,并与框架层客户端进行通信。