1. Android 源码中预创建/data目录的必要性
在Android系统开发过程中,我们经常需要在/data分区预创建特定目录结构。这个需求主要来自以下几个典型场景:
- 系统服务需要固定路径存放运行时数据(如/data/cloudphone)
- 应用预置需要特定目录存放APK文件(如/data/ssd/apks)
- 性能优化需要将频繁访问的数据放在特定存储区域(如/data/ssd)
然而,直接在Android源码中实现这个看似简单的需求却面临两个关键技术挑战:
重要提示:Android 10及以上版本对/data分区的访问控制更加严格,任何不规范的操作都可能导致系统启动失败或安全策略冲突。
2. 核心问题解析与技术难点
2.1 权限控制机制分析
Android的init进程采用分阶段初始化策略,其中关键阶段包括:
- early-init
- init
- post-fs-data(文件系统准备就绪后)
- boot-completed
在传统的vendor分区初始化方案中,开发者习惯将自定义脚本放在/vendor/etc/init/目录下,这些脚本由vendor_init上下文执行。但Android的neverallow规则明确禁止:
te复制neverallow vendor_init data_file_type:dir { create setattr };
这意味着任何尝试通过vendor_init在/data下创建目录的操作都会被SELinux拒绝。
2.2 SELinux标签配置要求
在/data下创建的自定义目录必须正确设置以下属性:
- 文件安全上下文(如u:object_r:apk_data_file:s0)
- 目录权限(如0771)
- 加密标志(encryption=None)
- 所有者/组(如system:system)
错误配置会导致应用无法访问目录或产生权限拒绝错误。
3. 完整解决方案实现
3.1 创建正确的rc文件
推荐在device/
bash复制# 文件路径:device/xxx/common/rootdir/vendor/etc/init/xxx-data.rc
on post-fs-data
# 创建一级目录
mkdir /data/cloudphone 0771 system system
restorecon_recursive /data/cloudphone
# 创建嵌套目录结构
mkdir /data/ssd 0771 system system
mkdir /data/ssd/apks 0771 system system
restorecon_recursive /data/ssd
关键参数说明:
0771:目录权限,允许system用户和组读写执行system system:所有者和所属组restorecon_recursive:递归恢复SELinux上下文
3.2 SELinux策略配置
在device/xxx/sepolicy目录下添加.te文件:
te复制# file_contexts
/data/cloudphone(/.*)? u:object_r:cloudphone_data_file:s0
/data/ssd(/.*)? u:object_r:ssd_data_file:s0
/data/ssd/apks(/.*)? u:object_r:apk_data_file:s0
# xxx_data.te
type cloudphone_data_file, file_type, data_file_type;
type ssd_data_file, file_type, data_file_type;
allow init cloudphone_data_file:dir { create setattr relabelfrom relabelto };
allow init ssd_data_file:dir { create setattr relabelfrom relabelto };
3.3 Android.bp集成配置
确保rc文件和sepolicy被正确打包:
python复制# device/xxx/common/Android.bp
prebuilt_etc {
name: "xxx-data.rc",
src: "vendor/etc/init/xxx-data.rc",
sub_dir: "init",
filename: "xxx-data.rc",
}
se_policy {
name: "xxx_sepolicy",
srcs: [
"sepolicy/*.te",
],
}
4. 验证与调试方法
4.1 编译部署验证
bash复制# 全量编译后刷机验证
make -j24
fastboot flashall -w
# 检查目录创建结果
adb shell ls -lZ /data
预期输出应包含:
code复制drwxrwx--x system system u:object_r:cloudphone_data_file:s0 cloudphone
drwxrwx--x system system u:object_r:ssd_data_file:s0 ssd
4.2 常见问题排查
-
目录未创建
- 检查rc文件是否被正确打包到vendor/etc/init/
- 确认trigger阶段为post-fs-data
-
SELinux权限拒绝
bash复制
adb logcat | grep avc根据avc拒绝日志补充sepolicy规则
-
目录权限不正确
- 确保mkdir命令参数格式正确
- 验证restorecon执行成功
5. 高级应用场景
5.1 动态目录创建
对于需要运行时创建的目录,可在系统服务中添加:
java复制public class DirectoryCreator {
private static final String TAG = "DirectoryCreator";
private static final String CLOUDPHONE_DIR = "/data/cloudphone";
public static void ensureDirs() {
File dir = new File(CLOUDPHONE_DIR);
if (!dir.exists()) {
if (dir.mkdirs()) {
SELinux.restorecon(dir);
dir.setReadable(true, false);
dir.setWritable(true, false);
dir.setExecutable(true, false);
}
}
}
}
5.2 加密目录处理
对于需要加密的目录,添加encryption参数:
rc复制on post-fs-data
mkdir /data/secure_data 0700 system system encryption=Requires
6. 最佳实践建议
-
目录结构规划原则
- 一级目录不超过5个
- 嵌套层级不超过3层
- 命名采用小写+下划线格式
-
权限设置指南
- 系统服务目录:0770
- 应用共享目录:0771
- 敏感数据目录:0700
-
维护性考虑
- 在Makefile中添加清理规则
- 提供目录迁移方案
- 编写配套的单元测试
在实际项目中,我们采用这套方案成功部署了多个定制化目录结构。特别是在Android 12的GKI架构下,这种符合规范的预创建方法能确保系统稳定性和兼容性。
