Android Q应用安装流程分析

1、adb install xxx

会响应到adb_commandline,本文重点讲解单个app安装install_app

// system/core/adb/client/commandline.cpp
int adb_commandline(int argc, const char** argv) {
    ...
    } else if (!strcmp(argv[0], "install")) {
        if (argc < 2) error_exit("install requires an argument");
        return install_app(argc, argv); // 安装单个应用,本文重点讲解
    } else if (!strcmp(argv[0], "install-multiple")) {
        if (argc < 2) error_exit("install-multiple requires an argument");
        return install_multiple_app(argc, argv);
    } else if (!strcmp(argv[0], "install-multi-package")) {
        if (argc < 3) error_exit("install-multi-package requires an argument");
        return install_multi_package(argc, argv);
    } else if (!strcmp(argv[0], "uninstall")) {
        if (argc < 2) error_exit("uninstall requires an argument");
        return uninstall_app(argc, argv);
    }
    ...
}
// system/core/adb/client/adb_install.cpp
int install_app(int argc, const char** argv) {
    ...
    return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
                                      use_fastdeploy, use_localagent);
    ...
}
static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy,
                              bool use_localagent) {
    ...
    result = pm_command(argc, argv);
    ...
}
static int pm_command(int argc, const char** argv) {
    std::string cmd = "pm";

    while (argc-- > 0) {
        cmd += " " + escape_arg(*argv++);
    }
    return send_shell_command(cmd);
}
// system/core/adb/client/commandline.cpp
int send_shell_command(const std::string& command, bool disable_shell_protocol,
                       StandardStreamsCallbackInterface* callback) {
    // 这块较为繁琐,原理是是通过socket传递到frameworks/native/cmds/cmd/cmd.cpp中
}
// frameworks/native/cmds/cmd/cmd.cpp
int cmdMain(const std::vector<std::string_view>& argv, TextOutput& outputLog, TextOutput& errorLog,
            int in, int out, int err, RunMode runMode) {
    ...
    String16 serviceName = String16(cmd.data(), cmd.size());
    for (int i = 1; i < argc; i++) {
        args.add(String16(argv[i].data(), argv[i].size()));
    }
    sp<IBinder> service = sm->checkService(serviceName);
    ...
    status_t error = IBinder::shellCommand(service, in, out, err, args, cb, result);
    ...
}
// frameworks/base/core/java/android/os/Binder.java
public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
            @Nullable FileDescriptor err,
            @NonNull String[] args, @Nullable ShellCallback callback,
            @NonNull ResultReceiver resultReceiver) throws RemoteException {
    // 这里会根据不同服务传递到不服务对应的onShellCommand接口中
    onShellCommand(in, out, err, args, callback, resultReceiver);
}
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public void onShellCommand(FileDescriptor in, FileDescriptor out,
            FileDescriptor err, String[] args, ShellCallback callback,
            ResultReceiver resultReceiver) {
    (new PackageManagerShellCommand(this)).exec(
            this, in, out, err, args, callback, resultReceiver);
}
public int onCommand(String cmd) {
    ...
    case "install":
        return runInstall();
    ...
}
private int runInstall() throws RemoteException {
    ...
    // 创建session
    final int sessionId = doCreateSession(params.sessionParams,
        params.installerPackageName, params.userId);
    ...
    // 把apk信息写入到sessionID对应的session中
    if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, splitName,
        false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
        return 1;
    }
    // 提交session,执行安装
    if (doCommitSession(sessionId, false /*logSuccess*/)
        != PackageInstaller.STATUS_SUCCESS) {
        return 1;
    }
    ...
}
private int doCommitSession(int sessionId, boolean logSuccess)
            throws RemoteException {
    ...
    session.commit(receiver.getIntentSender());
    ...
}
// frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
    ...
    mHandler.obtainMessage(MSG_COMMIT).sendToTarget();// handler到handleCommit
}
private void handleCommit() {
    ...
    commitNonStagedLocked(childSessions);
    ...
}
private void commitNonStagedLocked(List<PackageInstallerSession> childSessions)
            throws PackageManagerException {
    ...
    mPm.installStage(committingSession);
    ...
}
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
void installStage(ActiveInstallSession activeInstallSession) {
    ...
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    ...
    mHandler.sendMessage(msg); // handler到HandlerParams.startCopy
}

// InstallParams extends HandlerParams
// InstallParams单个应用安装
private abstract class HandlerParams {
    final void startCopy() {
        handleStartCopy(); // 最重要的工作就是计算应用安装的位置
        handleReturnCode();
    }
}
class InstallParams extends HandlerParams {
    void handleStartCopy() {
        ...
        final InstallArgs args = createInstallArgs(this);
        ...
    }
    void handleReturnCode() {
        ...
        // FileInstallArgs.copyApk
        //   FileInstallArgs.doCopyApk
        //     PackageManagerServiceUtils.copyPackage
        //       PackageParser.parsePackageLite
        //       copyFile(pkg.baseCodePath, targetDir, "base.apk");
        mRet = mArgs.copyApk();
        processPendingInstall(mArgs, mRet);
    }
}
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)));
    }
}
private void processInstallRequestsAsync(boolean success,
            List<InstallRequest> installRequests) {
    ...
    request.args.doPreInstall(request.installResult.returnCode); // 准备工作
    ...
    installPackagesTracedLI(installRequests);
    ...
    request.args.doPostInstall(...); // 清理工作
    ...
    restoreAndPostInstall(...); // 后续工作(比如发送应用变化相关的广播)
}
private void installPackagesTracedLI(List<InstallRequest> requests) {
    ...
    //  -> preparePackageLI
    //  -> scanPackageTracedLI
    //  -> reconcilePackagesLocked
    //  -> commitPackagesLocked
    //  -> executePostCommitSteps
    installPackagesLI(requests);// 具体安装过程
    ...
}

2、preparePackageLI

1、准备scanFlags

SCAN_NEW_INSTALL
SCAN_UPDATE_SIGNATURE
SCAN_INITIAL(已经初始化过, args.move != null)
SCAN_DONT_KILL_APP(不需要杀掉, (installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0)
SCAN_AS_INSTANT_APP(instantApp)
SCAN_AS_FULL_APP(fullApp)
SCAN_AS_VIRTUAL_PRELOAD(虚拟预加载, virtualPreload)

2、instantApp不能安装在外置存储

if (instantApp && onExternal) throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);

3、解析Package

pkg = pp.parsePackage(tmpPackageFile, parseFlags);

4、instantApp校验

instantApp只能在android O及以上使用

5、重命名库

renameStaticSharedLibraryPackage
名字上添加版本号,方便多版本共存

6、替换安装检测

检测是否安装过(mOriginalPackages和非mOriginalPackages)
有父包禁止安装
检查targetSDK
禁止PERSISTENT 非 stage安装

 synchronized (mPackages) {
    // Check if installing already existing package
    ...
}

7、与老版本签名比对

PackageSetting ps = mSettings.mPackages.get(pkgName);
    if (ps != null) {
        if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
    }
    ...
}

8、权限处理

非系统应用移除PROTECTION_FLAG_INSTANT
替换安装有升级Key,验证升级Key(checkUpgradeKeySetLocked)
替换安装没有升级Key,匹配证书(checkCapability)

9、系统应用不支持instantApp和安装在外置存储

10、abi处理

move:更新primaryCpuAbi 和 secondaryCpuAbi
非Move:derivePackageAbi

11、覆盖安装

if (replace) {
    targetVolumeUuid = null;
    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.
    ...

静态共享库禁止同版本升级
验证签名(checkUpgradeKeySetLocked、checkCapability)
升级限制验证(升级哈希匹配才能升级)
检查shared user id
禁止 instant app替换full app
更新后被移除的子包
系统包设置FLAG_UPDATED_SYSTEM_APP

12、新安装

检查系统是否已存在

3、scanPackageTracedLI

主要用于生成PackageSetting数据结构。
执行完scanPackageTrackLI之后Pms的两大核心数据结构都已经准备好了:

final ArrayMap<String, PackageParser.Package> mPackages = new ArrayMap<>()
mSettings.mPackages的PackageSetting

PackageParser.Package为静态数据,扫描完成后就不会发生变化。PackageSetting用于存储安装应用的动态数据,如权限授予情况等。PackageParser.Package由于是静态数据,扫描apk就可以获取。PackageSetting生成之后会被记录到文件中,以后每次系统启动都会重新加载。

4、reconcilePackagesLocked

生成PackageSetting和PackageParser.Package数据结构后,还需要对多个安装apk结果进行核对,这就是reconcilePackagesLocked函数。

1、复制现有包以便与传入包合并(combinedPackages.putAll(request.allPackages))
2、用传入包替换现有包(combinedPackages.put(scanResult.pkgSetting.name, scanResult.request.pkg))
3、根据版本号添加静态共享库(addSharedLibraryToPackageVersionMap)
4、删除非系统应用的现有包(mayDeletePackageLocked)
5、验证签名(checkUpgradeKeySetLocked、verifySignatures)

5、commitPackagesLocked

经过上述几个步骤,两个核心数据结构虽然已经生成,但是并没有添加到容器中去(PackageManagerService.mPackages 和 PackageManagerService.mSettings.mPackage), 所以该包里面的组件等还不能查询到,也不能启动。 所以需要commitPackagesLocked来进行提交, 提交之后该应用就算完整发布了。

1、覆盖安装

1、设置安装和更新时间(setInstallAndUpdateTime)
2、系统应用-移除现有包(removePackageLI)
3、系统应用-记录现有包需要删除的覆盖安装的应用
4、系统应用-处理子包
4、非系统应用-删除包(executeDeletePackageLIF)

2、commitReconciledScanResultLocked

1、shared user改变,移除old shared user
2、PackageSetting更新
3、更新共享库(executeSharedLibrariesUpdateLPr)
4、更新签名信息(pkgSetting.signatures.mSigningDetails)
5、采用另一个package的权限所有权(mSettings.mPermissions.transferPermissions)
6、更新AbiCodePath(mInstaller.rmdex)
7、commitPackageSettings

3、commitPackageSettings

1、更新静态共享库(commitSharedLibraryInfoLocked、updateSharedLibrariesLocked)
2、更新库时杀掉依赖程序(killApplication)
3、添加组件、权限组、权限(addAllComponents、addAllPermissionGroups、addAllPermissions)
4、更新mInstrumentation
5、更新mProtectedBroadcasts
6、撤销需要撤销的权限(revokeRuntimePermissionsIfGroupChanged)

6、executePostCommitSteps

1、准备APP数据(prepareAppDataAfterInstallLIF)
2、notifyPackageUpdated
3、prepareAppProfiles
4、编译布局资源(compileLayouts)
5、执行Dex优化(performDexOpt)

7、restoreAndPostInstall

1、备份恢复(restoreAtInstallForUser)
2、使用rollbackMnaager恢复数据(snapshotAndRestoreUserData)
3、安装成功(POST_INSTALL, 没有doRestore)

8、handlePackagePostInstall

1、发送移除广播(sendPackageRemovedBroadcasts)
2、设置白名单限制权限(setWhitelistedRestrictedPermissions)
3、运行时权限(grantRequestedRuntimePermissions、grantRuntimePermissionsGrantedToDisabledPackage)
4、ACTION_PACKAGE_ADDED、ACTION_PACKAGE_REPLACED广播

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