8.3 PackageManagerService启动过程使用的核心组件

在分析PackageManagerService的启动过程时,使用了Installer的多个方法。Android APK的安装和卸载是主要由Installer和Installd完成的。

Installer是Java层提供的Java API接口,Installd则是系统启动阶段由init进程启动的Daemon Service。Installer与Installd通过Socket通信,Installer是Socket的Client, Installd则是Socket的Server。通过Socket通信,将Installer的API调用转化为Installd中具体的命令,这种转化关系通过cmds[]数组配置和映射。Installer与Installd的关系如图8-8所示。

8.3 PackageManagerService启动过程使用的核心组件 - 图1

图 8-8 Installer与Installd的关系

从图8-8可以看出,Installd系统由从左到右三部分组成,分别是:1)Java Client,2)Init Service,3)Installd Methods。在系统启动阶段,init启动Installd这个Daemon Service, Installd启动后,通过cmds数组建立Installer与Installd命令之间的映射关系,Installd中的每一个命令最终由commands.c实现。接下来分别详细分析Installer和Installd。

8.3.1 Java层的Installer

首先分析Java Client,即Java层的Installer,定义于frameworks/base/services/java/com/android/server/pm/Installer.java中,提供了APK安装和删除过程中涉及的多数操作,其类图如图8-9所示。

8.3 PackageManagerService启动过程使用的核心组件 - 图2

图 8-9 Installer类图

Installer中定义了处理Socket连接和APK文件的各种方法,这里以install方法为例,代码如下:


int ret=mInstaller.install(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.uid);


传入的三个参数在install方法内部被拼装成以下字符串:


“install<pkgName><pkg.applicationInfo.uid><pkg.applicationInfo.uid>”


接下来以该字符串为参数调用execute方法,代码如下:


public int install(String name, int uid, int gid){

StringBuilder builder=new StringBuilder("install");

builder.append('');

builder.append(name);

builder.append('');

builder.append(uid);

builder.append('');

builder.append(gid);

return execute(builder.toString());

}

private int execute(String cmd){

String res=transaction(cmd);

try{

return Integer.parseInt(res);

}catch(NumberFormatException ex){

return-1;

}

}


execute方法进而调用transaction方法与Installd进行Socket通信,代码如下:


private synchronized String transaction(String cmd){

if(!connect()){//连接Installd的Socket

return"-1";

}

if(!writeCommand(cmd)){//发出请求,cmd即上文中拼装的字符串

//如果请求失败,重新请求一次。这种情况一般在Installd异常退出时发生

if(!connect()||!writeCommand(cmd)){

return"-1";

}

}

if(readReply()){//读取请求的返回数据

String s=new String(buf,0,buflen);

return s;

}else{

return"-1";

}

}


transaction()方法是典型的连接→请求→响应的操作模型。首先通过connect()方法连接Socket服务端;然后通过writeCommand(cmd)方法向服务端发送请求;请求返回后,通过readReply()读取响应数据。

connect()使用了标准的Socket进行进程间通信,代码如下:


private boolean connect(){

try{

mSocket=new LocalSocket();

//指定Socket服务端为Installd

LocalSocketAddress address=new LocalSocketAddress("installd",

LocalSocketAddress.Namespace.RESERVED);

mSocket.connect(address);//连接Installd

mIn=mSocket.getInputStream();//读取输入输出数据

mOut=mSocket.getOutputStream();

}catch(IOException ex){

disconnect();

return false;

}

return true;

}


connect()方法连接到Installd的Socket后,会以mIn和mOut两个全局变量分别持有输入输出流的引用。writeCommand()方法将请求命令写入到mOut中,而readReply()方法则是从mIn中读取服务端返回的响应数据。

Installer中的方法名与写到Socket的命令名不是完全对应的,其对应关系如表8-2所示。

8.3 PackageManagerService启动过程使用的核心组件 - 图3

8.3 PackageManagerService启动过程使用的核心组件 - 图4