# huaweicloud-cloudPhone-aosp14 **Repository Path**: HuaweiCloudDeveloper/huaweicloud-cloud-phone-aosp14 ## Basic Information - **Project Name**: huaweicloud-cloudPhone-aosp14 - **Description**: 云手机AOSP14开源代码,用于构建运行在华为云手机服务器的Android14镜像 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 28 - **Created**: 2025-01-06 - **Last Updated**: 2025-07-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # [华为云手机开源项目仓主入口](https://gitee.com/HuaweiCloudDeveloper/huaweicloud-cloud-phone) # huaweicloud-cloudPhone-aosp14 #### 介绍 云手机AOSP14开源代码,用于构建运行在华为云手机服务器的Android14镜像 #### 软件架构 本软件基于AOSP 14.0.0_r75开发 #### 内容摘要 一 客户对接阶段 使用说明 1 申请权限 2 准备开发环境 二 客户日常修改代码编译构建阶段 使用说明 3. 修改代码 4. 配置脚本 5. 构建镜像 7. 版本管理 8. 与原仓库保持同步更新 三 客户定制化需求: 使用说明 6. 自定义系统签名 四 客户镜像管理: 支持镜像查询以及同region不同账号镜像共享 https://support.huaweicloud.com/api-cph/cph_api_0559.html #### 使用说明 ##### 1. 申请权限 请按照以下格式发送邮件,成功后可通过客户经理获取专属构建服务器地址和密钥 ``` 收件人: cphdeveloper@huawei.com 邮件标题: 申请AOSP14自定义镜像构建权限 邮件内容: 公司名称: 联系人: 联系电话: 客户经理: ``` ##### 2. 准备开发环境 建议使用Ubuntu 18.04,并安装curl,git,gpg等常用软件,确保开发环境可以联网 ##### 3. 修改代码 ###### 3.1 fork本仓库,本地修改代码 ``` 建议: 1. 在aosp/vendor/isula目录下放置新增的代码文件或者预编译产物 2. 在aosp/vendor/isula/下对应mk文件中声明构建规则 3. 如无特殊需求,不要修改aosp/vendor目录下的其他文件,否则可能会对镜像的基本功能造成影响 ``` ###### 3.2 提交代码,创建tag或branch + (必选)tag/branch即版本号,需满足命名规范,见`7.版本管理` + (可选)添加镜像构建账号 如果将代码仓设为私有,请将以下账户添加为只读成员,用于拉取代码和构建镜像 ``` cphdeveloper@huawei.com ``` 并咨询华为技术人员,在私有代码仓为cphdeveloper@huawei.com添加部署公钥 + (可选)本地编译验证 如需本地验证是否存在编译错误,可拉取原生AOSP代码后,复制本地代码到原生目录,执行以下命令进行验证 ``` lunch aosp_arm64-trunk_staging-eng make framework -j ``` ##### 4. 配置脚本 修改build.sh,其中SERVER_ADDR和KEY为在`1.申请权限`步骤中获取的专属构建服务器地址和密钥 ``` BRANCH_TAG 代码的版本号,例如v1.2.1 KEY 专属构建密钥 IMG_NAME 自定义的镜像产物别名 IMG_UPLOAD_REGION 指定镜像上传region, 支持多region上传, 多个region间使用‘,’分隔 SERVER_ADDR 专属构建服务器 ``` ##### 5. 构建镜像 执行build.sh,支持以下参数 + 全量构建 会清理中间产物,速度较慢,建议生产环境使用。 ``` ./build.sh ``` + 增量构建 不清理中间产物,速度较快,建议仅用于调试阶段。 ``` ./build.sh -i ``` + 查看构建日志 如果构建失败,可通过日志找到错误,修正后重新触发构建。 ``` ./build.sh -l ``` 构建成功后,返回镜像ID ##### 6. 自定义系统签名 构建前务必制作自定义签名,否则构建会出错。通过以下步骤制作私有系统签名,加密后上传,构建系统可保证私有的系统签名不会泄露。 ###### 6.1 制作私有系统签名 请参考 https://android.googlesource.com/platform/build/+/refs/tags/android-14.0.0_r75/target/product/security/README ###### 6.2 打包加密私有系统签名文件 完成以上步骤后,将生成的所有文件放入`security`文件夹,`security`文件夹应包含以下文件 ``` . |-- media.pk8 |-- media.x509.pem |-- platform.pk8 |-- platform.x509.pem |-- releasekey.pk8 |-- releasekey.x509.pem |-- shared.pk8 |-- shared.x509.pem |-- testkey.pk8 |-- testkey.x509.pem |-- sdk_sandbox.pk8 |-- sdk_sandbox.x509.pem |-- bluetooth.pk8 |-- bluetooth.x509.pem |-- nfc.pk8 |-- nfc.x509.pem |-- networkstack.pk8 |-- networkstack.x509.pem ``` 依次执行以下命令完成打包: ``` cd security tar czvf security.tar.gz ./* ``` 下载并导入gpg公钥 ``` gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys c5c7a35662a6f8080e27ab464a8e757d9b31879e ``` 并设置公钥信任等级为5 = I trust ultimate ``` gpg --edit-key C5C7A35662A6F8080E27AB464A8E757D9B31879E gpg> trust gpg> 5 gpg> save ``` ctrl+D 退出gpg设置界面后执行 ``` gpg --recipient C5C7A35662A6F8080E27AB464A8E757D9B31879E --output security.tar.gz.gpg --encrypt security.tar.gz ``` ###### 6.3 上传私有系统签名文件 将加密后的私有系统签名文件包提交到代码仓的以下路径: ``` /aosp/vendor/common/certificates/security.tar.gz.gpg ``` ##### 7. 版本管理 本仓库使用tag进行版本管理。命名规则如下,可在本仓库的标签页查看所有tag,例如 ``` v1.0.0 v1.1.0 v1.2.0 ``` fork自本仓库的代码也必须创建tag,前两位必须与本仓库的tag保持一致,用来表示其对应关系,第三位可以设置为任意数字,例如 ``` v1.2.1 ``` 也支持使用branch方式替换tag ##### 8. 与原仓库保持同步更新 如果在fork代码后,本仓库的代码又发生了更新,可以使用以下方法同步原仓库的更新。 ###### 8.1 进入本地仓库目录 ###### 8.2 设置upstream仓库 ``` git remote add upstream git@gitee.com:HuaweiCloudDeveloper/huaweicloud-cloudPhone-aosp14.git ``` 设置后通过以下命令进行查看 ``` git remote -v ``` 显示以下两行表示设置成功 ``` upstream git@gitee.com:HuaweiCloudDeveloper/huaweicloud-cloudPhone-aosp14.git (fetch) upstream git@gitee.com:HuaweiCloudDeveloper/huaweicloud-cloudPhone-aosp14.git (push) ``` ###### 8.3 执行命令`git status`检查本地是否有未提交的修改。如果有,则把你本地的有效修改,先从本地仓库推送到你gitee的仓库。最后再执行一次`git status`检查本地已无未提交的修改。 ``` git add -A 或者 git add filename git commit -m "your note" git push origin master git status ``` ###### 8.4 抓取原仓库的指定tag ``` git pull upstream tag v1.2.0 ``` ###### 8.5 切换到master分支 ``` git checkout master ``` ###### 8.6 合并upstream的master分支 ``` git merge upstream/master ``` ###### 8.7 如有冲突,本地处理冲突后提交代码 ###### 8.8 创建tag ``` git tag -a v1.2.1 -m "update tag v1.2.1" ``` ###### 8.9 推送代码 ``` git push --tags ``` ##### 11. 镜像管理 支持镜像查询以及同region不同账号镜像共享 https://support.huaweicloud.com/api-cph/cph_api_0559.html 注意:同一个账号在同一个region下最多支持创建200个镜像文件 ##### 12. 客户自定义镜像Superuser引入参考: Superuser为定制手机root的一个开源方案,用户可根据需要自行引入或定制该方案,在云手机自定义镜像的引入方法参考示例如下(以最小功能引入su为例): ###### 12.1 下载开源代码Superuser到本地 下载zip包或git clone到本地 ###### 12.2 解压代码包,拷贝以下文件到AOSP源码目录 ``` cp -ra Superuser-master/Superuser-master/Superuser/jni/su/ huaweicloud-cloud-phone-aosp14/aosp/vendor/isula/Superuser ``` ###### 12.3 进入拷贝后的开源代码目录 ``` cd huaweicloud-cloud-phone-aosp14/aosp/vendor/isula/Superuser ``` ###### 12.4 touch Android.mk,编译文件内容如下 ``` LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := su LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES) LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_STATIC_LIBRARIES := libcutils libc LOCAL_SRC_FILES := su.c utils.c daemon.c pts.c include $(BUILD_EXECUTABLE) ``` ###### 12.5 编辑su.c 添加头文件 ``` #include #include #include #include // 添加该头文件 #include "su.h" #include "utils.h" ``` 注释以下代码块避免编译错误 ``` // dumpstate (which logs to logcat/shell) will spam the crap out of the system with su calls if (strcmp("/system/bin/dumpstate", ctx->from.bin) == 0) send_to_app = 0; // 注释以下两行 // if (send_to_app) // send_result(ctx, DENY); LOGW("request rejected (%u->%u %s)", ctx->from.uid, ctx->to.uid, cmd); fprintf(stderr, "%s\n", strerror(EACCES)); exit(EXIT_FAILURE); } static __attribute__ ((noreturn)) void allow(struct su_context *ctx) { char *arg0; int argc, err; umask(ctx->umask); int send_to_app = 1; // no need to log if called by root if (ctx->from.uid == AID_ROOT) send_to_app = 0; // dumpstate (which logs to logcat/shell) will spam the crap out of the system with su calls if (strcmp("/system/bin/dumpstate", ctx->from.bin) == 0) send_to_app = 0; // 注释以下两行 // if (send_to_app) // send_result(ctx, ALLOW); char *binary; ``` 注释以下代码块放开权限 ``` if (ctx.from.uid == AID_ROOT) { LOGD("Allowing root/system/radio."); allow(&ctx); } // verify superuser is installed //if (stat(ctx.user.base_path, &st) < 0) { // send to market (disabled, because people are and think this is hijacking their su) // if (0 == strcmp(JAVA_PACKAGE_NAME, REQUESTOR)) // silent_run("am start -d http://www.clockworkmod.com/superuser/install.html -a android.intent.action.VIEW"); // PLOGE("stat %s", ctx.user.base_path); // deny(&ctx); //} // odd perms on superuser data dir if (st.st_gid != st.st_uid) { LOGE("Bad uid/gid %d/%d for Superuser Requestor application", (int)st.st_uid, (int)st.st_gid); deny(&ctx); } // always allow if this is the superuser uid // superuser needs to be able to reenable itself when disabled... //if (ctx.from.uid == st.st_uid) { allow(&ctx); //} // check if superuser is disabled completely if (access_disabled(&ctx.from)) { LOGD("access_disabled"); deny(&ctx); ``` 注释以下代码块避免编译错误 ``` if (seteuid(st.st_uid)) { PLOGE("seteuid (%lu)", st.st_uid); deny(&ctx); } // 注释以下代码 // dballow = database_check(&ctx); // switch (dballow) { // case INTERACTIVE: // break; // case ALLOW: // LOGD("db allowed"); // allow(&ctx); /* never returns */ // case DENY: // default: // LOGD("db denied"); // deny(&ctx); /* never returns too */ // } socket_serv_fd = socket_create_temp(ctx.sock_path, sizeof(ctx.sock_path)); LOGD(ctx.sock_path); if (socket_serv_fd < 0) { deny(&ctx); } signal(SIGHUP, cleanup_signal); signal(SIGPIPE, cleanup_signal); signal(SIGTERM, cleanup_signal); signal(SIGQUIT, cleanup_signal); signal(SIGINT, cleanup_signal); signal(SIGABRT, cleanup_signal); // 注释以下代码 // if (send_request(&ctx) < 0) { // deny(&ctx); // } atexit(cleanup); fd = socket_accept(socket_serv_fd); if (fd < 0) { deny(&ctx); ``` ``` // 注释以下代码避免运行时报错 // setenv("LD_LIBRARY_PATH", "/vendor/lib:/system/lib", 0); ``` ###### 12.6 编辑pts.c,添加以下头文件 ``` #include #include #include // 添加该头文件 #include "pts.h" /** * Helper functions ``` ###### 12.7 添加su启动配置rc文件(也可在其它rc文件中修改) touch superuser.rc ``` on init start superuser service superuser /system/bin/su --daemon user root group root seclabel u:r:shell:s0 ``` ###### 12.8 修改aosp/vendor/isula/copyfiles.mk文件,添加superuser.rc ``` PRODUCT_COPY_FILES += \ system/core/rootdir/init.zygote64.rc:system/etc/init/hw/init.zygote64.rc \ vendor/common/etc/handheld_core_hardware.xml:system/etc/permissions/handheld_core_hardware.xml \ vendor/isula/Superuser/superuser.rc:system/etc/init/superuser.rc \ # hals ``` ###### 12.9 修改aosp/vendor/isula/packages.mk ,添加su ``` # migrate from goldfish PRODUCT_PACKAGES += \ sh_vendor \ stagefright \ toybox_vendor \ su \ ``` ##### 13. 自定义属性文件使用说明 若用户有需要在云手机预置属性,可新建device.prop文件,推送到路径/data/local/custom/下,重启手机系统后生效。 + 文件位置 /data/local/custom/device.prop + 格式 key=value键值对,如以下示例 ``` customer.property.disable=false customer.property.name=cloudphone ``` + 生效规则 默认遵循AOSP原生属性配置规则,非ro属性用户配置值覆盖系统原生值,ro属性不可写。 如需支持ro属性定制,可参考以下方式修改源码文件 aosp/system/core/init/property_service.cpp 修改PropertySet函数 ``` prop_info* pi = (prop_info*)__system_property_find(name.c_str()); if (pi != nullptr) { // 注释以下代码,去除对ro属性的写入限制 // ro.* properties are actually "write-once". //if (StartsWith(name, "ro.")) { // *error = "Read-only property was already set"; // return {PROP_ERROR_READ_ONLY_PROPERTY}; //} __system_property_update(pi, value.c_str(), valuelen); } else { ``` + AOSP原生属性设置API ro可写定制 通过adb shell登入手机使用命令setprop设置属性,或第三方应用调用原生接口设置属性,默认AOSP原生规则ro属性不可写,若用户有需求支持ro可写,可参考以下方法修改源码: aosp/system/core/libbase/properties.cpp修改__system_property_set接口 ``` int __system_property_set(const char* key, const char* value) { if (key == nullptr || *key == '\0') return -1; if (value == nullptr) value = ""; // 注释以下拦截ro属性修改的代码 // bool read_only = !strncmp(key, "ro.", 3); // if (read_only) { // const auto [it, success] = g_properties.insert({key, value}); // return success ? 0 : -1; // } if (strlen(value) >= 92) return -1; g_properties[key] = value; return 0; } ```