阅读 7153

老板:小X,我这个Android系统你安排定制下

伊博,饿了么物流移动组资深 Android 工程师,对移动端自动化测试深有研究,个人站点:www.mio4kon.com

前言

老板: 小X啊,我这个手机咋没啥特色啊.你看这个设置页和别人的都没啥区别.
小X: emmmm..你想要啥特色?
老板: 就是那种能体现我的特色的特色啊.
小X: 好好好,特色是吧?安排上.

今天这篇博客就记录了小X是如何通过修改 Android 的系统源码来定制 Android 系统的.

内容主要包括下面几点:

  • 系统环境的预准备
  • 下载 Android 源码
  • 编译 Android 源码
  • 刷写设备
  • 定制 Android 系统

下载 Android 源码并编译网上已经有很多教程了,我这里使用的是 mac 系统编译,如果你也查找过相关博客可以知道 mac 系统编译 Android 源码会遇到很多坑.网上也有利用 docker 来创建 Ubuntu Image 的方式来进行 Android 系统编译的,例如 weishu 大佬的这篇<史上最简单Android源码编译环境搭建方法>, 利用了 docker-aosp 开源项目来进行傻瓜式的下载编译.最开始我也尝试使用这种方式,不过发现在新版的 mac 系统,因为 uid 的问题导致无法编译成功(issues19), 我也没有过多的花时间去解决这个 uid 的问题,总之最后我选择使用了传统的方式,尤其是第一次尝试下载编译的,还是不要把这个过程变成一个黑匣子.

下面我会用我的踩坑经验来尽量让大家避免在下载编译,包括刷机时会遇到各种各样的错误.保证按照这个教程最后都能刷机成功,不过由于环境的复杂性,可能还是会遇到其他我没遇到的问题,这时可以 Google 之..

环境预准备

下面是我的初始环境,电脑系统也是刚重装过的

  • 系统: macOS High Sierra 10.13.6
  • xcode: v9.4.1
  • 手机: Nexus 6
  • 终端: zsh

分盘

编译 Android 源码需要保证自己的所在的分区格式是区分大小写的,而一般 macOS 默认是不区分的,所以需要创建独立的分区来编译源码.

创建存放 dmg 的位置

cd ~
mkdir asop
复制代码

利用 hdiutil 创建分区

hdiutil create -type SPARSE -fs 'Case-sensitive Journaled HFS+' -size 200g ~/aosp/android.dmg -volname android

这里我创建 200G 的分区用来下载和编译.最终实际使用了接近 130G.所以建议有足够大小的最好创建超过 150G 的分区大小.

创建挂载的快捷命令

vi ~/.zshrc 将下面内容复制到最后.

# mount the android file image
function mountAndroid { hdiutil attach ~/aosp/android.dmg.sparseimage -mountpoint /Volumes/android; }

# unmount the android file image
function umountAndroid() { hdiutil detach /Volumes/android; }

复制代码

应用配置 source ~/.zshrc

挂载 mountAndroid

这样就会在 /Volumes 目录下创建了 android 目录.这就是之后下载源码的路径,这里我创建了子文件夹来存放: /Volumes/android/ANDROID_SPACE

xcode配置

这一段很重要,把 xcode 配好,后面会顺畅很多,如果系统没有装 xcode 那是最好的.去下载 9 以下的版本(下载地址),这里我下载了 8.3.3 版本.

网上很多教程是用新版本 xcode, 然后通过修改下载后的源码中的某配置来添加支持的方式,如下:

vi build/core/combo/mac_version.mk
添加10.13在后面 mac_sdk_versions_supported := 10.8 10.9 10.10 10.11 10.13
复制代码

不过这种方式最后在编译的时候,都会有各种各样的问题.

现在如果你和我一样,都是使用 xcode 8.3.3 的话,还需要到 github.com/phracker/Ma… 中下载 10.11mac sdk,否则还是会在编译时出现一些错误.

将下载下来的压缩包解压放到👇目录即可:

/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/

总结下来其实很简单:

  1. 建议组合 xcode 8.3.3 + 10.11 mac sdk
  2. 尝试组合 xcode 9.x.x + 10.11 mac sdk

为啥是建议 8.3.3 呢,因为我就是在这个环境下,保证不会报错的.(踩坑过程中,有个报错,很多人建议是用9以下的版本,实际最后才发现其实可能只是和 mac sdk 相关联,并不是 xcode)

其他配置

确保环境变量 /opt/local/bin 显示在 /usr/bin 之前

检验: echo $PATH ,否则的话将👇的代码复制到 ~/.zshrc 中.

export PATH=/opt/local/bin:$PATH

然后再应用配置 source ~/.zshrc

安装MacPorts

www.macports.org/install.php

获取 Make、Git 和 GPG 程序包

POSIXLY_CORRECT=1 sudo port install gmake libsdl git gnupg

设置fd上限

ulimit -S -n 1024

下载源码

我们通过 Repo 下载 Android 源码.

Repo 是谷歌用 Python 脚本写的调用 git 的一个脚本, 可以用来管理多个git库.

安装 Repo

mkdir ~/bin
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
# 无法连接 Google 可以使用清华大学镜像
curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo  > ~/bin/repo(清华大学镜像)
chmod a+x ~/bin/repo
复制代码

安装完成后,为了方便使用可以将 repo 添加到环境变量:

#repo
export PATH=/Users/xxx/bin:$PATH
复制代码

下载完之后,如果网无法连接 Google , 还需要👇配置:

vi ~/bin/repo
修改 REPO_URL = 'https://gerrit-googlesource.proxy.ustclug.org/git-repo'
复制代码

选择编译的版本

先切到下载目录

cd /Volumes/android/ANDROID_SPACE

根据自己的手机机型,以及自己想安装的版本号,来选择具体的版本,连接如下👇:

source.android.com/source/buil…

比如这里我选择的是如下版本(android-7.1.1_r57):

这里要记住 NGI77B 这个代码,后面需要用到, 选择好之后,初始化 repo 的配置.

repo init -u https://android.googlesource.com/platform/manifest -b android-7.1.1_r57
repo init -u https://aosp.tuna.tsinghua.edu.cn/platform/manifest -b android-7.1.1_r57 (清华大学镜像)
复制代码

下载

repo sync

编译源码

编译准备

首先,如果你用的是 zsh, 请一定要切到bash. 直接输入bash即可.切记切记.

下载对应设备硬件相关内容

下载地址👇:

developers.google.com/android/dri…

对应之前我们选择的版本的代码,例如我的是 NGI77B,选择下载对应的驱动:

将下载的文件都解压,其实就是三个 shell 命令,将他们拷贝到下载的 Android 目录下.并执行:

./extract-broadcom-shamu.sh
./extract-moto-shamu.sh
./extract-qcom-shamu.sh
复制代码

中间需要接受相应 license,输出 I ACCEPT 即可.

配置相应的环境变量,可以直接复制👇的,需要改一改相应的目录名

#java home
export JAVA_7_HOME=`/usr/libexec/java_home -v 1.7`
export JAVA_8_HOME=`/usr/libexec/java_home -v 1.8`
#defalut java_home
export JAVA_HOME=$JAVA_8_HOME
export PATH=${JAVA_HOME}/bin:$PATH
export ANDROID_JAVA_HOME=$JAVA_HOME

#android
export ANDROID_HOME=/Users/mio/Library/Android/sdk
export PATH=/Users/xxx/Library/Android/sdk/build-tools/27.0.3:$PATH
export PATH=/Users/xxx/Library/Android/sdk/platform-tools:/Users/mio/Library/Android/sdk/tools:$PATH
复制代码

ANDROID_JAVA_HOME 一定要配~~

Ps: 记得安装 Java 和 Android sdk ,这里不多说了

清理

make clobber

初始化编译的环境

source build/envsetup.sh

选择编译目标

lunch命令会列出可以编译的目标,如下:

Lunch menu... pick a combo:
     1. aosp_arm-eng
     2. aosp_arm64-eng
     3. aosp_mips-eng
     4. aosp_mips64-eng
     5. aosp_x86-eng
     6. aosp_x86_64-eng
     7. full_fugu-userdebug
     8. aosp_fugu-userdebug
     9. mini_emulator_arm64-userdebug
     10. m_e_arm-userdebug
     11. m_e_mips-userdebug
     12. m_e_mips64-eng
     13. mini_emulator_x86-userdebug
     14. mini_emulator_x86_64-userdebug
     15. aosp_dragon-userdebug
     16. aosp_dragon-eng
     17. aosp_marlin-userdebug
     18. aosp_sailfish-userdebug
     19. aosp_flounder-userdebug
     20. aosp_angler-userdebug
     21. aosp_bullhead-userdebug
     22. hikey-userdebug
     23. aosp_shamu-userdebug
复制代码

具体目标可以根据 source.android.com/source/runn… 来选择.

因为我是 Nexus6 所以选择 23.

编译

make -j16

我下载的源码 bison 是存在 bug 的,所以需要打上修复后的 commit.可以先尝试编译,如果发现类似下面的错误可以如我所说的方式修复.

错误内容❌:

FAILED: /bin/bash -c "prebuilts/misc/darwin-x86/bison/bison -d --defines=out/host/darwin-x86/obj/STATIC_LIBRARIES/libaidl-common_intermediates/aidl_language_y.h -o out/host/darwin-x86/obj/STATIC_LIBRARIES/libaidl-common_intermediates/aidl_language_y.cpp system/tools/aidl/aidl_language_y.yy"

修复方法:

cd external/bison
git cherry-pick c0c852bd6fe462b148475476d9124fd740eba160
cd ../../
make bison
cp /Volumes/android/ANDROID_SPACE/out/host/darwin-x86/bin/bison /Volumes/android/ANDROID_SPACE/prebuilts/misc/darwin-x86/bison/
cd ../../
复制代码

然后删除out文件夹,再进行编译.理论上如果之前按照我说的配置好 xcode,应该不会再报奇葩的错误了.

然后就是等了.应该会比下载的时候快上不少.

刷写设备

编译完成后刷机其实非常简单

如果没有 fastboot 和 adb 执行👇的代码:

make fastboot adb

进入 fastboot 模式

adb reboot bootloader

解锁

fastboot flashing unlock

刷机

fastboot flashall -w

友情提示: 如果刷跪了,可以直接到👉网址下载官方镜像,重新刷.

修改源码

导入源码到 Android Studio

编译 idegen 模块,用来生成可以让 idea/as 打开的工程.

mmm development/tools/idegen/
./development/tools/idegen/idegen.sh
复制代码

执行完成后,在当前目录下生成android.ipr,android.iml,android.iws三个文件.

其中android.ipr文件可以直接用 Android Studio 打开.

但是如果所有模块都通过 AS 打开会非常的慢,所以我们可以先编辑android.iml, 通过👇这种方式可以排除不需要的模块.

<excludeFolder url="file://$MODULE_DIR$/xxxxxx" />

例如,我不需要 prebuild 模块,便可以在该文件里添加下面一行配置:

<excludeFolder url="file://$MODULE_DIR$/prebuilt" />

PS: 当然我们也可以直接使用 sublime 打开想要修改的模块.

DEBUG 源码

导入到 AS 之后,例如我们要 debug com.android.settings.dashboard.SummaryLoader这个类.我们按照平时的方式打断点,之后点击下面的按钮:

在出现的对话框中选择 setting 进程即可.

编译单独的模块

上面使用的 mmm命令实际上就是我们所说的编译单独模块的一个命令.如果提示找不到 mmm 命令.则需要先执行下面的命令(你肯定很熟悉:

source build/envsetup.sh

编译源码常用的有下面几种方式:

  • -m 编译所有模块
  • -mm 编译当前目录下的模块,不包括依赖
  • -mmm 编译指定路径下的模块,不包括依赖
  • -mmma 编译指定路径下的模块,包括依赖

实战一: 修改 Settings 模块

例如我们修改packages/apps/Settings/下的 String.xml为👇:

<string name="notification_summary_none" msgid="3440195312233351409">"已允许所有应用发送通知~~~喵"</string>

修改完成后,需要对 Settings 模块重新打包.

mmm packages/apps/Settings/

打包完成后我们需要去重新生成 system.imgae:

make snod

image 生成后,基本就大功告成了,我们只需要将它刷到手机当中即可.

adb reboot bootloader
fastboot flash system out/target/product/shamu/system.img
复制代码

刷完之后就可以看到成果了👇:

实战二: 添加自己的模块

使用 Android Studio 创建一个测试项目 AuthorSample,并将项目复制到 packages/apps目录下面.

由于是 AS 项目,所以我们在👇目录创建一个 Android.mk 文件,用来配置编译信息.

/Volumes/android/ANDROID_SPACE/packages/apps/AuthorSample/app/src/main

Android.mk内容如下:

#必备的两行,声明LOCAL_PATH变量,也就是当前的路径
#并清除其它变量
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

#包名,这生成的apk名字。AuthorSample.apk
LOCAL_PACKAGE_NAME := AuthorSample

LOCAL_CERTIFICATE := platform

#如果使用的系统的包,需要引入他们使用的资源文件,否则会提示编译资源找不到的错误
LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/res
LOCAL_RESOURCE_DIR += frameworks/support/v7/appcompat/res
#LOCAL_RESOURCE_DIR += frameworks/support/v7/gridlayout/res
#LOCAL_RESOURCE_DIR += frameworks/support/v7/recyclerview/res

#指定该模块的编译版本为optional
#user: 指该模块只在user版本下才编译
#eng: 指该模块只在eng版本下才编译
#tests: 指该模块只在tests版本下才编译
#optional:指该模块在所有版本下都编译
LOCAL_MODULE_TAGS := tests

#源码所在目录,这里就在当前位置的java目录下。所以直接写java
LOCAL_SRC_FILES := $(call all-java-files-under, java)

#LOCAL_SRC_FILES += \
# src/xx/xx/xx/XxxOne.aidl \
# src/xx/xx/xx/XxxTwo.aidl

#依赖的jar包,包括系统的和第三方的(放在libs目录)jar包
LOCAL_STATIC_JAVA_LIBRARIES +=  android-support-v4 \
                                android-support-v7-appcompat
                                #android-support-v7-recyclerview

#混淆文件名
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
#关闭混淆
LOCAL_PROGUARD_ENABLED := disabled

LOCAL_AAPT_FLAGS := --auto-add-overlay
LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.appcompat
#LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.gridlayout
#LOCAL_AAPT_FLAGS += --extra-packages com.android.datetimepicker
#LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.recyclerview

#打包成apk
include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
复制代码

再利用 mmm 命令编译该模块:

mmm packages/apps/AuthorSample/app/src/main/

编译完成之后 apk 会生成在👇目录中:

/Volumes/android/ANDROID_SPACE/out/target/product/shamu/data/app/AuthorSample/AuthorSample.apk

我们只需要将 apk push 到手机的 /system/app/ 目录下即可.

adb root 
adb remount
adb push /Volumes/android/ANDROID_SPACE/out/target/product/shamu/data/app/AuthorSample/AuthorSample.apk /system/app/
复制代码

重启之后你就发现手机上已经自带了你所写的应用(无法卸载哦~~)




阅读博客还不过瘾?

欢迎大家扫二维码通过添加群助手,加入交流群,讨论和博客有关的技术问题,还可以和博主有更多互动

博客转载、线下活动及合作等问题请邮件至 shadowfly_zyl@hotmail.com 进行沟通

关注下面的标签,发现更多相似文章
评论

查看更多 >