Linux点灯会了,Android点灯你会吗?Android系统架构如下:
阅读Android源码:
http://androidxref.com/
为了更好的理解安卓的层次关系,本文在RK3399的安卓系统上增加LED灯的外设,并使用APP打开关闭LED灯。以这样一个最简单的实例,来演示从上层到底层的调用过程。首先从最底层的kernel层开始。
1、驱动开发
一、驱动开发
Kernel层就是要将LED硬件接入到系统,完成驱动的开发。Linux下的驱动是使用C语言进行开发的,可分为三类设备类型:字符设备,块设备,网络设备。每种类型的驱动都有他自有的驱动框架,学习驱动开发就是要熟悉各种驱动架构,并根据实际需求在框架内添加内容。LED的驱动我们选择最简单的杂项字符类设备驱动即可。
从原理图中可以得到两个GPIO:GPIO1_C7和GPIO1_D0,驱动三极管来使得LED灯亮灭。
图:led灯原理图
1)设备树文件(kernel/arch/arm64/boot/dts/rockchip/rk3399-nanopi4-common.dtsi)
代码语言:javascript复制test-leds{
compatible = "test,leds";
led1-work = <&gpio1 23 GPIO_ACTIVE_LOW>;
led2-work = <&gpio1 24 GPIO_ACTIVE_LOW>;
status = "okay";
};
2) 驱动文件(kernel/drivers/gpio/gpio-testled.c)
代码语言:javascript复制#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
MODULE_AUTHOR("embeddedtech");
MODULE_LICENSE("Dual BSD/GPL");
#define LEDCTRL_MAGIC 'k'
#define LED1CTRL_ON_CMD _IO (LEDCTRL_MAGIC, 1)
#define LED1CTRL_OFF_CMD _IO (LEDCTRL_MAGIC, 2)
#define LED2CTRL_ON_CMD _IO (LEDCTRL_MAGIC, 3)
#define LED2CTRL_OFF_CMD _IO (LEDCTRL_MAGIC, 4)
struct led_data {
int led1_pin; //led1引脚
int led2_pin; //led2引脚
};
struct led_data led_info;
/*
* Open the device; in fact, there's nothing to do here.
*/
int testled_open (struct inode *inode, struct file *filp)
{
return 0;
}
ssize_t testled_read(struct file *file, char __user *buff, size_t count, loff_t *offp)
{
return 0;
}
ssize_t testled_write(struct file *file, const char __user *buff, size_t count, loff_t *offp)
{
return 0;
}
static long testled_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case LED1CTRL_ON_CMD: {
gpio_direction_output(led_info.led1_pin,1);
break;
}
case LED1CTRL_OFF_CMD: {
gpio_direction_output(led_info.led1_pin,0);
break;
}
case LED2CTRL_ON_CMD: {
gpio_direction_output(led_info.led2_pin,1);
break;
}
case LED2CTRL_OFF_CMD: {
gpio_direction_output(led_info.led2_pin,0);
break;
}
default: {
printk("testled compat Default %dn",cmd);
break;
}
}
return 0;
}
long testled_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case LED1CTRL_ON_CMD: {
gpio_direction_output(led_info.led1_pin,1);
break;
}
case LED1CTRL_OFF_CMD: {
gpio_direction_output(led_info.led1_pin,0);
break;
}
case LED2CTRL_ON_CMD: {
gpio_direction_output(led_info.led2_pin,1);
break;
}
case LED2CTRL_OFF_CMD: {
gpio_direction_output(led_info.led2_pin,0);
break;
}
default: {
printk("testled Default %dn",cmd);
break;
}
}
return 0;
}
static int testled_release(struct inode *node, struct file *file)
{
return 0;
}
/*
* Our various sub-devices.
*/
/* Device 0 uses remap_pfn_range */
static struct file_operations testled_remap_ops = {
.owner = THIS_MODULE,
.open = testled_open,
.release = testled_release,
.read = testled_read,
.write = testled_write,
.unlocked_ioctl = testled_ioctl,
.compat_ioctl = testled_compat_ioctl,
};
static struct miscdevice testled_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "test-led",
.fops = &testled_remap_ops,
};
/*
* Module housekeeping.
*/
static int testled_probe(struct platform_device *pdev)
{
int ret;
struct device_node *led_node = pdev->dev.of_node;
//enum of_gpio_flags work_flags;
ret = misc_register(&testled_misc);
if(ret < 0){
pr_err("testled_misc_register fails!!!n");
return ret;
}
led_info.led1_pin = of_get_named_gpio(led_node, "led1-work", 0);
if(!gpio_is_valid(led_info.led1_pin)){
pr_err("led1 pin invaild: %d",led_info.led1_pin);
ret = -ENODEV;
goto probe_fail;
}
ret = gpio_request(led_info.led1_pin, "led1_pin");
if(ret < 0){
pr_err("led1 pin request failed.");
goto probe_fail;
}
led_info.led2_pin = of_get_named_gpio(led_node, "led2-work", 0);
if(!gpio_is_valid(led_info.led2_pin)){
pr_err("led2 pin invaild: %d",led_info.led2_pin);
ret = -ENODEV;
goto probe_fail;
}
ret = gpio_request(led_info.led2_pin, "led2_pin");
if(ret < 0){
pr_err("led2 pin request failed.");
goto probe_fail;
}
printk("led1 pin is: %d,led2 pin is: %d",led_info.led1_pin,led_info.led2_pin);
return 0;
probe_fail:
return ret;
}
static int testled_remove(struct platform_device *pdev)
{
pr_err("testled_misc_remove!!!n");
misc_deregister(&testled_misc);
return 0;
}
static struct of_device_id testled_table[] = {
{ .compatible = "test,leds",}, //Compatible node must match dts
{ },
};
static struct platform_driver testled_driver = {
.probe = testled_probe,
.remove = testled_remove,
.driver = {
.name = "test_leds",
.owner = THIS_MODULE,
.of_match_table = testled_table,
}
};
static int testled_init(void)
{
int ret;
ret = platform_driver_register(&testled_driver);
if(ret < 0)
pr_err("platform_register_driver fails!!!n");
pr_err("platform_register_driver succeeds :ret=%d!!!n",ret);
return ret;
}
static void testled_cleanup(void)
{
platform_driver_unregister(&testled_driver);
}
module_init(testled_init);
module_exit(testled_cleanup);
3)Makefile文件(kernel/drivers/gpio/Makefile)
代码语言:javascript复制obj-$(CONFIG_GPIO_TEST_LED) = gpio-testled.o
4) Kconfig文件 (kernel/drivers/gpio/Kconfig)
代码语言:javascript复制config GPIO_TEST_LED
bool "select test leds"
default y
5)驱动配置文件(kernel/arch/arm64/configs/nanopi4_nougat_defconfig)
代码语言:javascript复制CONFIG_GPIO_TEST_LED=y
6)修改节点权限(device/rockchip/common/ueventd.rockchip.rc)
代码语言:javascript复制/dev/test_leds 0666 system system
设备驱动文件在与 dts匹配的关键字是 compatible 属性。即比较驱动文件中 of_device_id 结构体元素的 .compatible 成员变量和 dts 文件中 node 中 compatible 属性两个字符串,匹配成功之后会进行probe,驱动就被加载进内核了。Dts文件可以放入一些数据、属性等,驱动文件加载是可以来读取,这样如果更改硬件管脚等就不需要改动驱动文件,只改设备树文件即可,使得驱动移植和维护更加方便。
Makefile文件、Kconfig文件和驱动配置文件使得gpio-testled.c的驱动可以被编译进内核,简易调试也可以只在Makefile文件添加 obj-y = gpio-testled.o就行。
驱动加载成功的标志是在设备的dev/目录下生成了test-led的设备文件节点,后面的上层就是以此文件来调用led驱动的。
二、驱动测试
驱动完成后要测试一下驱动对外的接口函数是否正常,可以写一个简易的测试代码。
- 测试代码gpioleds_test.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/ioctl.h>
#define LEDCTRL_MAGIC 'k'
#define LED1CTRL_ON_CMD _IO (LEDCTRL_MAGIC, 1)
#define LED1CTRL_OFF_CMD _IO (LEDCTRL_MAGIC, 2)
#define LED2CTRL_ON_CMD _IO (LEDCTRL_MAGIC, 3)
#define LED2CTRL_OFF_CMD _IO (LEDCTRL_MAGIC, 4)
int main()
{
int i = 0;
int dev_fd;
dev_fd = open("/dev/test-led",O_RDWR | O_NONBLOCK);
if ( dev_fd == -1 ) {
printf("Cann't open file /dev/test-ledn");
exit(1);
}
printf("Led1 onn");
ioctl (dev_fd, LED1CTRL_ON_CMD,0);
getchar();
ioctl (dev_fd, LED1CTRL_OFF_CMD,0);
printf("led1 offn");
printf("Led2 onn");
ioctl (dev_fd, LED2CTRL_ON_CMD,0);
getchar();
ioctl (dev_fd, LED2CTRL_OFF_CMD,0);
printf("led2 offn");
close(dev_fd);
return 0;
}
2)Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:=
gpioleds_test.c
LOCAL_SHARED_LIBRARIES :=
liblog
libc
libutils
libcrypto
libhardware
LOCAL_MODULE := gpioleds_test
LOCAL_MODULE_TAGS :=
LOCAL_MODULE_PATH := $(LOCAL_PATH)
LOCAL_CFLAGS =-D_FORTIFY_SOURCE=0
LOCAL_CFLAGS = -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0
include $(BUILD_EXECUTABLE
3)编译成可执行文件
在安卓目录external/下新建gpioleds_test目录,将gpioleds_test.c和Android.mk拷贝进目录,编译此目录。
代码语言:javascript复制mmm external/gpioleds_test/
完成在目录下生成二进制文件gpioleds_test。拷贝进安卓设备。
4)测试
二进制文件拷贝进安卓设备后,赋予777权限,然后运行。
代码语言:javascript复制Chmod 777 /data/user/gpioleds_test
./ data/user/gpioleds_test
2、HAL硬件抽象层
硬件抽象层(Hardware Abstraction Layer,简称HAL)是介于android内核kernel和上层之间的抽象出来的一层结构,是对Linux驱动的一个封装,对上层提供统一接口,上层应用不必知道下层硬件具体怎么工作的,屏蔽了底层的实现细节。为什么有了 硬件抽象层有其存在的意义:
1)不是所有的硬件设备都有标准的Linux内核接口,通过HAL层封装了一套固定的向上接口,可以使得上层的开发逻辑更清晰简单。HAL框架是固定的,开发人员只需要按照框架开发即可,无需关注与上层的交互上,将精力放在HAL层本身的实现上即可。
2)从商业角度,硬件厂商可以把一些核心的算法、调试参数、实现逻辑等放在HAL层而不是kenel层,kenel层只是简单与硬件做数据交互。这样的好处是可以不用遵Linux的GPL开源协议,保护自身的商业机密。
Hal架构图
模块类型结构体hw_module_t,设备类型结构体hw_device_t,
两个结构体的详细内容可以参考源码路径:/hardware/libhardware/include/hardware/hardware.h。HAL层开发主要工作是建立好自定义的结构体,并实现hw_device_t的内部的几个关键函数。
- 头文件hardware/libhardware/include/hardware/testled_hal.h
#ifndef _LED_HAL_H_
#define _LED_HAL_H_
#include <hardware/hardware.h>
#define LED_HAL_MODULE_ID "testled_hal"
struct testled_module_t {
struct hw_module_t common;
};
struct testled_device_t {
struct hw_device_t common;
int (*open)(void);
int (*control)(int on);
};
#endif
头文件内申明了led的两个关键结构体testled_module_t和testled_device_t,结构体的实现在c文件中。
2)c文件 hardware/libhardware/modules/testled/testled_hal.c
代码语言:javascript复制#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <hardware/testled_hal.h>
#include <linux/ioctl.h>
//日志的标签
代码语言:javascript复制#define LOG_TAG "testled_hal"
#include <utils/Log.h>
#define LEDCTRL_MAGIC 'k'
#define LED1CTRL_ON_CMD _IO (LEDCTRL_MAGIC, 1)
#define LED1CTRL_OFF_CMD _IO (LEDCTRL_MAGIC, 2)
#define LED2CTRL_ON_CMD _IO (LEDCTRL_MAGIC, 3)
#define LED2CTRL_OFF_CMD _IO (LEDCTRL_MAGIC, 4)
static int fd;
int testled_hal_dev_close(struct hw_device_t *device)
{
if(device != NULL)
{
struct testled_device_t *temp = (struct testled_device_t *)device;
free(temp);
}
close(fd);
return 0;
}
int testled_hal_open_dev(void)
{
ALOGD("--%s--", __func__);
fd = open("/dev/test-led", O_RDWR);
if(fd < 0)
{
ALOGE("open failed : %s", strerror(errno));
return fd;
}
return 0;
}
int testled_hal_control_dev(int on)
{
ALOGD("--%s--", __func__);
int ret;
switch(on){
case 0:
ret = ioctl(fd, LED1CTRL_ON_CMD,0);
break;
case 1:
ret = ioctl(fd, LED1CTRL_OFF_CMD,0);
break;
case 2:
ret = ioctl(fd, LED2CTRL_ON_CMD,0);
break;
case 3:
ret = ioctl(fd, LED2CTRL_OFF_CMD,0);
break;
default:
break;
}
if(ret < 0){
ALOGE("control failed : %s", strerror(errno));
return ret;
}
return 0;
}
int testled_hal_module_open(const struct hw_module_t *module, const char *id,
struct hw_device_t **device)
{
ALOGD("--%s--", __func__);
struct testled_device_t *led_dev = NULL;
led_dev = (struct testled_device_t *)malloc(sizeof(struct testled_device_t));
if (led_dev == NULL)
{
ALOGE("malloc failed");
return -1;
}
ALOGD("malloc success");
//初始化device对象
led_dev->common.tag = HARDWARE_DEVICE_TAG;
led_dev->common.version = 1;
led_dev->common.module = module;
led_dev->common.close = testled_hal_dev_close;
led_dev->open = testled_hal_open_dev;
led_dev->control = testled_hal_control_dev;
//将当前的led_dev传递给jni层
代码语言:javascript复制 *device = (struct hw_device_t *)led_dev;
return 0;
}
struct testled_device_t testled_hal_methods = {
open : testled_hal_module_open,
};
struct testled_module_t HAL_MODULE_INFO_SYM = {
common : {
tag : HARDWARE_MODULE_TAG,
version_major : 1,
version_minor : 0,
id : LED_HAL_MODULE_ID,
name : "testled hal module",
methods : &testled_hal_methods,
},
};
主要实现了hal结构体中的close,open,control函数,并将函数传给结led_dev构体。
代码语言:javascript复制led_dev->common.close = testled_hal_dev_close;
led_dev->open = testled_hal_open_dev;
led_dev->control = testled_hal_control_dev;
- Android.mk hardware/libhardware/modules/testled/Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := testled_hal.default
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_SRC_FILES := testled_hal.c
LOCAL_SHARED_LIBRARIES := liblog libcutils
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)
将c文件编译成模块
- hardware/libhardware/modules/Android.mk内加入testled
三、编译
模块编译
mmm hardware/libhardware/modules/ testled
在out/target/product/nanopc-t4/system/lib/hw/ 目录下生生成test_led_hal.default.so
全部编译后,test_led_hal.default.so在设备的/system/lib/hw路径下,android frameworks中的JNI调用led设备时,通过一系列转换就会调用到这个库内部的函数,从而调动掉底层的led驱动。
3、JNI
1.frameworks/base/services/core/jni/com_android_server_TestLedService.cpp
代码语言:javascript复制#define LOG_TAG "TestLedServiceJni"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include
#include
#include
#include
#include
namespace android
{
struct testled_device_t* testled_device = NULL;
static void testled_setLed1(JNIEnv* env, jobject clazz, jint value) {
int val = value;
ALOGI("TestLed JNI: set value %d to led1.", val);
if(!testled_device) {
ALOGI("TestLed JNI: device is not open.");
return;
}
if(val > 0){
testled_device->control(0);//on
}else{
testled_device->control(1);//off
}
}
static void testled_setLed2(JNIEnv* env, jobject clazz, jint value)
{
int val = value;
ALOGI("TestLed JNI: set value %d to led2.", val);
if(!testled_device) {
ALOGI("TestLed JNI: device is not open.");
return;
}
if(val > 0){
testled_device->control(2);//on
}else{
testled_device->control(3);//off
}
}
static inline int testled_device_open(const hw_module_t* module, struct testled_device_t** device) {
return module->methods->open(module, LED_HAL_MODULE_ID, (struct hw_device_t**)device);
}
static jboolean testled_init(JNIEnv* env, jclass clazz) {
testled_module_t* module;
ALOGI("TestLed JNI: initializing......");
if(hw_get_module(LED_HAL_MODULE_ID, (const struct hw_module_t**)&module) == 0) {
ALOGI("TestLed JNI: testled Stub found.");
if(testled_device_open(&(module->common), &testled_device) == 0) {
ALOGI("TestLed JNI: testled device is open.");
//testled_device->open();
return 0;
}
ALOGE("TestLed JNI: failed to open testled device.");
return -1;
}
ALOGE("TestLed JNI: failed to get testled stub module.");
return -1;
}
static const JNINativeMethod method_table[] = {
{"testledinit_native", "()Z", (void*)testled_init},
{"setled1_native", "(I)V", (void*)testled_setLed1},
{"setled2_native", "(I)V", (void*)testled_setLed2},
};
int register_android_server_TestLedService(JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/TestLedService", method_table, NELEM(method_table));
}
};
com_android_server_TestLedService.cpp文件实现了JNI方法testled_setLed1,testled_setLed2,还有JNI初始化的入口函数testled_init。
在testled_init内通过LED_HAL_MODULE_ID匹配到了Hal内的硬件模块,再通过testled_device_open得到了device结构体。在testled_setLed1和 testled_setLed2调用了device结构体在Hal层实现的led操作函数接口。
注意com_android_server_TestLedService文件的命令方法,com_android_server表示的是包名,表示硬件服务TestLedService在frameworks/base/services/java目录下的com/android/server目录下,此目录下的TestLedService的类是可以直接调用jni的内容的。
2.frameworks/base/services/core/jni/onload.cpp
namespace中增加函数声明
代码语言:javascript复制namespace android {
……
int register_android_server_TestLedService(JNIEnv *env);
};
JNI_OnLoad增加register_android_server_TestLedService的函数调用
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
…
register_com_android_server_rkdisplay_RkDisplayModes(env);
register_android_server_TestLedService(env);
return JNI_VERSION_1_4;
}
这一步是Framework加载jni库,即在/system/lib/hw/test_led_hal.default.so被导入了。
3.frameworks/base/services/core/jni/Android.mk
在LOCAL_SRC_FILES变量中增加一行
代码语言:javascript复制LOCAL_SRC_FILES =
……
$(LOCAL_REL_DIR)/com_android_server_TestLedService.cpp
$(LOCAL_REL_DIR)/onload.cpp
4.局部编译
mmm frameworks/base/services/jni
make snod
重新打包的system.img镜像文件就包含了LedTest的JNI方法了,后面我们可以通过Android系统的Framework层提供的硬件服务LedTestService来调用这些JNI方法,进而调用更低层的硬件抽象层接口去访问硬件了。Framework层的LedTestService服务在下一篇实现。
4、service
Android内有许多系统管理服务,如
- 窗口管理服务WindowManagerService
- 电源管理服务PowerManagerService
- 通知管理服务NotifacationManagerService
- 电池管理服务BatteryManagerService等等
这些Manager提供了很多对系统层的控制接口。并且他们是由SystemServer在开机后启动并管理的。自定义的服务加入后,也类似这些系统服务,开机会自动启动,并且也有同样的上层的访问接口。
添加一个自定义的系统服务,需要按照service的框架要求实现以下几步:
1、TestLedService服务
路径:frameworks/base/services/core/java/com/android/server/TestLedService.java
代码语言:javascript复制package com.android.server;
import android.content.Context;
import android.util.Slog;
import android.app.ITestLedService;
import android.content.BroadcastReceiver;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.SystemProperties;
import android.content.pm.PackageManager;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.DataOutputStream;
import java.io.DataInputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestLedService extends ITestLedService.Stub {
private final Context mContext;
private boolean mFlag;
private boolean mled2On = false;
private int mFlashDelay = 1000;
public TestLedService(Context context) {
super();
mContext = context;
testledinit_native();
mFlag = true;
if(mFlag){
new Thread(new led2Flash()).start();
}
}
public void SetLed1(int on){
setled1_native(on);
}
public void SetLed2(int delay){
mFlashDelay = delay;
if(!mFlag && mFlashDelay != 0){
new Thread(new led2Flash()).start();
}
if(delay == 0){
mFlag = false;
}
}
class led2Flash implements Runnable {
public void run(){
while(true){
Slog.i("TestLed_service", "led2 flash");
if(mled2On){
setled2_native(0);
mled2On = false;
}else{
setled2_native(1);
mled2On = true;
}
try{Thread.sleep(mFlashDelay);}catch(Exception e){e.printStackTrace();}
if(!mFlag){
break;
}
}
}
}
private static native boolean testledinit_native();
private static native void setled1_native(int val);
private static native void setled2_native(int val);
}
TestLedService实现了两个led的控制的函数,SetLed1设置led1开与关,setLed2设置led2的闪烁频率。分别调用了JNI的函数setled1_native和setled2_native。
2、TestLedManager.java
路径:frameworks/base/core/java/android/app/TestLedManager.java
代码语言:javascript复制package android.app;
import android.app.ITestLedService;
import android.util.Slog;
import android.os.RemoteException;
public class TestLedManager {
private final ITestLedService mService;
public TestLedManager(ITestLedService mService) {
this.mService = mService;
}
public void SetLed1(int on){
try {
mService.SetLed1(on);
} catch (RemoteException ex) {
ex.printStackTrace();
}
}
public void SetLed2(int delay){
try {
mService.SetLed2(delay);
} catch (RemoteException ex) {
ex.printStackTrace();
}
}
}
Manager是APP和service通讯的桥梁,通过系统接口getSystemService,app获取到该service的Manager。而在TestLedManager中就是将service内实现的函数接口重新封装了一下。
3、ITestLedService.aidl
路径:frameworks/base/core/java/android/app/ITestLedService.aidl
package android.app;
代码语言:javascript复制// Declare any non-default types here with import statements
interface ITestLedService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void SetLed1(int on);
void SetLed2(int delay);
}
TestLedService和TestLedManager在不同的进程,所以要考虑到进程通讯的问题。Manager通过增加一个aidl文件来描述通讯接口。
4、aidl加入编译mk
路径:frameworks/base/Android.mk
代码语言:javascript复制LOCAL_SRC_FILES =
……
core/java/android/service/quicksettings/IQSTileService.aidl
core/java/android/app/ITestLedService.aidl
5、Context.java新增服务名
路径:frameworks/base/core/java/android/content/Context.java
public static final String TestLed_SERVICE="TestLed";
6、注册服务
路径:frameworks/base/core/java/android/app/SystemServiceRegistry.java
代码语言:javascript复制import android.app.TestLedManager;
import android.app.ITestLedService;
final class SystemServiceRegistry {
……
static {
……
registerService(Context.TestLed_SERVICE, TestLedManager.class,
new CachedServiceFetcher<TestLedManager>() {
@Override
public TestLedManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.TestLed_SERVICE);
ITestLedService service = ITestLedService.Stub.asInterface(b);
return new TestLedManager(service);
}});
7、启动服务
路径:frameworks/base/services/java/com/android/server/SystemServer.java
代码语言:javascript复制private void startOtherServices() {
……
TestLedService TestLed = new TestLedService(context);
ServiceManager.addService(Context.TestLed_SERVICE, TestLed);
8、seliunx相关
路径:system/sepolicy/service.te
添加
type TestLed_service, system_api_service, system_server_service, service_manager_type;
路径:system/sepolicy/service_contexts
添加
TestLed u:object_r:TestLed_service:s0
编译过程中会报错,You have tried to change the API from what has been previously approved.是因为新增了系统api函数,make update -api 后再次编译即可。
烧录进设备并启动后,adb shell service list | grep TestLed *,就可看到新增的service已经在运行了,就说明成功了。