应用安装(三)- 系统实现apk安装整体流程

系统源码参考:android 11。

系统侧实现apk安装,主要通过PakcageManagerService来完成,安装过程主要分为复制apk和安装apk两个阶段,本篇文章现在针对整体安装流程进行梳理,中间牵扯到的重要模块之后单独梳理。

一、复制apk流程解析

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

void installStage(ActiveInstallSession activeInstallSession) {
    if (DEBUG_INSTANT) {
        if ((activeInstallSession.getSessionParams().installFlags
                & PackageManager.INSTALL_INSTANT_APP) != 0) {
            Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName());
       }
    }

    final Message msg = mHandler.obtainMessage(INIT_COPY);
   //初始化InstallParams,后续会由他来主导安装任务
   final InstallParams params = new InstallParams(activeInstallSession);
   params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
   msg.obj = params;
   Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
           System.identityHashCode(msg.obj));
   Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
           System.identityHashCode(msg.obj));
  //发送INIT_COPY消息
   mHandler.sendMessage(msg);
}

这个方法主要干了两件事:

  • 初始化InstallParams
  • 发送INIT_COPY消息

初始化InstallParams,在构造方法中:

InstallParams(ActiveInstallSession activeInstallSession) {
    super(activeInstallSession.getUser());
   final PackageInstaller.SessionParams sessionParams =
            activeInstallSession.getSessionParams();
   if (DEBUG_INSTANT) {
        if ((sessionParams.installFlags
                & PackageManager.INSTALL_INSTANT_APP) != 0) {
            Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName());
       }
    }
    verificationInfo = new VerificationInfo(
            sessionParams.originatingUri,
           sessionParams.referrerUri,
           sessionParams.originatingUid,
           activeInstallSession.getInstallerUid());
   origin = OriginInfo.fromStagedFile(activeInstallSession.getStagedDir());
   move = null;
   installReason = fixUpInstallReason(
            activeInstallSession.getInstallSource().installerPackageName,
           activeInstallSession.getInstallerUid(),
           sessionParams.installReason);
   observer = activeInstallSession.getObserver();
   installFlags = sessionParams.installFlags;
   installSource = activeInstallSession.getInstallSource();
   volumeUuid = sessionParams.volumeUuid;
   packageAbiOverride = sessionParams.abiOverride;
   grantedRuntimePermissions = sessionParams.grantedRuntimePermissions;
   whitelistedRestrictedPermissions = sessionParams.whitelistedRestrictedPermissions;
   autoRevokePermissionsMode = sessionParams.autoRevokePermissionsMode;
   signingDetails = activeInstallSession.getSigningDetails();
   requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
   forceQueryableOverride = sessionParams.forceQueryableOverride;
   mDataLoaderType = (sessionParams.dataLoaderParams != null)
            ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
   mSessionId = activeInstallSession.getSessionId();
}

这里很明显,之前PackageInstaller将apk写入session,这里通过session获取到安装包信息,并赋值给InstallParams,同时这里额外初始化了一个VerificationInfo验证信息类。

再来看发送INIT_COPY消息:

case INIT_COPY: {
    HandlerParams params = (HandlerParams) msg.obj;
   if (params != null) {
        if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
       Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
               System.identityHashCode(params));
       Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
       params.startCopy();
       Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
   }
    break;
}

这里的HandlerParams是抽象基类,具体实现是前面传入的InstallParams,由它来执行startCopy

final void startCopy() {
    if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
   handleStartCopy(); 
   handleReturnCode();
}

startCopy分了两步,一个个来看。

/*
* Invoke remote method to get package information and install
* location values. Override install location based on default
* policy if needed and then create install arguments based
* on the install location.
*/
public void handleStartCopy() {
    int ret = PackageManager.INSTALL_SUCCEEDED;
   // If we're already staged, we've firmly committed to an install location
   if (origin.staged) {
        if (origin.file != null) {
            installFlags |= PackageManager.INSTALL_INTERNAL;
       } else {
            throw new IllegalStateException("Invalid stage location");
       }
    }
    final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
   final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
   PackageInfoLite pkgLite = null;
    //通过PackageParser.parsePackageLite解析一个精简版的包信息
   pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
           origin.resolvedPath, installFlags, packageAbiOverride);
   if (DEBUG_INSTANT && ephemeral) {
        Slog.v(TAG, "pkgLite for install: " + pkgLite);
   }
    /*
    * If we have too little free space, try to free cache
    * before giving up.
    */
    //检查剩余的磁盘空间
        //如果是通过ActiveInstallSession 初始化的InstallParams,origin.staged为true
   if (!origin.staged && pkgLite.recommendedInstallLocation
            == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
        // TODO: focus freeing disk space on the target device
       final StorageManager storage = StorageManager.from(mContext);
       final long lowThreshold = storage.getStorageLowBytes(
                Environment.getDataDirectory());
       final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
                origin.resolvedPath, packageAbiOverride);
       if (sizeBytes >= 0) {
            try {
                mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
               pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                       origin.resolvedPath, installFlags, packageAbiOverride);
           } catch (InstallerException e) {
                Slog.w(TAG, "Failed to free cache", e);
           }
        }
        /*
        * The cache free must have deleted the file we downloaded to install.
        *
        * TODO: fix the "freeCache" call to not delete the file we care about.
        */
       if (pkgLite.recommendedInstallLocation
                == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
            pkgLite.recommendedInstallLocation
                    = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
       }
    }
    if (ret == PackageManager.INSTALL_SUCCEEDED) {
        int loc = pkgLite.recommendedInstallLocation;
       if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
            ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
       } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
            ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
       } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
            ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
       } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
            ret = PackageManager.INSTALL_FAILED_INVALID_APK;
       } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
            ret = PackageManager.INSTALL_FAILED_INVALID_URI;
       } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
            ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
       } else {
            // Override with defaults if needed.
           //确定推荐安装
           loc = installLocationPolicy(pkgLite);
           if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
                ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
           } else if (loc == PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION) {
                ret = PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
           } else if (!onInt) {
                // Override install location with flags
               if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
                    // Set the flag to install on external media.
                   installFlags &= ~PackageManager.INSTALL_INTERNAL;
               } else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
                    if (DEBUG_INSTANT) {
                        Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
                   }
                    installFlags |= PackageManager.INSTALL_INSTANT_APP;
                   installFlags &= ~PackageManager.INSTALL_INTERNAL;
               } else {
                    // Make sure the flag for installing on external
                   // media is unset
                   installFlags |= PackageManager.INSTALL_INTERNAL;
               }
            }
        }
    }

    //创建InstallArgs
    final InstallArgs args = createInstallArgs(this);
   mVerificationCompleted = true;
   mIntegrityVerificationCompleted = true;
   mEnableRollbackCompleted = true;
   mArgs = args;
   if (ret == PackageManager.INSTALL_SUCCEEDED) {
        final int verificationId = mPendingVerificationToken++;
       // Perform package verification (unless we are simply moving the package).
       if (!origin.existing) {
            PackageVerificationState verificationState =
                    new PackageVerificationState(this);
           mPendingVerification.append(verificationId, verificationState);
            //发起安装包完整性校验
           sendIntegrityVerificationRequest(verificationId, pkgLite, verificationState);
           ret = sendPackageVerificationRequest(
                    verificationId, pkgLite, verificationState);
           // If both verifications are skipped, we should remove the state.
           if (verificationState.areAllVerificationsComplete()) {
                mPendingVerification.remove(verificationId);
           }
        }
  ...
    mRet = ret;

}

整个方法都是基于PackageManagerServiceUtils.getMinimalPackageInfo发起的,根据对包解析的情况,来决定如下事情的处理:

  • 是否有足够的磁盘空间,不够则清理;
  • 确定推荐安装位置
  • 初始化InstallArgs来做后续的copy apk任务;
  • 发起包的完整性校验;

然后接着走handleReturnCode方法

void handleReturnCode() {
    //校验通过
    if (mVerificationCompleted
            && mIntegrityVerificationCompleted && mEnableRollbackCompleted) {
        if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
            String packageName = "";
           ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
                    new ParseTypeImpl(
                            (changeId, packageName1, targetSdkVersion) -> {
                                ApplicationInfo appInfo = new ApplicationInfo();
                               appInfo.packageName = packageName1;
                               appInfo.targetSdkVersion = targetSdkVersion;
                               return mPackageParserCallback.isChangeEnabled(changeId,
                                       appInfo);
                           }).reset(),
                   origin.file, 0);
           if (result.isError()) {
                Slog.e(TAG, "Can't parse package at " + origin.file.getAbsolutePath(),
                       result.getException());
           } else {
                packageName = result.getResult().packageName;
           }
            try {
                observer.onPackageInstalled(packageName, mRet, "Dry run", new Bundle());
           } catch (RemoteException e) {
                Slog.i(TAG, "Observer no longer exists.");
           }
            return;
       }
        if (mRet == PackageManager.INSTALL_SUCCEEDED) {
            mRet = mArgs.copyApk();
       }
        processPendingInstall(mArgs, mRet);
   }
}

校验完成,由InstallArgs发起apk copy操作。之前在初始化的时候会根据是否已经安装了当前应用来初始化不同的实现类:

private InstallArgs createInstallArgs(InstallParams params) {
    if (params.move != null) {
               //处理已安装的应用程序   
        return new MoveInstallArgs(params);
   } else {
               //处理新安装的应用程序
        return new FileInstallArgs(params);
   }
}

而后续发起的copyApk流程最终结果是将apk和lib copy到确认的安装目录下

void handleReturnCode() {
   ...
        if (mRet == PackageManager.INSTALL_SUCCEEDED) {
            mRet = mArgs.copyApk();
       }
        processPendingInstall(mArgs, mRet);
   }
}
/data/app/com.miui.packageinstaller-Qp2hRWM-6O9lWcKH-GT-dw== # ls -al
-rw-r--r--  1 system system  6385298 2021-04-28 07:44 base.apk. //apk
drwxr-xr-x  3 system system     4096 2021-04-28 07:44 lib     //native lib

整个流程粗略看起来比较简单,但是其中有几个细节值得深挖一下:确定推荐安装位置、包完整性校验。这个后续有时间再单独分析。

二、安装apk流程解析

g

handleReturnCode最终执行processPendingInstall发起安装:

private void processPendingInstall(final InstallArgs args, final int currentStatus) {
   if (args.mMultiPackageInstallParams != null) {
       args.mMultiPackageInstallParams.tryProcessInstallRequest(args, currentStatus);
   } else {
       PackageInstalledInfo res = createPackageInstalledInfo(currentStatus);
       processInstallRequestsAsync(
               res.returnCode == PackageManager.INSTALL_SUCCEEDED,
               Collections.singletonList(new InstallRequest(args, res)));
   }
}

多包和单包安装,最终都会走到processInstallRequestsAsync方法

private void processInstallRequestsAsync(boolean success,
       List<InstallRequest> installRequests) {
   mHandler.post(() -> {
       if (success) {
           for (InstallRequest request : installRequests) {
                //安装前处理
               request.args.doPreInstall(request.installResult.returnCode);
           }
           synchronized (mInstallLock) {
                 //发起安装
               installPackagesTracedLI(installRequests);
           }
           for (InstallRequest request : installRequests) {
                //安装后处理
               request.args.doPostInstall(
                       request.installResult.returnCode, request.installResult.uid);
           }
       }
       for (InstallRequest request : installRequests) {
           restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
                   new PostInstallData(request.args, request.installResult, null));
       }
   });
}

这里分了三步:对安装前、中、后分别做了处理。这里args对应的是FileInstallArgs。

2.1 安装前后处理工作

FileInstallArgs

int doPreInstall(int status) {
    if (status != PackageManager.INSTALL_SUCCEEDED) {
        cleanUp();
   }
    return status;
}

int doPostInstall(int status, int uid) {
    if (status != PackageManager.INSTALL_SUCCEEDED) {
        cleanUp();
   }
    return status;
}

安装前后,都会判断是否安装成功,如果不成功,走相同清理流程。clearup就是对安装相关目录文件进行清理。

2.2 发起安装流程

installPackagesTracedLI直接调用installPackagesLI来处理安装:

/**
* Installs one or more packages atomically. This operation is broken up into four phases:
* <ul>
*     <li><b>Prepare</b>
*         <br/>Analyzes any current install state, parses the package and does initial
*         validation on it.</li>
*     <li><b>Scan</b>
*         <br/>Interrogates the parsed packages given the context collected in prepare.</li>
*     <li><b>Reconcile</b>
*         <br/>Validates scanned packages in the context of each other and the current system
*         state to ensure that the install will be successful.
*     <li><b>Commit</b>
*         <br/>Commits all scanned packages and updates system state. This is the only place
*         that system state may be modified in the install flow and all predictable errors
*         must be determined before this phase.</li>
* </ul>
*
* Failure at any phase will result in a full failure to install all packages.
*/

@GuardedBy("mInstallLock")
private void installPackagesLI(List<InstallRequest> requests) {
   final Map<String, ScanResult> preparedScans = new ArrayMap<>(requests.size());
   final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());
   final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
   final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
   final Map<String, VersionInfo> versionInfos = new ArrayMap<>(requests.size());
   final Map<String, PackageSetting> lastStaticSharedLibSettings =
           new ArrayMap<>(requests.size());
   final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
   boolean success = false;
   try {
       Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
       for (InstallRequest request : requests) {
           // TODO(b/109941548): remove this once we've pulled everything from it and into
           //                    scan, reconcile or commit.
           final PrepareResult prepareResult;
                // 1.prepare
               prepareResult =
                       preparePackageLI(request.args, request.installResult);
          ...
                // 2.scan
               final ScanResult result = scanPackageTracedLI(
                       prepareResult.packageToScan, prepareResult.parseFlags,
                       prepareResult.scanFlags, System.currentTimeMillis(),
                       request.args.user, request.args.abiOverride);
...

       ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
               installResults,
               prepareResults,
               mSharedLibraries,
               Collections.unmodifiableMap(mPackages), versionInfos,
               lastStaticSharedLibSettings);
       CommitRequest commitRequest = null;
       synchronized (mLock) {
           Map<String, ReconciledPackage> reconciledPackages;
           try {
               Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages”);
                 // 3.reconcile
               reconciledPackages = reconcilePackagesLocked(
                       reconcileRequest, mSettings.mKeySetManagerService);
         ...
               commitRequest = new CommitRequest(reconciledPackages,
                       mUserManager.getUserIds());
                // 4.commit
               commitPackagesLocked(commitRequest);
               success = true;
           } finally {
               Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
           }
       }
                   //5.执行应用程序安装
       executePostCommitSteps(commitRequest);
  ...
}

主要工作分四个阶段:

  • prepare: 解析apk。
  • scan: 扫描apk,更新共享库和settings信息。
  • reconcile:验证扫描包状态以确保安装成功。
  • commit:提交所有扫描的包并更新系统状态。

这里后面会分别来进行分析。

最后:executePostCommitSteps 执行安装后的扫尾工作

/**
* On successful install, executes remaining steps after commit completes and the package lock
* is released. These are typically more expensive or require calls to installd, which often
* locks on {@link #mLock}.
*/
private void executePostCommitSteps(CommitRequest commitRequest) {
    final ArraySet<IncrementalStorage> incrementalStorages = new ArraySet<>();
   for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
        final boolean instantApp = ((reconciledPkg.scanResult.request.scanFlags
                        & PackageManagerService.SCAN_AS_INSTANT_APP) != 0);
       final AndroidPackage pkg = reconciledPkg.pkgSetting.pkg;
       final String packageName = pkg.getPackageName();
       final boolean onIncremental = mIncrementalManager != null
               && isIncrementalPath(pkg.getCodePath());
       …
        //createAppData
        prepareAppDataAfterInstallLIF(pkg);
      …
        //dex2oat编译
           mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
                   null /* instructionSets */,
                   getOrCreateCompilerPackageStats(pkg),
                   mDexManager.getPackageUseInfoOrDefault(packageName),
                   dexoptOptions);
           Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
       }
        // Notify BackgroundDexOptService that the package has been changed.
       // If this is an update of a package which used to fail to compile,
       // BackgroundDexOptService will remove it from its denylist.
       // TODO: Layering violation
       BackgroundDexOptService.notifyPackageChanged(packageName);
       notifyPackageChangeObserversOnUpdate(reconciledPkg);
   }
    NativeLibraryHelper.waitForNativeBinariesExtraction(incrementalStorages);
}

这里主要是通过installd干两件事:createAppData、dex2oat 编译

©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容