vendor/giec/ 厂商定制层分析

基于 device-giec.mk 逐行追踪,理解"厂商如何改系统" 参考代码库:vendor/giec/,Android 14 + Amlogic S905X5M


目录

  1. device-giec.mk 总览
  2. GApps 集成(条件编译)
  3. SELinux 策略
  4. HIDL 服务声明(manifest + matrix)
  5. HIDL 客户端与服务端(hwstbcmdservice)
  6. 预置 APK
  7. 预置二进制与 Shell 脚本
  8. 设备配置与条件编译
  9. ADB 与调试配置
  10. 签名配置

1. device-giec.mk 总览

vendor/giec/
├── Android.mk                     # 入口:遍历所有 subdir
├── device-giec.mk                 # ★ 核心 Makefile,被 device 层 include
├── common/
│   ├── Android.mk                 # 编译 apps/ 和 libraries/ 下的模块
│   ├── Android.bp
│   ├── sepolicy/                  # SELinux 规则(.te 文件)
│   ├── manifest.xml               # HIDL 服务声明(设备提供什么服务)
│   ├── compatibility_matrix.xml   # HIDL 需求声明(设备需要什么服务)
│   ├── keys/
│   │   └── adbkey.pub             # ADB 预置公钥
│   ├── libraries/hwstbcmdapi/     # ★ 核心 HIDL 服务完整实现
│   └── files/Privacy_Policy.txt   # 隐私政策
├── apps/                          # 预置 APK
│   ├── OTAClient/
│   ├── BazeportLauncher/
│   ├── BazeportSystem/
│   ├── LeanKeyboard/
│   ├── Glauncher/
│   ├── STB-TEST/
│   ├── remove_unused_module/
│   └── busybox/
├── hardware/interfaces/           # HIDL .hal 定义
│   └── hwstbcmdservice/
│       └── 1.0/
└── executable/                    # Shell 脚本工具集
    ├── get_display_id.sh
    └── network/

device-giec.mk 在构建系统中被 device/amlogic/ross/device.mk(或类似设备配置)通过 $(call inherit-product, vendor/giec/device-giec.mk) 包含。它的作用是把厂商所有的定制内容注入到构建产物中


2. GApps 集成(条件编译)

ifeq ($(BOARD_COMPILE_GAPPS_TV),true)
$(call inherit-product, vendor/gapps_tv/arm/arm-vendor.mk)
endif
  • 通过环境变量 BOARD_COMPILE_GAPPS_TV=true 控制是否集成 Google 移动服务(Android TV 版)
  • GApps 放在独立的 vendor/gapps_tv/ 目录,与厂商定制代码解耦
  • 这是 Android 构建系统典型的条件编译模式

3. SELinux 策略

SELinux (Security-Enhanced Linux) 是 Linux 内核的强制访问控制(MAC) 系统,由 NSA 开发。与传统的"用户/组/其他"权限(DAC,自主访问控制)不同,SELinux 对所有进程和资源的访问都通过安全策略进行强制检查。

Android 从 5.0 起强制开启 SELinux(Enforcing 模式)。它的核心作用:即使一个进程被提权攻击,SELinux 策略也会阻止它访问未授权的资源。在 Android 嵌入式系统中,厂商需要为每个新增的守护进程编写 SELinux 策略。

BOARD_SEPOLICY_DIRS += vendor/giec/common/sepolicy

此行把厂商的 sepolicy 目录加入编译。目录中的 .te 文件会被编译进 bootimage 中的 sepolicy 二进制。

在参考代码库中的体现:厂商为自定义的 hwstbcmdservice HAL 服务单独定义了一个 SELinux 域 hal_hwstbcmdservice,并为 system_app 和 platform_app 授予了调用该服务的权限。

3.1 文件清单

文件 用途
hal_hwstbcmdservice.te ★ 核心域:为 hwstbcmdservice HAL 进程定义的 SELinux 域(65KB,超详细)
system_app.te 允许 system_app 和 platform_app 找到并使用 hwstbcmdservice HIDL 服务
adbd.te 允许 adbd 读取 /vendor/etc/adb/ 下的预置 ADB 密钥
avc.te 解决 avc denials:补充 hal_hwstbcmdservice 对 device 和 vndbinder 的访问
hal_wifi_default.te 允许 WiFi HAL 读调试属性
hal_wifi_hostapd_default.te 允许 WiFi Hostapd HAL 读 sysfs 和 vendor 属性
permissive.te 将 hal_hwstbcmdservice 设为 permissive 模式
vendor_property.te 定义厂商自定义属性的类型和访问规则
file_contexts 为 HAL 二进制和 ADB 密钥目录分配安全上下文
property_contexts 为厂商自定属性(蓝牙、WiFi、自动连接前缀)绑定上下文
hwservice_contexts 为 HIDL 服务接口绑定安全上下文
hwservice.te 声明 HIDL 服务接口类型

3.2 hal_hwstbcmdservice 域(核心)

# 声明域和可执行文件类型
type hal_hwstbcmdservice, domain;
type hal_hwstbcmdservice_exec, exec_type, vendor_file_type, file_type;

# 使用 hwbinder 通信
hwbinder_use(hal_hwstbcmdservice);

# 声明为服务管理器可管理的服务类型
type hwstbcmdservice_1_0_service, service_manager_type;

# init 进程启动此守护进程
init_daemon_domain(hal_hwstbcmdservice)

# 注册 HIDL 服务到 hwservicemanager
add_hwservice(hal_hwstbcmdservice, vnd_hwstbcmdservice_hwservice)

# 设备和文件访问(精简示例)
allow hal_hwstbcmdservice device:chr_file { ioctl };
allow hal_hwstbcmdservice proc:file { read write open getattr };
allow hal_hwstbcmdservice sysfs:file { open write read };

这个 .te 文件有 300 行,几乎是把所有可能需要的权限都加上了(包括很多被注释掉的)。它承担了大量功能:

  • 执行 shell 命令(popen)、读写 sysfs/proc、操作设备节点
  • 网络操作(udp_sockettcp_socketpacket_socket
  • 与 system_app、platform_app 进行 Binder 通信
  • 读日志、写 sdcard、重启系统

3.3 system_app.te —— 客户端访问权限

allow system_app vnd_hwstbcmdservice_hwservice:hwservice_manager { find };
allow system_app hal_hwstbcmdservice:binder { call transfer };
allow platform_app vnd_hwstbcmdservice_hwservice:hwservice_manager { find };

system_appplatform_app 需要显式授权才能通过 hwservicemanager 查找(find)并使用(call/transfer)这个 HAL 服务。

3.4 permissive.te —— 调试阶段的"大赦"

Permissive 模式:SELinux 有两种工作模式。Enforcing(强制)模式会阻止违规操作并记录日志,Permissive(宽容)模式只记录日志但不阻止操作。Permissive 通常用于开发和调试阶段,方便快速定位哪些访问被 SELinux 阻止了(在 dmesg \| grep avc 中查看)。

permissive hal_hwstbcmdservice;

这行说:hal_hwstbcmdservice 域的所有访问都不被阻止,只记录日志。这是厂商开发阶段的常见做法——先让功能跑起来,再慢慢收紧规则。生产固件中应该去掉这行。

在参考代码库中的体现:整个 permissive.te 只有这一条规则。结合 hal_hwstbcmdservice.te 长达 300 行的 allow 规则来看,该服务的权限尚未完全收敛,处于"边跑边加"的阶段。

3.5 属性与上下文映射

property_contexts 定义了厂商自定属性:

ro.vendor.bluetooth.disable     → vendor_custom_prop
ro.vendor.wifi.disable          → vendor_custom_prop
ro.vendor.autoconnectbt.nameprefix → vendor_custom_prop
persist.vendor.need.btsetup     → vendor_custom_prop

vendor_property.te 中:

type vendor_custom_prop, property_type;
get_prop(appdomain, vendor_custom_prop);    # 所有应用可读
set_prop(vendor_init, vendor_custom_prop);  # 仅 vendor_init 可写

file_contexts 关键映射:

/vendor/bin/hw/giec.hardware.hwstbcmdservice@1.0-service → hal_hwstbcmdservice_exec
/vendor/etc/adb(/.*)?  → vendor_configs_file

hwservice_contexts 映射 HIDL 接口到安全上下文:

giec.hardware.hwstbcmdservice::IHwstbcmdservice → vnd_hwstbcmdservice_hwservice

4. HIDL 服务声明(manifest + matrix)

HIDL (HAL Interface Definition Language) 是 Android Treble 架构引入的硬件抽象层接口定义语言。它的核心目的:将 Framework 与硬件厂商的 HAL 实现解耦,使得 Framework 可以独立更新而无需等待芯片厂商适配。

工作方式:通过 .hal 文件定义接口(类似 C++ 的头文件),然后由 hidl-gen 工具自动生成 C++/Java 的客户端和服务端代码。通信使用 hwbinder(与 Android 应用层的 Binder 不同的 IPC 通道)。

在参考代码库中的体现:厂商自定义了一个 giec.hardware.hwstbcmdservice@1.0 的 HIDL 服务,定义在 hardware/interfaces/hwstbcmdservice/1.0/IHwstbcmdservice.hal,包含一个执行 shell 命令的接口。这是典型的"通过 HIDL 给应用层暴露底层能力"的模式。

Treble 架构下,系统中有两个角色文件来管理 HIDL 服务:

  • manifest.xml:设备提供了哪些 HIDL 服务
  • compatibility_matrix.xml:设备需要 Framework 提供哪些服务

4.1 manifest.xml —— 我提供什么

<manifest version="1.0" type="device" target-level="8">
    <hal format="hidl">
        <name>giec.hardware.hwstbcmdservice</name>
        <transport>hwbinder</transport>
        <version>1.0</version>
        <interface>
            <name>IHwstbcmdservice</name>
            <instance>default</instance>
        </interface>
    </hal>
</manifest>
  • type="device":这是一个设备端 manifest,声明设备实际提供的 HIDL 服务
  • target-level="8":对应 Android 14 的 API 级别
  • transport="hwbinder":使用 hwbinder 通信(不是 vsbinder 或 binder)
  • instance="default":服务实例名,创建服务时用这个名字注册

4.2 compatibility_matrix.xml —— 我需要什么

<compatibility-matrix version="1.0" type="device">
    <hal format="hidl" optional="true">
        <!-- 内容同 manifest,但与 Framework 的 matrix 做匹配检查 -->
    </hal>
</compatibility-matrix>
  • type="device":设备端的 matrix,声明 Framework 必须提供满足这些需求的服务
  • optional="true":这个服务是可选的,Framework 没有也能工作
  • 作用:Framework 的 compatibility_matrix.xml 和设备的 manifest.xml 做交叉检查

简单理解:manifest = “我有什么”,matrix = “我需要什么”。系统启动时 hwservicemanager 根据 manifest 注册服务,Framework 根据 matrix 查找所需服务。


5. HIDL 客户端与服务端(hwstbcmdservice)

这是这个 vendor 层最核心的代码。完整调用链:

Java App (ShellCmd.java)
  → JNI (cn_giec_adp_cmd.cpp)
    → Client (HiStbcmdserviceManagerClient.cpp)
      → HIDL IPC (hwbinder)
        → HAL Service (Hwstbcmdservice.cpp)
          → Native (StbCmdShellhal.c)
            → popen() 执行 shell 命令

5.1 HIDL 接口定义

hardware/interfaces/hwstbcmdservice/1.0/IHwstbcmdservice.hal

package giec.hardware.hwstbcmdservice@1.0;
interface IHwstbcmdservice {
    hsInvokeHal(string request, int32_t type) generates(string result);
};

非常简洁:一个函数,输入字符串命令,输出字符串结果type 参数控制是否等待返回结果(0=不等待,1=等待)。

5.2 构建配置

hardware/interfaces/Android.bp 中定义了 HIDL package root:

hidl_package_root {
    name: "giec.hardware",
    path: "vendor/giec/hardware/interfaces",
}

cc_defaults {
    name: "hidl_giec",
    cflags: ["-Wall", "-Werror"],
}

1.0/Android.bp 使用 hidl-gen 生成代码:

hidl_interface {
    name: "giec.hardware.hwstbcmdservice@1.0",
    root: "giec.hardware",
    system_ext_specific: true,  # 在 system_ext 分区
    srcs: ["IHwstbcmdservice.hal"],
    gen_java: true,             # 同时生成 Java 绑定
}

default/Android.bp 编译实现和服务:

# 实现库(.so)
cc_library_shared {
    name: "giec.hardware.hwstbcmdservice@1.0-impl",
    srcs: ["Hwstbcmdservice.cpp"],
    shared_libs: ["libstbcmdservicehal", "giec.hardware.hwstbcmdservice@1.0"],
}

# 可执行服务
cc_binary {
    name: "giec.hardware.hwstbcmdservice@1.0-service",
    relative_install_path: "hw",     # → /vendor/bin/hw/
    init_rc: ["...service.rc"],
    shared_libs: ["giec.hardware.hwstbcmdservice@1.0-impl"],
}

5.3 hwbinder 与 vndbinder

HIDL 服务通信使用 hwbinder,这是 Android 中与普通 Binder 不同的独立 IPC 通道。

Android 有三种 Binder 域:

Binder 域 设备节点 用途
binder /dev/binder Framework 内部(Java 应用间的 IPC)
hwbinder /dev/hwbinder Framework 与 HAL 之间的 HIDL 通信
vndbinder /dev/vndbinder Vendor 进程之间的 Binder 通信

为什么要隔离? 为了 Treble 架构的"Framework 可独立升级"目标。Framework 只用 binder 和 hwbinder,vendor 进程使用 vndbinder。这样 Framework 更新不会影响 vendor 域的 Binder 协议。

在参考代码库中的体现service.cpp 中通过 ProcessState::initWithDriver("/dev/vndbinder") 显式指定使用 vndbinder,表明该 HAL 服务属于 vendor 域。

5.4 HAL 服务实现

Hwstbcmdservice.cpp —— HIDL 接口实现:

Return<void> Hwstbcmdservice::hsInvokeHal(
    const hidl_string& request, int32_t type, hsInvokeHal_cb _hidl_cb)
{
    const char* result = hsInvokeNative(request.c_str(), type);
    hidl_string cb(result);
    _hidl_cb(cb);    // 通过回调返回结果(HIDL 异步模式的标准做法)
    return Void();
}

service.cpp —— main 函数,注册为系统服务:

int main() {
    ProcessState::initWithDriver("/dev/vndbinder");  // 使用 vndbinder
    configureRpcThreadpool(1, true);
    sp<IHwstbcmdservice> service = HIDL_FETCH_IHwstbcmdservice("default");
    service->registerAsService("default");  // 注册为 "default" 实例
    joinRpcThreadpool();
}

关键点:

  • 使用 /dev/vndbinder(vendor 域的 Binder 驱动),与 Framework 的 binder 隔离
  • HIDL_FETCH_IHwstbcmdservice 是 HIDL 的约定入口函数名,按服务名动态加载实现

5.5 Init RC 文件

service hwstbcmdservice-1-0 /vendor/bin/hw/giec.hardware.hwstbcmdservice@1.0-service
    class hal       # 属于 hal 类(与其它 HAL 服务一起启动/停止)
    user root       # 以 root 运行(因为它需要执行 shell 命令)
    group root system

5.6 底层执行引擎

StbCmdShellhal.c —— 真正的"干活"代码:

char* hsInvokeNative(const char* request, int type) {
    static char reply[BUFFER_MAX];  // 静态缓冲区,4096 字节
    int ret = do_system_cmd(request, reply, flag);
    return reply;
}

static int do_system_cmd(const char *arg, char *reply, bool isNeedRet) {
    fpRead = popen(arg, "r");    // 执行 shell 命令
    if (!isNeedRet) {
        pclose(fpRead);          // type=0: 不等待结果
        return 0;
    }
    while (fgets(buf, ..., fpRead) != NULL) {
        strncat(reply, buf, ...); // type=1: 读取命令输出
    }
    pclose(fpRead);
}

本质:这个 HIDL 服务就是一个"受保护的 shell 命令执行器"。App 通过它执行任意 shell 命令,绕过了 Android 应用沙箱的限制。

libstbcmdservicehalAndroid.bp

cc_library_shared {
    name: "libstbcmdservicehal",
    srcs: ["StbCmdShellhal.c"],
    shared_libs: ["liblog", "libutils", "libcutils"],
}

5.7 客户端库

HiStbcmdserviceManagerClient.cpp —— C++ 客户端:

char* HiStbcmdserviceManagerClient::hsInvokeHalClient(char* request, int type) {
    sp<IHwstbcmdservice> mHal = IHwstbcmdservice::getService();  // 获取服务
    auto cbfun = [&](hidl_string strReply) {
        memcpy(result, strReply.c_str(), strReply.size() + 1);
    };
    mHal->hsInvokeHal(request, type, cbfun);   // 调用 HIDL 方法
    return result;
}

HiStbcmdserviceManagerClient.h —— 类声明:

namespace android {
class HiStbcmdserviceManagerClient : public RefBase {
public:
    char* hsInvokeHalClient(char* request, int type);
};
}

5.8 JNI 桥接

cn_giec_adp_cmd.cpp —— JNI 实现:

JNIEXPORT jstring JNICALL Java_cn_giec_shellcmd_ShellCmd_hsInvokeJni(
    JNIEnv *env, jclass clazz, jstring request, jint type)
{
    char* requestStr = jstringToChar(env, request);
    char* reply = HiStbcmdserviceManagerClient().hsInvokeHalClient(requestStr, type);
    return env->NewStringUTF(reply);
}

注册方式(传统的 AndroidRuntime::registerNativeMethods):

static JNINativeMethod gMethods[] = {
    {"hsInvokeJni", "(Ljava/lang/String;I)Ljava/lang/String;",
     (void *)Java_cn_giec_shellcmd_ShellCmd_hsInvokeJni},
};

jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    register_android_histbcmdservicemanage_common(env);
    return JNI_VERSION_1_4;
}

5.9 Java API

ShellCmd.java —— 暴露给系统应用的接口:

package cn.giec.shellcmd;

public class ShellCmd {
    static { System.loadLibrary("histbcmdservice_jni"); }

    private static native String hsInvokeJni(String cmd, int type);

    public static String exec(String cmd, boolean isReturn) {
        String fullCmd = "PATH=/system/bin:/system/xbin:/vendor/bin:/vendor/xbin " + cmd;
        return hsInvokeJni(fullCmd, isReturn ? 1 : 0);
    }

    public static void exec(String cmd) {
        exec(cmd, false);
    }
}

stbshellcmd 静态 Java 库的构建(Android.bp):

java_library_static {
    name: "stbshellcmd",
    srcs: ["java/**/*.java"],
    platform_apis: true,       # 使用系统 API
}

5.10 完整调用链图

┌─────────────────────────────────────────────────────────────────┐
│  Java 系统应用                                                   │
│  ShellCmd.exec("ifconfig eth0 down")                            │
└──────────────────────────┬──────────────────────────────────────┘
                           │ JNI
┌──────────────────────────▼──────────────────────────────────────┐
│  libhistbcmdservice_jni.so (JNI 桥接)                           │
│  cn_giec_adp_cmd.cpp                                            │
└──────────────────────────┬──────────────────────────────────────┘
                           │ C++ 调用
┌──────────────────────────▼──────────────────────────────────────┐
│  libhistbcmdservicemanageclient.so (HIDL 客户端)                │
│  HiStbcmdserviceManagerClient.cpp                               │
│  IHwstbcmdservice::getService() → hwbinder                      │
└──────────────────────────┬──────────────────────────────────────┘
                           │ HIDL IPC (hwbinder)
┌──────────────────────────▼──────────────────────────────────────┐
│  giec.hardware.hwstbcmdservice@1.0-service (HAL 服务进程)       │
│  /vendor/bin/hw/giec.hardware.hwstbcmdservice@1.0-service      │
│  Hwstbcmdservice.cpp → hsInvokeHal()                           │
└──────────────────────────┬──────────────────────────────────────┘
                           │ C 函数调用
┌──────────────────────────▼──────────────────────────────────────┐
│  libstbcmdservicehal.so (本地命令执行)                          │
│  StbCmdShellhal.c → hsInvokeNative() → do_system_cmd()         │
│  → popen("ifconfig eth0 down", "r")                            │
└──────────────────────────┬──────────────────────────────────────┘
                           │ fork+exec
                           ▼
                    Linux 内核执行 shell 命令

这个模式在嵌入式 Android 中很典型:厂商提供一个"后门"HAL 服务,让系统应用可以执行 shell 命令,绕过 Android 应用沙箱的限制。这是厂商定制 Rom 的常见做法。

5.11 device-giec.mk 中的引用

# HIDL 客户端库(被 JNI 库链接)
PRODUCT_PACKAGES += \
    libhistbcmdservice_jni \
    libhistbcmdservicemanageclient \
    libstbcmdservicehal

# HIDL Treble 服务
PRODUCT_PACKAGES += \
    giec.hardware.hwstbcmdservice@1.0 \
    giec.hardware.hwstbcmdservice@1.0-impl \
    giec.hardware.hwstbcmdservice@1.0-service

6. 预置 APK

6.1 列表

PRODUCT_PACKAGES += \
    LeanKeyboard \       # ATV 定制键盘
    OTAClient \          # OTA 升级客户端
    BazeportLauncher \   # 定制 Launcher(桌面)
    BazeportSystem \     # 系统工具
    luojHello            # 测试/示例应用

6.2 预置 APK 的 Android.mk 模式

简单预置(LeanKeyboard、OTAClient、luojHello):

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
APPS := $(notdir $(wildcard $(LOCAL_PATH)/*.apk))
APP_NAME := $(basename $(APPS))
LOCAL_MODULE := $(APP_NAME)
LOCAL_SRC_FILES := $(APPS)
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := platform    # 用平台 key 签名
LOCAL_PRIVILEGED_MODULE := false
include $(BUILD_PREBUILT)

复杂的预置(BazeportLauncher):

LOCAL_PRIVILEGED_MODULE := true    # 特权应用
LOCAL_SYSTEM_EXT_MODULE := true    # 安装在 system_ext 分区
LOCAL_PREBUILT_JNI_LIBS := ...    # 携带本地 .so 库
LOCAL_REQUIRED_MODULES += \
    privapp-permissions-bazeport-next.xml \
    default-permissions-bazeport-next.xml

6.3 权限管理(BazeportLauncher 示例)

privapp-permissions-bazeport-next.xml —— 特权权限白名单:

<privapp-permissions package="com.bazeport.next">
    <!-- 系统级权限 -->
    <permission name="android.permission.INSTALL_PACKAGES"/>
    <permission name="android.permission.REBOOT"/>
    <permission name="android.permission.INJECT_EVENTS"/>
    <!-- ... 共计 60+ 个权限 -->
</privapp-permissions>

default-permissions-bazeport-next.xml —— 运行时权限预授权:

<exception package="com.bazeport.next">
    <permission name="android.permission.ACCESS_FINE_LOCATION" fixed="true"/>
    <!-- fixed="true" 意味着用户不能撤销此权限 -->
</exception>

权限 XML 为什么要单独放在 system_ext/etc/permissions/? Android 10+ 要求:privapp 权限必须在 system_ext/etc/permissions/ 下声明才能被识别。

6.4 移除不需要的应用

# 类型为 FAKE,只声明了一串 LOCAL_OVERRIDES_PACKAGES
PRODUCT_PACKAGES += remove_unused_module

# 移除的这些应用不会出现在最终的系统中
LOCAL_OVERRIDES_PACKAGES += \
    Browser2 ABUpdater RemoteIME DeskClock \
    TVLauncher \               # 如果用了 Glauncher
    AtvRemoteService SetupWraithPrebuilt \  # GApps 中的一些组件
    LiveTv TV DroidLogicLiveTv DroidLogicTvInput

LOCAL_OVERRIDES_PACKAGES 是 Android 构建系统提供的包替换机制:如果模块 A override 了模块 B,那么构建时 A 会替代 B 被打包进系统。


7. 预置二进制与 Shell 脚本

7.1 busybox

PRODUCT_PACKAGES += busybox

Busybox 是嵌入式 Linux 的瑞士军刀。Android TV 的 Shell 脚本需要它提供的命令(如 udhcpcawkgrep)。

构建方式(apps/busybox/Android.mk):

LOCAL_MODULE := busybox
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_OUT)/bin     # → /system/bin/busybox
LOCAL_SRC_FILES_arm := arm/busybox
LOCAL_SRC_FILES_arm64 := arm64/busybox     # 不同架构预编译
include $(BUILD_PREBUILT)

7.2 Shell 脚本

PRODUCT_COPY_FILES += \
    vendor/giec/executable/get_display_id.sh:vendor/bin/get_display_id.sh \
    vendor/giec/executable/network/setup_station.sh:vendor/bin/setup_station.sh \
    vendor/giec/executable/network/setup_trunk.sh:vendor/bin/setup_trunk.sh \
    vendor/giec/executable/network/setup_bridge.sh:vendor/bin/setup_bridge.sh \
    vendor/giec/executable/network/udhcpc.script:vendor/bin/udhcpc.script \
    vendor/giec/executable/network/udhcpc_vlan.script:vendor/bin/udhcpc_vlan.script \
    vendor/giec/executable/network/get_network_config.sh:vendor/bin/get_network_config.sh \
    vendor/giec/executable/network/cleanup_network.sh:vendor/bin/cleanup_network.sh

PRODUCT_COPY_FILES 语法:源路径:目标路径,把文件复制到目标镜像的指定路径。

网络工具脚本说明

这些脚本实现了三种网络模式的切换:

脚本 模式 网络拓扑
setup_station.sh station 模式 eth0 直接配置 DHCP/静态 IP
setup_bridge.sh bridge 模式 eth0 + ap0 桥接到 br0,统一获取 IP
setup_trunk.sh trunk 模式 eth0 创建 VLAN 子接口,与 ap0 桥接
cleanup_network.sh NAT 模式清理 移除 bridge/VLAN,恢复 eth0

get_network_config.sh 输出 JSON 格式的网络状态,供 Kotlin/Java 应用读取。

7.3 热点默认开启

PRODUCT_PROPERTY_OVERRIDES += persist.vendor.hotspot.enabled=true

PRODUCT_PROPERTY_OVERRIDES:在 /product/build.prop 中写入系统属性,覆盖默认值。


8. 设备配置与条件编译

8.1 串口控制台

BOARD_KERNEL_CMDLINE += console=ttyS0,921600

ifeq ($(RO_BOOT_ENABLE_CONSOLE),true)
    BOARD_KERNEL_CMDLINE += androidboot.enableconsole=1
endif
  • BOARD_KERNEL_CMDLINE:追加到 kernel 启动命令行
  • 串口 0,波特率 921600
  • androidboot.enableconsole=1:控制台在 user 版本中也启用(生产环境中默认关闭)

8.2 条件编译模块

# 选择 Launcher
ifeq ($(NEED_GLAUNCHER), true)
    PRODUCT_PACKAGES += Glauncher
endif

# 工厂测试工具
ifeq ($(NEED_FACTORY_TEST), true)
    PRODUCT_PACKAGES += STB_TEST
endif

# WiFi/BT 测试工具
ifeq ($(BUILD_WIFI_BT_TEST_TOOLS),true)
$(call inherit-product, vendor/giec/wifi_bt_test_package.mk)
endif

这些变量在 build 命令行或 device.mk 中设置:

export NEED_GLAUNCHER=true
./mk ross

9. ADB 与调试配置

9.1 ADB 预置密钥

PRODUCT_COPY_FILES += \
    vendor/giec/common/keys/adbkey.pub:vendor/etc/adb/preinstalled_keys
  • 将公钥复制到 /vendor/etc/adb/preinstalled_keys
  • 使用此公钥的主机连接此设备时无需在设备上确认授权
  • 这在产线测试和开发阶段非常有用

9.2 ADBD 默认开启

ifeq ($(DEFAULT_ADBD_ON),true)
    PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
        persist.sys.usb.config=adb
endif

PRODUCT_DEFAULT_PROPERTY_OVERRIDESPRODUCT_PROPERTY_OVERRIDES 区别:

  • PRODUCT_DEFAULT_* 只对 userdebug/eng 版本生效
  • PRODUCT_* 对所有版本(包括 user)生效

10. 签名配置

ifeq ($(USE_RELEASE_KEY),true)
    PRODUCT_DEFAULT_DEV_CERTIFICATE := $(CERTIFICATE_DIR)/releasekey
else
    CERTIFICATE_DIR := build/make/target/product/security
    PRODUCT_DEFAULT_DEV_CERTIFICATE := $(CERTIFICATE_DIR)/testkey
endif
  • testkey:AOSP 自带的开发用密钥(公开的,不安全)
  • releasekey:厂商自己的私钥(需要保密)
  • 预置 APK 用 LOCAL_CERTIFICATE := platform 签名,这会使用 PRODUCT_DEFAULT_DEV_CERTIFICATE 指定目录下的 platform.x509.pem / platform.pk8

Android 应用签名:Android 要求每个 APK 都必须用证书签名。系统应用签名尤其重要——只有与系统镜像使用相同密钥签名的应用才能获得 system 级别的 UID 和权限。这就是为什么厂商预置 APK 的 Makefile 中都有 LOCAL_CERTIFICATE := platform

Platform 签名的价值:如果 APK 声明了 android:sharedUserId="android.uid.system" 或请求了系统级权限(如 INSTALL_PACKAGES),它必须使用 platform 密钥签名,否则系统不会授予相应权限。

Android 有四组签名密钥

密钥 用途
testkey 普通 APK,默认
platform 系统级 APK(需与 system 共享 UID 的)
shared 共享 UID 的 APK
media media 相关 APK

总结

device-giec.mk 虽然只有 133 行,但涉及了每个 Android 厂商定制层的核心要素

SELinux 策略   ─── 让定制服务能跑起来
HIDL 服务       ─── 提供底层能力的 IPC 通道
预置 APK        ─── 厂商自带的系统应用
Shell 脚本      ─── 网络/调试等运行时工具
构建条件        ─── 区分开发/生产/测试版本
签名密钥        ─── 安全与权限控制

核心理念:Treble 架构下,厂商在 /vendor 分区中进行定制,不需要修改 Framework。 真正的厂商改动都在 vendor/giec/ 这个目录中。