1. 程式人生 > >Android中應用安裝分析

Android中應用安裝分析

generate upgrade 版本 線程 title 回調函數 ebe children 應用商店

#1 安裝方式

  • 1 安裝系統APK和預制APK時,通過PMS的構造函數中安裝,即第一次開機時安裝應用,沒有安裝界面。
  • 2 網絡下載安裝,通過應用商店等,即調用PackageManager.installPackages(),有安裝界面。
  • 3 通過adb工具安裝,沒有安裝界面,它通過啟動pm腳本的形式,然後調用com.android.commands.pm.Pm類,之後調用到PMS.installStage()完成安裝。
  • 4 安裝本地apk,有安裝界面,由PackageInstaller系統應用安裝。
    上述幾種方式均通過PackageInstallObserver來監聽安裝是否成功。

#2 安裝流程分析

2.1 首次安裝

首次安裝即系統第一次開機時安裝應用,包括系統應用和預制應用,其最主要過程在PMS構造函數中,

整個過程關鍵步驟大致為上述15步,與應用安裝相關實際上就是掃描和安裝兩步。方法調用時序圖如圖1所示。
[圖1 PMS安裝應用時序圖]
技術分享圖片

  • 1 向動態設置中添加系統默認的共享ID(system、phone、log、nfc、bluetooth、shell、se等)。
  • 2 初始化成員變量,如Installer、PackageDexOptimizer、DexManager、ArtManagerService、MoveCallbacks、OnPermissionChangeListeners等,並獲取系統配置。
  • 3 啟動一個服務類線程。
  • 4 初始化用戶管理服務
  • 5 將權限配置傳入包管理器
  • 6 清除代碼路徑不存在的孤立包
  • 7 將系統應用權限從安裝態升級為運行時
  • 8 在掃描應用前,手機供應商的覆蓋安裝包(/overlay)
  • 9 掃描應用目錄,依次為特權系統目錄 priv-app、普通目錄 app、供應商系統目錄 vendor/app等
  • 10 解析存儲管理器
  • 11 如果是第一次開機,需要初始化用戶的默認偏好應用
  • 12 在啟動時,為用戶準備好存儲空間,因為SystemUI等啟動不能等待用戶
  • 13 安裝應用,完成後檢查webview,默認瀏覽器等。
  • 14 啟動PackageInstallerService
  • 15 向系統組件暴露私有服務
    下面我們結合代碼做詳細分析
  1. 判斷應用包是否已安裝,如果包名存在於uninstalled_deapp.xml中或者已安裝,則直接返回null。

2.2 下載安裝

下載安裝可分為兩部分:拷貝應用和安裝應用。拷貝過程的函數調用時序圖如圖2所示。
【圖2 下載安裝應用程序時序圖】
技術分享圖片

frameworks層的入口函數為PackageManager.installPackage,由應用市場APP調用,然後調用PMS.installPackageAsUser,然後發送消息INIT_COPY、MCS_BOUND開始復制,調用HandlerParams.startCopy。這個方法主要分兩部分,一部分是拷貝應用的執行程序,另一部分是創建應用的數據目錄,拷貝部分由handleStartCopy完成。之後調用handlerReturnCode來處理創建數據目錄。拷貝部分會調用DefaultContainerService來完成,該服務為那些可能位於可刪除空間上的文件提供檢查和拷貝功能。當底層設置被移除時,這樣設計可以防止系統進程保留打開的文件時,不被內核殺死。

handleStartcopy實現在PMS內部類InstallParams中,它的功能是調用遠程方法獲取包信息和安裝位置,如有必要則給與默認車輛覆蓋安裝位置,然後基於安裝位置創建安裝參數。下面我們結合關鍵代碼做進一步分析。

首先是拷貝應用過程

  • 1 PMS.installPackageAsUser的功能主要是:根據uid確定installFlags,並校驗權限,並構造InstallParam,然後發送INIT_COPY消息。
@Override
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
        int installFlags, String installerPackageName, int userId) {
    mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);

    final int callingUid = Binder.getCallingUid();
    enforceCrossUserPermission(callingUid, userId,
            true /* requireFullPermission */, true /* checkShell */, "installPackageAsUser");

    if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
        try {
            if (observer != null) {
                observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
            }
        } catch (RemoteException re) {
        }
        return;
    }

    if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
        installFlags |= PackageManager.INSTALL_FROM_ADB;

    } else {
        // Caller holds INSTALL_PACKAGES permission, so we‘re less strict
        // about installerPackageName.

        installFlags &= ~PackageManager.INSTALL_FROM_ADB;
        installFlags &= ~PackageManager.INSTALL_ALL_USERS;
    }

    UserHandle user;
    if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
        user = UserHandle.ALL;
    } else {
        user = new UserHandle(userId);
    }

    // Only system components can circumvent runtime permissions when installing.
    if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
            && mContext.checkCallingOrSelfPermission(Manifest.permission
            .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
        throw new SecurityException("You need the "
                + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
                + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
    }

    if ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
            || (installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
        throw new IllegalArgumentException(
                "New installs into ASEC containers no longer supported");
    }

    final File originFile = new File(originPath);
    final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);

    final Message msg = mHandler.obtainMessage(INIT_COPY);
    final VerificationInfo verificationInfo = new VerificationInfo(
            null /*originatingUri*/, null /*referrer*/, -1 /*originatingUid*/, callingUid);
    final InstallParams params = new InstallParams(origin, null /*moveInfo*/, observer,
            installFlags, installerPackageName, null /*volumeUuid*/, verificationInfo, user,
            null /*packageAbiOverride*/, null /*grantedPermissions*/,
            null /*certificates*/, PackageManager.INSTALL_REASON_UNKNOWN);
    params.setTraceMethod("installAsUser").setTraceCookie(System.identityHashCode(params));
    msg.obj = params;

    Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installAsUser",
            System.identityHashCode(msg.obj));
    Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
            System.identityHashCode(msg.obj));

    mHandler.sendMessage(msg);
}
  • 2 之後根據Handler.doHandleMessage調用到InstallParams.handleStartCopy方法,首先檢查文件和cid是否已生成,如生成則設置installFlags。
// [InstallParams.handleStartCopy]
if (origin.staged) {
    if (origin.file != null) {
        installFlags |= PackageManager.INSTALL_INTERNAL;
        installFlags &= ~PackageManager.INSTALL_EXTERNAL;
    } else if (origin.cid != null) {
        installFlags |= PackageManager.INSTALL_EXTERNAL;
        installFlags &= ~PackageManager.INSTALL_INTERNAL;
    } else {
        throw new IllegalStateException("Invalid stage location");
    }
}
  • 3 然後檢查空間大小,如果空間不夠則釋放無用空間。
// [InstallParams.handleStartCopy]
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 = mContainerService.calculateInstalledSize(
            origin.resolvedPath, isForwardLocked(), packageAbiOverride);

    try {
        mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
        pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
                installFlags, packageAbiOverride);
    } catch (InstallerException e) {
        Slog.w(TAG, "Failed to free cache", e);
    }


    if (pkgLite.recommendedInstallLocation
            == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
        pkgLite.recommendedInstallLocation
            = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
    }
}
  • 4 覆蓋原有安裝位置的文件,並根據返回結果來確定函數的返回值,並設置installFlags。
// [InstallParams.handleStartCopy]
// Override with defaults if needed.
loc = installLocationPolicy(pkgLite);
if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
    ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
} else if (!onSd && !onInt) {
    // Override install location with flags
    if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
        // Set the flag to install on external media.
        installFlags |= PackageManager.INSTALL_EXTERNAL;
        installFlags &= ~PackageManager.INSTALL_INTERNAL;
    } else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
        if (DEBUG_EPHEMERAL) {
            Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
        }
        installFlags |= PackageManager.INSTALL_INSTANT_APP;
        installFlags &= ~(PackageManager.INSTALL_EXTERNAL
                |PackageManager.INSTALL_INTERNAL);
    } else {
        // Make sure the flag for installing on external
        // media is unset
        installFlags |= PackageManager.INSTALL_INTERNAL;
        installFlags &= ~PackageManager.INSTALL_EXTERNAL;
    }
}
  • 5 確定是否有任何已安裝的包驗證器,如有,則延遲檢測。主要分三步:首先新建一個驗證Intent,然後設置相關的信息,之後獲取驗證器列表,最後向每個驗證器發送驗證Intent。
// [InstallParams.handleStartCopy]
final Intent verification = new Intent( //構造驗證Intent
                            Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
    // ......

    final PackageVerificationState verificationState = new PackageVerificationState(
            requiredUid, args);

    mPendingVerification.append(verificationId, verificationState);
    // 獲取驗證器列表
    final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
            receivers, verificationState);

    DeviceIdleController.LocalService idleController = getDeviceIdleController();
    final long idleDuration = getVerificationTimeout();

    /*
     * If any sufficient verifiers were listed in the package
     * manifest, attempt to ask them.
     */
    if (sufficientVerifiers != null) {
        final int N = sufficientVerifiers.size();
        if (N == 0) {
            Slog.i(TAG, "Additional verifiers required, but none installed.");
            ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
        } else {
            for (int i = 0; i < N; i++) {
                final ComponentName verifierComponent = sufficientVerifiers.get(i);
                idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
                        verifierComponent.getPackageName(), idleDuration,
                        verifierUser.getIdentifier(), false, "package verifier");
                // 向每個驗證器發送驗證Intent
                final Intent sufficientIntent = new Intent(verification);
                sufficientIntent.setComponent(verifierComponent);
                mContext.sendBroadcastAsUser(sufficientIntent, verifierUser);
            }
        }
    }
  • 6 向驗證器客戶端發送intent,只有當驗證成功之後才會開啟copy工作。如果沒有任何驗證器則直接拷貝。

下面為安裝過程入口是PMS.processPendingInstall方法,調用時序圖如圖3
【圖3 下載安裝-安裝過程圖】
技術分享圖片

  • 1 首先啟動一個新線程,然後設置安裝信息,處理安裝參數,開始安裝,並發送關於安裝狀態的廣播,然後處理安裝完的事情,比如打印錯誤信息,清除臨時文件等。
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() {
            mHandler.removeCallbacks(this);
             // Result object to be returned
            PackageInstalledInfo res = new PackageInstalledInfo();
            res.setReturnCode(currentStatus);
            res.uid = -1;
            res.pkg = null;
            res.removedInfo = null;
            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                args.doPreInstall(res.returnCode);
                synchronized (mInstallLock) {
                    installPackageTracedLI(args, res);
                }
                args.doPostInstall(res.returnCode, res.uid);
    //......
}
  • 2 installPackageTracedLI是安裝過程的核心方法,然後調用installPackageLI.首先檢查安裝包的完整性並解析安裝包。
//[PMS.installPackageLI]
// 完整性校驗
if (instantApp && (forwardLocked || onExternal)) {
    Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked
            + " external=" + onExternal);
    res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
    return;
}

// 檢索包設置,並解析應用
final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
        | PackageParser.PARSE_ENFORCE_CODE
        | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
        | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
        | (instantApp ? PackageParser.PARSE_IS_EPHEMERAL : 0)
        | (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);

Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
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);
}
  • 3 檢查SDK版本和沙箱版本,同時檢查是否有靜態共享庫,如有則需要放在內部存儲中。
//[PMS.installPackageLI]
//檢查SDK版本和沙箱版本
if (instantApp && pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
    Slog.w(TAG, "Instant app package " + pkg.packageName + " does not target O");
    res.setError(INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE,
            "Instant app package must target O");
    return;
}
if (instantApp && pkg.applicationInfo.targetSandboxVersion != 2) {
    Slog.w(TAG, "Instant app package " + pkg.packageName
            + " does not target targetSandboxVersion 2");
    res.setError(INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE,
            "Instant app package must use targetSanboxVersion 2");
    return;
}
//檢查是否有靜態共享庫
if (pkg.applicationInfo.isStaticSharedLibrary()) {
    // Static shared libraries have synthetic package names
    renameStaticSharedLibraryPackage(pkg);

    // No static shared libs on external storage
    if (onExternal) {
        Slog.i(TAG, "Static shared libs can only be installed on internal storage.");
        res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
                "Packages declaring static-shared libs cannot be updated");
        return;
    }
}
  • 4 檢查是否有子安裝包,如有則子安裝包也需要檢測。
//[PMS.installPackageLI]
// If we are installing a clustered package add results for the children
if (pkg.childPackages != null) {
    synchronized (mPackages) {
        final int childCount = pkg.childPackages.size();
        for (int i = 0; i < childCount; i++) {
            PackageParser.Package childPkg = pkg.childPackages.get(i);
            PackageInstalledInfo childRes = new PackageInstalledInfo();
            childRes.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
            childRes.pkg = childPkg;
            childRes.name = childPkg.packageName;
            PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
            if (childPs != null) {
                childRes.origUsers = childPs.queryInstalledUsers(
                        sUserManager.getUserIds(), true);
            }
            if ((mPackages.containsKey(childPkg.packageName))) {
                childRes.removedInfo = new PackageRemovedInfo(this);
                childRes.removedInfo.removedPackage = childPkg.packageName;
                childRes.removedInfo.installerPackageName = childPs.installerPackageName;
            }
            if (res.addedChildPackages == null) {
                res.addedChildPackages = new ArrayMap<>();
            }
            res.addedChildPackages.put(childPkg.packageName, childRes);
        }
    }
}
  • 5 檢查安裝包是否已存在,如已存在則需要檢查舊的父包、沙箱、sdk等是否已為空,否則會報錯。
  • 6 校驗安裝包簽名
//[PMS.installPackageLI]
PackageSetting signatureCheckPs = ps;
if (pkg.applicationInfo.isStaticSharedLibrary()) {
    SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
    if (libraryEntry != null) {
        signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
    }
}

// Quick sanity check that we‘re signed correctly if updating;
// we‘ll check this again later when scanning, but we want to
// bail early here before tripping over redefined permissions.
if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) {
    if (!checkUpgradeKeySetLP(signatureCheckPs, pkg)) {
        res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
                + pkg.packageName + " upgrade keys do not match the "
                + "previously installed version");
        return;
    }
} else {
    try {
        verifySignaturesLP(signatureCheckPs, pkg);
    } catch (PackageManagerException e) {
        res.setError(e.error, e.getMessage());
        return;
    }
}
  • 7 設置相關的全向,包括生成權限、移植權限等
  • 8 如果這是一個系統應用,則檢查是否在外部存儲上或是是否被其他應用替換等
//[PMS.installPackageLI]
if (systemApp) {
    if (onExternal) {
        // Abort update; system app can‘t be replaced with app on sdcard
        res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
                "Cannot install updates to system apps on sdcard");
        return;
    } else if (instantApp) {
        // Abort update; system app can‘t be replaced with an instant app
        res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
                "Cannot update a system app with an instant app");
        return;
    }
}
  • 9 生成安裝包Abi(Application binary interface,應用二進制接口,描述應用程序和操作系統之間或其他應用程序的低級接口)
//[PMS.installPackageLI]
try {
    String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?
        args.abiOverride : pkg.cpuAbiOverride);
    final boolean extractNativeLibs = !pkg.isLibrary();
    derivePackageAbi(pkg, new File(pkg.codePath), abiOverride,
            extractNativeLibs, mAppLib32InstallDir);
} catch (PackageManagerException pme) {
    Slog.e(TAG, "Error deriving application ABI", pme);
    res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
    return;
}
  • 10更新共享庫
//[PMS.installPackageLI]
synchronized (mPackages) {
    try {
        updateSharedLibrariesLPr(pkg, null);
    } catch (PackageManagerException e) {
        Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
    }
}
  • 11如有必要,優化dex文件
//[PMS.installPackageLI]
final boolean performDexopt = (res.returnCode == PackageManager.INSTALL_SUCCEEDED)
    && !forwardLocked
    && !pkg.applicationInfo.isExternalAsec()
    && (!instantApp || Global.getInt(mContext.getContentResolver(),
    Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
    && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);

if (performDexopt) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
// Do not run PackageDexOptimizer through the local performDexOpt
// method because `pkg` may not be in `mPackages` yet.
//
// Also, don‘t fail application installs if the dexopt step fails.
DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
        REASON_INSTALL,
        DexoptOptions.DEXOPT_BOOT_COMPLETE |
        DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
        null /* instructionSets */,
        getOrCreateCompilerPackageStats(pkg),
        mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
        dexoptOptions);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
  • 12替換安裝,則直接安裝新包,這裏應用時生成應用數據目錄。ps:替換安裝:其主要過程為更新設置,清除原有的某些APP數據,重新生成相關的app數據目錄等步驟,同事要區分系統應用替換和非系統應用替換。而安裝新包:則直接更新設置,生成APP數據即可。
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.mVersionCode != pkg.mVersionCode) {
                res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
                        + "static-shared libs cannot be updated");
                return;
            }
        }
        replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                installerPackageName, res, args.installReason);
    } else {
        installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                args.user, installerPackageName, volumeUuid, res, args.installReason);
    }
}
  • 13 如果是安裝一個不存在的包,則調用PMS.installNewPackageLIF方法。首先會檢查是否有重復的包名,並更新設置,然後根據安裝的結果,如果安裝失敗則刪除安裝過程中產生的文件。
private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags,
        int scanFlags, UserHandle user, String installerPackageName, String volumeUuid,
        PackageInstalledInfo res, int installReason) {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage");

    // Remember this for later, in case we need to rollback this install
    String pkgName = pkg.packageName;

    if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);

    synchronized(mPackages) {
        final String renamedPackage = mSettings.getRenamedPackageLPr(pkgName);
        if (renamedPackage != null) {
            // 如果已有相同包名的應用,則報錯
            res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
                    + " without first uninstalling package running as "
                    + renamedPackage);
            return;
        }
        if (mPackages.containsKey(pkgName)) {
            // Don‘t allow installation over an existing package with the same name.
            res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
                    + " without first uninstalling.");
            return;
        }
    }

    try {
        PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags, System.currentTimeMillis(), user);

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

        if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
            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);
    }

    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
  • 14 然後為已安裝的應用準備數據目錄,其依次的順序是
    • PMS.prepareAppDataAfterInstallLIF
    • PMS.prepareAppDataLIF
    • PMS.prepareAppDataLeafLIF
    • Installer.createAppData

這個方法是PMS與Installer交互的接口函數,這裏的數據目錄是CE類型。

private void prepareAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
    if (DEBUG_APP_DATA) {
        Slog.v(TAG, "prepareAppData for " + pkg.packageName + " u" + userId + " 0x"
                + Integer.toHexString(flags));
    }

    final String volumeUuid = pkg.volumeUuid;
    final String packageName = pkg.packageName;
    final ApplicationInfo app = pkg.applicationInfo;
    final int appId = UserHandle.getAppId(app.uid);

    Preconditions.checkNotNull(app.seInfo);

    long ceDataInode = -1;
    try {
        // 調用Installd守護進程的入口
        ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
                appId, app.seInfo, app.targetSdkVersion);
    } catch (InstallerException e) {
        //......
    }
    // Prepare the application profiles.
    mArtManagerService.prepareAppProfiles(pkg, userId);

    if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
        // TODO: mark this structure as dirty so we persist it!
        synchronized (mPackages) {
            final PackageSetting ps = mSettings.mPackages.get(packageName);
            if (ps != null) {
                ps.setCeDataInode(ceDataInode, userId);
            }
        }
    }

    prepareAppDataContentsLeafLIF(pkg, userId, flags);
}
  • 15 如果是替換應用,一般情況是應用更新,或者是重新安裝。它的主要過程包括:驗證簽名,如是系統更新則還需要校驗hash值,檢查共享ID的更改情況,不允許完整更新,更新已被刪除數據,最後根據應用是否是系統應用來判斷接下去的操作。
private void replacePackageLIF(PackageParser.Package pkg, final int policyFlags, int scanFlags,
        UserHandle user, String installerPackageName, PackageInstalledInfo res,
        int installReason) {
    final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;

    final PackageParser.Package oldPackage;
    final PackageSetting ps;
    final String pkgName = pkg.packageName;
    final int[] allUsers;
    final int[] installedUsers;

    // ......

    boolean sysPkg = (isSystemApp(oldPackage));
    if (sysPkg) {
        // Set the system/privileged flags as needed
        final boolean privileged =
                (oldPackage.applicationInfo.privateFlags
                        & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
        final int systemPolicyFlags = policyFlags
                | PackageParser.PARSE_IS_SYSTEM
                | (privileged ? PackageParser.PARSE_IS_PRIVILEGED : 0);

        replaceSystemPackageLIF(oldPackage, pkg, systemPolicyFlags, scanFlags,
                user, allUsers, installerPackageName, res, installReason);
    } else {
        replaceNonSystemPackageLIF(oldPackage, pkg, policyFlags, scanFlags,
                user, allUsers, installerPackageName, res, installReason);
    }
}
  • 16 最後這兩個方法均會調用到PMS.prepareAppDataLeafLIF。
  • 17 安裝完成後,更新設置,更新安裝鎖等。

2.3 adb安裝

關於adb安裝,其copy過程與下載安裝不同,但安裝過程卻與下載過程是相同的,這裏不做重復分析,需要註意的是adb安裝是不能替換安裝的,具體原因?

拷貝過程
其調用時序圖如圖4 所示。
【圖4 adb安裝-copy過程時序圖】
技術分享圖片

  • 1 adb的入口在com.android.commands.pm.Pm類,那麽這是如何調用到這個類的呢,這是adb命令通過adbd守護進程調用到/system/bin/pm這個腳本,其腳本源碼如下:
base=/system
export CLASSPATh-$base/framework/pm.jar
exec app_process $base/bin.com.android.commands.pm.Pm "$@"
  • 2 Pm類通過腳本啟動,執行順序是main->run->runInstall,然後提交session。
public static void main(String[] args) {
    int exitCode = 1;
    try {
        exitCode = new Pm().run(args);
    } catch (Exception e) {
        Log.e(TAG, "Error", e);
        System.err.println("Error: " + e);
        if (e instanceof RemoteException) {
            System.err.println(PM_NOT_RUNNING_ERR);
        }
    }
    System.exit(exitCode);
}
public int run(String[] args) throws RemoteException {
    boolean validCommand = false;
    if (args.length < 1) {
        return showUsage();
    }
    mAm = IAccountManager.Stub.asInterface(ServiceManager.getService(Context.ACCOUNT_SERVICE));
    mUm = IUserManager.Stub.asInterface(ServiceManager.getService(Context.USER_SERVICE));
    mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));

    if (mPm == null) {
        System.err.println(PM_NOT_RUNNING_ERR);
        return 1;
    }
    mInstaller = mPm.getPackageInstaller();

    mArgs = args;
    String op = args[0];
    mNextArg = 1;
    //......
    if ("install".equals(op)) {
        return runInstall();
    }
    //......
}
  • 3 Pm.runInstall中首先是創建session,然後提交session,代碼如下。
    private int runInstall() throws RemoteException {
        long startedTime = SystemClock.elapsedRealtime();
        final InstallParams params = makeInstallParams();
        final String inPath = nextArg();
        if (params.sessionParams.sizeBytes == -1 && !STDIN_PATH.equals(inPath)) {
            File file = new File(inPath);
            if (file.isFile()) {
                try {
                    ApkLite baseApk = PackageParser.parseApkLite(file, 0);
                    PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
                            null, null);
                    params.sessionParams.setSize(
                            PackageHelper.calculateInstalledSize(pkgLite, false,
                            params.sessionParams.abiOverride));
                } catch (PackageParserException | IOException e) {
                    System.err.println("Error: Failed to parse APK file: " + e);
                    return 1;
                }
            } else {
                System.err.println("Error: Can‘t open non-file: " + inPath);
                return 1;
            }
        }

        final int sessionId = doCreateSession(params.sessionParams,
                params.installerPackageName, params.userId);

        try {
            if (inPath == null && params.sessionParams.sizeBytes == -1) {
                System.err.println("Error: must either specify a package size or an APK file");
                return 1;
            }
            if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
                    false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
                return 1;
            }
            Pair<String, Integer> status = doCommitSession(sessionId, false /*logSuccess*/);
            if (status.second != PackageInstaller.STATUS_SUCCESS) {
                return 1;
            }
            Log.i(TAG, "Package " + status.first + " installed in " + (SystemClock.elapsedRealtime()
                    - startedTime) + " ms");
            System.out.println("Success");
            return 0;
        } finally {
            try {
                mInstaller.abandonSession(sessionId);
            } catch (Exception ignore) {
            }
        }
    }
  • 4 這裏Pm相當於客戶端,接受session的服務端在PackageInstallerSession中,這裏利用AIDL來完成傳輸,其調用過程為:
    • Pm.doCommitSession
    • PackageInstaller.Session.commit
    • IPackageInstallerSession.commit
    • PackageInstallerSession.commit
    • Handler.Callback.handleMessage
    • PackageInstallerSession.commitLock
    • PMS.installStage

以上關於session傳遞過程暫不分析,下面我們來詳細看下installStage方法。

  • 5 installStage方法主要功能就是構造InstallParam對象,並發送INIT_COPY。
void installStage(String packageName, File stagedDir, String stagedCid,
        IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
        String installerPackageName, int installerUid, UserHandle user,
        Certificate[][] certificates) {
    if (DEBUG_EPHEMERAL) {
        if ((sessionParams.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
            Slog.d(TAG, "Ephemeral install of " + packageName);
        }
    }
    final VerificationInfo verificationInfo = new VerificationInfo(
            sessionParams.originatingUri, sessionParams.referrerUri,
            sessionParams.originatingUid, installerUid);

    final OriginInfo origin;
    if (stagedDir != null) {
        origin = OriginInfo.fromStagedFile(stagedDir);
    } else {
        origin = OriginInfo.fromStagedContainer(stagedCid);
    }

    final Message msg = mHandler.obtainMessage(INIT_COPY);
    final int installReason = fixUpInstallReason(installerPackageName, installerUid,
            sessionParams.installReason);
    final InstallParams params = new InstallParams(origin, null, observer,
            sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
            verificationInfo, user, sessionParams.abiOverride,
            sessionParams.grantedRuntimePermissions, certificates, installReason);
    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));

    mHandler.sendMessage(msg);
}
  • 6 發送完Handler消息後就與下載安裝過程相同了。

2.4 本地安裝

本地安裝參與對象包括PackageInstaller應用,PMS兩部分。下面我們就來分析下PackageInstaller是如何調用到PMS中的。函數調用時序圖如圖5所示。
【圖5 本地安裝前提調用時序圖】
技術分享圖片

  • 1 點擊文件管理器中的apk文件時,會調用到FolderFragment類的openFile方法,然後調用startActivitySafety方法啟動PackageInstallerActivity。
private void openFile(File f) {  
    final Uri fileUri = Uri.fromFile(f);  
    final Intent intent = new Intent();  
    intent.setAction(android.content.Intent.ACTION_VIEW);  
    intent.putExtra(Intent.EXTRA_TITLE, f.getName());  
    intent.putExtra(EXTRA_ALL_VIDEO_FOLDER, true);  
    Uri contentUri = null;  
    String type = getMIMEType(f);  
    //......  
        if (contentUri != null) {  
            intent.setDataAndType(contentUri, type);  
        } else {  
            intent.setDataAndType(fileUri, type);  
        }  
        try {  
            startActivitySafely(intent);  
        }   
    //......  
}
  • 2 如下為PackageInstallerActivity.onCreate方法源碼,其主要過程初始化各個服務的成員變量如PMS,校驗session,並加載UI界面,然用戶確定是否安裝。
//[PackageInstallerActivity.java]
protected void onCreate(Bundle icicle) {
    super.onCreate(icicle);

    if (icicle != null) {
        mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);
    }
    //初始化各個關鍵參數
    mPm = getPackageManager();
    mIpm = AppGlobals.getPackageManager();
    mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
    mInstaller = mPm.getPackageInstaller();
    mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);

    final Intent intent = getIntent();

    mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);
    mSourceInfo = intent.getParcelableExtra(EXTRA_ORIGINAL_SOURCE_INFO);
    mOriginatingUid = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
            PackageInstaller.SessionParams.UID_UNKNOWN);
    mOriginatingPackage = (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN)
            ? getPackageNameForUid(mOriginatingUid) : null;


    final Uri packageUri;
    //校驗session
    if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
        final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
        final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
        if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
            Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
            finish();
            return;
        }

        mSessionId = sessionId;
        packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath));
        mOriginatingURI = null;
        mReferrerURI = null;
    } else {
        mSessionId = -1;
        packageUri = intent.getData();
        mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
        mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
    }

    // if there‘s nothing to do, quietly slip into the ether
    if (packageUri == null) {
        Log.w(TAG, "Unspecified source");
        setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
        finish();
        return;
    }

    if (DeviceUtils.isWear(this)) {
        showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR);
        return;
    }

    boolean wasSetUp = processPackageUri(packageUri);
    if (!wasSetUp) {
        return;
    }

    // 加載UI界面
    bindUi(R.layout.install_confirm, false);
    checkIfAllowedAndInitiateInstall();
}
  • 3 當用戶點擊安裝按鈕時,響應函數為PackageInstallerActivity.onClick方法,
//[PackageInstallerActivity.java]
public void onClick(View v) {
    if (v == mOk) {
        if (mOk.isEnabled()) {
            if (mOkCanInstall || mScrollView == null) {
                if (mSessionId != -1) {
                    mInstaller.setPermissionsResult(mSessionId, true);
                    finish();
                } else {
                    startInstall();
                }
            } else {
                mScrollView.pageScroll(View.FOCUS_DOWN);
            }
        }
    } else if (v == mCancel) {
        // Cancel and finish
        setResult(RESULT_CANCELED);
        if (mSessionId != -1) {
            mInstaller.setPermissionsResult(mSessionId, false);
        }
        finish();
    }
}
  • 4 之後調用 PackageInstallerActivity.startInstall方法,構造Intent,然後啟動InstallInstalling,並銷毀PackageInstallerActivity。
private void startInstall() {
    // Start subactivity to actually install the application
    Intent newIntent = new Intent();
    newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
            mPkgInfo.applicationInfo);
    newIntent.setData(mPackageURI);
    newIntent.setClass(this, InstallInstalling.class);
    String installerPackageName = getIntent().getStringExtra(
            Intent.EXTRA_INSTALLER_PACKAGE_NAME);
    if (mOriginatingURI != null) {
        newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
    }
    if (mReferrerURI != null) {
        newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
    }
    if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
        newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
    }
    if (installerPackageName != null) {
        newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
                installerPackageName);
    }
    if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
        newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
        newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    }
    if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
    startActivity(newIntent);
    finish();
}
  • 5 之後啟動InstallInstalling,因為Activity中的默認成員方法的執行順序是onCreate->onStart->onResume...其中onCreate的方法中主要過程包括:
    • 1 獲取待安裝應用信息
    • 2 根據應用安裝與否決定如何調用方法
    • 3 如果已存在,則直接調用PackageManager.installExistingPackage
    • 4 如果不存在則構造session
    • 5 之後則為安裝事件廣播添加一個監測
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.install_installing);
    // 獲取待安裝應用信息
    ApplicationInfo appInfo = getIntent()
            .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
    mPackageURI = getIntent().getData();
    // 如果應用已存在,則使用這條路徑安裝
    if ("package".equals(mPackageURI.getScheme())) {
        try {
            getPackageManager().installExistingPackage(appInfo.packageName);
            launchSuccess();
        } catch (PackageManager.NameNotFoundException e) {
            launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
        }
    } else { //否則使用session提交安裝應用
        final File sourceFile = new File(mPackageURI.getPath());
        PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo,
                sourceFile), R.id.app_snippet);
        // 如果session已存在,則獲取sessionId等數據
        if (savedInstanceState != null) {
            mSessionId = savedInstanceState.getInt(SESSION_ID);
            mInstallId = savedInstanceState.getInt(INSTALL_ID);

            // Reregister for result; might instantly call back if result was delivered while
            // activity was destroyed
            try {
                InstallEventReceiver.addObserver(this, mInstallId,
                        this::launchFinishBasedOnResult);
            } catch (EventResultPersister.OutOfIdsException e) {
                // Does not happen
            }
        } else { // 否則創建session
            PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                    PackageInstaller.SessionParams.MODE_FULL_INSTALL);
            // ......
            try {
                mInstallId = InstallEventReceiver
                        .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                this::launchFinishBasedOnResult);
            } catch (EventResultPersister.OutOfIdsException e) {
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
            //創建session
            try {
                mSessionId = getPackageManager().getPackageInstaller().createSession(params);
            } catch (IOException e) {
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
        }
        //......
        mSessionCallback = new InstallSessionCallback();
    }
}
  • 6 InstallInstalling.onStart中註冊回調函數,然後onResume中執行AsyncTask。
@Override
protected void onResume() {
    super.onResume();

    // This is the first onResume in a single life of the activity
    if (mInstallingTask == null) {
        PackageInstaller installer = getPackageManager().getPackageInstaller();
        PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
        //如果session非空,則執行AsyncTask
        if (sessionInfo != null && !sessionInfo.isActive()) {
            mInstallingTask = new InstallingAsyncTask();
            mInstallingTask.execute();
        } else {
            // we will receive a broadcast when the install is finished
            mCancelButton.setEnabled(false);
            setFinishOnTouchOutside(false);
        }
    }
}
  • 7 AsyncTask是Android提供的一種輕量級的異步類,執行過程可以表示為5個階段。
    • 1 準備執行,onPreExecute()
    • 2 正在後臺執行,doInBackgroud()
    • 3 進度更新,onProcessUpdate()
    • 4 完成後臺任務,onPostExecute()
    • 5 取消任務,onCacelled()

此處重寫了方法onPostExecute方法,源碼如下。

@Override
protected void onPostExecute(PackageInstaller.Session session) {
    if (session != null) {
        Intent broadcastIntent = new Intent(BROADCAST_ACTION);
        broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        broadcastIntent.setPackage(
                getPackageManager().getPermissionControllerPackageName());
        broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);

        PendingIntent pendingIntent = PendingIntent.getBroadcast(
                InstallInstalling.this,
                mInstallId,
                broadcastIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);
        //提交session
        session.commit(pendingIntent.getIntentSender());
        mCancelButton.setEnabled(false);
        setFinishOnTouchOutside(false);
    } else {
        getPackageManager().getPackageInstaller().abandonSession(mSessionId);

        if (!isCancelled()) {
            launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
        }
    }
}
  • 8 session對象傳輸順序為:
    • 1 PackageInstaller.Session.commit
    • 2 IPackageInstallerSession.commit
    • 3 PackageInstallerSession.commit
    • 4 Handler.Callback.handleMessage
    • 5 PackageInstallerSession.commitLock
    • 6 PMS.installStage
      這裏是不是似曾相識,這一步跟Adb安裝的第4步幾乎相同,之後就調用installStage方法完成安裝。

#3 總結

安裝應用的場景就是上述所示的PMS構造函數安裝、adb安裝、網絡下載安裝、本地安裝。其最終的入口為PMS.prepareAppDataLeafLIF,然後調用Installer類完成安裝,這裏涉及到System_server到Installd守護進程的轉移。

Android中應用安裝分析