Android内核解读-应用的安装过程

时间:2023-03-08 17:39:09

前言

我们知道,在android手机上安装一个apk很简单,只要打开apk文件,默认就会弹出安装界面,然后点击确定,经过若干秒后,apk就安装成功了,可是你知道apk的安装过程是什么吗?你知道android系统在安装一个apk的时候都干了什么吗?在本文中,将一一解答这个问题。简单来说,apk的安装过程分两步:第一步,将apk文件复制到程序目录下(/data/app/);第二步,为应用创建数据目录(/data/data/package name/)、提取dex文件到指定目录(/data/dalvik-cache/)、修改系统包管理信息。注意,本文的分析基于Android
4.3源码

apk的复制过程

apk的安装从PackageManager的installApk方法开始,由于PackageManager所对应的binder服务为PackageManagerService(PMS),所以,真正的安装过程都在PackageManagerService中完成。PackageManagerService的installApk方法最终调用了installPackageWithVerificationAndEncryption方法,该方法的核心就是在最后发送了一个INIT_COPY的消息,这个消息的含义是完成apk的拷贝过程。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public

void

installPackageWithVerificationAndEncryption(Uri packageURI,
        IPackageInstallObserver
observer,
int

flags, String installerPackageName,
        VerificationParams
verificationParams, ContainerEncryptionParams encryptionParams) {
    mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
            null);
    final

int

uid = Binder.getCallingUid();
    if

(isUserRestricted(UserHandle.getUserId(uid), UserManager.DISALLOW_INSTALL_APPS)) {
        try

{
            observer.packageInstalled(,
PackageManager.INSTALL_FAILED_USER_RESTRICTED);
        }
catch

(RemoteException re) {
        }
        return;
    }
    UserHandle
user;
    if

((flags&PackageManager.INSTALL_ALL_USERS) !=
0)
{
        user
= UserHandle.ALL;
    }
else

{
        user
=
new

UserHandle(UserHandle.getUserId(uid));
    }
    final

int

filteredFlags;
    if

(uid == Process.SHELL_UID || uid ==
0)
{
        if

(DEBUG_INSTALL) {
            Slog.v(TAG,
Install from ADB);
        }
        filteredFlags
= flags | PackageManager.INSTALL_FROM_ADB;
    }
else

{
        filteredFlags
= flags & ~PackageManager.INSTALL_FROM_ADB;
    }
    verificationParams.setInstallerUid(uid);
    final

Message msg = mHandler.obtainMessage(INIT_COPY);
    msg.obj
=
new

InstallParams(packageURI, observer, filteredFlags, installerPackageName,
            verificationParams,
encryptionParams, user);
    mHandler.sendMessage(msg);
}

通过分析代码可以发现,真正实现apk拷贝的方法是InstallParams的handleStartCopy方法,InstallParams中有重试机制,拷贝如果失败的话会重试,最多重试4次。在拷贝之前,还必须做一件事情,那就是绑定media container service,安装过程中一些状态的检查会用到这个服务,代码如下所示:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class

PackageHandler
extends

Handler {
    private

boolean

mBound =
false;
    final

ArrayList<handlerparams> mPendingInstalls =
        new

ArrayList<handlerparams>();
    private

boolean

connectToService() {
        if

(DEBUG_SD_INSTALL) Log.i(TAG, Trying to bind to +
                 DefaultContainerService);
        Intent
service =
new

Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
        Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
        if

(mContext.bindServiceAsUser(service, mDefContainerConn,
                Context.BIND_AUTO_CREATE,
UserHandle.OWNER)) {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            mBound
=
true;
            return

true
;
        }
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        return

false
;
    }
    ...
}</handlerparams></handlerparams>

现在分析一下InstallParams的handleStartCopy方法,这个方法很长,代码就不帖出来了,大家可以自己去看看,这里主要分析下它的工作流程:

1. 检查安装位置标记位是否有冲突,如果有冲突,则安装失败,这里的有冲突是指“一个apk同时要求被安装到内部存储和sd卡”

2. 调用MCS服务的getMinimalPackageInfo方法来得到apk的推荐安装位置,并检查是否能够进行正常的安装。在这一步,有可能抛出一些无法安装的状态位:存储空间不足、程序已经安装、无效的apk文件等,这个时候安装过程终止

3. 到这一步,表示程序可以正常安装,同时MCS服务服务可能会根据需要调整安装位置,在InstallParams的installLocationPolicy中完成

4. 文件的复制过程,PMS针对内部存储和sd卡分别提供了一个类:FileInstallArgs和AsecInstallArgs,并分别调用二者的copyApk方法来完成apk的复制过程

经过了上面4步,待安装apk已经被复制到了/data/app/目录了。

apk的解析过程

上面,apk已经被复制到了/data/app/目录,安装的第一步已经完成,那么系统是什么时候对apk进行dex提取和解析的呢,这还要从PMS说起,在PMS内部有一个AppDirObserver类,顾名思义,它的作用是应用目录观察者,它时刻观察着应用目录/data/app/,当目录内部结构改变的时候(创建文件和删除文件)它会做出相应行为,下面看下它的代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
private

final

class

AppDirObserver
extends

FileObserver {
    public

AppDirObserver(String path,
int

mask,
boolean

isrom) {
        super(path,
mask);
        mRootDir
= path;
        mIsRom
= isrom;
    }
    //在/data/app/目录下添加或删除apk的时候,此方法会被调用
    public

void

onEvent(
int

event, String path) {
        String
removedPackage =
null;
        int

removedAppId = -
1;
        int[]
removedUsers =
null;
        String
addedPackage =
null;
        int

addedAppId = -
1;
        int[]
addedUsers =
null;
        //
TODO post a message to the handler to obtain serial ordering
        synchronized

(mInstallLock) {
            String
fullPathStr =
null;
            File
fullPath =
null;
            if

(path !=
null)
{
                fullPath
=
new

File(mRootDir, path);
                fullPathStr
= fullPath.getPath();
            }
            if

(DEBUG_APP_DIR_OBSERVER)
                Log.v(TAG,
File  + fullPathStr +  changed:  + Integer.toHexString(event));
            if

(!isPackageFilename(path)) {
                if

(DEBUG_APP_DIR_OBSERVER)
                    Log.v(TAG,
Ignoring change of non-
package

file:  + fullPathStr);
                return;
            }
            //
Ignore packages that are being installed or
            //
have just been installed.
            if

(ignoreCodePath(fullPathStr)) {
                return;
            }
            PackageParser.Package
p =
null;
            PackageSetting
ps =
null;
            //
reader
            synchronized

(mPackages) {
                p
= mAppDirs.get(fullPathStr);
                if

(p !=
null)
{
                    ps
= mSettings.mPackages.get(p.applicationInfo.packageName);
                    if

(ps !=
null)
{
                        removedUsers
= ps.queryInstalledUsers(sUserManager.getUserIds(),
true);
                    }
else

{
                        removedUsers
= sUserManager.getUserIds();
                    }
                }
                addedUsers
= sUserManager.getUserIds();
            }
            //当apk被删除的时候,往往意味着这个apk被卸载
            if

((event&REMOVE_EVENTS) !=
0)
{
                if

(ps !=
null)
{
                    if

(DEBUG_REMOVE) Slog.d(TAG, Package disappeared:  + ps);
                    //removePackageLI方法完成卸载apk的主要功能
                    removePackageLI(ps,
true);
                    removedPackage
= ps.name;
                    removedAppId
= ps.appId;
                }
            }
             
            //新添加了一个apk,往往意味着一个新的apk被安装
            if

((event&ADD_EVENTS) !=
0)
{
                if

(p ==
null)
{
                    if

(DEBUG_INSTALL) Slog.d(TAG, New file appeared:  + fullPath);
                    //scanPackageLI方法完成了apk安装的第二个步骤
                    p
= scanPackageLI(fullPath,
                            (mIsRom
? PackageParser.PARSE_IS_SYSTEM
                                    |
PackageParser.PARSE_IS_SYSTEM_DIR:
0)
|
                            PackageParser.PARSE_CHATTY
|
                            PackageParser.PARSE_MUST_BE_APK,
                            SCAN_MONITOR
| SCAN_NO_PATHS | SCAN_UPDATE_TIME,
                            System.currentTimeMillis(),
UserHandle.ALL);
                    if

(p !=
null)
{
                        /*
                         *
TODO this seems dangerous as the package may have
                         *
changed since we last acquired the mPackages
                         *
lock.
                         */
                        //
writer
                        synchronized

(mPackages) {
                            updatePermissionsLPw(p.packageName,
p,
                                    p.permissions.size()
>
0

? UPDATE_PERMISSIONS_ALL :
0);
                        }
                        addedPackage
= p.applicationInfo.packageName;
                        addedAppId
= UserHandle.getAppId(p.applicationInfo.uid);
                    }
                }
            }
            //
reader
            synchronized

(mPackages) {
                mSettings.writeLPr();
            }
        }
        //下面两个if语句块大家应用不陌生吧,在我们的应用中想监听应用的安装和卸载,
        //就是通过收听ACTION_PACKAGE_ADDED和ACTION_PACKAGE_REMOVED这两个广播来实现的
        if

(removedPackage !=
null)
{
            Bundle
extras =
new

Bundle(
1);
            extras.putInt(Intent.EXTRA_UID,
removedAppId);
            extras.putBoolean(Intent.EXTRA_DATA_REMOVED,
false);
            sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
removedPackage,
                    extras,
null,
null,
removedUsers);
        }
        if

(addedPackage !=
null)
{
            Bundle
extras =
new

Bundle(
1);
            extras.putInt(Intent.EXTRA_UID,
addedAppId);
            sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
addedPackage,
                    extras,
null,
null,
addedUsers);
        }
    }
    private

final

String mRootDir;
    private

final

boolean

mIsRom;
}

下面,我们主要分析一下scanPackageLI方法,还是仅仅分析,不帖代码,因为代码太长了,帖出来没法看了,这个方法不仅仅是完成apk包的扫描,还解析AndroidManifest.xml文件并提取出所有的intent-filter和permission信息,apk安装的主要功能都由它来完成的,当apk包扫描完成后,系统会调用updatePermissionsLPw方法更新系统所具有的权限。

scanPackageLI方法有两个,其第一个参数分别接受File和PackageParser.Package类型,第一个方法会从File中提取出package信息然后再调用第二个方法,下面分析第二个scanPackageLI方法,其完成的事情如下:

1. 如果包名是android,则会做一些特殊处理,这个包名为android的应用是系统内部应用的,其他应用的包名如果叫android则安装会有问题,大家可以试一下

2. 解析常见的use-feature、shared-userId、use-library标签并保存到成员变量中

3. 进行签名验证,对应的方法是verifySignaturesLP,验证失败则应用无法安装

4. 创建应用程序目录/data/data/包名,同时将apk中提取出dex文件并保存到/data/dalvik-cache,把apk当做zip解压就能得到dex文件

5. 解析AndroidManifest.xml文件,提取出所需信息,包括具有intent-filter的四大组件信息(Activity、Service、BroadcastReceiver、ContentProvider)和声明的系统权限等

到此为止,scanPackageLI方法结束了。而updatePermissionsLPw的作用是对系统中所有的权限进行更新,大家可以查看下/system/etc/permissons目录,下面定义了android系统中所有的权限,开发中最常用的权限定义在目录下的platform.xml里面,大家可以打开看看,可以看到常见的访问网络、读写外部存储等权限等都是在这里定义的。权限更新完毕以后,系统就会发送ACTION_PACKAGE_ADDED广播,告知所有应用有新应用安装了。另外,大家可以查看下data/system/目录,里面有两个文件packages.list和packages.xml,在packages.list里面放的是手机上安装的所有应用列表,而packages.xml中存放的是所有应用的设置应用,比如一个应用声明了哪些系统权限就定义在这里面。关于应用的卸载,我们可以想到是应用安装过程的逆过程,大致要做的是:停止应用、删除各种文件,更新系统设置、权限等,大家感兴趣自己看一下,完全是安装过程的逆过程,这里不介绍了。