Android apk安装过程分析

一、何谓apk成功安装到系统

1.1、一个apk怎样才可以称作成功安装到了Android系统?

可以类比一下新生儿。一个刚出生的孩子怎么样才能算是一个完整的社会人呢?

婴儿刚从母亲体内分娩出来,他实际还不能称为一个"社会人",他需要向户籍管理单位进行注册,添加户口页,分配社保ID,完成一些列注册手续之后,才能称为一个完全意义上的“社会人”,并且可以享受国家社会的相关福利。

apk的安装也是类似的,不仅仅需要将apk的实体放到系统的特定目录(/data/app/),而且需要向PackageManagerService注册包名、以及apk声明的四大组件(Activity、Service、ContentProvider、BroadcastReceiver),该apk才能算是成功安装到了Android系统。

大概可分为两个方面:

  • 1、安装apk到特定的目录
  • 2、注册包名、四大组件到PackageManagerService

只有完成了第2步(向系统注册),Android系统才知道有这么一个apk存在,才可以管理(或者说启动)该apk

1.2、apk安装相关的目录

apk安装目录:

  • 1、 /system/app: 系统自带的应用程序,获得adb root权限才能删除
  • 2、 /data/app :用户程序安装的目录。安装时把apk文件复制到此目录

dex、odex保存目录:

  • 3、/data/dalvik-cache:将apk中的dex文件安装到dalvik-cache目录下(dex文件是dalvik虚拟机的可执行文件,当然,ART–Android Runtime的可执行文件格式为oat,启用ART时,系统会执行dex文件转换至oat文件)

用户数据目录:

  • 4、/data/data :存放应用程序的数据,无论是系统app还是普通app,app产生的用户数据都存放在/data/data/包名/目录下。

packages.xml注册表目录

  • 5、/data/system :该目录下的packages.xml文件,类似于Windows的注册表。这个文件是在解析apk时由writeLP()创建的,里面记录了系统的permissions,以及每个apk的name,codePath,flags,ts,version,uesrid等信息,这些信息主要通apk的AndroidManifest.xml解析获取,解析完apk后将更新信息写入这个文件并保存到flash,下次开机直接从里面读取相关信息添加到内存相关列表中。当有apk升级,安装或删除时会更新这个文件。

二、PackageManagerService概述

2.1、PackageManagerService 有三大职责

PackageManagerService 是Android系统中所有包的大管家,其最简化的类结构如下:

public class PackageManagerService extends IPackageManager.Stub
        implements PackageSender{

    //保存所有已安装的apckage信息
    final Settings mSettings;

    //保存 包名-Package的键值对
    final ArrayMap mPackages =
            new ArrayMap();

     //保存所有已安装包中注册的Activity信息
    // All available activities, for your resolving pleasure.
    final ActivityIntentResolver mActivities =
            new ActivityIntentResolver();
    //保存所有已安装apk中注册的Receiver信息
    // All available receivers, for your resolving pleasure.
    final ActivityIntentResolver mReceivers =
            new ActivityIntentResolver();

    //保存所有已安装apk中注册的Service信息
    // All available services, for your resolving pleasure.
    final ServiceIntentResolver mServices = new ServiceIntentResolver();

    //保存所有已安装apk中注册ContentProvider信息
    // All available providers, for your resolving pleasure.
    final ProviderIntentResolver mProviders = new ProviderIntentResolver();
    
    //安装一个apk
    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {

    }

 }


PackageManagerService 有三大职责:

(1)开机扫描过程

开机后,加载/data/system/packages.xml文件,然后扫描特定安装目录:/system/app、/system/priv-app、/system/vendor/app、/data/app
解析里面的apk文件,将静态信息提取到PackageManagerService中的主要数据结构中。

(2)安装apk

将一个新的apk 安装到Android系统当中

(3)解析Intent

解析Intent意图,通过Intent获取到目的Activity、Provider、BroadCastReceiver 或Service。

2.2、Settings

上文提到 /data/system/package.xml 记录所有已安装的包信息,package.xml的内容如下:



     
     
        
        
        
        
        
        
        
        
        
        
        

      
            
                
            
            
                
                
                
                
            
        

        ....
        省略若干个package 声明
 
(1)记录了一个应用的基本信息,签名和声明的权限。
- name表示应用的包名
- codePath表示的是apk文件的路径
- nativeLibraryPath表示应用的native库的存储路径
- flags是指应用的属性,如FLAG_SYSTEM、FLAG_PERSISTENT等
- it表示应用安装的时间
- ut表示应用最后一次修改的时间
- version表示应用的版本号
- userId表示所属于的id
(2)表示应用的签名
- count表示标签中包含有多少个证书
- cert表示具体的证书的值
(3)表示应用声明使用的权限,每一个子标签代表一项权限

/data/system/package.xml,被PackageManagerService解析后会存储到mSettings 属性中。

 public final class Settings {
    /** Map from package name to settings */
    final ArrayMap mPackages = new ArrayMap<>();
 }

Settings中持有一个以package为键,以PackageSetting为值的ArrayMap,一个PackageSetting 就对应packages.xml中的一个package。

public final class PackageSetting extends PackageSettingBase {
    PackageParser.Package pkg;
    SharedUserSetting sharedUser;
    private int sharedUserId;
 }

PackageSetting 继承自PackageSettingBase。

  public final class PackageSetting extends PackageSettingBase {
    PackageParser.Package pkg;
    SharedUserSetting sharedUser;
    private int sharedUserId;
 }

2.3、mPackages

mPackages 维护了所有已安装apk的详细解析信息,包名为key,PackageParser.Package 为value

final ArrayMap mPackages =
            new ArrayMap();

2.4、四大组件解析器

  • ActivityIntentResolver mActivities 持有所有注册的activity信息,用于通过intent解析成合理的Activity
final class ActivityIntentResolver extends IntentResolver {

        // Keys are String (activity class name), values are Activity.
        private final ArrayMap mActivities
                = new ArrayMap();

       public List queryIntent(Intent intent, String resolvedType, int flags,
                int userId) {}

     }
  • ActivityIntentResolver mReceivers 用于解析BroadCastReceiver
  • ServiceIntentResolver mServices 维护所有注册的Service,用于通过Intent解成目标Service
  • ProviderIntentResolver mProviders 维护所有注册的ContentPrivider 用于将Intent解析成目标ContentProvider

三、apk安装具体安装过程:

3.1、apk安装的主要流程描述

本文主要阐述普通apk的安装流程,大概分为以下几个步骤:

image
  • (1) 拷贝apk文件到指定目录

在Android系统中,apk安装有固定的目录,默认情况下,用户安装的apk首先会被拷贝到 /data/app 目录下,同时会创建/data/app/pkg/lib目录,将apk中的so文件解压到此目录

/data/app目录是用户有权限访问的目录,在安装apk的时候会自动选择该目录存放用户安装的文件。而系统出厂的apk文件则被放到了 /system 分区下,包括:/system/app,/system/vendor/app,以及 /system/priv-app 等等,该分区只有Root权限的用户才能访问。这也就是为什么在没有Root手机之前,我们无法删除系统出厂的app的原因了。

  • (2) 扫描apk,解析AndroidManifest.xml文件,向PackagManagerService中注册该package、以及apk声明的四大组件。
  • (3)创建apk的用户数据目录,即/data/data/pkg/目录
/data/data/pkg为应用程序的数据目录(以应用的包名命名),存放应用的相关数据,如数据库、xml文件、cache
  • (4)为apk执行dexopt优化。
对于普通apk,dexopt优化后的文件保存在/data/app/oat目录
  • (5)安装完成发送ACTION_PACKAGE_ADDED广播。

3.2、apk安装代码分析

网络上有很多apk安装流程分析,但是版本都比较老了(Android 6.0左右),和最新的代码有些地方对不上,本文以Android 9.0 代码为基准,分析一下apk的安装流程:

PagkageManagerService的安装apk的入口是installStage

void installStage(String packageName, File stagedDir,
            IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
            String installerPackageName, int installerUid, UserHandle user,
            PackageParser.SigningDetails signingDetails) {
   
        final VerificationInfo verificationInfo = new VerificationInfo(
                sessionParams.originatingUri, sessionParams.referrerUri,
                sessionParams.originatingUid, installerUid);

        final OriginInfo origin = OriginInfo.fromStagedFile(stagedDir);

       
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        final int installReason = fixUpInstallReason(installerPackageName, installerUid,
                sessionParams.installReason);
        //(1)创建一个InstallParams对象,封装到INIT_COPY消息中
        final InstallParams params = new InstallParams(origin, null, observer,
                sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
                verificationInfo, user, sessionParams.abiOverride,
                sessionParams.grantedRuntimePermissions, signingDetails, installReason);
        params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
        msg.obj = params;
        //(2)发送INIT_COPY 消息
        mHandler.sendMessage(msg);
    }

installStage()方法主要目的是发送一个INIT_COPY Handler消息。

我们看下Handler的处理函数,比较重要的是INIT_COPY和MCS_BOUND 两个消息。

void doHandleMessage(Message msg) {
            switch (msg.what) {
               
                case INIT_COPY: {
                    HandlerParams params = (HandlerParams) msg.obj;
                    int idx = mPendingInstalls.size();
                  
                    // If a bind was already initiated we dont really
                    // need to do anything. The pending install
                    // will be processed later on.
                    if (!mBound) {
                    
                        //连接DefaultContainerService,它负责具体的copy操作
                        if (!connectToService()) {
                            Slog.e(TAG, "Failed to bind to media container service");
                            params.serviceError();
                           
                            return;
                        } else {
                            // Once we bind to the service, the first
                            // pending request will be processed.
                            mPendingInstalls.add(idx, params);
                        }
                    } else {
                        mPendingInstalls.add(idx, params);
                        // Already bound to the service. Just make
                        // sure we trigger off processing the first request.
                        if (idx == 0) {
                            mHandler.sendEmptyMessage(MCS_BOUND);
                        }
                    }
                    break;
                }
                case MCS_BOUND: {
              
                    if (msg.obj != null) {
                        mContainerService = (IMediaContainerService) msg.obj;
                    
                    }
                    if (mContainerService == null) {
                       //... 省略不重要的代码
                    } else if (mPendingInstalls.size() > 0) {
                        HandlerParams params = mPendingInstalls.get(0);
                        if (params != null) {
                            
                            //(2)执行copy操作
                            if (params.startCopy()) {
                            

                                if (mPendingInstalls.size() > 0) {
                                    mPendingInstalls.remove(0);
                                }
                                if (mPendingInstalls.size() == 0) { //空闲时,解绑mContainerService
                                    if (mBound) {
                                        
                                        removeMessages(MCS_UNBIND);
                                        Message ubmsg = obtainMessage(MCS_UNBIND);
                                    
                                        sendMessageDelayed(ubmsg, 10000);
                                    }
                                } else {
                                  
                                    mHandler.sendEmptyMessage(MCS_BOUND);
                                }
                            }
             
                        }
                    } 
                    break;
                }

  • INIT_COPY 的主要作用是connectToService()连接DefaultContainerService,copyapk的操作是借助DefaultContainerService来完成的。connectToService成功之后,会发送MCS_BOUND消息
  • MCS_BOUND 消息主要负责完成apk的拷贝 以及apk中so文件的提取
private abstract class HandlerParams {

    final boolean startCopy() {
        boolean res;
        try {
            //...省略不重要的信息

            //(1)开始copy
            handleStartCopy();
            res = true;
            
        } catch (RemoteException e) {
            if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
            mHandler.sendEmptyMessage(MCS_RECONNECT);
            res = false;
        }
        //(2) copy完成后的操作
        handleReturnCode();
        return res;
    }
}

startCopy 主要做了两件事情:

  • handleStartCopy() 完成拷贝apk的过程
  • handleReturnCode() 拷贝之后的后续操作

handleStartCopy 主要生成了一个InstallArgs对象,然后调用copyApk方法

 public void handleStartCopy(){
    final InstallArgs args = createInstallArgs(this);
    ret = args.copyApk(mContainerService, false);
 }

copyApk->doCopyApk


 private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {


        //(1) copy文件到安装目录:系统app 安装到/system/app/ 普通app安装到/data/app
        int ret = PackageManager.INSTALL_SUCCEEDED;
        ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
        if (ret != PackageManager.INSTALL_SUCCEEDED) {
            Slog.e(TAG, "Failed to copy package");
            return ret;
        }


        //(2) 将apk中的so文件复制出来,保存到安装目录。如:/datap/app/lib/
         final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
            NativeLibraryHelper.Handle handle = null;
            try {
                handle = NativeLibraryHelper.Handle.create(codeFile);
                ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
                        abiOverride);
            } catch (IOException e) {
                Slog.e(TAG, "Copying native libraries failed", e);
                ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
            } finally {
                IoUtils.closeQuietly(handle);
            }
 }

我们再看看拷贝完apk之后调用的handleReturnCode()

void handleReturnCode() {
            // If mArgs is null, then MCS couldn't be reached. When it
            // reconnects, it will try again to install. At that point, this
            // will succeed.
            if (mArgs != null) {
                processPendingInstall(mArgs, mRet);
            }
}   

processPendingInstall 完成后续的安装任务

   private void processPendingInstall(final InstallArgs args, final int currentStatus) {
        // Queue up an async operation since the package installation may take a little while.
        mHandler.post(new Runnable() {
            public void run() {
                PackageInstalledInfo res = new PackageInstalledInfo();
                res.setReturnCode(currentStatus);
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                            args.doPreInstall(res.returnCode);
                            synchronized (mInstallLock) {
                                installPackageTracedLI(args, res);
                            }
                            args.doPostInstall(res.returnCode, res.uid);
                }
                //...省略不重要的代码
            }
    }

processPendingInstall->installPackageTracedLI->installPackageLI

installPackageLI 是安装的核心操作

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {

                    //(1)利用PackageParser解析apk
                    final PackageParser.Package pkg;
                    try {
                        pkg = pp.parsePackage(tmpPackageFile, parseFlags);
                        DexMetadataHelper.validatePackageDexMetadata(pkg);
                    } catch (PackageParserException e) {
                        res.setError("Failed parse during installPackageLI", e);
                        return;
                    } finally {
                        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                    }

                    //(2) 执行installNewPackageLIF 或者replacePackageLIF() 
                    try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
                            "installPackageLI")) {
                        if (replace) {
                            if (pkg.applicationInfo.isStaticSharedLibrary()) {
                                // Static libs have a synthetic package name containing the version
                                // and cannot be updated as an update would get a new package name,
                                // unless this is the exact same version code which is useful for
                                // development.
                                PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
                                if (existingPkg != null &&
                                        existingPkg.getLongVersionCode() != pkg.getLongVersionCode()) {
                                    res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
                                            + "static-shared libs cannot be updated");
                                    return;
                                }
                            }
                            replacePackageLIF(pkg, parseFlags, scanFlags, args.user,
                                    installerPackageName, res, args.installReason);
                        } else {
                            installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                                    args.user, installerPackageName, volumeUuid, res, args.installReason);
                        }
                    }


                    //(3) 对apk就行dexopt优化
                    mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,null /* instructionSets */,getOrCreateCompilerPackageStats(pkg),

      }  
  • (1) 利用PackageParser解析该apk
  • (2) 如果apk第一安装,则执行installNewPackageLIF 否则执行替换操作replacePackageLIF() ,我们主要分析首次安装的操作。
  • (3) 对apk就行dexopt优化

我们重点看下installNewPackageLIF 做了哪些事情:


      private void installNewPackageLIF(PackageParser.Package pkg, final @ParseFlags int parseFlags,
        final @ScanFlags int scanFlags, UserHandle user, String installerPackageName,
        String volumeUuid, PackageInstalledInfo res, int installReason) {

            try {

                //此处仅保留了核心的代码,省略了不重要的代码

                //(1)扫描package,向PackageManagerService中注册该package
                PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags,
                        System.currentTimeMillis(), user);

          
                updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);

                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {

                    //(2)创建/data/data/包名  app数据目录
                    prepareAppDataAfterInstallLIF(newPackage);

                } else {
                    // Remove package from internal structures, but keep around any
                    // data that might have already existed
                    deletePackageLIF(pkgName, UserHandle.ALL, false, null,
                            PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
                }
            } catch (PackageManagerException e) {
                res.setError("Package couldn't be installed in " + pkg.codePath, e);
            }



        }

重要的操作由两个:scanPackageTracedLI 和prepareAppDataAfterInstallLIF()

  • scanPackageTracedLI

scanPackageTracedLI()最终会调用commitPackageSettings()

scanPackageTracedLI->scanPackageNewLI->commitScanResultsLocked-> commitPackageSettings

commitPackageSettings 是真正完成向PackageManagerService注册pkg和四大组件的方法,调用此方法之后,Android系统就可以查询该apk的信息了。


 /**    
     * 该方法是 将pkg和四大组件 注册到PackageManagerService的地方
     * Adds a scanned package to the system. When this method is finished, the package will
     * be available for query, resolution, etc...
     */
private void commitPackageSettings(PackageParser.Package pkg,
            @Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting, UserHandle user,
            final @ScanFlags int scanFlags, boolean chatty) {


        synchronized (mPackages) {


            // Add the new setting to mSettings
            //将pkg 插入到mSettings
            mSettings.insertPackageSettingLPw(pkgSetting, pkg);
            // Add the new setting to mPackages
            //将当前包pkg 插入到mPackages
            mPackages.put(pkg.applicationInfo.packageName, pkg);
            // Make sure we don't accidentally delete its data.
            final Iterator iter = mSettings.mPackagesToBeCleaned.iterator();
            while (iter.hasNext()) {
                PackageCleanItem item = iter.next();
                if (pkgName.equals(item.packageName)) {
                    iter.remove();
                }
            }

      

            int N = pkg.providers.size();
            StringBuilder r = null;
            int i;
            for (i=0; i
  • prepareAppDataAfterInstallLIF

我们再看prepareAppDataAfterInstallLIF,经过多次调用,最终进入到prepareAppDataLeafLIF

prepareAppDataAfterInstallLIF->prepareAppDataLIF->prepareAppDataLeafLIF

prepareAppDataLeafLIF方法内,利用mInstaller完成了app用户数据目录的创建.

private void prepareAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {

    //...省略不重要的代码

    try {
        ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
                appId, seInfo, app.targetSdkVersion);
    } catch (InstallerException e) {

    }
}
  • handlePackagePostInstall

执行完上述安装操作之后,会发送POST_INSTALL 消息,进入到handlePackagePostInstall方法

handlePackagePostInstall 会发送ACTION_PACKAGE_ADDED广播给监听者。

 private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
            boolean killApp, boolean virtualPreload, String[] grantedPermissions,
            boolean launchedForRestore, String installerPackage,
            IPackageInstallObserver2 installObserver) {

            sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                        extras, 0 /*flags*/,
                        null /*targetPackage*/, null /*finishedReceiver*/,
                        updateUserIds, instantUserIds);

}

至此一个新apk的安装完成。

3.3、Android 6.0 下的完整调用流程

下面 Android 6.0 源码 apk安装的完成调用过程,虽然最新的android sdk一些方法已经发生了变化,但是多少也可以做一些参考。

├── PMS.installPackage()
    └── PMS.installPackageAsUser()
         |传递 InstallParams 参数
        PackageHandler.doHandleMessage().INIT_COPY
         |
        PackageHandler.doHandleMessage().MCS_BOUND
         ├── HandlerParams.startCopy()
         │    ├── InstallParams.handleStartCopy()
         │    │    └──InstallArgs.copyApk()
         │    └── InstallParams.handleReturnCode()
         │         └── PMS.processPendingInstall()
         │              ├── InstallArgs.doPreInstall()
         │              ├── PMS.installPackageLI()
         │              │    ├── PackageParser.parsePackage()
         │              │    ├── PackageParser.collectCertificates()
         │              │    ├── PackageParser.collectManifestDigest()
         │              │    ├── PackageDexOptimizer.performDexOpt()
         │              │    ├── InstallArgs.doRename()
         │              │    │    └── InstallArgs.getNextCodePath()
         │              │    ├── replacePackageLI()
         │              │    │    ├── shouldCheckUpgradeKeySetLP()
         │              │    │    ├── compareSignatures()
         │              │    │    ├── replaceSystemPackageLI()
         │              │    │    │    ├── killApplication()
         │              │    │    │    ├── removePackageLI()
         │              │    │    │    ├── Settings.disableSystemPackageLPw()
         │              │    │    │    ├── createInstallArgsForExisting()
         │              │    │    │    ├── deleteCodeCacheDirsLI()
         │              │    │    │    ├── scanPackageLI()
         │              │    │    │    └── updateSettingsLI()
         │              │    │    └── replaceNonSystemPackageLI()
         │              │    │         ├── deletePackageLI()
         │              │    │         ├── deleteCodeCacheDirsLI()
         │              │    │         ├── scanPackageLI()
         │              │    │         └── updateSettingsLI()
         │              │    └── installNewPackageLI()
         │              │         ├── scanPackageLI()
         │              │         └── updateSettingsLI()
         │              ├── InstallArgs.doPostInstall()
         │              ├── BackupManager.restoreAtInstall()
         │              └── sendMessage(POST_INSTALL)
         │                   |
         │                  PackageHandler.doHandleMessage().POST_INSTALL
         │                   ├── grantRequestedRuntimePermissions()
         │                   ├── sendPackageBroadcast()
         │                   └── IPackageInstallObserver.onPackageInstalled()
         └── PackageHandler.doHandleMessage().MCS_UNBIND
              └── PackageHandler.disconnectService()![image](https://upload-images.jianshu.io/upload_images/5713484-36bddaa4264c58d4.png)

四、参考文章

https://www.jianshu.com/p/f47e45602ad2

https://www.jianshu.com/p/953475cea991

你可能感兴趣的:(Android apk安装过程分析)