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-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-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所示。