vendor/giec/ 厂商定制层分析
基于
device-giec.mk逐行追踪,理解"厂商如何改系统" 参考代码库:vendor/giec/,Android 14 + Amlogic S905X5M
目录
- device-giec.mk 总览
- GApps 集成(条件编译)
- SELinux 策略
- HIDL 服务声明(manifest + matrix)
- HIDL 客户端与服务端(hwstbcmdservice)
- 预置 APK
- 预置二进制与 Shell 脚本
- 设备配置与条件编译
- ADB 与调试配置
- 签名配置
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_socket、tcp_socket、packet_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_app 和 platform_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/binderFramework 内部(Java 应用间的 IPC) hwbinder /dev/hwbinderFramework 与 HAL 之间的 HIDL 通信 vndbinder /dev/vndbinderVendor 进程之间的 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 应用沙箱的限制。
libstbcmdservicehal 的 Android.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 脚本需要它提供的命令(如 udhcpc、awk、grep)。
构建方式(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_OVERRIDES 与 PRODUCT_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/ 这个目录中。