Android Framework-Android启动过程

第一个系统进程(init)

Android设备的启动必须经历3个阶段,即Boot Loader、Linux Kernel和Android系统服务,默认情况下它们都有各自的启动界面。严格来说,Android系统实际上是运行于Linux内核之上的一系列“服务进程”,并不算一个完整意义上的“操作系统”。这些进程是维持设备正常工作的关键,而它们的“老祖宗”就是init。
init的PID值为0,它通过解析init.rc脚本来构建出系统的初始运行形态。
 init.rc实例分析

 export PATH /sbin:/system/sbin:/system/bin #响应boot事件,设置系
统环境变量
 export LD_LIBRARY_PATH /system/lib #响应boot事件,设置库路径
 mkdir /dev #创建/dev目录
 mkdir /proc #创建/proc目录
 mkdir /sys #创建/sys目录。这时还没有超出on boot的作用范围,下同
 mount tmpfs tmpfs /dev
 mkdir /dev/pts
 mkdir /dev/socket
 mount devpts devpts /dev/pts
 mount proc proc /proc
 mount sysfs sysfs /sys #以上几行用于挂载文件系统以及创建新的目录
 write /proc/cpu/alignment 4 #打开文件,并写入数值
 ifup lo #建立lo网络连接
 hostname localhost #设置主机名
 domainname localhost #设置域名
 mount yaffs2 mtd@system /system
 mount yaffs2 mtd@userdata /data
 import /system/etc/init.conf #导入另一个配置文件
 class_start default #启动所有标志为default的服务
service adbd /sbin/adbd #启动adbd服务进程
 user adb
 group adb # Adbd是android debug bridge daemon的缩写,它为开发者与设
备之间建立了一条通道。 
service usbd /system/bin/usbd -r
 user usbd
 group usbd
 socket usbd 666 #启动usbd服务。
service zygote /system/bin/app_process -Xzygote /system/bin --
zygote
 socket zygote 666 #启动zygote服务。Zygote是系统的“孵化器”,负责生产
“进程”
on device-added-/dev/compass
 start akmd #当增加了/dev/compass节点后,启动akmd服务
on device-removed-/dev/compass
 stop akmd #当移除了/dev/compass节点后,停止akmd服务
service akmd /sbin/akmd
 disabled
 user akmd
 group akmd #因为这里对akmd服务使用了disabled选项,所以系统不会主动去启
动它。而是要等到上面
 #描述的/dev/compass节点出现时,才显式地调用此服务

系统关键服务的启动简析

作为Android系统的第一个进程,init将通过解析init.rc来陆续启动其他关键的系统服务进程——其中最重要的就是ServiceManager、Zygote和SystemServer。
Android的“DNS服务器”——ServiceManager
ServiceManager是Binder机制中的“DNS服务器”,负责域名(某Binder服务在ServiceManager注册时提供的名称)到IP地址(由底层Binder驱动分配的值)的解析。
ServiceManager是在Init.rc里描述并由init进程启动的。如下所示:

/*sytem/core/rootdir/Init.rc*/
service servicemanager /system/bin/servicemanager
 class core
 user system
 group system
 critical
 onrestart restart healthd
 onrestart restart zygote
 onrestart restart media
 onrestart restart surfaceflinger
 onrestart restart drm

可以看到,servicemanger是一个Linux程序。它在设备中的存储路径是/system/bin/service- manager,源码路径则是/frameworks/native/cmds/servicemanager。
ServiceManager所属class是core,其他同类的系统进程包括ueventd、console(/system/bin/sh)、adbd等。根据core组的特性,这些进程会同时被启动或停止。另外,critical选项说明它是系统的关键进程——意味着如果进程不幸地在4分钟内异常退出超过4次,则设备将重启并进入还原模式。当ServiceManager每次重启时,其他关键进程如Zygote、media、surfaceflinger等也会被restart。
“孕育”新的线程和进程——Zygote
Zygote这个词的字面意思是“受精卵”,因而可以“孕育”出一个“新生命”。正如其名所示,Android中大多数应用进程和系统进程都是通过Zygote来生成的。
以init.zygote64.rc为例,相关代码如下:

service zygote /system/bin/app_process64 -Xzygote /system/bin --
zygote --start-system-server
 class main
 socket zygote stream 660 root system
 onrestart write /sys/android_power/request_state wake
 onrestart write /sys/power/state on
 onrestart restart media
 onrestart restart netd

从上面这段脚本描述可以看出:

ServiceName: zygote
Path: /system/bin/app_process64
Arguments: -Xzygote /system/bin --zygote --start-system-server

Zygote所属class为main,而不是core。和其同class的系统进程有netd、debuggerd、rild等。
从zygote的path路径可以看出,它所在的程序名叫“app_process64”,而不像ServiceManager一样在一个独立的程序中。通过指定–zygote参数,app_process可以识别出用户是否需要启动zygote。app_process又是何方神圣呢?先来看看它的Android.mk:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
 app_main.cpp
LOCAL_SHARED_LIBRARIES := \
 libcutils \
 libutils \
 liblog \
 libbinder \
 libandroid_runtime
LOCAL_MODULE:= app_process
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := app_process32
LOCAL_MODULE_STEM_64 := app_process64
include $(BUILD_EXECUTABLE)

上述是构建Multilib(64位和32位系统)的一个编译脚本范例。其中LOCAL_MULTILIB用于指示你希望针对的硬件平台架构。可选值如下:“32” :表示只编译32位版本。“64”: 表示只编译64位版本。 “both” :表示同时编译32位和64位的版本。“” :表示由系统根据其他变量来决定要编译的目标。
从上面的描述可以很明显地看到,app_process其实扮演了一个类似于“壳”的角色,那么它容纳了哪些“内容”呢?

/*frameworks/base/cmds/app_process/App_main.cpp*/
int main(int argc, char* const argv[])
{…
 AppRuntime runtime(argv[0], computeArgBlockSize(argc,
argv));//Android运行时环境
 …
 bool zygote = false;
 bool startSystemServer = false;
 bool application = false;
 String8 niceName;
 String8 className;
 ++i; // Skip unused "parent dir" argument.
 while (i < argc) {
 const char* arg = argv[i++];
 if (strcmp(arg, "--zygote") == 0) { //当前进程是否用于承载
zygote
 zygote = true;
 niceName = ZYGOTE_NICE_NAME;
 } else if (strcmp(arg, "--start-system-server") == 0) {//
是否需要启动system server
 startSystemServer = true;
 } else if (strcmp(arg, "--application") == 0) {
 application = true;
 } else if (strncmp(arg, "--nice-name=", 12) == 0) {
 niceName.setTo(arg + 12);
 } else if (strncmp(arg, "--", 2) != 0) {
 className.setTo(arg);
 break;
 } else {
 --i;
 break;
 }
 }if (zygote) {
 runtime.start("com.android.internal.os.ZygoteInit",
args);
 } else if (className) {
 runtime.start("com.android.internal.os.RuntimeInit",
args);
 } else {}
}

这个函数用于解析启动app_process时传入的参数,具体如下。
–zygote: 表示当前进程用于承载zygote。
–start-system-server:是否需要启动system server。
–application:启动进入独立的程序模式。
–nice-name:此进程的“别名”。
对于非zygote的情况下,在上述参数的末尾会跟上main class的名称,而后的其他参数则属于这个class的主函数入参;对于zygote的情况,所有参数则会作为它的主函数入参使用。
在我们这个场景中,init.rc指定了–zygote选项,因而app_process接下来将启动“ZygoteInit”并传入“start-systemserver”。之后ZygoteInit会运行于Java虚拟机上,为什么?
原因就是runtime这个变量——它实际上是一个AndroidRuntime对象,其start函数源码如下:

/*frameworks/base/core/jni/AndroidRuntime.cpp*/
void AndroidRuntime::start(const char* className, const char*
options)
{
 …
 JNIEnv* env;
 if (startVm(&mJavaVM, &env) != 0) {//启动虚拟机
 return;
 }
 onVmCreated(env);//虚拟机启动后的回调}

我们这里假设VM可以成功启动,并进入ZygoteInit的执行中:

/*frameworks/base/core/java/com/android/internal/os/ZygoteInit.ja
va*/ 
public static void main(String argv[]) {
 try {…
 boolean startSystemServer = false;
 String socketName = "zygote";
 String abiList = null;
 for (int i = 1; i < argv.length; i++) {
 if ("start-system-server".equals(argv[i])) {
 startSystemServer = true;//需要启动System
Server
 } else if (argv[i].startsWith(ABI_LIST_ARG)) {
 abiList =
argv[i].substring(ABI_LIST_ARG.length());
 } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
 socketName =
argv[i].substring(SOCKET_NAME_ARG.length());
 } else {
 throw new RuntimeException("Unknown command
line argument: " + argv[i]);
 }
 }
 if (abiList == null) {
 throw new RuntimeException("No ABI list
supplied.");
 }
 registerZygoteSocket(socketName);//注册一个Socket
 preload();//预加载各类资源if (startSystemServer) {
 startSystemServer(abiList, socketName);//后面对此
函数进行详细分析
 }
 Log.i(TAG, "Accepting command socket connections");
 runSelectLoop(abiList);
 closeServerSocket();
 } catch (MethodAndArgsCaller caller) {
 caller.run();
 } catch (RuntimeException ex) {
 Log.e(TAG, "Zygote died with exception", ex);
 closeServerSocket();
 throw ex;
 }
 }

ZygoteInit的主函数并不复杂,它主要完成两项工作:

  • 注册一个Socket
    Zygote是“孵化器”,一旦有新程序需要运行时,系统会通过这个Socket(完整的名称为ANDROID_SOCKET_zygote)在第一时间通知“总管家”,并由它负责实际的进程孵化过程。
  • 预加载各类资源
    函数preload用于加载虚拟机运行时所需的各类资源,包括:
    preloadClasses();
    preloadResources();
    preloadOpenGL();
    preloadSharedLibraries();
  • 启动System Server
    如果app_process的调用参数中带有“–start-system-server”,那么此时就会通过startSystemServer来启动System Server。Zygote在前期主要担任启动系统服务的工作,后期则又担当“程序孵化”的重任。但是Zygote只在init.rc中被启动一次,它如何协调好这两项工作的关系呢?我们可以推断一下,上述的startSystemServer应该会新建一个专门的进程来承载系统服务的运行,而后app_process所在的进程则转变为Zygote的“孵化器”守护进程。那么是不是这样子的呢?
/*
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
*/
private static boolean startSystemServer(String abiList, String
socketName)
 throws MethodAndArgsCaller, RuntimeException {/* Hardcoded command line to start the system server */
 String args[] = {
 "--setuid=1000",
 "--setgid=1000",
 "--
setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,
 1032,3001,3002,3003,3006,3007",
 "--capabilities=" + capabilities + "," +
capabilities,
 "--runtime-init",
 "--nice-name=system_server",
 "com.android.server.SystemServer",
 };
 ZygoteConnection.Arguments parsedArgs = null;
 int pid;
 try {
 parsedArgs = new ZygoteConnection.Arguments(args);
 
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
 
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
 /* Request to fork the system server process */
 pid = Zygote.forkSystemServer(
 parsedArgs.uid, parsedArgs.gid,
 parsedArgs.gids,
 parsedArgs.debugFlags,
 null,
 parsedArgs.permittedCapabilities,
 parsedArgs.effectiveCapabilities); //果然需要
fork一个新进程
 } catch (IllegalArgumentException ex) {
 throw new RuntimeException(ex);
 }
 if (pid == 0) {//子进程,即System Server所承载进程
 if (hasSecondZygote(abiList)) {
 waitForSecondaryZygote(socketName);
 }
 handleSystemServerProcess(parsedArgs);//启动各System
Server
 }
 return true;
 }

forkSystemServer在内部利用UNIX的fork机制创建了一个新进程;而这个“新生儿”(即pid == 0分支)会在随后的执行过程中通过handleSystemServerProcess来启动各种支撑系统运行的System Server。
System Server的启动是在startSystemServer中完成的。Zygote首先会利用Zygote.forkSystemServer来孵化出一个子进程,然后在pid==0的分支中调用handleSystem ServerProcess,后者在函数的末尾又会进一步调用RuntimeInit.zygoteInit:

/*frameworks/base/core/java/com/android/internal/os/RuntimeInit.j
ava */ 
public static final void zygoteInit(int targetSdkVersion,
String[] argv, 
ClassLoader classLoader) throws ZygoteInit.MethodAndArgsCaller {commonInit();
 nativeZygoteInit();
 applicationInit(targetSdkVersion, argv, classLoader);
 }

函数zygoteInit通过3个方面来完成初始化,分别是:

  • commonInit
    通用部分的初始化,包括设置默认的uncaught exception handler(具体对应的是RuntimeInit中的UncaughtHandler类);为HttpURLConnection准备好默认的HTTP User-Agent (User Agent包含了
    与系统浏览器相关的一系列信息,如“Dalvik/1.1.0 (Linux; U;Android Eclair Build/MASTER)”.);开启trace模式(只在emulator下才有必要)等。
  • nativeZygoteInit
    这是一个本地初始化函数
  • applicationInit
    这个函数的声明为:private static void applicationInit(int targetSdkVersion, String[] argv, Class Loader classLoader);从中可以看出它是程序运行的“起点”。
    第二个参数argv,这个String[]实际上包含了两个重要的成员变量,即startClass和startArgs。而这两个变量的赋值可以追溯到startSystemServer中,具体代码如下:
String args[] = {
 "--setuid=1000",
 "--setgid=1000", 
 "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,
 1009,1010,1018,1032,3001,3002,3003,3006,3007",
 "--capabilities=" + capabilities + "," +
capabilities,
 "--runtime-init",
 "--nice-name=system_server",
 "com.android.server.SystemServer",
 };

换句话说,startClass对应的就是com.android.server.SystemServer。因而applicationInit最终将调用
main@SystemServer:

public static void main(String[] args) {
 new SystemServer().run();
 }

经过上面的初始化后,程序现在会有两个分支,其一是nativeZygoteInit主导的本地系统服务的启动;另一个则是applicationInit负责的Java层系统服务的启动。
在这里插入图片描述
Zygote和System Server的启动流程

Android的“系统服务”——SystemServer
 SystemServer是Android进入Launcher前的最后准备。 一旦我们在init.rc中为Zygote指定了启动参数–start-system-server,那么ZygoteInit就会调用startSystemServer来进入SystemServer。而且系统服务又分别分为Java层和本地层两类。其中Native层服务的实现体在android_servers中,需要在run@SystemServer中首先通过System.loadLibrary(“android_servers”)加载到内存中才能使用。而nativeInit则负责为启动本地层服务而努力。
再回到Java层来看一下这类系统服务是如何管理的。从代码中可以看到,这部分Server又可细分为3类,如下所示:

  • Bootstrap Services
    BootStrap的原意是“引导程序”,用在这里则代表系统服务中最核心的那一部分。另外,这些Services间相互的依赖关系比较强,因而需要在一起统一管理启动,具体对应的是startBootstrapServices这个
    函数。按照Android的建议,如果你自己添加的系统服务和它们也有较强的依赖,那么可以与这类系统服务统一放置,否则就应该考虑下面所述的另两类服务:
/*frameworks/base/services/java/com/android/server/SystemServer.j
ava*/ 
 private void startBootstrapServices() {
 mInstaller =
mSystemServiceManager.startService(Installer.class);
 mActivityManagerService =
mSystemServiceManager.startService(
 
ActivityManagerService.Lifecycle.class).getService();
 
mActivityManagerService.setSystemServiceManager(mSystemServiceMan
ager);
 mPowerManagerService =
mSystemServiceManager.startService(PowerManagerService.class);
 mActivityManagerService.initPowerManagement();
 mDisplayManagerService =
mSystemServiceManager.startService(DisplayManagerService.class);
 
mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR
_DEFAULT_DISPLAY);
 // Only run "core" apps if we're encrypting the device.
 String cryptState = SystemProperties.get("vold.decrypt");
 if (ENCRYPTING_STATE.equals(cryptState)) {
 Slog.w(TAG, "Detected encryption in progress - only
parsing core apps");
 mOnlyCore = true;
 } else if (ENCRYPTED_STATE.equals(cryptState)) {
 Slog.w(TAG, "Device encrypted - only parsing core
apps");
 mOnlyCore = true;
 }
 // Start the package manager.
 Slog.i(TAG, "Package Manager");
 mPackageManagerService =
PackageManagerService.main(mSystemContext, mInstaller,
 mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
mOnlyCore);
 mFirstBoot = mPackageManagerService.isFirstBoot();
 mPackageManager = mSystemContext.getPackageManager();
 Slog.i(TAG, "User Service");
 ServiceManager.addService(Context.USER_SERVICE,
UserManagerService.getInstance());
 // Initialize attribute cache used to cache resources
from packages.
 AttributeCache.init(mSystemContext);
 // Set up the Application instance for the system process
and get started.
 mActivityManagerService.setSystemProcess();
 }

上面的函数中被调用最多的语句是mSystemServiceManager.startService。目前所有System
Service都统一由SystemServiceManager来管理。System Service Manager首先会启动Installer,这是为了让Installer可以优先完成初始化,并完成关键目录(如/data/user)的创建。这些都是其他服务可以顺利启动的先决条件。接下来启动的系统服务是ActivityManagerService,在AMS之后相继启动的服务包括电源管理Power Manager、Display Manager、PackageManager等,最后调用setSystemProcess来添加进程相关的服务,如meminfo、gfxinfo、dbinfo、cpuinfo等,从而完成最核心部分系统服务的启动。

  • Core Services
    Core Service相对于BootStrap的优先级略低,主要包括LED和背光管理器、电池电量管理器、应用程序使用情况(Usage Status)管理器等
  • Other Services
    这部分服务在3类Service中优先级最低,但数量却最多。比如AccountManagerService、VibratorService、MountService、NetworkManagementService、NetworkStatsService、ConnectivityService、WindowManagerService、UsbService、SerialService、AudioService等。这些服务全面构筑起Android系统这座“参天大厦”,为其他进程、应用程序的正常运行奠定了基础。
    最后,SystemServer通过Looper.loop()进入长循环中,并依托onZygoteInit中启动的Binder服务接受和处理外界的请求。
    Vold和External Storage存储设备
    Android系统中的内/外存储设备定义如下:
  • Internal Storage
    按照Android的设计理念,Internal Storage代表的是/data存储目录。
  • External Storage
    所有除Internal Storage之外的可存储区域

从物理设备的角度来看,External Storage由如下几种类型组成:

  • Emulated Storage
    Android设备中存在的一个典型做法,是从Internal Storage(如Flash)中划分一定的区域(如1GB)来作为外部存储设备,称为Emulated Storage
  • SDCARD/USB Devices
    通过扩展卡槽或者USB端口来扩展设备的存储能力,也是Android设备中的常见情况。
    Android系统中的外部存储设备由Vold和Mount Service来统一管理。其中Vold对应的源码路径是:AOSP/system/vold。它是通过init.rc启动的,如下所示
on post-fs-data
…
 start vold

Vold在启动以后,会通过NETLINK和内核取得联系,并根据后者提供的event来构建存储设备管理系统。和以往版本不同的是,Vold的配置文件不再是vold.fstab,而变成了/fstab.<ro.hardware>。例如
AOSP/device/fugu/fstab.fugu
Android 6.0及以后版本中,根据设备具体情况不同主要有如下几种典型配置:
(1)Emulated primary only
即只有Emulated Storage的情况,此时fstab.device的配置范例如
下:
/devices//xhci-hcd.0.auto/usb auto auto defaults
voldmanaged=usb:auto
(2)Physical primary only
即只有一个外置物理存储设备的情况,此时fstab.device的配置范
例如下:
/devices/platform/mtk-msdc.1/mmc_host* auto auto defaults
voldmanaged=sdcard0:auto,encryptable=userdata,noemulatedsd
(3)Emulated primary, physical secondary
有两个外置的物理存储设备,它们会被分别设定为primary和secondary,此时fstab.device的配置范例如下:
/devices/platform/mtk-msdc.1/mmc_host* auto auto defaults
voldmanaged=sdcard1:auto,encryptable=userdata

Vold在启动过程中会通过process_config函数来处理fstab配置文件,并把它们存储在VolumeManager的全局变量中。后续当收到内核的NetlinkEvent (add)时,VolumeManager再在handleBlockEvent中根据
规则判断本次事件是否和之前记录的fstab配置相匹配——如果答案是肯定的话,则新创建一个Disk对象来管理,并将它们统一添加到mDisks中,如图所示。
在这里插入图片描述

我们不难发现,Emulated Storage所需的存储空间来源于设备的data分区。换句话说,Emulated Storage的存储空间和data分区是共享存储区域的。Emulated Storage当然也是由Volume Manager来统一管理的,如下所示:

/*system/vold/VolumeManager.cpp*/
int VolumeManager::start() {CHECK(mInternalEmulated == nullptr);
 mInternalEmulated =
std::shared_ptr<android::vold::VolumeBase>(
 new android::vold::EmulatedVolume("/data/media"));
 mInternalEmulated->create();
 return 0;
}

VolumeManager::start会被vold的main函数调用,因而从Vold的角度来看所有Android设备都是带有Emulated Storage的,只不过最终是否需要执行mount操作则由MountService来决定。从EmulatedVolume构造函数的参数可以看到,它在data分区中对应的路径是/data/media。VolumeManager的全局变量mInternalEmulated用于记录系统的EmulatedStorage。

接下来mInternalEmulated->create()除了给Storage创建运行环境外,还会向MountService发送一个VolumeCreated的消息,并将自身的状态迁移到kUnmounted。MountService收到这一信息后,会根据系统的实际情况决定是否挂载这个Storage——如果答案是肯定的话,那么它会回应一个mount指令给vold,而后者对此的处理过程中会进一步调用到doMount函数——这个函数最关键的步骤之一是fork一个新进程,用于运行/system/bin/sdcard。

例如domount @EmulatedVolume.cpp中的如下代码段:

if (!(mFusePid = fork())) {
 if (execl(kFusePath, kFusePath,
 "-u", "1023", // AID_MEDIA_RW
 "-g", "1023", // AID_MEDIA_RW
 "-m",
 "-w",
 mRawPath.c_str(),
 label.c_str(),
 NULL)) {
 PLOG(ERROR) << "Failed to exec";
 }
 LOG(ERROR) << "FUSE exiting";
 _exit(1);
 }

变量kFusePath指向的是“/system/bin/sdcard”,我们可以通过execl系统调用将其启动起来。Sdcard daemon对应的源代码目录是AOSP/system/core/sdcard。
Sdcard daemon属于Fuse Service。Fuse的全称是“Filesystem in Userspace”,即在用户态实现的一种File System。它的典型框架如图所示
在这里插入图片描述
当使用者(左半部分)希望访问FUSE文件系统时,这一请求会经过Kernel的VFS首先传递给FUSE对应的驱动模块,然后再通知到用户层的fuse管控程序(例如这个场景中的sdcard)。后者处理请求完成后会将结果数据返回给最初的调用者,从而完成一次交互过程。可见与传统的文件系统相比,FUSE文件系统因为处理层次较多,所以在效率上注定会存在不足的地方。不过“瑕不掩瑜”,FUSE文件系统的灵活性依然为其获得了广泛的应用
了解了FUSE文件系统后,我们再来看sdcard daemon是怎么做的。简单来说它会执行以下几个核心操作。

  • 将/dev/fuse挂载到3个目标路径下
    这几个目标路径分别是:/mnt/runtime/default/%s、/mnt/runtime/read/%s和/mnt/runtime/write/%s,其中“%s”代表的是Volume的label,在Emulated Storage这个场景下对应的是“emulated”。
  • 创建3个线程
    在代码中对应的是thread_default、thread_read和thread_write。这3个线程启动后都会进入for死循环,然后不停地从自己对应的fuse->fd(即打开/dev/fuse产生的文件描述符)中读取fuse模块发过来的消息命令,并根据命令的具体类型(例如FUSE_READ、FUSE_WRITE、FUSE_OPEN等)执行处理函数。
    为什么我们需要将/dev/fuse挂载到3个路径下,并通过不同的线程来管理呢?这和Android 6.0中引入的Runtime Permission有关系。
    Runtime permission允许用户在程序运行到某些特别功能时再动态决定是否赋予程序相应的权限。这样带来的好处是用户可以更清楚地知道应用程序需要(或者已经授予了)哪些权限,以实现更为“透明”的管
    理。不过Runtime Permission只对那些系统认为危险的权限进行保护,大家可以利用如下命令获取详细的权限列表:
adb shell pm list permissions -g -d

Runtime Permission权限管理方式的一种很重要的特性就是要求应用程序的权限可以在运行过程中进行动态调整,而且不能导致应用程序的重启。这其中就涉及Package Manager Service、Activity
Manager Service、Zygote等多个系统服务,我们按照顺序逐一阐述。
首先需要关注的是应用程序启动时的初始化权限处理,此时AMS在startProcessLocked中会做如下处理:

/*frameworks/base/services/core/java/com/android/server/am/Activi
tyManagerService.java*/
private final void startProcessLocked(ProcessRecord app, String
hostingType,
 String hostingNameStr, String abiOverride, String
entryPoint, String[] entryPointArgs) {…
try {
 checkTime(startTime, "startProcess: getting gids from
package manager");
 final IPackageManager pm =
AppGlobals.getPackageManager();
 permGids = pm.getPackageGids(app.info.packageName,
app.userId);
 MountServiceInternal mountServiceInternal =
LocalServices.getService(
 MountServiceInternal.class);
 mountExternal =
mountServiceInternal.getExternalStorageMountMode(uid,
 app.info.packageName);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
 …
 Process.ProcessStartResult startResult =
Process.start(entryPoint,
 app.processName, uid, uid, gids, debugFlags,
mountExternal,
 app.info.targetSdkVersion, app.info.seinfo,
requiredAbi, 
 instructionSet, app.info.dataDir,
entryPointArgs);

mountExternal是由MountService提供的,其代码实现如下所示:

public int getExternalStorageMountMode(int uid, String
packageName) {
 // No locking - CopyOnWriteArrayList
 int mountMode = Integer.MAX_VALUE;
 for (ExternalStorageMountPolicy policy : mPolicies) {
 final int policyMode = policy.getMountMode(uid,
packageName);
 if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) {
 return Zygote.MOUNT_EXTERNAL_NONE;
 }
 mountMode = Math.min(mountMode, policyMode);
 }
 if (mountMode == Integer.MAX_VALUE) {
 return Zygote.MOUNT_EXTERNAL_NONE;
 }
 return mountMode;
 }

这个函数的处理逻辑是:遍历所有的Policy规则,并从中挑选出数值最小的MountMode——按照由小而大的顺序排列,它们依次是:MOUNT_EXTERNAL_NONE、MOUNT_EXTERNAL_DEFAULT、
MOUNT_EXTERNAL_READ和MOUNT_EXTERNAL_WRITE。应用程序的MountMode的具体取值主要由PMS的checkUidPermission来判断,而后者则会根据APP在AndroidManifest中申请WRITE_MEDIA_STORAGE、READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE等权限的情况来给出结论
当Zygote孵化出一个应用程序进程后,会在MountEmulatedStorage中对Mount Mode做进一步处理,核心实现如下:

if (unshare(CLONE_NEWNS) == -1) {
 ALOGW("Failed to unshare(): %s", strerror(errno));
 return false;
 }
 String storageSource;
 if (mount_mode == MOUNT_EXTERNAL_DEFAULT) {
 storageSource = "/mnt/runtime/default";
 } else if (mount_mode == MOUNT_EXTERNAL_READ) {
 storageSource = "/mnt/runtime/read";
 } else if (mount_mode == MOUNT_EXTERNAL_WRITE) {
 storageSource = "/mnt/runtime/write";
 } else {
 // Sane default of no storage visible
 return true;
 }
 if (TEMP_FAILURE_RETRY(mount(storageSource.string(),
"/storage",
 NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {}

透过不同的“View”所能“看到”的Mount Tree是不一样的,从而实现了程序在外部存储上的分权限管理。更为重要的是,采用这种实现方式在应对Runtime Permission变化时是不需要重启应用程序就可以生效的。具体来说,当程序取得Storage新的Runtime Permission以后,PMS会通过MountService向Vold的CommandListener发送一条名为“remount_uid”的命令,后者则进一步将消息传递给Vold的VolumeManager::remountUid函数——在这个函数中就可以对相应的进程进行“视角”的重新调整,从而达到我们的预期效果。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值