news 2026/8/1 1:53:59

Android8.1系统Led的控制从底层到上层的实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android8.1系统Led的控制从底层到上层的实现

我们都知道,安卓系统是一个分层的系统,那么它的底层到上层或者上层到底层的标准流程是怎么走的呢?这里通过apk操作一个GPIO控制led的亮灭从而实现从上层到底层的完整调用流程。写得不足之处欢迎有识之士不吝赐教,在此先行谢过,技术无边,欢迎各位大牛来此多多交流。(这里只给出驱动部分的代码,其他部分的代码请到我的网盘地址那里去下载,那里有代码包括相关说明)。
网盘链接:链接:https://pan.baidu.com/s/1gQNui11XkawMX78tvG592w 提取码:4ou4

Android Version:v8.0.1
Linux Version:V4.4
Hardware:mtk6739

1、在kernel层,编写和添加驱动和编译文件Malefie,创建设备节点文件/dev/led

在kernel-4.4/drivers目录新建目录led_test,新建led_test.c

#include<linux/init.h>#include<linux/slab.h>#include<linux/module.h>#include<linux/kernel.h>#include<linux/fs.h>#include<linux/cdev.h>#include<linux/device.h>#include<linux/ioctl.h>#include<linux/uaccess.h>#include<linux/string.h>#include<linux/wait.h>#include<linux/types.h>#include<linux/proc_fs.h>#include<linux/of.h>#include<linux/of_gpio.h>#include<linux/gpio.h>#defineBUFSIZE1024/*ioctl*/#defineLED_ON1#defineLED_OFF0/*主设备和从设备号变量*/staticinttest_major=0;staticinttest_minor=0;/*设备类别和设备变量*/staticstructcdevcdev;staticstructclass*test_class=NULL;staticstructdevice*test_class_dev=NULL;intnumber_of_devices=1;staticdev_tdev=0;staticchar*buf;staticunsignedintlen;staticunsignedintgpio_led;/*从dts中解析出gpio口*/staticintget_gpio_number(void){structdevice_node*node=NULL;intgpio=0;printk("---czd--- Enter %s\n",__func__);node=of_find_compatible_node(NULL,NULL,"led_node");if(node==NULL){printk("%s:node:led_node not find!\n",__func__);return-EFAULT;}gpio=of_get_named_gpio(node,"gpio_num",0);returngpio;}/*打开设备方法*/staticinttest_open(structinode*inode,structfile*filp){intret;printk("---czd--- Enter %s\n",__func__);gpio_led=get_gpio_number();if(gpio_is_valid(gpio_led)){ret=gpio_request(gpio_led,"gpio_led");if(ret){printk("---czd--- failed to get %d gpio.\n",gpio_led);return-1;}}elsegpio_led=-1;gpio_direction_output(gpio_led,1);printk("---czd--- gpio is valid and pull up now\n");return0;}/*设备文件释放时调用*/staticinttest_release(structinode*inode,structfile*filp){return0;}/************************ * file_operations->read * 可以在adb工具进入机器的dev目录,执行adb shell && cd dev && cat led, * 即可读出节点le'd的内容 * 函数原型:static ssize_t read(struct file *file, char *buf, * size_t count, loff_t *ppos) ************************/staticssize_ttest_read(structfile*file,char__user*buffer,size_tcount,loff_t*f_pos){if(*f_pos>0)return0;printk("---start read---\n");printk("the string is >>>>> %s\n",buf);if(copy_to_user(buffer,buf,len))return-EFAULT;*f_pos=*f_pos+len;returnlen;}/************************ * file_operations->write * 可以在adb工具进入机器的dev目录,执行adb shell && cd dev && echo 12345 > led, * 即可把12345写入节点led * 函数原型:static ssize_t write(struct file *file, const char *buf, * size_t count, loff_t *ppos) ************************/staticssize_ttest_write(structfile*file,constchar__user*buffer,size_tcount,loff_t*f_pos){if(count<=0)return-EFAULT;printk("---start write---\n");len=count>BUFSIZE?BUFSIZE:count;// kfree memory by kmalloc beforeif(buf!=NULL)kfree(buf);buf=(char*)kmalloc(len+1,GFP_KERNEL);if(buf==NULL){printk("device_create kmalloc fail!\n");return-EFAULT;}//memset(buf, 0, sizeof(buf));memset(buf,0,len+1);if(copy_from_user(buf,buffer,len))return-EFAULT;printk("device_create writing :%s",buf);returnlen;}staticlongtest_ioctl(structfile*filp,unsignedintcmd,unsignedlongarg){printk("---czd--- Enter %s\n",__func__);printk("-----czd--- kernel cmd is : %d\n",cmd);switch(cmd){caseLED_ON:printk("---czd--- you set gpio up\n");gpio_set_value(gpio_led,1);break;caseLED_OFF:printk("---czd--- you set gpio down\n");gpio_set_value(gpio_led,0);break;default:return-EINVAL;}return0;}/*设备文件操作方法表*/staticstructfile_operationstest_fops={.owner=THIS_MODULE,.open=test_open,.release=test_release,.read=test_read,.write=test_write,.unlocked_ioctl=test_ioctl,};staticvoidtest_setup_cdev(void){interror;dev_tdevno=MKDEV(test_major,test_minor);cdev_init(&cdev,&test_fops);cdev.owner=THIS_MODULE;cdev.ops=&test_fops;/*注册字符设备*/error=cdev_add(&cdev,devno,1);if(error)printk("Error %d adding test_setup_cdev",error);}staticint__inittest_init(void){interr=-1;printk("---czd--- Initializing test device.\n");/*动态分配主设备和从设备号*/err=alloc_chrdev_region(&dev,0,number_of_devices,"led");if(err<0){printk("Failed to alloc char dev region.\n");returnerr;}test_major=MAJOR(dev);test_minor=MINOR(dev);printk("---czd--- test_major ==> %d ; test_minor ==> %d \n",test_major,test_minor);/*初始化设备*/test_setup_cdev();/*在/sys/class/目录下创建设备类test_class*/test_class=class_create(THIS_MODULE,"led");if(IS_ERR(test_class)){printk("Err: failed in creating class.\n");return-1;}/*在/dev/目录和/sys/class/led目录下分别创建设备文件led*/test_class_dev=device_create(test_class,NULL,MKDEV(test_major,0),NULL,"led");if(IS_ERR(test_class_dev)){printk("Err: failed in creating device.\n");return-1;}printk("---czd--- Succedded to initialize test device.\n");return0;}/*模块卸载方法*/staticvoid__exittest_exit(void){dev_tdevno=MKDEV(test_major,test_minor);cdev_del(&cdev);device_destroy(test_class,devno);//delete device node under /devclass_destroy(test_class);//delete class created by usunregister_chrdev_region(devno,number_of_devices);gpio_free(gpio_led);printk("Char driver test_exit.\n");}module_init(test_init);module_exit(test_exit);MODULE_AUTHOR("czd");MODULE_DESCRIPTION("Device_create Driver");MODULE_LICENSE("GPL");

dts添加设备节点信息如下:

led_node{compatible="led_node";gpio_num=<&pio90>;status="okay";};

2、在HAL层,增加模块接口,以访问驱动程序
主要是操作驱动程序,open,ioctl,close
生成led.default.so 在system/lib/hw/目录下

在hardware/libhardware/include/hardware目录新建led.h文件
hardware/libhardware/include/hardware/led.h

在modules目录新建led目录,添加以下文件:
hardware/libhardware/modules/led/led.c
hardware/libhardware/modules/led/Andorid.mk
执行编译命令:mmm hardware/libhardware/modules/led/,编译得到led.default.so文件

在 led_init 函数中,通过Android硬件抽象层提供的hw_get_module方法来加载模块 LED_HARDWARE_MODULE_ID 的硬件抽象层模块,
其中,LED_HARDWARE_MODULE_ID 是在<hardware/led.h>中定义的。Android硬件抽象层会根据 LED_HARDWARE_MODULE_ID 的值
在Android系统的/system/lib/hw目录中找到相应的模块,然后加载起来,并且返回hw_module_t接口给调用者使用。

3、JNI层,编写JNI方法,在应用程序框架层提供Java接口访问硬件
frameworks/base/services/core/jni/com_android_server_led_LedService.cpp
生成文件打包进system/lib/libandroid_servers.so
Android系统初始化时,会自动加载JNI方法.

在frameworks/base/services/core/jni/添加com_android_server_led_LedService.cpp文件
在com_android_server_led_LedService.cpp文件中,实现JNI方法。注意文件的命令方法,com_android_server_led前缀表示的是包名,
表示硬件服务LedService是放在frameworks/base/services/core/java/目录下的com/android/server/led目录的,即存在一个命令
为com.android.server.led.LedService的类。
简单地说,LedService是一个提供Java接口的硬件访问服务类。

然后修改同目录下的onload.cpp文件,首先在namespace android增加register_android_server_LedService函数声明,这样,
在Android系统初始化时,就会自动加载该JNI方法调用表。

然后修改同目录下的Android.mk文件,在LOCAL_SRC_FILES变量中增加一行:
com_android_server_led_LedService.cpp \

4、framework层,应用程序框架层,增加硬件服务接口
jni在注册方法时用到com/android/server/led/LedService
所以在frameworks/base/services/core/java/com/android/server/目录下新建led文件夹,添加LedService.java文件

在Android系统中,硬件服务一般是运行在一个独立的进程中为各种应用程序提供服务。
因此,调用这些硬件服务的应用程序与这些硬件服务之间的通信需要通过代理来进行创建aidl文件来描述通信接口
在frameworks/base/core/java/android/os/目录新建ILedService.aidl文件

然后修改frameworks/base/services/core/jni/Android.mk文件,在LOCAL_SRC_FILES += \ 增加
+ $(LOCAL_REL_DIR)/com_android_server_led_LedService.cpp \

在frameworks/base/Android.mk里增加对aidl的编译项
LOCAL_SRC_FILES +=
+core/java/android/os/ILedService.aidl \

编译后会在out目录下产生ILedService.java文件
out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/ILedService.java

5、通过Manager调用Service
Manager管理service,在注册服务的时候就实例化一个Manager,App获取服务getService也就获取了这个对象
添加frameworks/base/core/java/android/os/LedManager.java文件

6、添加注册服务,addService开机服务就可以启动

通过service list 命令查看服务是否启动

service list | grep led

7 led: [android.os.ILedService]

具体文件修改如下:
— a/frameworks/base/services/java/com/android/server/SystemServer.java
+++ b/frameworks/base/services/java/com/android/server/SystemServer.java
@@ -103,6 +103,7 @@ import com.android.server.usage.UsageStatsService;
import com.android.server.vr.VrManagerService;
import com.android.server.webkit.WebViewUpdateService;
import com.android.server.wm.WindowManagerService;
+import com.android.server.led.LedService;

import dalvik.system.VMRuntime;

@@ -516,6 +517,7 @@ public final class SystemServer {
*/
private void startOtherServices() {
final Context context = mSystemContext;
+ LedService led = null;
VibratorService vibrator = null;
IMountService mountService = null;
NetworkManagementService networkManagement = null;
@@ -987,6 +989,15 @@ public final class SystemServer {
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
+
+ try{
+ Slog.i(TAG,“Led Service”);
+ led = new LedService(context);
+ ServiceManager.addService(Context.LED_SERVICE, led);
+ Slog.i(TAG,“Led Service-----”);
+ }catch(Throwable e){
+ Slog.e(TAG,“Failure starting LedService”, e);
+ }

— a/frameworks/base/core/java/android/content/Context.java
+++ b/frameworks/base/core/java/android/content/Context.java
@@ -3431,6 +3431,15 @@ public abstract class Context {
public static final String SERIAL_SERVICE = “serial”;

/**
+ * Use with {@link #getSystemService} to retrieve a {@link
+ * android.os.LedManager} for access to led ports.
+ *
+ * @see #getSystemService
+ *
+ * @hide
+ */
+ public static final String LED_SERVICE = “led”;

注册服务
— a/frameworks/base/core/java/android/app/SystemServiceRegistry.java
+++ b/frameworks/base/core/java/android/app/SystemServiceRegistry.java
@@ -127,6 +127,8 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.CaptioningManager;
import android.view.inputmethod.InputMethodManager;
import android.view.textservice.TextServicesManager;
+import android.os.LedManager;
+import android.os.ILedService;

import java.util.HashMap;

@@ -480,6 +482,14 @@ final class SystemServiceRegistry {
return new SerialManager(ctx, ISerialManager.Stub.asInterface(b));
}});
+ registerService(Context.LED_SERVICE, LedManager.class,
+ new CachedServiceFetcher() {
+ @Override
+ public LedManager createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(Context.LED_SERVICE);
+ return new LedManager(ctx, ILedService.Stub.asInterface(b));
+ }});
+

7、更新API
make update-api -j8

8、修改节点权限

Android在Selinux下要获取对内核节点访问的权限,需要修改.te文件(根据各个平台不同而异,这里是mtk平台的权限)

MTK平台的修改节点权限(android8.1):

(1)修改linux下节点自身的权限

— a/device/mediatek/mt6739/init.mt6739.rc

+++ b/device/mediatek/mt6739/init.mt6739.rc

@@ -807,7 +807,13 @@ on boot

#added for sunwave finger tee hidl

chown system system /dev/sunwave_fp

chmod 0660 /dev/sunwave_fp
+
+#added for led by czd
+ chown system system /dev/led
+ chmod 0660 /dev/led
+

(2)为/dev/led节点定义一个名字led_device
— a/device/mediatek/mt6739/sepolicy/bsp/file_contexts
+++ b/device/mediatek/mt6739/sepolicy/bsp/file_contexts
@@ -9,4 +9,5 @@
# Data files

/dev/goodix_fp u:object_r:fingerprint_device:s0
+/dev/led u:object_r:led_device:s0

(3)将led_device声明为dev_type
— a/device/mediatek/mt6739/sepolicy/basic/device.te
+++ b/device/mediatek/mt6739/sepolicy/basic/device.te
@@ -4,3 +4,4 @@

type gps_emi_device, dev_type;
type mntl_block_device, dev_type;
+type led_device,dev_type;

(4)修改读写权限
— a/device/mediatek/mt6739/sepolicy/basic/system_server.te
+++ b/device/mediatek/mt6739/sepolicy/basic/system_server.te
@@ -5,4 +5,6 @@
# Date : WK15.45
# Operation : Migration
# Purpose : for debug
-allow system_server gps_data_file:dir search;
\ No newline at end of file
+allow system_server gps_data_file:dir search;
+allow system_server led_device:chr_file { open read write ioctl getattr };

(5)增加服务的权限
sepolicy 部分添加
参考来自以下测试验证平台MSM8909 android8.1(验证可行)

一. sepolicy 部分添加

  1. \system\sepolicy\public\service.te 定义服务名称和属性

type xxx_service,app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;

2.\system\sepolicy\private\service_contexts 添加服务名称

xxx u:object_r:xxx_service:s0

3.\system\sepolicy\private\compat\26.0\26.0.cil 文件最后添加

(typeattributeset xxx_service_26_0 (xxx_service))

4.\system\sepolicy\prebuilts\api\26.0\public\service.te 定义服务名称和属性

type xxx_service,app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;

5.\system\sepolicy\prebuilts\api\26.0\private\service_contexts 添加服务名称

xxx u:object_r:xxx_service:s0

6.\system\sepolicy\prebuilts\api\26.0\nonplat_sepolicy.cil 添加相应配置

6.1 typeattributeset system_server_service 在最后添加自定义的服务 xxx_service_26_0

6.2 typeattributeset app_api_service 在最后添加自定义的服务 xxx_service_26_0

6.3 typeattributeset ephemeral_app_api_service 在最后添加自定义的服务 xxx_service_26_0

6.4 typeattributeset service_manager_type 在最后添加自定义的服务 xxx_service_26_0

6.5 添加一对 配置(参考已有的例子)

(typeattribute xxx_service_26_0)
(roletype object_r xxx_service_26_0)

9、编译更新系统镜像到机器emmc(flash)
make -j8 2>&1 | tee make.log

10、android app 测试
10.1 eclipse做法:
将out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar里的
android/os/LedManager.class
导入eclipse 的sdk/platforms/android-xx/android.jar包中
的android/os目录

10.2 Android stadio做法:
将out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar导入libs目录

11、总结

APP调用了LedManager里的LedOn LedOff
LedManager服务通过aidl调用到LedService.java里的setOn setOff
LedService通过jni提供的接口调用led_on led_off
private static native boolean led_on(int led);
private static native boolean led_off(int led);
jni注册的方法表
{“led_on”, “(I)Z”, (void*)led_on},
{“led_off”, “(I)Z”, (void*)led_off},
jni层调用 led_on----->return sLedDevice->set_on(sLedDevice,led);
led_off---->return sLedDevice->set_off(sLedDevice,led);

调用到硬件抽象层led.default.so里的
dev->set_on = led_on;
dev->set_off = led_off;
led_on/led_off通过ioctl调用到驱动调置gpio的高低电平

资料参考来自网上:

http://www.cnblogs.com/hackfun/p/7418902.html http://blog.csdn.net/baiduluckyboy/article/details/6973015http://blog.csdn.net/luoshengyang/article/details/6567257

https://blog.csdn.net/shui1025701856/article/details/78480776

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/1 1:53:32

如何让你的桌面“活“起来?DyberPet桌面宠物框架终极指南

如何让你的桌面"活"起来&#xff1f;DyberPet桌面宠物框架终极指南 【免费下载链接】DyberPet Desktop Cyber Pet Framework based on PySide6 项目地址: https://gitcode.com/GitHub_Trending/dy/DyberPet 你是否厌倦了单调的桌面壁纸和冰冷的图标&#xff1…

作者头像 李华
网站建设 2026/8/1 1:50:21

微信透明头像技术原理与本地实现方案解析

1. 项目概述&#xff1a;透明头像的“技术考古”与现状最近又有朋友来问&#xff0c;说想给微信换个透明头像&#xff0c;在网上搜了一圈&#xff0c;发现很多2022年甚至更早的教程都失效了&#xff0c;问我这个“老古董”还有没有招。这让我想起了几年前折腾这个事的热潮&…

作者头像 李华
网站建设 2026/8/1 1:48:14

ThinkGo:一个轻量级的 Go 语言 MVC 框架

ThinkGo 是一个轻量级的 Go 语言 MVC 框架&#xff0c;目前支持路由、中间件、控制器、请求、响应、Session、视图、日志、缓存、ORM等 web 框架应该具备的基本功能&#xff0c;ThinkGo致力于让代码简洁且富于表达力&#xff0c;帮助开发者快速构建一个 Web 应用。 特性 简洁…

作者头像 李华
网站建设 2026/8/1 1:44:07

使用SPI控制DAC8551

简 介&#xff1a; 本文介绍了通过CIU32单片机SPI接口控制DAC8551数模转换器的测试过程。首先改进了电路连接&#xff0c;将DAC8551的三线控制信号接入CIU32的SPI接口&#xff0c;并制作单面PCB测试板。通过设置SPI模式&#xff08;时钟常态低电平、数据下降沿锁定&#xff09;…

作者头像 李华
网站建设 2026/8/1 1:42:10

3步快速上手:在macOS上运行Windows软件和游戏的完整指南

3步快速上手&#xff1a;在macOS上运行Windows软件和游戏的完整指南 【免费下载链接】Whisky A modern Wine wrapper for macOS built with SwiftUI 项目地址: https://gitcode.com/gh_mirrors/wh/Whisky 想在苹果电脑上运行Windows专属的办公软件吗&#xff1f;希望畅玩…

作者头像 李华
网站建设 2026/8/1 1:41:05

自然语言处理基础与核心技术解析

1. 自然语言处理的基础认知框架自然语言处理&#xff08;NLP&#xff09;作为人工智能领域最具挑战性的分支之一&#xff0c;其核心目标是让机器具备理解、生成和处理人类语言的能力。我在过去五年参与企业级NLP系统开发时发现&#xff0c;90%的项目问题都源于对基础概念的误解…

作者头像 李华