前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android新增LED设备--从底层到上层理解安卓架构

Android新增LED设备--从底层到上层理解安卓架构

作者头像
Jasonangel
发布2021-08-26 15:07:35
2.6K0
发布2021-08-26 15:07:35
举报
文章被收录于专栏:嵌入式Linux系统开发

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 %d\n",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 %d\n",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驱动的。

二、驱动测试

驱动完成后要测试一下驱动对外的接口函数是否正常,可以写一个简易的测试代码。

  1. 测试代码gpioleds_test.c
代码语言:javascript
复制
#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-led\n");
exit(1);
}
printf("Led1 on\n");
ioctl (dev_fd, LED1CTRL_ON_CMD,0);
getchar();
ioctl (dev_fd, LED1CTRL_OFF_CMD,0);
printf("led1 off\n");

printf("Led2 on\n");
ioctl (dev_fd, LED2CTRL_ON_CMD,0);
getchar();
ioctl (dev_fd, LED2CTRL_OFF_CMD,0);
printf("led2 off\n");

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的内部的几个关键函数。

  1. 头文件hardware/libhardware/include/hardware/testled_hal.h
代码语言:javascript
复制
#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;
  1. Android.mk hardware/libhardware/modules/testled/Android.mk
代码语言:javascript
复制
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文件编译成模块

  1. 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已经在运行了,就说明成功了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-08-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 嵌入式Linux系统开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档