什么是 GPIO?如何在 Android 中控制 LED 灯的开关?

解读

国内 Android 面试里,这道题常被用来区分“只会写业务代码”与“真正做过 BSP/系统层适配”的候选人。
面试官想听到三条主线:

  1. GPIO 的硬件本质与 Linux 抽象;
  2. 从内核到 HAL、JNI、Framework、App 的完整调用链;
  3. 权限、SELinux、签名、接口暴露等国内项目必踩的坑。
    回答时先给一句话定义,再分层展开,最后给出可直接在 AOSP 里落地的最小实现,基本就能拿到高分。

知识点

  1. GPIO(General-Purpose Input/Output):SoC 引脚的可编程数字 IO,电平 0/1,方向输入/输出。
  2. Linux gpiolib:内核通过 sysfs /sys/class/gpio 或 libgpiod 提供统一抽象;Android 10 以后官方推荐 libgpiod,不再走 sysfs。
  3. Android HAL:硬件抽象层位于 hardware/libhardware,接口用 C 头文件声明,实现放在 vendor/ 分区,开机被 hwservicemanager 注册为 ILedIGpio HAL service。
  4. JNI 与 AIDL:Framework 通过 JNI 调用 HAL,再用 AIDL 向上暴露 ILedManager.aidl,App 拿到 system_server 代理。
  5. 权限模型:
    • 普通 app 无直接 /dev/gpiochip0 访问权;
    • 必须声明厂商自定义权限 com.xxx.permission.CONTROL_LED,并在 privapp-permissions.xml 中白名单;
    • SELinux 给 hal_led_default 域添加 allow hal_led_default gpio_device:chr_file { read write open };
  6. 折叠屏/车载场景:LED 可能由 PMIC、LP5569 等 PWM 控制器驱动,GPIO 只负责使能;需确认设备树 pinctrlgpio-hog 配置。
  7. 国内合规:通过 MSSL 或国密芯片的 TEE 调用时,GPIO 操作要在 TA 内部完成,防止用户空间绕过。

答案

一句话定义:GPIO 是芯片引脚级的可编程数字 IO,Android 中控制 LED 的本质是把 GPIO 置高/置低,但需经过“内核→HAL→Framework→应用”四级隔离。

最小可落地的实现步骤(AOSP 12 以上,libgpiod 方案):

  1. 设备树
    leds-gpio 节点里把 GPIO 号、有效电平、默认状态写死,防止被用户空间抢占。

    led0: gpio_led@0 {
        gpios = <&gpio2 15 GPIO_ACTIVE_HIGH>;
        default-state = "off";
        linux,default-trigger = "none";
    };
    
  2. 内核驱动
    内核自带 drivers/leds/leds-gpio.c,编译进 zImage 即可,自动生成 /sys/class/leds/led0/brightness

  3. 自定义 HAL(AIDL 模式)
    目录 hardware/interfaces/led/1.0/ 新建 ILed.hal → 生成 ILed.cpp 实现,内部通过 gpiod_ctxless_set_value 操作 gpiochip0 line15,编译为 android.hardware.led@1.0-service 可执行文件,开机由 rc 脚本拉起,注册到 hwservicemanager

  4. Framework 封装
    services/core/java/com/android/server/led/LedService.java 通过 JNI 调用上述 HAL,再在 SystemServer.java 启动时注册;对外提供 LedManager.systemControl(String name, boolean on)

  5. 应用调用
    厂商预装 App 在 AndroidManifest.xml 声明
    <uses-permission android:name="com.xxx.permission.CONTROL_LED"/>
    代码里

    LedManager lm = (LedManager) getSystemService("led");
    lm.systemControl("led0", true);   // 开灯
    
  6. 权限与 SELinux
    device/xxx/sepolicy/vendor/hal_led.te 补充规则,确保 hal_led_default 域可以访问 gpio_device
    etc/permissions/privapp-permissions-xxx.xml 把包名加入白名单,防止 avc denied

  7. 验证
    adb shell lshal debug android.hardware.led@1.0::ILed/default 能列出服务;
    adb shell gpiodetect 确认 gpiochip0 存在;
    adb shell cat /sys/kernel/debug/gpio 查看 line15 状态;
    最终用示波器或万用表量 LED 引脚,电平跟随 App 指令翻转即通过。

拓展思考

  1. 用户空间直接 mmap /dev/mem 操作寄存器行不行?
    国内部分旧方案为了“省 HAL”这么干,结果 SELinux 永远报 avc,且 CTS 无法通过;Google 从 Android 11 开始把 /dev/mem 列为 neverallow,必须走 HAL。

  2. 如果 LED 接在 PMIC 的 PWM 引脚而非 SoC GPIO,该怎么适配?
    把 HAL 实现换成 leds-lp55xx 驱动,通过 i2c_transfer 写寄存器;Framework 层接口不变,App 无感知,体现 HAL 抽象价值。

  3. 车载 IVI 场景要求功能安全 ASIL-B,GPIO 控制需要双区备份怎么办?
    在 TEE 侧部署安全 OS,GPIO 操作封装为 TA 接口,REE 侧 HAL 通过 qseecom_send_cmd 调用;即使 Linux 被攻破,LED 状态机仍由安全世界守护。

  4. 折叠屏手机有 3 颗 LED(红绿蓝),如何支持并发闪烁而不卡顿?
    内核改用 pwm-backlight + 定时器队列,HAL 层缓存颜色队列,Framework 层把闪烁指令打包为 WorkRequest 交给 WorkManager,确保亮屏时不会抢占 UI 线程。

  5. 国内出海项目需过 Google GMS 认证,LED 接口是否必须隐藏?
    GMS 要求非 SDK 接口黑名单,厂商自定义的 LedManager 不能出现在 @SystemApi 之外;正确做法是把接口标记为 @hide,只对预装白名单 App 开放,否则会被扫描工具判为违规暴露。