Android Q應(yīng)用安裝流程分析

1、adb install xxx

會(huì)響應(yīng)到adb_commandline碘赖,本文重點(diǎn)講解單個(gè)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); // 安裝單個(gè)應(yīng)用驾荣,本文重點(diǎn)講解
    } 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) {
    // 這塊較為繁瑣,原理是是通過(guò)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 {
    // 這里會(huì)根據(jù)不同服務(wù)傳遞到不服務(wù)對(duì)應(yīng)的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 {
    ...
    // 創(chuàng)建session
    final int sessionId = doCreateSession(params.sessionParams,
        params.installerPackageName, params.userId);
    ...
    // 把a(bǔ)pk信息寫入到sessionID對(duì)應(yīng)的session中
    if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, splitName,
        false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
        return 1;
    }
    // 提交session普泡,執(zhí)行安裝
    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單個(gè)應(yīng)用安裝
private abstract class HandlerParams {
    final void startCopy() {
        handleStartCopy(); // 最重要的工作就是計(jì)算應(yīng)用安裝的位置
        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); // 準(zhǔn)備工作
    ...
    installPackagesTracedLI(installRequests);
    ...
    request.args.doPostInstall(...); // 清理工作
    ...
    restoreAndPostInstall(...); // 后續(xù)工作(比如發(fā)送應(yīng)用變化相關(guān)的廣播)
}
private void installPackagesTracedLI(List<InstallRequest> requests) {
    ...
    //  -> preparePackageLI
    //  -> scanPackageTracedLI
    //  -> reconcilePackagesLocked
    //  -> commitPackagesLocked
    //  -> executePostCommitSteps
    installPackagesLI(requests);// 具體安裝過(guò)程
    ...
}

2播掷、preparePackageLI

1、準(zhǔn)備scanFlags

SCAN_NEW_INSTALL
SCAN_UPDATE_SIGNATURE
SCAN_INITIAL(已經(jīng)初始化過(guò), args.move != null)
SCAN_DONT_KILL_APP(不需要?dú)⒌? (installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0)
SCAN_AS_INSTANT_APP(instantApp)
SCAN_AS_FULL_APP(fullApp)
SCAN_AS_VIRTUAL_PRELOAD(虛擬預(yù)加載, virtualPreload)

2撼班、instantApp不能安裝在外置存儲(chǔ)

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

3歧匈、解析Package

pkg = pp.parsePackage(tmpPackageFile, parseFlags);

4、instantApp校驗(yàn)

instantApp只能在android O及以上使用

5砰嘁、重命名庫(kù)

renameStaticSharedLibraryPackage
名字上添加版本號(hào)件炉,方便多版本共存

6、替換安裝檢測(cè)

檢測(cè)是否安裝過(guò)(mOriginalPackages和非mOriginalPackages)
有父包禁止安裝
檢查targetSDK
禁止PERSISTENT 非 stage安裝

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

7矮湘、與老版本簽名比對(duì)

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

8妻率、權(quán)限處理

非系統(tǒng)應(yīng)用移除PROTECTION_FLAG_INSTANT
替換安裝有升級(jí)Key,驗(yàn)證升級(jí)Key(checkUpgradeKeySetLocked)
替換安裝沒(méi)有升級(jí)Key板祝,匹配證書(checkCapability)

9、系統(tǒng)應(yīng)用不支持instantApp和安裝在外置存儲(chǔ)

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.
    ...

靜態(tài)共享庫(kù)禁止同版本升級(jí)
驗(yàn)證簽名(checkUpgradeKeySetLocked、checkCapability)
升級(jí)限制驗(yàn)證(升級(jí)哈希匹配才能升級(jí))
檢查shared user id
禁止 instant app替換full app
更新后被移除的子包
系統(tǒng)包設(shè)置FLAG_UPDATED_SYSTEM_APP

12伏伯、新安裝

檢查系統(tǒng)是否已存在

3橘洞、scanPackageTracedLI

主要用于生成PackageSetting數(shù)據(jù)結(jié)構(gòu)。
執(zhí)行完scanPackageTrackLI之后Pms的兩大核心數(shù)據(jù)結(jié)構(gòu)都已經(jīng)準(zhǔn)備好了:

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

PackageParser.Package為靜態(tài)數(shù)據(jù)说搅,掃描完成后就不會(huì)發(fā)生變化炸枣。PackageSetting用于存儲(chǔ)安裝應(yīng)用的動(dòng)態(tài)數(shù)據(jù),如權(quán)限授予情況等弄唧。PackageParser.Package由于是靜態(tài)數(shù)據(jù)适肠,掃描apk就可以獲取。PackageSetting生成之后會(huì)被記錄到文件中候引,以后每次系統(tǒng)啟動(dòng)都會(huì)重新加載侯养。

4、reconcilePackagesLocked

生成PackageSetting和PackageParser.Package數(shù)據(jù)結(jié)構(gòu)后澄干,還需要對(duì)多個(gè)安裝apk結(jié)果進(jìn)行核對(duì)逛揩,這就是reconcilePackagesLocked函數(shù)柠傍。

1、復(fù)制現(xiàn)有包以便與傳入包合并(combinedPackages.putAll(request.allPackages))
2辩稽、用傳入包替換現(xiàn)有包(combinedPackages.put(scanResult.pkgSetting.name, scanResult.request.pkg))
3惧笛、根據(jù)版本號(hào)添加靜態(tài)共享庫(kù)(addSharedLibraryToPackageVersionMap)
4、刪除非系統(tǒng)應(yīng)用的現(xiàn)有包(mayDeletePackageLocked)
5逞泄、驗(yàn)證簽名(checkUpgradeKeySetLocked患整、verifySignatures)

5、commitPackagesLocked

經(jīng)過(guò)上述幾個(gè)步驟炭懊,兩個(gè)核心數(shù)據(jù)結(jié)構(gòu)雖然已經(jīng)生成并级,但是并沒(méi)有添加到容器中去(PackageManagerService.mPackages 和 PackageManagerService.mSettings.mPackage), 所以該包里面的組件等還不能查詢到,也不能啟動(dòng)侮腹。 所以需要commitPackagesLocked來(lái)進(jìn)行提交嘲碧, 提交之后該應(yīng)用就算完整發(fā)布了。

1父阻、覆蓋安裝

1愈涩、設(shè)置安裝和更新時(shí)間(setInstallAndUpdateTime)
2、系統(tǒng)應(yīng)用-移除現(xiàn)有包(removePackageLI)
3加矛、系統(tǒng)應(yīng)用-記錄現(xiàn)有包需要?jiǎng)h除的覆蓋安裝的應(yīng)用
4履婉、系統(tǒng)應(yīng)用-處理子包
4、非系統(tǒng)應(yīng)用-刪除包(executeDeletePackageLIF)

2斟览、commitReconciledScanResultLocked

1毁腿、shared user改變,移除old shared user
2苛茂、PackageSetting更新
3已烤、更新共享庫(kù)(executeSharedLibrariesUpdateLPr)
4、更新簽名信息(pkgSetting.signatures.mSigningDetails)
5妓羊、采用另一個(gè)package的權(quán)限所有權(quán)(mSettings.mPermissions.transferPermissions)
6胯究、更新AbiCodePath(mInstaller.rmdex)
7、commitPackageSettings

3躁绸、commitPackageSettings

1裕循、更新靜態(tài)共享庫(kù)(commitSharedLibraryInfoLocked、updateSharedLibrariesLocked)
2净刮、更新庫(kù)時(shí)殺掉依賴程序(killApplication)
3剥哑、添加組件、權(quán)限組庭瑰、權(quán)限(addAllComponents星持、addAllPermissionGroups、addAllPermissions)
4弹灭、更新mInstrumentation
5督暂、更新mProtectedBroadcasts
6揪垄、撤銷需要撤銷的權(quán)限(revokeRuntimePermissionsIfGroupChanged)

6、executePostCommitSteps

1逻翁、準(zhǔn)備APP數(shù)據(jù)(prepareAppDataAfterInstallLIF)
2饥努、notifyPackageUpdated
3、prepareAppProfiles
4八回、編譯布局資源(compileLayouts)
5酷愧、執(zhí)行Dex優(yōu)化(performDexOpt)

7、restoreAndPostInstall

1缠诅、備份恢復(fù)(restoreAtInstallForUser)
2溶浴、使用rollbackMnaager恢復(fù)數(shù)據(jù)(snapshotAndRestoreUserData)
3、安裝成功(POST_INSTALL, 沒(méi)有doRestore)

8管引、handlePackagePostInstall

1士败、發(fā)送移除廣播(sendPackageRemovedBroadcasts)
2、設(shè)置白名單限制權(quán)限(setWhitelistedRestrictedPermissions)
3褥伴、運(yùn)行時(shí)權(quán)限(grantRequestedRuntimePermissions谅将、grantRuntimePermissionsGrantedToDisabledPackage)
4、ACTION_PACKAGE_ADDED重慢、ACTION_PACKAGE_REPLACED廣播

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末饥臂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子似踱,更是在濱河造成了極大的恐慌隅熙,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件核芽,死亡現(xiàn)場(chǎng)離奇詭異猛们,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)狞洋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)绿店,“玉大人吉懊,你說(shuō)我怎么就攤上這事〖傥穑” “怎么了借嗽?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)转培。 經(jīng)常有香客問(wèn)我恶导,道長(zhǎng),這世上最難降的妖魔是什么浸须? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任惨寿,我火速辦了婚禮邦泄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘裂垦。我一直安慰自己顺囊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布蕉拢。 她就那樣靜靜地躺著特碳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晕换。 梳的紋絲不亂的頭發(fā)上午乓,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音闸准,去河邊找鬼益愈。 笑死,一個(gè)胖子當(dāng)著我的面吹牛恕汇,可吹牛的內(nèi)容都是我干的腕唧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼瘾英,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼枣接!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起缺谴,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤但惶,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后湿蛔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體膀曾,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年阳啥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了添谊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡察迟,死狀恐怖斩狱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情扎瓶,我是刑警寧澤所踊,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站概荷,受9級(jí)特大地震影響秕岛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一继薛、第九天 我趴在偏房一處隱蔽的房頂上張望修壕。 院中可真熱鬧,春花似錦惋增、人聲如沸叠殷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)林束。三九已至,卻和暖如春稽亏,著一層夾襖步出監(jiān)牢的瞬間壶冒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工截歉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胖腾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓瘪松,卻偏偏與公主長(zhǎng)得像咸作,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宵睦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355