Android(安卓)ROM开发之预制GMS
-
- GMS 简介
- 集成 GMS 到 Android源码
- GMS OVERRIDES OVERLAY 机制
- GMS 覆盖 源码中的 webviewchromium
- 单独升级某个 GMS 应用问题
- 查看编译生成的GMS应用
- GMS应用定制修改
GMS 简介
GMS 的全称是 Google Mobile Services (GMS) 。它是 Google 开发的一系列 apk集合,这些 apk 都或多或少的依赖 Google 服务。它没有包含在 Android 的开源代码中,当然也就不能看到这些 apk 的实现代码。在整机开发中,如果目标客户是国外用户,那么往往就需要预制GMS。如果你是Android应用开发人员,那么本篇内容可能不适合你。
集成 GMS 到 Android源码
GMS的获取需要公司和Google达成协议,然后由Google release给公司,无法直接从网上下载。它的目录结构如下
拿到GMS包后需要将它集成进源码,这样编译出来的image文件才能包含了GMS应用。在编译系统的 makefile 中添加下面的语句,
$(call inherit-product, vendor/google/products/gms.mk)
就可以编译到GMS了。至于是选择往哪个makefile中添加,要根据不同公司对编译系统的修改而定,可以尝试在build/core/main.mk
中添加。
这里的gms.mk负责总揽GMS的编译,其中的 PRODUCT_PACKAGES
变量列举了哪些 apk 会被编译到,例如
PRODUCT_PACKAGES += \
GoogleBackupTransport \
SetupWizard
就表明 GoogleBackupTransport 和 SetupWizard 会被编译到。然而这里只是指定了apk 的会不会被编译,不同的 apk 编译的条件会不同,真正编译的 makefile 在google/apps 目录里对应 apk 的文件夹下。Google 已经为不同平台(x86/arm 等)设计好了对应的 GMS apk,并且在编译时会识别当前系统所在的平台,自动编译对应的 apk,极大的简化了 GMS 对不同平台的适配过程。
这里还需要注意某些GMS应用是包含lib库的,如果整包升级GMS google 都是将相应的Android.mk文件写好了的,一般集成不会出什么问题,但如果有些需求要单独升级GMS包里面的某个应用,就得小心lib库问题了,lib库有变动,相应的就需要修改编译的Android.mk文件。曾经遇到过一个单独升级LatinImeGoogle 应用问题,拿到新LatinImeGoogle后,adb install 安装后大概测试了下,没什么问题,就直接在代码里用新的LatinImeGoogle替换掉旧的,以为没什么问题,但出完版本后发现LatinImeGoogle一启用就报FC问题,错误log如下:
E/AndroidRuntime(10553): FATAL EXCEPTION: mainE/AndroidRuntime(10553): Process: com.google.android.inputmethod.latin, PID: 10553E/AndroidRuntime(10553): java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/system/app/LatinImeGoogle/LatinImeGoogle.apk"],nativeLibraryDirectories=[/system/app/LatinImeGoogle/lib/arm, /vendor/lib, /system/lib]]] couldn't find "libjni_keyboarddecoder.so"E/AndroidRuntime(10553): at java.lang.Runtime.loadLibrary(Runtime.java:366)
很明显缺少了libjni_keyboarddecoder.so文件。但为何之前adb install后测试没报出问题呢?原因就在与adb install 安装的应用系统会去解析打包在apk内部的lib库文件,所以没有问题很正常。而预制应用在Android L的版本上系统不会去解析打包在apk内部的lib文件。上面问题产生的原因就在于新的LatinImeGoogle增加了一个libjni_keyboarddecoder.so文件,但Android.mk没有同步修改,导致预制失败。打开LatinImeGoogle下的Android.mk,找到下面这句:
LOCAL_PREBUILT_JNI_LIBS := \ @lib/$(my_src_abi)/libjni_unbundled_latinimegoogle.so
在LOCAL_PREBUILT_JNI_LIBS里追加上缺少的lib库,问题就ok了。
LOCAL_PREBUILT_JNI_LIBS := \ @lib/$(my_src_abi)/libjni_unbundled_latinimegoogle.so \ @lib/$(my_src_abi)/libjni_keyboarddecoder.so
LOCAL_PREBUILT_JNI_LIBS表示apk自身的lib库文件,
“@”标识符会将其抽离出来放置在apk同级目录下的lib文件夹中,并将其从原来的apk文件中删除掉。这点可以从编译log里看出
GMS OVERRIDES && OVERLAY 机制
由于 GMS 包中的某些应用功能与源码中的应用功能相似,所以 Google 设置了OVERRIDES 机制,开启开机制后可以屏蔽掉源码中的某些应用。比如 Hangouts 应用的功能与 Mms 应用类似,在 Hangouts 的 Android.mk 文件中有这样一句:
LOCAL_OVERRIDES_PACKAGES := Mms
这句话的意思就是屏蔽掉 Mms 模块,如果不注释掉这句最终编译生成的镜像文件中将不会包含 Mms 应用。这一点要特别注意在集成完 GMS 后最好在 GMS 包的根目录下是用命令:
grep LOCAL_OVERRIDES_PACKAGES . -nsr –include=*.mk
查看有哪些应用开启了 OVERRIDES 机制,防止需要的应用被 GMS 包中的应用屏蔽掉。与 OVERRIDES 类似的还有 OVERLAY,在google/products/gms_overlay
下包含一些xml 文件,这些文件里定义了一些字串,会替换源码中的同名字串,如果源码中没有该字串,会引起编译错误,这时就需要注释掉该字串。
GMS 覆盖 源码中的 webviewchromium
webviewchromium是WebView的内核,4.4之后的代码位于源码中的external/chromium_org
路径下,GMS包中包含了编译好的webviewchromium,如果要使用它,而不是直接由源码编译生成,可以修改gms.mk文件,在其中有如下内容:
# Setting PRODUCT_PREBUILT_WEBVIEWCHROMIUM as yes will prevent from building# webviewchromium from source, and use prebuilt WebViewGoogle.apk in GMS packPRODUCT_PREBUILT_WEBVIEWCHROMIUM := yesifeq ($(PRODUCT_PREBUILT_WEBVIEWCHROMIUM),yes)PRODUCT_PACKAGES += WebViewGoogle# The following framework overlay must be included if prebuilt WebViewGoogle.apk# is usedPRODUCT_PACKAGE_OVERLAYS += vendor/google/apps/WebViewGoogle/overlayendif
将 PRODUCT_PREBUILT_WEBVIEWCHROMIUM 的值设置为 yes 即可覆盖源码中的webviewchromium。
如果要验证覆盖是否成功可以通过检查UserAgent的变化来实现。具体如下:
1.写一个 webview 的 demo,打印其 UserAgent 值。例如:
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); WebView webview = new WebView(this); webview.layout(0, 0, 0, 0); WebSettings settings = webview.getSettings(); String ua = settings.getUserAgentString(); Log.d("UserAgent", "UserAgent="+ua); setContentView(webview);}
2.修 改external/chromium_org/android_webview/common/aw_content_client.cc
文 件中定义 UserAgent 部分的代码 ,例如将其修改成:
std::string GetUserAgent() { // "Version/4.0" had been hardcoded in the legacy WebView. std::string product = "Version/3.0 " + GetProduct(); if (CommandLine::ForCurrentProcess()->HasSwitch( switches::kUseMobileUserAgent)) { product += " Mobile_TEST"; } return content::BuildUserAgentFromProduct(product);}
这时打印出 webview 的 Demo 中的 UserAgent,若没有出现” Mobile_TEST”的字段,则表示已经将系统的 webview 内核成功覆盖。
单独升级某个 GMS 应用问题
单独升级 GMS 中的某个应用有风险,如果确实要升级,除了替换掉旧 apk 时,要注意新 apk 的文件结构是否和之前的结构一致。文件有新增,删除,重命名等情况,相应的 Android.mk 文件也要跟着修改.
上图是Velvet应用新旧版本的lib库文件结构对比。如果通过 adb install 命令安装新的 Velvet 应用,进行测试会发现没有什么问题,一切正常。但如果直接替换应用提交代码,download 新版本后发现报 lib 库文件缺失的 FC 问题。这就是由于其中的 lib 库文件名发生了变化,而 make 文件没有同步修改造成的。如下修改能避免该问题:
查看编译生成的GMS应用
不是GMS包中的所有应用都需要预置,如果要查看系统最终预置了哪些GMS应用,可以使用命令adb shell pm list packages
查看,由于大部分的GMS应用包名都是com.google.xxx的形式命名的,使用
adb shell pm list packages|grep google
命令可以更加方便的查看。如果要查看GMS 应用包名与 APK 名的对应关系,可以用命令
adb shell pm list packages -f |grep google
。
输出结果类似如下:
package:/system/app/ConfigUpdater/ConfigUpdater.apk=com.google.android.configupdaterpackage:/system/app/talkback/talkback.apk=com.google.android.marvin.talkbackpackage:/system/app/Gmail2/Gmail2.apk=com.google.android.gmpackage:/system/priv-app/SetupWizard/SetupWizard.apk=com.google.android.setupwizardpackage:/system/app/Music2/Music2.apk=com.google.android.musicpackage:/system/app/CloudPrint/CloudPrint.apk=com.google.android.apps.cloudprint
GMS应用定制修改
GMS的预制并不是简单的将GMS包加入到源码,然后编译生成就完事了,不同的客户肯定对预置的应用有不同的定制需求,聪明的Google也预先考虑到了这点,对GMS中的部分应用提供了定制接口。比如可以定制Chrome的书签和主页,可以定制开机向导的流程,甚至允许往开机向导里面加入自己的页面。
预制只是开始,定制才是噩梦的开端。本篇先到此为止,针对定制GMS会另写一篇。
更多相关文章
- Ubuntu11.10 64Bit版上的Android(安卓)4.0 开发环境搭建
- Android实现打开系统照相机&相册图片展示
- Android(安卓)Device中添加busybox
- Android(安卓)建立AIDL的步骤
- [Android]自定义组件示例:使用attrs.xml文件定制RadioButton
- Android(安卓)ProgressBar 自定义样式(一)
- 玩懂Log,打开Android大门(sundy深入浅出)之一
- Android生成签名文件并用其对apk文件进行签名(Failure [INSTALL_P
- Android知识体系总结之Android部分之Toast篇