APK安装流程概述

Posted 浪里小白龙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了APK安装流程概述相关的知识,希望对你有一定的参考价值。



. APK安装简介


APKandroid Package的缩写。


Android应用安装有如下四种方式:

1.系统应用安装――开机时完成,没有安装界面;

2.网络下载应用安装――通过market应用完成,没有安装界面;

3.ADB工具安装――没有安装界面;

4.第三方应用安装――通过SD卡里的APK文件安装,有安装界面,由packageinstaller.apk应用处理安装及卸载过程的界面。

 


应用安装涉及到如下几个目录:

  • system/app----------------系统自带的应用程序,获得adb root权限才能删除;
  • data/app-------------------用户程序安装的目录,安装时把apk 文件复制到此目录;
  • data/data-------------------存放应用程序的数据;
  • data/dalvik-cache---------将apk中的dex文件安装到dalvik-cache目录下(dex文件是dalvik虚拟机的可执行文件,其大小约为原始apk文件大小的四分之一)。



. 系统应用安装

 

1. 了解须知:

1. 对于在/system/app/data/app目录下的APK文件,在PackageManagerService的启动过程中,会扫描安装

2.PackageManagerServicesystem_server启动,它全面负责应用包的安装,卸载,权限检查等工作。

3.在每次开机的时 候,PackageManagerService都会在其构造函数中,对指定的目录的APK进行扫描。对于没有安装的APK文件会触发安装过程。



2. 实现原理:

(1). 开机启动PackageManagerService,通过SystemServer.startBootstrapServices()启动。

1 public static PackageManagerService main(Context context, Installer installer,
2             boolean factoryTest, boolean onlyCore) {
3         PackageManagerService m = HwServiceFactory.getHuaweiPackageManagerService(context, installer,
4                 factoryTest, onlyCore);
5         ServiceManager.addService("package", m);
6         return m;
7     }

 

 

(2). PackageManagerService初始化,执行构造方法,分为六个重要步骤。

 

第一步:创建Settings对象,添加shareUserId

 1         mSettings = new Settings(mPackages);
 2         mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
 3                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
 4         mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
 5                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
 6         mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
 7                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
 8         mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
 9                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
10         mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
11                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
12         mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
13                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

 


第二步:创建应用安装器Installer,来源是PackageManagerService参数之一。

 

第三步:构造SystemConfig,读取/system/etc/permissions/*.xml” 资源,获取mSystemPermissions(系统权限),mGlobalGidsGroup-ids),mAvailableFeatures(系统支持的features)属性。

执行顺序:

com.android.server.pm.PackageManagerService#PackageManagerService

--> com.android.server.SystemConfig#getInstance

--> com.android.server.SystemConfig#SystemConfig

--> com.android.server.SystemConfig#readPermissions

 

1    SystemConfig systemConfig = SystemConfig.getInstance();
2    mGlobalGids = systemConfig.getGlobalGids();
3    mSystemPermissions = systemConfig.getSystemPermissions();
4    mAvailableFeatures = systemConfig.getAvailableFeatures();

 

第四步:创建系统消息处理线程。

1   mHandlerThread = new ServiceThread(TAG,
2             Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
3   mHandlerThread.start();
4   mHandler = new PackageHandler(mHandlerThread.getLooper());
5   Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);

 

第五步:执行com.android.server.pm.Settings#readLPw, 读取安装包信息,并解析成对应的数据结构,包括以下重要文件:

  • packages.xml:记录系统中所有安装的应用信息,包括基本信息、签名和权限。

  • packages-backup.xmlpackages.xml文件的备份。

  • packages.list:保存普通应用的数据目录和uid等信息。

  • packages-stopped.xml:记录系统中被强制停止运行的应用信息。系统在强制停止某个应用时,会讲应用的信息记录到该文件中。

  • packages-stopped-backup.xmlpacakges-stopped.xml文件的备份。

这几个目录在创建Settings对象的时候,就已经被封装成对应的File文件。

packages-backup.xmlpackages.xml的备份文件。在每次写packages.xml文件的时候,都会将旧的 packages.xml文件先备份,这样做是为了防止写文件过程中文件以外损坏,还能从旧的文件中恢复。

package- restrictions.xml保存着受限制的APP的状态,比如某个APP处于disable状态,或者某个APP具有更高的优先级等。



第六步:执行PackageManagerService#scanDirLI

监控和扫描系统包安装目录:

  • /system/framework 系统库
  • /system/app 默认的系统应用
  • /vendor/app 厂商定制的应用

 

扫描非系统apk信息:

  • /data/app/
  • /system/preloadapp/
  • /data/app-private/

 

跟踪扫描安装过程

构建PackageParser对象

      调用PackageManagerService#scanPackageLI(xxx) 方法。

 

构建一个PackageParser.Package对象并返回

  调用PackageParser#parsePackage(java.io.File, int) 方法,扫描APK安装包的AndroidManifest.xml文件和提取证书信息,以此信息构建一个PackageParser.Package对象,并将其返回;


PackageParser.Package对象的信息保存到PackageManagerService

其中包括ContentProvider,Activity,Service,BroadcastReceiver


构建PackageSetting 对象

执行以下代码:

.PackageManagerService#scanPackageLI(xxx)

--> .PackageManagerService#scanPackageDirtyLI

构建PackageSetting 对象,这个对象中保存的信息最后会通过writeLPr写入到/data/system/packages.xml文件中去。



以上几个步骤可以用两个图代替:

----------------------------------------------------------------------------------------------------------------------------------------------------------------



----------------------------------------------------------------------------------------------------------------------------------------------------------------





调用mInstaller.createUserData()函数创建数据目录

调用PackageManagerService#createDataDirsLI方法,installd发送消息,为应用程序创建对应的数据目录,如果已经存在,也会重新创建一遍。

 

 

调用mInstaller.install()函数完成APK安装

 1  private int createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo) {
 2         int[] users = sUserManager.getUserIds();
 3         int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo);
 4         if (res < 0) {
 5             return res;
 6         }
 7         for (int user : users) {
 8             if (user != 0) {
 9                 res = mInstaller.createUserData(volumeUuid, packageName,
10                         UserHandle.getUid(user, uid), user, seinfo);
11                 if (res < 0) {
12                     return res;
13                 }
14             }
15         }
16         return res;
17     }

 

Installer.install()函数和createUserData()进行完成了命令组装工作,在组装完命令之后,将命令传递给InstallerConnectio.java处理。

 1 public int install(String uuid, String name, int uid, int gid, String seinfo) {
 2         StringBuilder builder = new StringBuilder("install");
 3         builder.append(\' \');
 4         builder.append(escapeNull(uuid));
 5         builder.append(\' \');
 6         builder.append(name);
 7         builder.append(\' \');
 8         builder.append(uid);
 9         builder.append(\' \');
10         builder.append(gid);
11         builder.append(\' \');
12         builder.append(seinfo != null ? seinfo : "!");
13         return mInstaller.execute(builder.toString());
14     }

 

通过分析InstallerConnection.java得到以下结论:

1. InstallerConnection连接一个名为Installd的服务

2. Install具体的命令有Installd完成



以下是InstallerConnection连接Installd服务的代码

 1 private boolean connect() {
 2         if (mSocket != null) {
 3             return true;
 4         }
 5         Slog.i(TAG, "connecting...");
 6         try {
 7             mSocket = new LocalSocket();
 8 
 9             LocalSocketAddress address = new LocalSocketAddress("installd",
10                     LocalSocketAddress.Namespace.RESERVED);
11 
12             mSocket.connect(address);
13 
14             mIn = mSocket.getInputStream();
15             mOut = mSocket.getOutputStream();
16         } catch (IOException ex) {
17             disconnect();
18             return false;
19         }
20         return true;
21     }

 


Installed介绍

Installd是一个native进程,该进程启动一个socket,然后处理来自Installer的命令。



PackageManagerService通过套接字的方式访问installd服务进程,在Android启动脚本init.rc中通过服务配置启动了installd服务进程。

1     service installd /system/bin/installd
2     class main
3     socket installd stream 600 system system

通过以上配置,init进程就会启动installd服务进程了。


Installed 进程的入口是main函数,该函数首先初始化一些变量就安装目录,然后从环境变量中取得installd套件字的句柄值,然后进入监听此socket,当客户端发送过来请求时,接收客户端的请求,并读取客户端发送过来的命令数据,并根据读取的客户端命令来执行命令操作。

1 int main(const int argc __unused, char *argv[]) {
 2     char buf[BUFFER_MAX];
 3     struct sockaddr addr;
 4     socklen_t alen;
 5     int lsocket, s;
 6     int selinux_enabled = (is_selinux_enabled() > 0);
 7 
 8     setenv("ANDROID_LOG_TAGS", "*:v", 1);
 9     android::base::InitLogging(argv);
10 
11     ALOGI("installd firing up\\n");
12 
13     union selinux_callback cb;
14     cb.func_log = log_callback;
15     selinux_set_callback(SELINUX_CB_LOG, cb);
16 
17     if (initialize_globals() < 0) {
18         ALOGE("Could not initialize globals; exiting.\\n");
19         exit(1);
20     }
21 
22     if (initialize_directories() < 0) {
23         ALOGE("Could not create directories; exiting.\\n");
24         exit(1);
25     }
26 
27     if (selinux_enabled && selinux_status_open(true) < 0) {
28         ALOGE("Could not open selinux status; exiting.\\n");
29         exit(1);
30     }
31 
32     lsocket = android_get_control_socket(SOCKET_PATH);
33     if (lsocket < 0) {
34         ALOGE("Failed to get socket from environment: %s\\n", strerror(errno));
35         exit(1);
36     }
37     if (listen(lsocket, 5)) {
38         ALOGE("Listen on socket failed: %s\\n", strerror(errno));
39         exit(1);
40     }
41     fcntl(lsocket, F_SETFD, FD_CLOEXEC);
42 
43     for (;;) {
44         alen = sizeof(addr);
45         s = accept(lsocket, &addr, &alen);
46         if (s < 0) {
47             ALOGE("Accept failed: %s\\n", strerror(errno));
48             continue;
49         }
50         fcntl(s, F_SETFD, FD_CLOEXEC);
51 
52         ALOGI("new connection\\n");
53         for (;;) {
54             unsigned short count;
55             if (readx(s, &count, sizeof(count))) {
56                 ALOGE("failed to read size\\n");
57                 break;
58             }
59             if ((count < 1) || (count >= BUFFER_MAX)) {
60                 ALOGE("invalid size %d\\n", count);
61                 break;
62             }
63             if (readx(s, buf, count)) {
64                 ALOGE("failed to read command\\n");
65                 break;
66             }
67             buf[count] = 0;
68             if (selinux_enabled && selinux_status_updated() > 0) {
69                 selinux_android_seapp_context_reload();
70             }
71             if (execute(s, buf)) break;
72         }
73         ALOGI("closing connection\\n");
74         close(s);
75     }
76 
77     return 0;
78 }

 


main函数调用execute函数,执行客户发送过来的请求命令。