本文以Content Shell为例,简要分析了Manifest文件的关键元素声明,后续篇章将逐步分析Content Shell的启动过程和内部结构等。
Android Manifest描述了应用程序的基本信息,包括权限,应用程序,Activity,内容提供者,Intent过滤器以及允许的后台service进程名和数量等等诸多信息。Chromium为每个平台都提供了一个外壳程序,Android平台也不例外,它只提供了一个简单的地址输入栏和导航栏,具备最基本的网页渲染功能,主要是为了展示如何通过Content API定制Chromium内核。
以下是用于创建ContentShell应用程序的Manifest文件 (src/content/shell/android/shell_apk/AndroidManifest.xml):
<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (c) 2012 The Chromium Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.chromium.content_shell_apk"> <permission android:name="org.chromium.content_shell.permission.SANDBOX" android:protectionLevel="signature" /> <application android:name="ContentShellApplication" android:icon="@mipmap/app_icon" android:label="Content Shell"> <activity android:name="ContentShellActivity" android:launchMode="singleTask" android:theme="@android:style/Theme.Holo.Light.NoActionBar" android:configChanges="orientation|keyboardHidden|keyboard|screenSize" android:hardwareAccelerated="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <!-- The following service entries exist in order to allow us to start more than one sandboxed process. --> <!-- NOTE: If you change the values of "android:process" for any of the below services, you also need to update kHelperProcessExecutableName in chrome_constants.cc. --> <service android:name="org.chromium.content.app.SandboxedProcessService0" android:process=":sandboxed_process0" android:permission="org.chromium.content_shell.permission.SANDBOX" android:isolatedProcess="true" android:exported="false" /> <service android:name="org.chromium.content.app.SandboxedProcessService1" android:process=":sandboxed_process1" android:permission="org.chromium.content_shell.permission.SANDBOX" android:isolatedProcess="true" android:exported="false" /> <service android:name="org.chromium.content.app.SandboxedProcessService2" android:process=":sandboxed_process2" android:permission="org.chromium.content_shell.permission.SANDBOX" android:isolatedProcess="true" android:exported="false" /> <service android:name="org.chromium.content.app.SandboxedProcessService3" android:process=":sandboxed_process3" android:permission="org.chromium.content_shell.permission.SANDBOX" android:isolatedProcess="true" android:exported="false" /> <service android:name="org.chromium.content.app.SandboxedProcessService4" android:process=":sandboxed_process4" android:permission="org.chromium.content_shell.permission.SANDBOX" android:isolatedProcess="true" android:exported="false" /> <service android:name="org.chromium.content.app.SandboxedProcessService5" android:process=":sandboxed_process5" android:permission="org.chromium.content_shell.permission.SANDBOX" android:isolatedProcess="true" android:exported="false" /> <service android:name="org.chromium.content.app.SandboxedProcessService6" android:process=":sandboxed_process6" android:permission="org.chromium.content_shell.permission.SANDBOX" android:isolatedProcess="true" android:exported="false" /> <service android:name="org.chromium.content.app.SandboxedProcessService7" android:process=":sandboxed_process7" android:permission="org.chromium.content_shell.permission.SANDBOX" android:isolatedProcess="true" android:exported="false" /> <service android:name="org.chromium.content.app.SandboxedProcessService8" android:process=":sandboxed_process8" android:permission="org.chromium.content_shell.permission.SANDBOX" android:isolatedProcess="true" android:exported="false" /> <service android:name="org.chromium.content.app.SandboxedProcessService9" android:process=":sandboxed_process9" android:permission="org.chromium.content_shell.permission.SANDBOX" android:isolatedProcess="true" android:exported="false" /> <service android:name="org.chromium.content.app.SandboxedProcessService10" android:process=":sandboxed_process10" android:permission="org.chromium.content_shell.permission.SANDBOX" android:isolatedProcess="true" android:exported="false" /> <service android:name="org.chromium.content.app.SandboxedProcessService11" android:process=":sandboxed_process11" android:permission="org.chromium.content_shell.permission.SANDBOX" android:isolatedProcess="true" android:exported="false" /> <service android:name="org.chromium.content.app.SandboxedProcessService12" android:process=":sandboxed_process12" android:permission="org.chromium.content_shell.permission.SANDBOX" android:isolatedProcess="true" android:exported="false" /> </application> <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="18" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> </manifest>
有过Android应用程序开发经验的人对这个Manifest文件并不陌生,例如Application,Activity和Permission等声明,但有几处还是值得特别说明的:
<permission>声明是对服务进程的保护,表明只有ContentShell应用程序本身才能启动服务进程,对于其他请求启动这些服务进程的应用程序,Android系统需要根据签名检查确定是否赋予给定的权限。如上所述,<service>元素显式地指定了必须拥有这个permission的应用程序才能操作服务进程。
这个manifest文件最引人注目的当属13个<service>元素的声明。我们知道,Chromium是一个架构在多进程模型上的系统,每个页面都运行在一个独立的渲染进程中,所有对本地资源的访问必须通过IPC(进程间通信)的方式请求浏览器进程去完成,例如网络资源的加载,文件系统的访问,窗口UI系统的操作等等,渲染进程的主要工作是页面的排版和JavaScript的执行,鉴于安全考虑,它运行在一个不受信任的沙箱(Sandbox)中。
在Android上,每个渲染进程实际上是一个隔离态的服务进程(isolatedProcess:true),这13个<service>元素显式指定了ContentShell最多只允许同时运行13个服务进程,也就是13个渲染进程。当渲染进程数到达上限时,需要从正在运行的服务进程中挑选一个去渲染新的页面,例如页面打开了一个新的链接地址。那么如何确定合理的服务进程数量呢?实际上,Chromium在运行时会根据系统的物理内存估计一下究竟能够启动多少个服务进程的,假设系统有1G的物理内容,一个渲染进程占用40M,那么实际启动的服务进程数为512M/40M = 12个,也就说尽管在Manifest中声明了13个服务进程,在1G的系统上实际运行过程中最多也只能启动12个服务进程了。
每个<service>元素都指定permission为org.chromium.content_shell.permission.SANDBOX,只有拥有了这个指定权限的应用程序才能执行startService,bindService或者stopService方法。
相应地,Chromium为这13个<service>元素创建了13个Java类,SandboxedProcessService[0…12], SandboxedProcessService继承于android.app.Service。将service进程整合到Chromium多进程模型中,还需要考虑如下几个问题:
上述问题的答案在ChildProcessService,ChildProcessLauncher, ChildProcessConnection等几个关键类的实现中。后续篇章将深入剖析它们的实现细节。