当前位置: 首页 > news >正文

Android学习28--LED点灯(Ver2)(TODO)

可以这样做,但它不是“App 直接调用 HAL”。完整链路是:

系统 App ↓ android.hardware.lights.LightsManager Framework LightsService ↓ Binder android.hardware.light.ILights/default ↓ Lights AIDL HAL ↓ write() /sys/class/leds/user-led/brightness ↓ LED 驱动

Android 14 推荐使用稳定版 AIDL Lights HAL。标准接口只有两个核心方法:

HwLight[] getLights(); void setLightState(int id, HwLightState state);

官方接口定义见 ILights.aidl。

一、先把内核驱动标准化

当前节点是:

/sys/devices/platform/soc/soc:user-led/brightness

能用,但量产最好把驱动注册成 Linux LED class,生成:

/sys/class/leds/user-led/brightness /sys/class/leds/user-led/max_brightness

内核驱动大致如下:

#include <linux/leds.h> struct user_led { struct led_classdev cdev; int gpio; }; static void user_led_set( struct led_classdev *cdev, enum led_brightness value) { struct user_led *led = container_of(cdev, struct user_led, cdev); gpio_set_value(led->gpio, value != LED_OFF); } static int user_led_probe(struct platform_device *pdev) { struct user_led *led; led = devm_kzalloc( &pdev->dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; led->cdev.name = "user-led"; led->cdev.max_brightness = 1; led->cdev.brightness_set = user_led_set; return devm_led_classdev_register( &pdev->dev, &led->cdev); }

不过现有节点也可以先用于 HAL 验证。

二、实现 Lights AIDL HAL

接口来自 AOSP:

hardware/interfaces/light/aidl

HAL 进程需要实现:

aidl::android::hardware::light::BnLights

示意实现:

#include <aidl/android/hardware/light/BnLights.h> #include <aidl/android/hardware/light/HwLight.h> #include <aidl/android/hardware/light/HwLightState.h> #include <aidl/android/hardware/light/LightType.h> #include <fstream> using aidl::android::hardware::light::BnLights; using aidl::android::hardware::light::HwLight; using aidl::android::hardware::light::HwLightState; using aidl::android::hardware::light::LightType; using ndk::ScopedAStatus; class Lights : public BnLights { public: ScopedAStatus getLights( std::vector<HwLight>* lights) override { HwLight light; light.id = 10; light.ordinal = 0; light.type = LightType::NOTIFICATIONS; lights->clear(); lights->push_back(light); return ScopedAStatus::ok(); } ScopedAStatus setLightState( int32_t id, const HwLightState& state) override { if (id != 10) { return ScopedAStatus::fromExceptionCode( EX_UNSUPPORTED_OPERATION); } // HwLightState.color 是 ARGB。 // RGB 任一分量非零就点亮。 bool on = (state.color & 0x00ffffff) != 0; std::ofstream node( "/sys/class/leds/user-led/brightness"); if (!node.is_open()) { return ScopedAStatus::fromServiceSpecificError( errno); } node << (on ? "1" : "0"); if (!node.good()) { return ScopedAStatus::fromServiceSpecificError( EIO); } return ScopedAStatus::ok(); } };

如果暂时保留当前驱动,路径改成:

"/sys/devices/platform/soc/soc:user-led/brightness"

对于二值 LED,可以只判断:

(state.color & 0x00ffffff) != 0

如果后续换成 RGB LED,则可分别解析:

red = (color >> 16) & 0xff; green = (color >> 8) & 0xff; blue = color & 0xff;

三、注册 Binder 服务

main.cpp

#include "Lights.h" #include <android/binder_manager.h> #include <android/binder_process.h> int main() { ABinderProcess_setThreadPoolMaxThreadCount(0); auto service = ndk::SharedRefBase::make<Lights>(); const std::string instance = std::string(Lights::descriptor) + "/default"; binder_status_t status = AServiceManager_addService( service->asBinder().get(), instance.c_str()); if (status != STATUS_OK) return 1; ABinderProcess_joinThreadPool(); return 0; }

四、Android.bp

cc_binary { name: "android.hardware.light-service.userled", vendor: true, relative_install_path: "hw", srcs: [ "main.cpp", "Lights.cpp", ], shared_libs: [ "libbase", "libbinder_ndk", "android.hardware.light-V2-ndk", ], init_rc: [ "android.hardware.light-service.userled.rc", ], vintf_fragments: [ "android.hardware.light-service.userled.xml", ], }

需要使用与当前 Android 14固件匹配的接口版本。当前 AOSP Lights AIDL 包定义为 vendor 可用、VINTF-stable 接口。AIDL HAL 文档、Lights AIDL Android.bp。

五、init rc

service vendor.light-default \ /vendor/bin/hw/android.hardware.light-service.userled class hal user system group system

是否需要disabledoneshot要看 lazy HAL 设计。普通常驻 HAL 不加oneshot

如果采用 lazy HAL,需要配合:

interface aidl android.hardware.light.ILights/default disabled oneshot

首版建议先做普通常驻服务,便于调试。

六、VINTF manifest

<manifest version="1.0" type="device"> <hal format="aidl"> <name>android.hardware.light</name> <version>2</version> <interface> <name>ILights</name> <instance>default</instance> </interface> </hal> </manifest>

有些版本也使用简化形式:

<hal format="aidl"> <name>android.hardware.light</name> <fqname>ILights/default</fqname> </hal>

实际格式要与板子当前 VINTF schema 和 AIDL 版本一致。官方默认示例可参考 lights-default.xml。

七、SELinux

不能继续使用:

chmod 0666 SELinux Permissive

应该给 LED 节点单独定义类型,例如:

type sysfs_user_led, fs_type, sysfs_type;

如果使用标准 LED class,可以通过genfs_contexts标记:

genfscon sysfs /class/leds/user-led \ u:object_r:sysfs_user_led:s0

实际 sysfs 内核路径经常不是表面软链接路径,需要用:

readlink -f /sys/class/leds/user-led

确认后填写真实路径。

HAL 定义独立 domain:

type hal_light_userled, domain; type hal_light_userled_exec, exec_type, vendor_file_type, file_type; init_daemon_domain(hal_light_userled) allow hal_light_userled sysfs_user_led:dir { search }; allow hal_light_userled sysfs_user_led:file { open write getattr };

服务二进制标签:

/vendor/bin/hw/android.hardware.light-service.userled \ u:object_r:hal_light_userled_exec:s0

还要补充 Binder/AIDL HAL 相关宏,具体宏名取决于 BSP sepolicy:

hal_server_domain( hal_light_userled, hal_light )

最终必须在:

getenforce = Enforcing

条件下验证。

八、Framework 如何连接 HAL

Android Framework 的LightsService会查找:

android.hardware.light.ILights/default

然后调用 HAL:

getLights() setLightState()

通常不需要修改SystemServer。只要:

  • HAL 名称正确;
  • VINTF 声明正确;
  • Binder 服务注册成功;
  • SELinux 放行;
  • Framework 与 vendor AIDL 版本兼容。

就能自动接上。

验证 HAL:

adb shell service list | grep -i light adb shell dumpsys lights adb shell logcat -b all | grep -i light adb shell ps -A | grep light

AIDL vendor service也可以检查:

adb shell service check \ android.hardware.light.ILights/default

九、App 调用 LightsManager

App 端可使用:

import android.hardware.lights.Light import android.hardware.lights.LightState import android.hardware.lights.LightsManager

获取灯:

val manager = getSystemService(LightsManager::class.java) val light = manager.lights.firstOrNull { it.type == Light.LIGHT_TYPE_NOTIFICATIONS }

打开会话并控制:

val session = manager.openSession() val onState = LightState.Builder() .setColor(0xFFFFFFFF.toInt()) .build() session.requestLights( LightsRequest.Builder() .addLight(light!!, onState) .build() )

关闭:

val offState = LightState.Builder() .setColor(0x00000000) .build() session.requestLights( LightsRequest.Builder() .addLight(light, offState) .build() )

Activity 销毁时:

session.close()

权限限制

这是关键点:普通第三方 App 通常不能任意控制设备灯。直接使用LightsManager.openSession()需要受保护的设备灯控制权限,例如:

<uses-permission android:name="android.permission.CONTROL_DEVICE_LIGHTS"/>

该权限不是普通运行时权限,通常要求受信任系统 App/平台签名。仅把 APK 放进priv-app也不一定足够,还需要:

  • platform/厂商签名;
  • privapp 权限白名单;
  • Framework 权限定义允许;
  • 必要时专用 SELinux App domain。

所以最终 App 一般要做成厂商系统 App。

十、LED 类型怎么选

如果这是通知灯,可以返回:

LightType::NOTIFICATIONS

如果是产品独有的功能灯,不建议冒充:

BACKLIGHT BATTERY CAMERA MICROPHONE

因为 Framework 可能自动控制这些灯,与你的 App 冲突。

需要考虑两个方案:

  • 让它作为NOTIFICATIONS灯,由 Android 通知系统控制;
  • 使用 Lights HAL 中额外的同类实例,并由特权 App 根据id/ordinal选择。

HwLight.id是 HAL 自定义的稳定 ID,例如10ordinal用于同类型多个灯排序。

如果只是业务 App 独占的一颗 LED,自定义 AIDL HAL 往往比标准 Lights HAL 更清晰。Lights HAL 的语义主要是 Android 系统灯,Framework 可能根据通知、充电、相机或麦克风状态主动接管。

十一、当前“无源码”条件的现实限制

完整实施至少需要:

匹配 Android 14 的 AIDL NDK 头文件和库 Lights HAL 二进制 init rc VINTF manifest vendor sepolicy App 系统签名/权限 重新制作 vendor.img/product.img

只有现成 image 时,可以先编译 HAL 二进制并通过 remount 做原型测试,但之前已经验证过:

  • overlay 中新增.rc可能不会被 init 早期加载;
  • 新增 VINTF 文件也有启动时序问题;
  • SELinux policy 不能靠普通 push 完整增加;
  • App 缺少 platform 签名权限。

因此:

原型验证: 现有启动脚本 → 启动自定义服务 → App 调用 正式量产: 编入 vendor.img/product.img + SELinux Enforcing

对于当前板子,我建议先做“小型自定义 AIDL/native daemon”验证 App→Binder→sysfs,确认业务接口;拿到匹配 BSP 后再决定是否迁移到标准 Lights HAL。这样风险最低,也不会和 Android 自带灯光策略发生冲突。

http://www.jsqmd.com/news/1396033/

相关文章:

  • Unity游戏管理器:场景加载与重启的完整实现方案
  • 把十年QQ空间说说完整搬回家:GetQzonehistory备份实战全记录
  • 拼多多数据采集快速上手:5分钟用 scrapy-pinduoduo 抓取热销商品与用户评论
  • 钉钉零代码打造培训考试闭环系统
  • 大数据分析工具有哪些?五款主流平台深度评测与选型指南
  • 从输入法到数据库:构建全链路姓名处理系统,解决生僻字乱码问题
  • Context Engineering:解决AI Agent幻觉与上下文失忆的工程化实践
  • 2026年AI工程化落地:从模型驱动到应用驱动,成本、评估与Agent实战
  • 计算机毕业设计之凿壁自习室管理系统的设计与实现
  • 复杂系统动力学建模:从多体协同到板凳龙运动仿真
  • 2026 年至今,衡阳专业的报废油漆回收公司联系方式,别再当冤大头了,这玩意儿处理竟能帮你省一大笔钱还不踩坑? - 行业鉴选官
  • AI大模型核心技术解析:从Transformer架构到实战应用指南
  • OpenClaw AI代理框架部署指南:从环境配置到生产级运维全解析
  • 告别网盘限速焦虑:这款九大平台通用的网盘直链下载助手,五分钟就能上手
  • B站API整理项目实战指南:从视频排行到弹幕字幕,一次上手
  • Thompson构造法:从正则表达式到NFA的完整实现与调试指南
  • 大模型时代TTS技术实战:从原理到应用的全链路指南
  • 应对公司电脑强制锁屏:合规优化与脚本方案详解
  • 渗透测试实战:从信息收集到内核提权全流程解析
  • HBase集群搭建实战:从HDFS/ZK配置到核心参数调优详解
  • Java面试核心体系:JVM、并发、Spring与数据库实战解析
  • 企业视频实时语音转写怎么做?——灵声智库流式 ASR、说话人区分与多会议室并发实践
  • 百度安全“龙虾”全家桶:安全产品的趣味营销解析
  • 工厂方法模式:优雅解耦对象创建,提升代码可维护性与扩展性
  • MPO光纤连接器:高密度数据中心布线的核心原理与实战指南
  • C++高并发编程:双缓冲无锁队列设计与实现
  • Anaconda默认启动环境配置指南:关闭base自动激活与多环境管理
  • 黄金票据与白银票据:Kerberos协议下的权限维持攻击与防御实战
  • LangChain+LangGraph+DeepAgent构建企业级AI Agent长期记忆架构实战
  • 从零搭建Hadoop 3.3.6集群:超详细步骤与核心配置解析