专栏首页嵌入式Linux系统开发Android新增LED设备--从底层到上层理解安卓架构

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

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)

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)

#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)

obj-$(CONFIG_GPIO_TEST_LED)    += gpio-testled.o

4) Kconfig文件 (kernel/drivers/gpio/Kconfig)

config GPIO_TEST_LED
bool "select test leds"
default y

5)驱动配置文件(kernel/arch/arm64/configs/nanopi4_nougat_defconfig)

CONFIG_GPIO_TEST_LED=y

6)修改节点权限(device/rockchip/common/ueventd.rockchip.rc)

/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
#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拷贝进目录,编译此目录。

mmm external/gpioleds_test/

完成在目录下生成二进制文件gpioleds_test。拷贝进安卓设备。

4)测试

二进制文件拷贝进安卓设备后,赋予777权限,然后运行。

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
#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

#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>

//日志的标签

#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层

    *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构体。

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
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

#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中增加函数声明

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变量中增加一行

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

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

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;

// 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

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

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

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

本文分享自微信公众号 - 嵌入式Linux系统开发(Jason_Linux_),作者:苏嵌

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-08-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【韦东山】嵌入式全系统:单片机-linux-Android对硬件操作的不同侧重点

    我是韦东山,一直从事嵌入式Linux培训,最近打算连载一系列文章。 正在录制全新的嵌入式Linux视频,使用新路线,不再从裸机/uboot开始,效率更高。 ...

    韦东山
  • 【韦东山】嵌入式全系统:单片机-linux-Android对硬件操作的不同侧重点

    我是韦东山,一直从事嵌入式Linux培训,最近打算连载一系列文章。 正在录制全新的嵌入式Linux视频,使用新路线,不再从裸机/uboot开始,效率更高。 对应...

    韦东山
  • HarmonyOS与Android的全面对比

    第二是我个人非常看好鸿蒙系统的未来,清楚明白华为和一些民族企业担负的责任和国人的期待,虽然带着一些民族感情;鸿蒙刚发布的时候自己是非常激动的,但是后来项目太忙一...

    肉眼品世界
  • 安卓第二夜 有趣的架构

    学习安卓的架构,是从操作系统的角度理解安卓。安卓使用Linux内核,但安卓的架构又与常见的Linux系统有很大的区别。我们先来回顾一下传统的Linux架构,再来...

    Vamei
  • 前端视角看HarmonyOS

    公元 2021 年 6 月 2 日,【 HarmonyOS2.0 】正式发布,以 JavaScript 作为 IoT 应用开发的架构语言,这是继 SpaceX ...

    公众号@魔术师卡颂
  • Android技术架构演进与未来

    众所周知,Android是谷歌开发的一款基于Linux的开源操作系统,每年迭代一次大版本升级。 小米、华为、OPPO、VIVO、三星等各大厂商对Android原...

    刘盼
  • 你准备好了Android8.0的VTS测试吗?

    本文首先简要介绍下Google引入VTS测试的初衷是什么,然后介绍VTS测试的一些特点,至于VTS测试的具体测试步骤、测试方法等等可以去参考Google文档...

    安智客
  • Android 1.5到10.0 都有哪些新特性?

    安卓3.0系统主要用于安卓的平板产品,画面动感,可操控性更强,代表有摩托罗拉的平板产品XOOM,3.1也已经发布,也主要用于平板产品。

    用户1269200
  • React-day1

    原生开发:它的英文单词是(NativeApp),指的就是使用 IOS、Android 官方提供的工具、开发平台、配套语言进行 手机App开发的方式;

    海仔
  • [005]frameworks/ml引发的思考

    Android P上介绍了那么多有关AI的功能,但是真正看起来,Android上AI还处于初级阶段,Android 8.0之后的源码中有一个新增目录:frame...

    王小二
  • @Android程序员:总是说Android凉了,其实是你自己跟不上,学习路线不对!

    我的很多读者都在反馈说,现在一个岗位可以收到的简历数,是前几年的几倍。我们必须承认,僧多粥少就是 Android 行业的现状,别说初中级工程师,就是高级工程师也...

    Android技术干货分享
  • Android 一直怎样在速度上追赶 iOS

    一直以来人们都有这样的印象,认为搭载iOS系统的iPhone一定比搭载Android系统的安卓手机流畅。潜移默化中,不少果粉甚至是普通吃瓜群众都形成了这样的思维...

    Anymarvel
  • 【重磅】谷歌正式发布TensorFlowLite,半监督跨平台快速训练ML模型

    来源:Google blog 编译:马文 Cecilia 【新智元导读】谷歌宣布推出 TensorFlow Lite,这是 TensorFlow 的针对移动设...

    新智元
  • 干货 | 携程机票前端安卓虚拟机测试集群建设实践

    在携程内部业务高频率敏捷迭代发布节奏下,线上生产服务质量需要同步快速提升。这就依赖自动化测试的覆盖率提升,测试任务执行频次提升,测试任务执行速率提升。

    携程技术
  • Android:寒冬已至?真正厉害的人永远没有寒冬,只有菜鸟永远在寻找南方!

    今年的行情相对去年整体要差一些,很多同学发现很多公司招人,兴冲冲去面试了,结果面了几家都是等通知,实际上就是招聘的职位有限,面试的人很多。

    Android技术干货分享
  • 前端开发华为鸿蒙系统应用 OpenHarmony JS

    其实说了这么多,也只是一个噱头,未来的路还很长,我经常再 blink 里面看到有人投票说,华为鸿蒙操作系统将取代安卓操作系统,我先否定一下。发展好了最多也只是三...

    孙叫兽
  • 笔记——JVM、DVM(dalvik)和ART之间的区别(二十)

    JVM本质上就是一个软件,是计算机硬件的一层软件抽象,在这之上才能够运行Java程序,JAVA在编译后会生成类似于汇编语言的.class字节码文件,与C语言编译...

    紫兮木溪
  • 手把手教你分析 Android 系统启动流程

    上一篇我们讲了 Linux 系统的启动流程,本文讲解一下 Andorid 系统的启动流程。

    Jasonangel
  • 谷歌限制华为安卓合作,余承东透露华为欲布局自研操作系统

    谷歌一位发言人在接受采访时表示,“我们正在遵守这一命令,并评估其影响。”但他没有透漏更多消息。

    新智元

扫码关注云+社区

领取腾讯云代金券