在之前的博客中已经提到过,当SystemServer创建PKMS时,在其构造函数中传入了一个Installer对象。
Installer是一个系统服务,可以和installd通信,完成一些重要的工作,例如利用dexopt函数对APK文件进行dex优化;存储空间不足时,利用freeCache函数清理存储空间。
接下来,我们就分析一下installd及一些重要工作的流程。
一、installd的初始化
1、installd启动
在init.rc中,有以下代码片段:
....... service installd /system/bin/installd class main socket installd stream 600 system system .......
容易看出installd将作为service被init进程启动,同时会创建一个名为installd的socket。
在frameworks/native/cmds/installd/installd.cpp中:
int main(const int argc, char *argv[]) {
return android::installd::installd_main(argc, argv);
}
static int installd_main(const int argc ATTRIBUTE_UNUSED, char *argv[]) {
.........
//以下初始化全局变量,包括创建data下的一些目录等
if (!initialize_globals()) {
ALOGE("Could not initialize globals; exiting.\n");
exit(1);
}
if (initialize_directories() < 0) {
ALOGE("Could not create directories; exiting.\n");
exit(1);
}
...........
//得到"installd" socket
lsocket = android_get_control_socket(SOCKET_PATH);
...........
//"installd"变成服务端
if (listen(lsocket, 5)) {
ALOGE("Listen on socket failed: %s\n", strerror(errno));
exit(1);
}
fcntl(lsocket, F_SETFD, FD_CLOEXEC);
for (;;) {
alen = sizeof(addr);
//接受Java层的installer服务连接,形成与之连接的socket "s"
s = accept(lsocket, &addr, &alen);
..........
fcntl(s, F_SETFD, FD_CLOEXEC);
..........
for (;;) {
unsigned short count;
//读取收到的消息的长度
if (readx(s, &count, sizeof(count))) {
ALOGE("failed to read size\n");
break;
}
//判断有效性
if ((count < 1) || (count >= BUFFER_MAX)) {
ALOGE("invalid size %d\n", count);
break;
}
//读取cmd
if (readx(s, buf, count)) {
ALOGE("failed to read command\n");
break;
}
..........
buf[count] = 0;
//执行cmd
if (execute(s, buf)) break;
}
.........
close(s);
}
return 0;
}
上面的代码中,我们省去了selinux相关的代码,只保留主干。
从主干代码容易看出,installd整体的结构非常简单,其实就是启动后,获取作为服务端的socket “installd”;
然后,监听”installd”,等待Java层installer服务的连接及命令的到来。
2、命令处理方式
一旦installd收到命令后,将调用execute函数进行处理,其代码如下:
static int execute(int s, char cmd[BUFFER_MAX]) {
char reply[REPLY_MAX];
char *arg[TOKEN_MAX+1];
.......
arg[0] = cmd;
//解析参数的个数
while (*cmd) {
//当发现空格时
if (isspace(*cmd)) {
*cmd++ = 0;
//参数个数+1
n++;
//保存参数
arg[n] = cmd;
if (n == TOKEN_MAX) {
ALOGE("too many arguments\n");
goto done;
}
}
if (*cmd) {
cmd++;
}
}
//cmds为一个数组,保存了不同命令及对应的处理函数
for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
if (!strcmp(cmds[i].name,arg[0])) {
if (n != cmds[i].numargs) {
//log
.......
} else {
//参数正确时,调用对应的执行函数进行处理
ret = cmds[i].func(arg + 1, reply);
}
}
}
...........
}
execute函数的思路很清晰,类似于收到命令后查表找到对应的处理函数,然后传入参数并执行函数。
在这一部分的最后,我们看看cmds中的内容:
struct cmdinfo {
const char *name;
unsigned numargs;
int (*func)(char **arg, char reply[REPLY_MAX]);
};
struct cmdinfo cmds[] = {
{ "ping", 0, do_ping },
{ "create_app_data", 7, do_create_app_data },
{ "restorecon_app_data", 6, do_restorecon_app_data },
{ "migrate_app_data", 4, do_migrate_app_data },
{ "clear_app_data", 5, do_clear_app_data },
{ "destroy_app_data", 5, do_destroy_app_data },
{ "move_complete_app", 7, do_move_complete_app },
{ "get_app_size", 6, do_get_app_size },
{ "get_app_data_inode", 4, do_get_app_data_inode },
{ "create_user_data", 4, do_create_user_data },
{ "destroy_user_data", 3, do_destroy_user_data },
{ "dexopt", 10, do_dexopt },
{ "markbootcomplete", 1, do_mark_boot_complete },
{ "rmdex", 2, do_rm_dex },
{ "freecache", 2, do_free_cache },
{ "linklib", 4, do_linklib },
{ "idmap", 3, do_idmap },
{ "createoatdir", 2, do_create_oat_dir },
{ "rmpackagedir", 1, do_rm_package_dir },
{ "clear_app_profiles", 1, do_clear_app_profiles },
{ "destroy_app_profiles", 1, do_destroy_app_profiles },
{ "linkfile", 3, do_link_file },
{ "move_ab", 3, do_move_ab },
{ "merge_profiles", 2, do_merge_profiles },
{ "dump_profiles", 3, do_dump_profiles },
};
可以比较清晰地看出cmds数组中存储的就是cmdinfo结构体,结构体中定义了名称、参数个数及对应的处理函数。
二、installer服务的启动
1、启动
在SystemServer.java的startBootstrapServices函数中:
private void startBootstrapServices() {
// Wait for installd to finish starting up so that it has a chance to
// create critical directories such as /data/user with the appropriate
// permissions. We need this to complete before we initialize other services.
Installer installer = mSystemServiceManager.startService(Installer.class);
..............
}
public SystemService startService(String className) {
final Class<SystemService> serviceClass;
try {
serviceClass = (Class<SystemService>)Class.forName(className);
} catch (ClassNotFoundException ex) {
//抛出异常
..........
}
return startService(serviceClass);
}
public <T extends SystemService> T startService(Class<T> serviceClass) {
try {
final String name = serviceClass.getName();
.............
final T service;
try {
//反射调用构造函数
Constructor<T> constructor = serviceClass.getConstructor(Context.class);
} catch(.....) {
.............
}.......
//SystemServiceManager的mServices中存储所有由其启动的服务
mServices.add(service);
// Start it.
try {
service.onStart();
} catch (RuntimeException ex) {
//抛出异常
...........
}
return service;
} finally {
//log
...........
}
}
从上面的代码容易看出,SystemServer利用SystemServerManager的startService启动服务时,其实就是通过反射来创建服务对象,然后调用服务对象的onStart函数。
因此,对于installer服务,我们此时只需要关注其构造函数和onStart函数:
public final class Installer extends SystemService {
........
public Installer(Context context) {
super(context);
//与installd沟通的桥梁
mInstaller = new InstallerConnection();
}
......
public void onStart() {
Slog.i(TAG, "Waiting for installd to be ready.");
//调用InstallerConnection的waitForConnection函数
mInstaller.waitForConnection();
}
........
}
从上面的代码可以看出,Installer继承自SystemService,其构造函数主要创建出InstallerConnection,然后在onStart函数中调用InstallerConnection的waitForConnection函数。
2、连接installd
接下来,我们就来看看InstallerConnection的waitForConnection函数:
public void waitForConnection() {
for (;;) {
try {
//"ping"应该只是确认installd是否存活
execute("ping");
return;
} catch (InstallerException ignored) {
}
Slog.w(TAG, "installd not ready");
SystemClock.sleep(1000);
}
}
跟进InstallerConnection的execute函数:
public String[] execute(String cmd, Object... args) throws InstallerException {
final StringBuilder builder = new StringBuilder(cmd);
//处理参数
for (Object arg : args) {
String escaped;
if (arg == null) {
escaped = "";
} else {
escaped = String.valueOf(arg);
}
if (escaped.indexOf('\0') != -1 || escaped.indexOf(' ') != -1 || "!".equals(escaped)) {
//错误格式抛出异常
........
}
if (TextUtils.isEmpty(escaped)) {
escaped = "!";
}
builder.append(' ').append(escaped);
}
//transact发送命令及对应参数,并返回结果
final String[] resRaw = transact(builder.toString()).split(" ");
int res = -1;
try {
//解析结果
res = Integer.parseInt(resRaw[0]);
} catch (ArrayIndexOutOfBoundsException | NumberFormatException ignored) {
}
if (res != 0) {
throw new InstallerException(
"Failed to execute " + cmd + " " + Arrays.toString(args) + ": " + res);
}
return resRaw;
}
容易看出transact函数负责进行实际的传输工作:
public synchronized String transact(String cmd) {
............
//连接"installd"
if (!connect()) {
Slog.e(TAG, "connection failed");
return "-1";
}
//发送命令
if (!writeCommand(cmd)) {
Slog.e(TAG, "write command failed? reconnect!");
if (!connect() || !writeCommand(cmd)) {
return "-1";
}
}
//读取返回结果
final int replyLength = readReply();
if (replyLength > 0) {
String s = new String(buf, 0, replyLength);
........
return s;
} else {
........
return "-1";
}
}
从上面的代码来看,整个通信结构非常的清晰,典型的socket通信架构。
为了分析的完整性,我们再看看connect、writeCommand和readReply函数:
private boolean connect() {
//第一次才需要进行实际的连接,之后就不需要了
if (mSocket != null) {
return true;
}
......
try {
mSocket = new LocalSocket();
//得到"installd"目的端地址
LocalSocketAddress address = new LocalSocketAddress("installd",
LocalSocketAddress.Namespace.RESERVED);
//进行连接的过程
mSocket.connect(address);
//以下得到输入流和输出流
mIn = mSocket.getInputStream();
mOut = mSocket.getOutputStream();
} catch (IOException ex) {
disconnect();
return false;
}
return true;
}
private boolean writeCommand(String cmdString) {
final byte[] cmd = cmdString.getBytes();
final int len = cmd.length;
........
buf[0] = (byte) (len & 0xff);
buf[1] = (byte) ((len >> 8) & 0xff);
try {
//写入长度
mOut.write(buf, 0, 2);
//写入命令和参数
mOut.write(cmd, 0, len);
} catch (IOException ex) {
Slog.e(TAG, "write error");
disconnect();
return false;
}
return true;
}
private int readReply() {
if (!readFully(buf, 2)) {
return -1;
}
final int len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
........
if (!readFully(buf, len)) {
return -1;
}
return len;
}
private boolean readFully(byte[] buffer, int len) {
try {
//还是靠mIn来读取数据
Streams.readFully(mIn, buffer, 0, len);
} catch (IOException ioe) {
Slog.e(TAG, "read exception");
disconnect();
return false;
}
........
return true;
}
至此Installer服务的启动主要的流程基本介绍完毕。
从上述代码可以看出,Installer服务完全可以看做是installd在Java层的代理。
该Java服务自身基本不承载任何主要的逻辑,就是通过Socket将请求发送给native层的installd进行处理。
接下来,我们以几个例子,看看installd的主要工作流程。
三、installd实例分析
1、ping命令
installd的cmds数组中定义:
....... { "ping", 0, do_ping }, .......
正如上文所述,在初始连接时,Java层的Installer服务将向installd发送ping命令。
此时,installd将调用do_ping函数进行处理。
static int do_ping(char **arg ATTRIBUTE_UNUSED, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
{
return 0;
}
明显可见,do_ping进行任何实际的操作,只是返回执行结果。因此,初始时Java层发送ping请求,确实只是确认installd处于正常状态。
2、freeCache命令
在安装APK文件时,如果系统空间不足,最终将发送freeCache的命令给installd。
根据如下installd中cmds数组,我们知道将调用do_free_cache进行处理。
......... { "freecache", 2, do_free_cache }, .........
我们看看do_free_cache函数:
static int do_free_cache(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
return free_cache(parse_null(arg[0]), (int64_t)atoll(arg[1]));
}
int free_cache(const char *uuid, int64_t free_size) {
...........
//得到需要清理的路径,例如'/data'目录或Sd card中的路径
auto data_path = create_data_path(uuid);
avail = data_disk_free(data_path);
........
//利用statfs函数得到路径对应的文件系统信息
//利用非超级用户可获取的块数,乘以经过优化的每个传输块大小
//得到当前系统的剩余空间大小
avail = data_disk_free(data_path);
.......
//此时不需要free cache
if (avail >= free_size) return 0;
auto users = get_known_users(uuid);
for (auto user : users) {
//添加指定路径中的cache文件
add_cache_files(cache, create_data_user_ce_path(uuid, user));
add_cache_files(cache, create_data_user_de_path(uuid, user));
add_cache_files(cache,
StringPrintf("%s/Android/data", create_data_media_path(uuid, user).c_str()));
}
//逐级删除cache文件
clear_cache_files(data_path, cache, free_size);
finish_cache_collection(cache);
//删除cache后,剩余可用内存大于要求,则返回0
return data_disk_free(data_path) >= free_size ? 0 : -1;
}
四、总结
本篇博客重在分析installd与Installer服务之间的通信结构。
installd中有些命令的实现细节还是比较复杂的,自己也没有做太深入的了解,就不在这里赘述了。
大家感兴趣的话,可以自己再分析一下。