From 788947b76d820d872613fd7fdc3ed828bfe16335 Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Mon, 25 Oct 2021 10:12:27 -0700 Subject: [PATCH] Consolidate installation logics from params/args classes Move mLocks/mSettings/mPackages dependencies from InstallParams, PackageHandler, VerificationParams into InstallPackageHelper. Also cleaning up InitAndSystemPackageHelper to move some of the core installation logic into InstallPackageHelper. Current goal is to have the "core" installation logic in one place. "core" means code blocks that uses mLock/mSettings/mPackages during installation. BYPASS_INCLUSIVE_LANGUAGE_REASON=need to deprecate some old constant names first BUG: 198177734 BUG: 201815903 Test: manual Change-Id: Idb2bf634ce58e968846d6f56c1d501ee8b3aafa3 --- .../server/pm/DeletePackageHelper.java | 9 +- .../pm/DomainVerificationConnection.java | 4 +- .../com/android/server/pm/HandlerParams.java | 153 +- .../server/pm/InitAndSystemPackageHelper.java | 434 +-- .../server/pm/InstallPackageHelper.java | 2693 ++++++++++++++++- .../com/android/server/pm/InstallParams.java | 1730 +---------- .../com/android/server/pm/PackageHandler.java | 421 +-- .../server/pm/PackageManagerService.java | 71 +- .../server/pm/PackageManagerServiceUtils.java | 29 + .../android/server/pm/VerificationParams.java | 112 +- ...tionHelper.java => VerificationUtils.java} | 18 +- 11 files changed, 2826 insertions(+), 2848 deletions(-) rename services/core/java/com/android/server/pm/{VerificationHelper.java => VerificationUtils.java} (85%) diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index 0feb9c5994a55..8b3852ead1759 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -83,19 +83,15 @@ final class DeletePackageHelper { private final UserManagerInternal mUserManagerInternal; private final PermissionManagerServiceInternal mPermissionManager; private final RemovePackageHelper mRemovePackageHelper; - // TODO(b/201815903): remove dependency to InitAndSystemPackageHelper - private final InitAndSystemPackageHelper mInitAndSystemPackageHelper; private final AppDataHelper mAppDataHelper; // TODO(b/198166813): remove PMS dependency DeletePackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper, - InitAndSystemPackageHelper initAndSystemPackageHelper, AppDataHelper appDataHelper) { mPm = pm; mUserManagerInternal = mPm.mInjector.getUserManagerInternal(); mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal(); mRemovePackageHelper = removePackageHelper; - mInitAndSystemPackageHelper = initAndSystemPackageHelper; mAppDataHelper = appDataHelper; } @@ -105,7 +101,6 @@ final class DeletePackageHelper { mUserManagerInternal = mPm.mInjector.getUserManagerInternal(); mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal(); mRemovePackageHelper = new RemovePackageHelper(mPm, mAppDataHelper); - mInitAndSystemPackageHelper = mPm.getInitAndSystemPackageHelper(); } /** @@ -282,7 +277,7 @@ final class DeletePackageHelper { Slog.i(TAG, "Enabling system stub after removal; pkg: " + stubPkg.getPackageName()); } - mInitAndSystemPackageHelper.enableCompressedPackage(stubPkg, stubPs); + new InstallPackageHelper(mPm).enableCompressedPackage(stubPkg, stubPs); } else if (DEBUG_COMPRESSION) { Slog.i(TAG, "System stub disabled for all users, leaving uncompressed " + "after removal; pkg: " + stubPkg.getPackageName()); @@ -422,7 +417,7 @@ final class DeletePackageHelper { // as well and fall back to existing code in system partition PackageSetting disabledPs = deleteInstalledSystemPackage(action, ps, allUserHandles, flags, outInfo, writeSettings); - mInitAndSystemPackageHelper.restoreDisabledSystemPackageLIF( + new InstallPackageHelper(mPm).restoreDisabledSystemPackageLIF( action, ps, allUserHandles, outInfo, writeSettings, disabledPs); } else { if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.getPackageName()); diff --git a/services/core/java/com/android/server/pm/DomainVerificationConnection.java b/services/core/java/com/android/server/pm/DomainVerificationConnection.java index 1a3f422e78547..f3f7bf95543df 100644 --- a/services/core/java/com/android/server/pm/DomainVerificationConnection.java +++ b/services/core/java/com/android/server/pm/DomainVerificationConnection.java @@ -41,14 +41,12 @@ public final class DomainVerificationConnection implements DomainVerificationSer final PackageManagerService mPm; final PackageManagerInternal mPmInternal; final UserManagerInternal mUmInternal; - final VerificationHelper mVerificationHelper; // TODO(b/198166813): remove PMS dependency DomainVerificationConnection(PackageManagerService pm) { mPm = pm; mPmInternal = mPm.mInjector.getLocalService(PackageManagerInternal.class); mUmInternal = mPm.mInjector.getLocalService(UserManagerInternal.class); - mVerificationHelper = new VerificationHelper(mPm.mContext); } @Override @@ -79,7 +77,7 @@ public final class DomainVerificationConnection implements DomainVerificationSer @Override public long getPowerSaveTempWhitelistAppDuration() { - return mVerificationHelper.getVerificationTimeout(); + return VerificationUtils.getVerificationTimeout(mPm.mContext); } @Override diff --git a/services/core/java/com/android/server/pm/HandlerParams.java b/services/core/java/com/android/server/pm/HandlerParams.java index 034688b321733..57a8a7abe6e80 100644 --- a/services/core/java/com/android/server/pm/HandlerParams.java +++ b/services/core/java/com/android/server/pm/HandlerParams.java @@ -16,22 +16,12 @@ package com.android.server.pm; -import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; - -import android.annotation.NonNull; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageInfoLite; -import android.content.pm.PackageManager; -import android.os.UserHandle; -import android.util.Pair; -import android.util.Slog; - import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; import static com.android.server.pm.PackageManagerService.TAG; -import com.android.internal.util.ArrayUtils; -import com.android.server.pm.parsing.pkg.AndroidPackage; +import android.annotation.NonNull; +import android.os.UserHandle; +import android.util.Slog; abstract class HandlerParams { /** User handle for the user requesting the information or installation. */ @@ -40,15 +30,13 @@ abstract class HandlerParams { int mTraceCookie; @NonNull final PackageManagerService mPm; - final VerificationHelper mVerificationHelper; - final BroadcastHelper mBroadcastHelper; + final InstallPackageHelper mInstallPackageHelper; // TODO(b/198166813): remove PMS dependency HandlerParams(UserHandle user, PackageManagerService pm) { mUser = user; mPm = pm; - mVerificationHelper = new VerificationHelper(mPm.mContext); - mBroadcastHelper = new BroadcastHelper(mPm.mInjector); + mInstallPackageHelper = new InstallPackageHelper(mPm); } UserHandle getUser() { @@ -73,135 +61,4 @@ abstract class HandlerParams { abstract void handleStartCopy(); abstract void handleReturnCode(); - - Pair verifyReplacingVersionCode(PackageInfoLite pkgLite, - long requiredInstalledVersionCode, int installFlags) { - if ((installFlags & PackageManager.INSTALL_APEX) != 0) { - return verifyReplacingVersionCodeForApex( - pkgLite, requiredInstalledVersionCode, installFlags); - } - - String packageName = pkgLite.packageName; - synchronized (mPm.mLock) { - // Package which currently owns the data that the new package will own if installed. - // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg - // will be null whereas dataOwnerPkg will contain information about the package - // which was uninstalled while keeping its data. - AndroidPackage dataOwnerPkg = mPm.mPackages.get(packageName); - if (dataOwnerPkg == null) { - PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); - if (ps != null) { - dataOwnerPkg = ps.getPkg(); - } - } - - if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) { - if (dataOwnerPkg == null) { - String errorMsg = "Required installed version code was " - + requiredInstalledVersionCode - + " but package is not installed"; - Slog.w(TAG, errorMsg); - return Pair.create( - PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg); - } - - if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) { - String errorMsg = "Required installed version code was " - + requiredInstalledVersionCode - + " but actual installed version is " - + dataOwnerPkg.getLongVersionCode(); - Slog.w(TAG, errorMsg); - return Pair.create( - PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg); - } - } - - if (dataOwnerPkg != null) { - if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, - dataOwnerPkg.isDebuggable())) { - try { - checkDowngrade(dataOwnerPkg, pkgLite); - } catch (PackageManagerException e) { - String errorMsg = "Downgrade detected: " + e.getMessage(); - Slog.w(TAG, errorMsg); - return Pair.create( - PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg); - } - } - } - } - return Pair.create(PackageManager.INSTALL_SUCCEEDED, null); - } - - private Pair verifyReplacingVersionCodeForApex(PackageInfoLite pkgLite, - long requiredInstalledVersionCode, int installFlags) { - String packageName = pkgLite.packageName; - - final PackageInfo activePackage = mPm.mApexManager.getPackageInfo(packageName, - ApexManager.MATCH_ACTIVE_PACKAGE); - if (activePackage == null) { - String errorMsg = "Attempting to install new APEX package " + packageName; - Slog.w(TAG, errorMsg); - return Pair.create(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED, errorMsg); - } - - final long activeVersion = activePackage.getLongVersionCode(); - if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST - && activeVersion != requiredInstalledVersionCode) { - String errorMsg = "Installed version of APEX package " + packageName - + " does not match required. Active version: " + activeVersion - + " required: " + requiredInstalledVersionCode; - Slog.w(TAG, errorMsg); - return Pair.create(PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg); - } - - final boolean isAppDebuggable = (activePackage.applicationInfo.flags - & ApplicationInfo.FLAG_DEBUGGABLE) != 0; - final long newVersionCode = pkgLite.getLongVersionCode(); - if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, isAppDebuggable) - && newVersionCode < activeVersion) { - String errorMsg = "Downgrade of APEX package " + packageName - + " is not allowed. Active version: " + activeVersion - + " attempted: " + newVersionCode; - Slog.w(TAG, errorMsg); - return Pair.create(PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg); - } - - return Pair.create(PackageManager.INSTALL_SUCCEEDED, null); - } - - /** - * Check and throw if the given before/after packages would be considered a - * downgrade. - */ - private static void checkDowngrade(AndroidPackage before, PackageInfoLite after) - throws PackageManagerException { - if (after.getLongVersionCode() < before.getLongVersionCode()) { - throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE, - "Update version code " + after.versionCode + " is older than current " - + before.getLongVersionCode()); - } else if (after.getLongVersionCode() == before.getLongVersionCode()) { - if (after.baseRevisionCode < before.getBaseRevisionCode()) { - throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE, - "Update base revision code " + after.baseRevisionCode - + " is older than current " + before.getBaseRevisionCode()); - } - - if (!ArrayUtils.isEmpty(after.splitNames)) { - for (int i = 0; i < after.splitNames.length; i++) { - final String splitName = after.splitNames[i]; - final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName); - if (j != -1) { - if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) { - throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE, - "Update split " + splitName + " revision code " - + after.splitRevisionCodes[i] - + " is older than current " - + before.getSplitRevisionCodes()[j]); - } - } - } - } - } - } } diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java index 5ff0a6f7a59e1..239a9d7151c7f 100644 --- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java +++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java @@ -16,22 +16,13 @@ package com.android.server.pm; -import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; -import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; -import static android.os.storage.StorageManager.FLAG_STORAGE_CE; -import static android.os.storage.StorageManager.FLAG_STORAGE_DE; -import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL; -import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME; import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME; -import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION; -import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING; -import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE; import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX; import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED; import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM; @@ -42,31 +33,21 @@ import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX; import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN; import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS; import static com.android.server.pm.PackageManagerService.TAG; -import static com.android.server.pm.PackageManagerServiceUtils.decompressFile; -import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles; import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; -import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive; -import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.ContentResolver; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.parsing.ParsingPackageUtils; import android.os.Environment; -import android.os.Process; import android.os.SystemClock; import android.os.Trace; -import android.os.UserHandle; -import android.system.ErrnoException; import android.util.ArrayMap; import android.util.EventLog; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; -import com.android.internal.content.F2fsUtils; -import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.om.OverlayConfig; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; @@ -74,15 +55,10 @@ import com.android.server.EventLogTags; import com.android.server.pm.parsing.PackageCacher; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.parsing.pkg.AndroidPackage; -import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.utils.WatchedArrayMap; -import libcore.io.IoUtils; - import java.io.File; -import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutorService; @@ -270,6 +246,8 @@ final class InitAndSystemPackageHelper { * If the package is scanned, it's not erased. */ final AndroidPackage scannedPkg = mPm.mPackages.get(ps.getPackageName()); + final PackageSetting disabledPs = + mPm.mSettings.getDisabledSystemPkgLPr(ps.getPackageName()); if (scannedPkg != null) { /* * If the system app is both scanned and in the @@ -278,7 +256,7 @@ final class InitAndSystemPackageHelper { * scanned package so the previously user-installed * application can be scanned. */ - if (mPm.mSettings.isDisabledSystemPackageLPr(ps.getPackageName())) { + if (disabledPs != null) { logCriticalInfo(Log.WARN, "Expecting better updated system app for " + ps.getPackageName() @@ -294,7 +272,7 @@ final class InitAndSystemPackageHelper { continue; } - if (!mPm.mSettings.isDisabledSystemPackageLPr(ps.getPackageName())) { + if (disabledPs == null) { logCriticalInfo(Log.WARN, "System package " + ps.getPackageName() + " no longer exists; its data will be wiped"); mRemovePackageHelper.removePackageDataLIF(ps, userIds, null, 0, false); @@ -303,8 +281,6 @@ final class InitAndSystemPackageHelper { // been removed. check the code path still exists and check there's // still a package. the latter can happen if an OTA keeps the same // code path, but, changes the package name. - final PackageSetting disabledPs = - mPm.mSettings.getDisabledSystemPkgLPr(ps.getPackageName()); if (disabledPs.getPath() == null || !disabledPs.getPath().exists() || disabledPs.getPkg() == null) { possiblyDeletedUpdatedSystemApps.add(ps.getPackageName()); @@ -454,7 +430,7 @@ final class InitAndSystemPackageHelper { // Uncompress and install any stubbed system applications. // This must be done last to ensure all stubs are replaced or disabled. - installSystemStubPackages(stubSystemApps, mScanFlags); + new InstallPackageHelper(mPm).installSystemStubPackages(stubSystemApps, mScanFlags); final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get() - cachedSystemApps; @@ -568,403 +544,11 @@ final class InitAndSystemPackageHelper { } } - /** - * Uncompress and install stub applications. - *

In order to save space on the system partition, some applications are shipped in a - * compressed form. In addition the compressed bits for the full application, the - * system image contains a tiny stub comprised of only the Android manifest. - *

During the first boot, attempt to uncompress and install the full application. If - * the application can't be installed for any reason, disable the stub and prevent - * uncompressing the full application during future boots. - *

In order to forcefully attempt an installation of a full application, go to app - * settings and enable the application. - */ - @GuardedBy({"mPm.mLock", "mPm.mInstallLock"}) - private void installSystemStubPackages(@NonNull List systemStubPackageNames, - @PackageManagerService.ScanFlags int scanFlags) { - for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) { - final String packageName = systemStubPackageNames.get(i); - // skip if the system package is already disabled - if (mPm.mSettings.isDisabledSystemPackageLPr(packageName)) { - systemStubPackageNames.remove(i); - continue; - } - // skip if the package isn't installed (?!); this should never happen - final AndroidPackage pkg = mPm.mPackages.get(packageName); - if (pkg == null) { - systemStubPackageNames.remove(i); - continue; - } - // skip if the package has been disabled by the user - final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); - if (ps != null) { - final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM); - if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { - systemStubPackageNames.remove(i); - continue; - } - } - - // install the package to replace the stub on /system - try { - installStubPackageLI(pkg, 0, scanFlags); - ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, - UserHandle.USER_SYSTEM, "android"); - systemStubPackageNames.remove(i); - } catch (PackageManagerException e) { - Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage()); - } - - // any failed attempt to install the package will be cleaned up later - } - - // disable any stub still left; these failed to install the full application - for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) { - final String pkgName = systemStubPackageNames.get(i); - final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName); - ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED, - UserHandle.USER_SYSTEM, "android"); - logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName); - } - } - - /** - * Extract, install and enable a stub package. - *

If the compressed file can not be extracted / installed for any reason, the stub - * APK will be installed and the package will be disabled. To recover from this situation, - * the user will need to go into system settings and re-enable the package. - */ - @GuardedBy({"mPm.mLock", "mPm.mInstallLock"}) - public boolean enableCompressedPackage(AndroidPackage stubPkg, - @NonNull PackageSetting stubPkgSetting) { - final int parseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY - | ParsingPackageUtils.PARSE_ENFORCE_CODE; - synchronized (mPm.mInstallLock) { - final AndroidPackage pkg; - try (PackageFreezer freezer = - mPm.freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) { - pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/); - synchronized (mPm.mLock) { - mAppDataHelper.prepareAppDataAfterInstallLIF(pkg); - try { - mPm.updateSharedLibrariesLocked(pkg, stubPkgSetting, null, null, - Collections.unmodifiableMap(mPm.mPackages)); - } catch (PackageManagerException e) { - Slog.w(TAG, "updateAllSharedLibrariesLPw failed: ", e); - } - mPm.mPermissionManager.onPackageInstalled(pkg, - Process.INVALID_UID /* previousAppId */, - PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT, - UserHandle.USER_ALL); - mPm.writeSettingsLPrTEMP(); - } - } catch (PackageManagerException e) { - // Whoops! Something went very wrong; roll back to the stub and disable the package - try (PackageFreezer freezer = - mPm.freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) { - synchronized (mPm.mLock) { - // NOTE: Ensure the system package is enabled; even for a compressed stub. - // If we don't, installing the system package fails during scan - enableSystemPackageLPw(stubPkg); - } - installPackageFromSystemLIF(stubPkg.getPath(), - mPm.mUserManager.getUserIds() /*allUserHandles*/, - null /*origUserHandles*/, - true /*writeSettings*/); - } catch (PackageManagerException pme) { - // Serious WTF; we have to be able to install the stub - Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(), - pme); - } finally { - // Disable the package; the stub by itself is not runnable - synchronized (mPm.mLock) { - final PackageSetting stubPs = mPm.mSettings.getPackageLPr( - stubPkg.getPackageName()); - if (stubPs != null) { - stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, - UserHandle.USER_SYSTEM, "android"); - } - mPm.writeSettingsLPrTEMP(); - } - } - return false; - } - mAppDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL, - FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL - | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); - mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(), - pkg.getBaseApkPath(), pkg.getSplitCodePaths()); - } - return true; - } - - @GuardedBy({"mPm.mLock", "mPm.mInstallLock"}) - private AndroidPackage installStubPackageLI(AndroidPackage stubPkg, - @ParsingPackageUtils.ParseFlags int parseFlags, - @PackageManagerService.ScanFlags int scanFlags) - throws PackageManagerException { - if (DEBUG_COMPRESSION) { - Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.getPackageName()); - } - // uncompress the binary to its eventual destination on /data - final File scanFile = decompressPackage(stubPkg.getPackageName(), stubPkg.getPath()); - if (scanFile == null) { - throw new PackageManagerException( - "Unable to decompress stub at " + stubPkg.getPath()); - } - synchronized (mPm.mLock) { - mPm.mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/); - } - mRemovePackageHelper.removePackageLI(stubPkg, true /*chatty*/); - final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm); - try { - return scanPackageHelper.scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null); - } catch (PackageManagerException e) { - Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(), - e); - // Remove the failed install - mRemovePackageHelper.removeCodePathLI(scanFile); - throw e; - } - } - - /** - * Decompresses the given package on the system image onto - * the /data partition. - * @return The directory the package was decompressed into. Otherwise, {@code null}. - */ - @GuardedBy("mPm.mInstallLock") - private File decompressPackage(String packageName, String codePath) { - final File[] compressedFiles = getCompressedFiles(codePath); - if (compressedFiles == null || compressedFiles.length == 0) { - if (DEBUG_COMPRESSION) { - Slog.i(TAG, "No files to decompress: " + codePath); - } - return null; - } - final File dstCodePath = - PackageManagerServiceUtils.getNextCodePath(Environment.getDataAppDirectory(null), - packageName); - int ret = PackageManager.INSTALL_SUCCEEDED; - try { - makeDirRecursive(dstCodePath, 0755); - for (File srcFile : compressedFiles) { - final String srcFileName = srcFile.getName(); - final String dstFileName = srcFileName.substring( - 0, srcFileName.length() - COMPRESSED_EXTENSION.length()); - final File dstFile = new File(dstCodePath, dstFileName); - ret = decompressFile(srcFile, dstFile); - if (ret != PackageManager.INSTALL_SUCCEEDED) { - logCriticalInfo(Log.ERROR, "Failed to decompress" - + "; pkg: " + packageName - + ", file: " + dstFileName); - break; - } - } - } catch (ErrnoException e) { - logCriticalInfo(Log.ERROR, "Failed to decompress" - + "; pkg: " + packageName - + ", err: " + e.errno); - } - if (ret == PackageManager.INSTALL_SUCCEEDED) { - final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME); - NativeLibraryHelper.Handle handle = null; - try { - handle = NativeLibraryHelper.Handle.create(dstCodePath); - ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot, - null /*abiOverride*/, false /*isIncremental*/); - } catch (IOException e) { - logCriticalInfo(Log.ERROR, "Failed to extract native libraries" - + "; pkg: " + packageName); - ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; - } finally { - IoUtils.closeQuietly(handle); - } - } - if (ret == PackageManager.INSTALL_SUCCEEDED) { - // NOTE: During boot, we have to delay releasing cblocks for no other reason than - // we cannot retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}. - // When we no longer need to read that setting, cblock release can occur always - // occur here directly - if (!mPm.isSystemReady()) { - if (mPm.mReleaseOnSystemReady == null) { - mPm.mReleaseOnSystemReady = new ArrayList<>(); - } - mPm.mReleaseOnSystemReady.add(dstCodePath); - } else { - final ContentResolver resolver = mPm.mContext.getContentResolver(); - F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath); - } - } - if (ret != PackageManager.INSTALL_SUCCEEDED) { - if (!dstCodePath.exists()) { - return null; - } - mRemovePackageHelper.removeCodePathLI(dstCodePath); - return null; - } - - return dstCodePath; - } - - - @GuardedBy("mPm.mLock") - private void enableSystemPackageLPw(AndroidPackage pkg) { - mPm.mSettings.enableSystemPackageLPw(pkg.getPackageName()); - } - - /** - * Tries to restore the disabled system package after an update has been deleted. - */ - @GuardedBy({"mPm.mLock", "mPm.mInstallLock"}) - public void restoreDisabledSystemPackageLIF(DeletePackageAction action, - PackageSetting deletedPs, @NonNull int[] allUserHandles, - @Nullable PackageRemovedInfo outInfo, - boolean writeSettings, - PackageSetting disabledPs) - throws SystemDeleteException { - // writer - synchronized (mPm.mLock) { - // NOTE: The system package always needs to be enabled; even if it's for - // a compressed stub. If we don't, installing the system package fails - // during scan [scanning checks the disabled packages]. We will reverse - // this later, after we've "installed" the stub. - // Reinstate the old system package - enableSystemPackageLPw(disabledPs.getPkg()); - // Remove any native libraries from the upgraded package. - removeNativeBinariesLI(deletedPs); - } - - // Install the system package - if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs); - try { - installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles, - outInfo == null ? null : outInfo.mOrigUsers, writeSettings); - } catch (PackageManagerException e) { - Slog.w(TAG, "Failed to restore system package:" + deletedPs.getPackageName() + ": " - + e.getMessage()); - // TODO(b/194319951): can we avoid this; throw would come from scan... - throw new SystemDeleteException(e); - } finally { - if (disabledPs.getPkg().isStub()) { - // We've re-installed the stub; make sure it's disabled here. If package was - // originally enabled, we'll install the compressed version of the application - // and re-enable it afterward. - final PackageSetting stubPs = mPm.mSettings.getPackageLPr( - deletedPs.getPackageName()); - if (stubPs != null) { - int userId = action.mUser == null - ? UserHandle.USER_ALL : action.mUser.getIdentifier(); - if (userId == UserHandle.USER_ALL) { - for (int aUserId : allUserHandles) { - stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android"); - } - } else if (userId >= UserHandle.USER_SYSTEM) { - stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android"); - } - } - } - } - } - - private void removeNativeBinariesLI(PackageSetting ps) { - if (ps != null) { - NativeLibraryHelper.removeNativeBinariesLI(ps.getLegacyNativeLibraryPath()); - } - } - - /** - * Installs a package that's already on the system partition. - */ - @GuardedBy({"mPm.mLock", "mPm.mInstallLock"}) - private void installPackageFromSystemLIF(@NonNull String codePathString, - @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings) - throws PackageManagerException { - final File codePath = new File(codePathString); - @ParsingPackageUtils.ParseFlags int parseFlags = - mPm.getDefParseFlags() - | ParsingPackageUtils.PARSE_MUST_BE_APK - | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR; - @PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM; - for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) { - ScanPartition partition = mDirsToScanAsSystem.get(i); - if (partition.containsFile(codePath)) { - scanFlags |= partition.scanFlag; - if (partition.containsPrivApp(codePath)) { - scanFlags |= SCAN_AS_PRIVILEGED; - } - break; - } - } - - final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm); - final AndroidPackage pkg = - scanPackageHelper.scanPackageTracedLI( - codePath, parseFlags, scanFlags, 0 /*currentTime*/, null); - - PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName()); - - try { - // update shared libraries for the newly re-installed system package - mPm.updateSharedLibrariesLocked(pkg, pkgSetting, null, null, - Collections.unmodifiableMap(mPm.mPackages)); - } catch (PackageManagerException e) { - Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage()); - } - - mAppDataHelper.prepareAppDataAfterInstallLIF(pkg); - - // writer - synchronized (mPm.mLock) { - PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName()); - - final boolean applyUserRestrictions = origUserHandles != null; - if (applyUserRestrictions) { - boolean installedStateChanged = false; - if (DEBUG_REMOVE) { - Slog.d(TAG, "Propagating install state across reinstall"); - } - for (int userId : allUserHandles) { - final boolean installed = ArrayUtils.contains(origUserHandles, userId); - if (DEBUG_REMOVE) { - Slog.d(TAG, " user " + userId + " => " + installed); - } - if (installed != ps.getInstalled(userId)) { - installedStateChanged = true; - } - ps.setInstalled(installed, userId); - if (installed) { - ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId); - } - } - // Regardless of writeSettings we need to ensure that this restriction - // state propagation is persisted - mPm.mSettings.writeAllUsersPackageRestrictionsLPr(); - if (installedStateChanged) { - mPm.mSettings.writeKernelMappingLPr(ps); - } - } - - // The method below will take care of removing obsolete permissions and granting - // install permissions. - mPm.mPermissionManager.onPackageInstalled(pkg, - Process.INVALID_UID /* previousAppId */, - PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT, - UserHandle.USER_ALL); - for (final int userId : allUserHandles) { - if (applyUserRestrictions) { - mPm.mSettings.writePermissionStateForUserLPr(userId, false); - } - } - - // can downgrade to reader here - if (writeSettings) { - mPm.writeSettingsLPrTEMP(); - } - } - } - public boolean isExpectingBetter(String packageName) { return mExpectingBetter.containsKey(packageName); } + + public List getDirsToScanAsSystem() { + return mDirsToScanAsSystem; + } } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 4c70cda911bba..30d32ad197ae7 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -16,41 +16,112 @@ package com.android.server.pm; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; +import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS; +import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GROUP; +import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; +import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION; +import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP; +import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; +import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; +import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; +import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; +import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED; +import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID; +import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY; +import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; +import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; +import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE; +import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP; +import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; +import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4; +import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; +import static android.os.incremental.IncrementalManager.isIncrementalPath; +import static android.os.storage.StorageManager.FLAG_STORAGE_CE; +import static android.os.storage.StorageManager.FLAG_STORAGE_DE; +import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL; +import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; +import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet; import static com.android.server.pm.InstructionSets.getPreferredInstructionSet; +import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP; +import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING; +import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE; +import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY; +import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY; +import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static com.android.server.pm.PackageManagerService.POST_INSTALL; +import static com.android.server.pm.PackageManagerService.PRECOMPILE_LAYOUTS; import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX; +import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP; import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP; +import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM; +import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM; +import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED; +import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT; +import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM; +import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT; +import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR; +import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD; import static com.android.server.pm.PackageManagerService.SCAN_BOOTING; import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP; import static com.android.server.pm.PackageManagerService.SCAN_IGNORE_FROZEN; +import static com.android.server.pm.PackageManagerService.SCAN_INITIAL; +import static com.android.server.pm.PackageManagerService.SCAN_MOVE; +import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL; +import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX; +import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE; import static com.android.server.pm.PackageManagerService.TAG; import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures; +import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists; +import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride; +import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.AppOpsManager; +import android.app.ApplicationPackageManager; import android.app.backup.IBackupManager; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentSender; +import android.content.pm.ApplicationInfo; import android.content.pm.DataLoaderType; +import android.content.pm.IPackageInstallObserver2; +import android.content.pm.PackageChangeEvent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageInfoLite; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.Signature; import android.content.pm.SigningDetails; +import android.content.pm.VerifierInfo; +import android.content.pm.dex.DexMetadataHelper; import android.content.pm.parsing.ParsingPackageUtils; import android.content.pm.parsing.component.ComponentMutateUtils; import android.content.pm.parsing.component.ParsedInstrumentation; +import android.content.pm.parsing.component.ParsedPermission; +import android.content.pm.parsing.component.ParsedPermissionGroup; +import android.content.pm.parsing.result.ParseResult; +import android.content.pm.parsing.result.ParseTypeImpl; +import android.net.Uri; import android.os.Binder; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; import android.os.Message; import android.os.Process; import android.os.RemoteException; @@ -60,33 +131,76 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.incremental.IncrementalManager; +import android.os.incremental.IncrementalStorage; +import android.os.storage.StorageManager; +import android.os.storage.VolumeInfo; +import android.stats.storage.StorageEnums; import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.EventLog; import android.util.Log; +import android.util.Pair; import android.util.Slog; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.content.F2fsUtils; +import com.android.internal.content.NativeLibraryHelper; +import com.android.internal.content.PackageHelper; +import com.android.internal.security.VerityUtils; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FrameworkStatsLog; +import com.android.server.EventLogTags; +import com.android.server.Watchdog; +import com.android.server.pm.dex.DexoptOptions; +import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.parsing.pkg.ParsedPackage; +import com.android.server.pm.permission.Permission; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.rollback.RollbackManagerInternal; import com.android.server.utils.WatchedLongSparseArray; +import dalvik.system.VMRuntime; + +import libcore.io.IoUtils; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.DigestException; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.cert.CertificateException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Set; final class InstallPackageHelper { + /** + * Whether verification is enabled by default. + */ + private static final boolean DEFAULT_VERIFY_ENABLE = true; + private final PackageManagerService mPm; private final AppDataHelper mAppDataHelper; private final PackageManagerServiceInjector mInjector; + private final BroadcastHelper mBroadcastHelper; // TODO(b/198166813): remove PMS dependency InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) { mPm = pm; mInjector = pm.mInjector; mAppDataHelper = appDataHelper; + mBroadcastHelper = new BroadcastHelper(mInjector); } InstallPackageHelper(PackageManagerService pm) { @@ -97,6 +211,7 @@ final class InstallPackageHelper { mPm = pm; mInjector = injector; mAppDataHelper = new AppDataHelper(pm, mInjector); + mBroadcastHelper = new BroadcastHelper(injector); } @GuardedBy("mPm.mLock") @@ -699,9 +814,7 @@ final class InstallPackageHelper { mPm.mPermissionManager.onPackageInstalled(pkgSetting.getPkg(), Process.INVALID_UID /* previousAppId */, permissionParamsBuilder.build(), userId); - } - if (pkgSetting.getPkg() != null) { synchronized (mPm.mInstallLock) { // We don't need to freeze for a brand new install mAppDataHelper.prepareAppDataAfterInstallLIF(pkgSetting.getPkg()); @@ -881,4 +994,2580 @@ final class InstallPackageHelper { } return false; } + + public void processInstallRequests(boolean success, List installRequests) { + List apexInstallRequests = new ArrayList<>(); + List apkInstallRequests = new ArrayList<>(); + for (InstallRequest request : installRequests) { + if ((request.mArgs.mInstallFlags & PackageManager.INSTALL_APEX) != 0) { + apexInstallRequests.add(request); + } else { + apkInstallRequests.add(request); + } + } + // Note: supporting multi package install of both APEXes and APKs might requir some + // thinking to ensure atomicity of the install. + if (!apexInstallRequests.isEmpty() && !apkInstallRequests.isEmpty()) { + // This should've been caught at the validation step, but for some reason wasn't. + throw new IllegalStateException( + "Attempted to do a multi package install of both APEXes and APKs"); + } + if (!apexInstallRequests.isEmpty()) { + if (success) { + // Since installApexPackages requires talking to external service (apexd), we + // schedule to run it async. Once it finishes, it will resume the install. + Thread t = new Thread(() -> installApexPackagesTraced(apexInstallRequests), + "installApexPackages"); + t.start(); + } else { + // Non-staged APEX installation failed somewhere before + // processInstallRequestAsync. In that case just notify the observer about the + // failure. + InstallRequest request = apexInstallRequests.get(0); + mPm.notifyInstallObserver(request.mInstallResult, + request.mArgs.mObserver); + } + return; + } + if (success) { + for (InstallRequest request : apkInstallRequests) { + request.mArgs.doPreInstall(request.mInstallResult.mReturnCode); + } + synchronized (mPm.mInstallLock) { + installPackagesTracedLI(apkInstallRequests); + } + for (InstallRequest request : apkInstallRequests) { + request.mArgs.doPostInstall( + request.mInstallResult.mReturnCode, request.mInstallResult.mUid); + } + } + for (InstallRequest request : apkInstallRequests) { + restoreAndPostInstall(request.mArgs.mUser.getIdentifier(), + request.mInstallResult, + new PostInstallData(request.mArgs, + request.mInstallResult, null)); + } + } + + private void installApexPackagesTraced(List requests) { + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installApexPackages"); + installApexPackages(requests); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + private void installApexPackages(List requests) { + if (requests.isEmpty()) { + return; + } + if (requests.size() != 1) { + throw new IllegalStateException( + "Only a non-staged install of a single APEX is supported"); + } + InstallRequest request = requests.get(0); + try { + // Should directory scanning logic be moved to ApexManager for better test coverage? + final File dir = request.mArgs.mOriginInfo.mResolvedFile; + final File[] apexes = dir.listFiles(); + if (apexes == null) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + dir.getAbsolutePath() + " is not a directory"); + } + if (apexes.length != 1) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Expected exactly one .apex file under " + dir.getAbsolutePath() + + " got: " + apexes.length); + } + try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) { + mPm.mApexManager.installPackage(apexes[0], packageParser); + } + } catch (PackageManagerException e) { + request.mInstallResult.setError("APEX installation failed", e); + } + PackageManagerService.invalidatePackageInfoCache(); + mPm.notifyInstallObserver(request.mInstallResult, request.mArgs.mObserver); + } + + @GuardedBy("mInstallLock") + private void installPackagesTracedLI(List requests) { + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages"); + installPackagesLI(requests); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + /** + * Installs one or more packages atomically. This operation is broken up into four phases: + *

    + *
  • Prepare + *
    Analyzes any current install state, parses the package and does initial + * validation on it.
  • + *
  • Scan + *
    Interrogates the parsed packages given the context collected in prepare.
  • + *
  • Reconcile + *
    Validates scanned packages in the context of each other and the current system + * state to ensure that the install will be successful. + *
  • Commit + *
    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.
  • + *
+ * + * Failure at any phase will result in a full failure to install all packages. + */ + @GuardedBy("mInstallLock") + private void installPackagesLI(List requests) { + final Map preparedScans = new ArrayMap<>(requests.size()); + final Map installArgs = new ArrayMap<>(requests.size()); + final Map installResults = new ArrayMap<>(requests.size()); + final Map prepareResults = new ArrayMap<>(requests.size()); + final Map versionInfos = new ArrayMap<>(requests.size()); + final Map lastStaticSharedLibSettings = + new ArrayMap<>(requests.size()); + final Map createdAppId = new ArrayMap<>(requests.size()); + boolean success = false; + final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm); + 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; + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage"); + prepareResult = + preparePackageLI(request.mArgs, request.mInstallResult); + } catch (PrepareFailure prepareFailure) { + request.mInstallResult.setError(prepareFailure.error, + prepareFailure.getMessage()); + request.mInstallResult.mOrigPackage = prepareFailure.mConflictingPackage; + request.mInstallResult.mOrigPermission = prepareFailure.mConflictingPermission; + return; + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + request.mInstallResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED); + request.mInstallResult.mInstallerPackageName = + request.mArgs.mInstallSource.installerPackageName; + + final String packageName = prepareResult.mPackageToScan.getPackageName(); + prepareResults.put(packageName, prepareResult); + installResults.put(packageName, request.mInstallResult); + installArgs.put(packageName, request.mArgs); + try { + final ScanResult result = scanPackageHelper.scanPackageTracedLI( + prepareResult.mPackageToScan, prepareResult.mParseFlags, + prepareResult.mScanFlags, System.currentTimeMillis(), + request.mArgs.mUser, request.mArgs.mAbiOverride); + if (null != preparedScans.put(result.mPkgSetting.getPkg().getPackageName(), + result)) { + request.mInstallResult.setError( + PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE, + "Duplicate package " + + result.mPkgSetting.getPkg().getPackageName() + + " in multi-package install request."); + return; + } + createdAppId.put(packageName, + scanPackageHelper.optimisticallyRegisterAppId(result)); + versionInfos.put(result.mPkgSetting.getPkg().getPackageName(), + mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg())); + if (result.mStaticSharedLibraryInfo != null) { + final PackageSetting sharedLibLatestVersionSetting = + mPm.getSharedLibLatestVersionSetting(result); + if (sharedLibLatestVersionSetting != null) { + lastStaticSharedLibSettings.put( + result.mPkgSetting.getPkg().getPackageName(), + sharedLibLatestVersionSetting); + } + } + } catch (PackageManagerException e) { + request.mInstallResult.setError("Scanning Failed.", e); + return; + } + } + ReconcileRequest + reconcileRequest = new ReconcileRequest(preparedScans, installArgs, + installResults, + prepareResults, + mPm.mSharedLibraries, + Collections.unmodifiableMap(mPm.mPackages), versionInfos, + lastStaticSharedLibSettings); + CommitRequest commitRequest = null; + synchronized (mPm.mLock) { + Map reconciledPackages; + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages"); + reconciledPackages = reconcilePackagesLocked( + reconcileRequest, mPm.mSettings.getKeySetManagerService(), + mPm.mInjector); + } catch (ReconcileFailure e) { + for (InstallRequest request : requests) { + request.mInstallResult.setError("Reconciliation failed...", e); + } + return; + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages"); + commitRequest = new CommitRequest(reconciledPackages, + mPm.mUserManager.getUserIds()); + commitPackagesLocked(commitRequest); + success = true; + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + executePostCommitSteps(commitRequest); + } finally { + if (success) { + for (InstallRequest request : requests) { + final InstallArgs args = request.mArgs; + if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) { + continue; + } + if (args.mSigningDetails.getSignatureSchemeVersion() != SIGNING_BLOCK_V4) { + continue; + } + // For incremental installs, we bypass the verifier prior to install. Now + // that we know the package is valid, send a notice to the verifier with + // the root hash of the base.apk. + final String baseCodePath = request.mInstallResult.mPkg.getBaseApkPath(); + final String[] splitCodePaths = request.mInstallResult.mPkg.getSplitCodePaths(); + final Uri originUri = Uri.fromFile(args.mOriginInfo.mResolvedFile); + final int verificationId = mPm.mPendingVerificationToken++; + final String rootHashString = PackageManagerServiceUtils + .buildVerificationRootHashString(baseCodePath, splitCodePaths); + VerificationUtils.broadcastPackageVerified(verificationId, originUri, + PackageManager.VERIFICATION_ALLOW, rootHashString, + args.mDataLoaderType, args.getUser(), mPm.mContext); + } + } else { + for (ScanResult result : preparedScans.values()) { + if (createdAppId.getOrDefault(result.mRequest.mParsedPackage.getPackageName(), + false)) { + scanPackageHelper.cleanUpAppIdCreation(result); + } + } + // TODO(b/194319951): create a more descriptive reason than unknown + // mark all non-failure installs as UNKNOWN so we do not treat them as success + for (InstallRequest request : requests) { + if (request.mInstallResult.mFreezer != null) { + request.mInstallResult.mFreezer.close(); + } + if (request.mInstallResult.mReturnCode == PackageManager.INSTALL_SUCCEEDED) { + request.mInstallResult.mReturnCode = PackageManager.INSTALL_UNKNOWN; + } + } + } + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + @GuardedBy("mInstallLock") + private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res) + throws PrepareFailure { + final int installFlags = args.mInstallFlags; + final File tmpPackageFile = new File(args.getCodePath()); + final boolean onExternal = args.mVolumeUuid != null; + final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0); + final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0); + final boolean virtualPreload = + ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0); + final boolean isRollback = args.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK; + @PackageManagerService.ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE; + if (args.mMoveInfo != null) { + // moving a complete application; perform an initial scan on the new install location + scanFlags |= SCAN_INITIAL; + } + if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) { + scanFlags |= SCAN_DONT_KILL_APP; + } + if (instantApp) { + scanFlags |= SCAN_AS_INSTANT_APP; + } + if (fullApp) { + scanFlags |= SCAN_AS_FULL_APP; + } + if (virtualPreload) { + scanFlags |= SCAN_AS_VIRTUAL_PRELOAD; + } + + if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile); + + // Validity check + if (instantApp && onExternal) { + Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal); + throw new PrepareFailure(PackageManager.INSTALL_FAILED_SESSION_INVALID); + } + + // Retrieve PackageSettings and parse package + @ParsingPackageUtils.ParseFlags final int parseFlags = + mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY + | ParsingPackageUtils.PARSE_ENFORCE_CODE + | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0); + + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); + final ParsedPackage parsedPackage; + try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) { + parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false); + AndroidPackageUtils.validatePackageDexMetadata(parsedPackage); + } catch (PackageManagerException e) { + throw new PrepareFailure("Failed parse during installPackageLI", e); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + // Instant apps have several additional install-time checks. + if (instantApp) { + if (parsedPackage.getTargetSdkVersion() < Build.VERSION_CODES.O) { + Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName() + + " does not target at least O"); + throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID, + "Instant app package must target at least O"); + } + if (parsedPackage.getSharedUserId() != null) { + Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName() + + " may not declare sharedUserId."); + throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID, + "Instant app package may not declare a sharedUserId"); + } + } + + if (parsedPackage.isStaticSharedLibrary()) { + // Static shared libraries have synthetic package names + PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage); + + // No static shared libs on external storage + if (onExternal) { + Slog.i(TAG, "Static shared libs can only be installed on internal storage."); + throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION, + "Packages declaring static-shared libs cannot be updated"); + } + } + + String pkgName = res.mName = parsedPackage.getPackageName(); + if (parsedPackage.isTestOnly()) { + if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) { + throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI"); + } + } + + // either use what we've been given or parse directly from the APK + if (args.mSigningDetails != SigningDetails.UNKNOWN) { + parsedPackage.setSigningDetails(args.mSigningDetails); + } else { + final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); + final ParseResult result = ParsingPackageUtils.getSigningDetails( + input, parsedPackage, false /*skipVerify*/); + if (result.isError()) { + throw new PrepareFailure("Failed collect during installPackageLI", + result.getException()); + } + parsedPackage.setSigningDetails(result.getResult()); + } + + if (instantApp && parsedPackage.getSigningDetails().getSignatureSchemeVersion() + < SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2) { + Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName() + + " is not signed with at least APK Signature Scheme v2"); + throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID, + "Instant app package must be signed with APK Signature Scheme v2 or greater"); + } + + boolean systemApp = false; + boolean replace = false; + synchronized (mPm.mLock) { + // Check if installing already existing package + if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { + String oldName = mPm.mSettings.getRenamedPackageLPr(pkgName); + if (parsedPackage.getOriginalPackages().contains(oldName) + && mPm.mPackages.containsKey(oldName)) { + // This package is derived from an original package, + // and this device has been updating from that original + // name. We must continue using the original name, so + // rename the new package here. + parsedPackage.setPackageName(oldName); + pkgName = parsedPackage.getPackageName(); + replace = true; + if (DEBUG_INSTALL) { + Slog.d(TAG, "Replacing existing renamed package: oldName=" + + oldName + " pkgName=" + pkgName); + } + } else if (mPm.mPackages.containsKey(pkgName)) { + // This package, under its official name, already exists + // on the device; we should replace it. + replace = true; + if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing package: " + pkgName); + } + + if (replace) { + // Prevent apps opting out from runtime permissions + AndroidPackage oldPackage = mPm.mPackages.get(pkgName); + final int oldTargetSdk = oldPackage.getTargetSdkVersion(); + final int newTargetSdk = parsedPackage.getTargetSdkVersion(); + if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1 + && newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) { + throw new PrepareFailure( + PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE, + "Package " + parsedPackage.getPackageName() + + " new target SDK " + newTargetSdk + + " doesn't support runtime permissions but the old" + + " target SDK " + oldTargetSdk + " does."); + } + // Prevent persistent apps from being updated + if (oldPackage.isPersistent() + && ((installFlags & PackageManager.INSTALL_STAGED) == 0)) { + throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK, + "Package " + oldPackage.getPackageName() + " is a persistent app. " + + "Persistent apps are not updateable."); + } + } + } + + PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName); + if (ps != null) { + if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps); + + // Static shared libs have same package with different versions where + // we internally use a synthetic package name to allow multiple versions + // of the same package, therefore we need to compare signatures against + // the package setting for the latest library version. + PackageSetting signatureCheckPs = ps; + if (parsedPackage.isStaticSharedLibrary()) { + SharedLibraryInfo libraryInfo = mPm.getLatestSharedLibraVersionLPr( + parsedPackage); + if (libraryInfo != null) { + signatureCheckPs = mPm.mSettings.getPackageLPr( + libraryInfo.getPackageName()); + } + } + + // Quick validity 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. + final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService(); + if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) { + if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) { + throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + + parsedPackage.getPackageName() + " upgrade keys do not match the " + + "previously installed version"); + } + } else { + try { + final boolean compareCompat = + isCompatSignatureUpdateNeeded(parsedPackage); + final boolean compareRecover = + isRecoverSignatureUpdateNeeded(parsedPackage); + // We don't care about disabledPkgSetting on install for now. + final boolean compatMatch = verifySignatures(signatureCheckPs, null, + parsedPackage.getSigningDetails(), compareCompat, compareRecover, + isRollback); + // The new KeySets will be re-added later in the scanning process. + if (compatMatch) { + synchronized (mPm.mLock) { + ksms.removeAppKeySetDataLPw(parsedPackage.getPackageName()); + } + } + } catch (PackageManagerException e) { + throw new PrepareFailure(e.error, e.getMessage()); + } + } + + if (ps.getPkg() != null) { + systemApp = ps.getPkg().isSystem(); + } + res.mOrigUsers = ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true); + } + + final int numGroups = ArrayUtils.size(parsedPackage.getPermissionGroups()); + for (int groupNum = 0; groupNum < numGroups; groupNum++) { + final ParsedPermissionGroup group = + parsedPackage.getPermissionGroups().get(groupNum); + final PermissionGroupInfo sourceGroup = mPm.getPermissionGroupInfo(group.getName(), + 0); + + if (sourceGroup != null && cannotInstallWithBadPermissionGroups(parsedPackage)) { + final String sourcePackageName = sourceGroup.packageName; + + if ((replace || !parsedPackage.getPackageName().equals(sourcePackageName)) + && !doesSignatureMatchForPermissions(sourcePackageName, parsedPackage, + scanFlags)) { + EventLog.writeEvent(0x534e4554, "146211400", -1, + parsedPackage.getPackageName()); + + throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP, + "Package " + + parsedPackage.getPackageName() + + " attempting to redeclare permission group " + + group.getName() + " already owned by " + + sourcePackageName); + } + } + } + + // TODO: Move logic for checking permission compatibility into PermissionManagerService + final int n = ArrayUtils.size(parsedPackage.getPermissions()); + for (int i = n - 1; i >= 0; i--) { + final ParsedPermission perm = parsedPackage.getPermissions().get(i); + final Permission bp = mPm.mPermissionManager.getPermissionTEMP(perm.getName()); + + // Don't allow anyone but the system to define ephemeral permissions. + if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0 + && !systemApp) { + Slog.w(TAG, "Non-System package " + parsedPackage.getPackageName() + + " attempting to delcare ephemeral permission " + + perm.getName() + "; Removing ephemeral."); + ComponentMutateUtils.setProtectionLevel(perm, + perm.getProtectionLevel() & ~PermissionInfo.PROTECTION_FLAG_INSTANT); + } + + // Check whether the newly-scanned package wants to define an already-defined perm + if (bp != null) { + final String sourcePackageName = bp.getPackageName(); + + if (!doesSignatureMatchForPermissions(sourcePackageName, parsedPackage, + scanFlags)) { + // If the owning package is the system itself, we log but allow + // install to proceed; we fail the install on all other permission + // redefinitions. + if (!sourcePackageName.equals("android")) { + throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION, + "Package " + + parsedPackage.getPackageName() + + " attempting to redeclare permission " + + perm.getName() + " already owned by " + + sourcePackageName) + .conflictsWithExistingPermission(perm.getName(), + sourcePackageName); + } else { + Slog.w(TAG, "Package " + parsedPackage.getPackageName() + + " attempting to redeclare system permission " + + perm.getName() + "; ignoring new declaration"); + parsedPackage.removePermission(i); + } + } else if (!PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())) { + // Prevent apps to change protection level to dangerous from any other + // type as this would allow a privilege escalation where an app adds a + // normal/signature permission in other app's group and later redefines + // it as dangerous leading to the group auto-grant. + if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_MASK_BASE) + == PermissionInfo.PROTECTION_DANGEROUS) { + if (bp != null && !bp.isRuntime()) { + Slog.w(TAG, "Package " + parsedPackage.getPackageName() + + " trying to change a non-runtime permission " + + perm.getName() + + " to runtime; keeping old protection level"); + ComponentMutateUtils.setProtectionLevel(perm, + bp.getProtectionLevel()); + } + } + } + } + + if (perm.getGroup() != null + && cannotInstallWithBadPermissionGroups(parsedPackage)) { + boolean isPermGroupDefinedByPackage = false; + for (int groupNum = 0; groupNum < numGroups; groupNum++) { + if (parsedPackage.getPermissionGroups().get(groupNum).getName() + .equals(perm.getGroup())) { + isPermGroupDefinedByPackage = true; + break; + } + } + + if (!isPermGroupDefinedByPackage) { + final PermissionGroupInfo sourceGroup = + mPm.getPermissionGroupInfo(perm.getGroup(), 0); + + if (sourceGroup == null) { + EventLog.writeEvent(0x534e4554, "146211400", -1, + parsedPackage.getPackageName()); + + throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP, + "Package " + + parsedPackage.getPackageName() + + " attempting to declare permission " + + perm.getName() + " in non-existing group " + + perm.getGroup()); + } else { + String groupSourcePackageName = sourceGroup.packageName; + + if (!PLATFORM_PACKAGE_NAME.equals(groupSourcePackageName) + && !doesSignatureMatchForPermissions(groupSourcePackageName, + parsedPackage, scanFlags)) { + EventLog.writeEvent(0x534e4554, "146211400", -1, + parsedPackage.getPackageName()); + + throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP, + "Package " + + parsedPackage.getPackageName() + + " attempting to declare permission " + + perm.getName() + " in group " + + perm.getGroup() + " owned by package " + + groupSourcePackageName + + " with incompatible certificate"); + } + } + } + } + } + } + + if (systemApp) { + if (onExternal) { + // Abort update; system app can't be replaced with app on sdcard + throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION, + "Cannot install updates to system apps on sdcard"); + } else if (instantApp) { + // Abort update; system app can't be replaced with an instant app + throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID, + "Cannot update a system app with an instant app"); + } + } + + if (args.mMoveInfo != null) { + // We did an in-place move, so dex is ready to roll + scanFlags |= SCAN_NO_DEX; + scanFlags |= SCAN_MOVE; + + synchronized (mPm.mLock) { + final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName); + if (ps == null) { + res.setError(INSTALL_FAILED_INTERNAL_ERROR, + "Missing settings for moved package " + pkgName); + } + + // We moved the entire application as-is, so bring over the + // previously derived ABI information. + parsedPackage.setPrimaryCpuAbi(ps.getPrimaryCpuAbi()) + .setSecondaryCpuAbi(ps.getSecondaryCpuAbi()); + } + + } else { + // Enable SCAN_NO_DEX flag to skip dexopt at a later stage + scanFlags |= SCAN_NO_DEX; + + try { + PackageSetting pkgSetting; + AndroidPackage oldPackage; + synchronized (mPm.mLock) { + pkgSetting = mPm.mSettings.getPackageLPr(pkgName); + oldPackage = mPm.mPackages.get(pkgName); + } + boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null + && pkgSetting.getPkgState().isUpdatedSystemApp(); + final String abiOverride = deriveAbiOverride(args.mAbiOverride); + boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem(); + final Pair + derivedAbi = mPm.mInjector.getAbiHelper().derivePackageAbi(parsedPackage, + isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred, + abiOverride, mPm.mAppLib32InstallDir); + derivedAbi.first.applyTo(parsedPackage); + derivedAbi.second.applyTo(parsedPackage); + } catch (PackageManagerException pme) { + Slog.e(TAG, "Error deriving application ABI", pme); + throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, + "Error deriving application ABI: " + pme.getMessage()); + } + } + + if (!args.doRename(res.mReturnCode, parsedPackage)) { + throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename"); + } + + try { + setUpFsVerityIfPossible(parsedPackage); + } catch (Installer.InstallerException | IOException | DigestException + | NoSuchAlgorithmException e) { + throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, + "Failed to set up verity: " + e); + } + + final PackageFreezer freezer = + freezePackageForInstall(pkgName, installFlags, "installPackageLI"); + boolean shouldCloseFreezerBeforeReturn = true; + try { + final AndroidPackage oldPackage; + String renamedPackage; + boolean sysPkg = false; + int targetScanFlags = scanFlags; + int targetParseFlags = parseFlags; + final PackageSetting ps; + final PackageSetting disabledPs; + if (replace) { + final String pkgName11 = parsedPackage.getPackageName(); + synchronized (mPm.mLock) { + oldPackage = mPm.mPackages.get(pkgName11); + } + if (parsedPackage.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 installed from adb which is useful for development. + if (oldPackage != null + && (installFlags & PackageManager.INSTALL_FROM_ADB) == 0) { + throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PACKAGE, + "Packages declaring " + + "static-shared libs cannot be updated"); + } + } + + final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0; + + final int[] allUsers; + final int[] installedUsers; + final int[] uninstalledUsers; + + synchronized (mPm.mLock) { + if (DEBUG_INSTALL) { + Slog.d(TAG, + "replacePackageLI: new=" + parsedPackage + ", old=" + oldPackage); + } + + ps = mPm.mSettings.getPackageLPr(pkgName11); + disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(ps); + + // verify signatures are valid + final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService(); + if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) { + if (!ksms.checkUpgradeKeySetLocked(ps, parsedPackage)) { + throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, + "New package not signed by keys specified by upgrade-keysets: " + + pkgName11); + } + } else { + SigningDetails parsedPkgSigningDetails = parsedPackage.getSigningDetails(); + SigningDetails oldPkgSigningDetails = oldPackage.getSigningDetails(); + // default to original signature matching + if (!parsedPkgSigningDetails.checkCapability(oldPkgSigningDetails, + SigningDetails.CertCapabilities.INSTALLED_DATA) + && !oldPkgSigningDetails.checkCapability(parsedPkgSigningDetails, + SigningDetails.CertCapabilities.ROLLBACK)) { + // Allow the update to proceed if this is a rollback and the parsed + // package's current signing key is the current signer or in the lineage + // of the old package; this allows a rollback to a previously installed + // version after an app's signing key has been rotated without requiring + // the rollback capability on the previous signing key. + if (!isRollback || !oldPkgSigningDetails.hasAncestorOrSelf( + parsedPkgSigningDetails)) { + throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, + "New package has a different signature: " + pkgName11); + } + } + } + + // don't allow a system upgrade unless the upgrade hash matches + if (oldPackage.getRestrictUpdateHash() != null && oldPackage.isSystem()) { + final byte[] digestBytes; + try { + final MessageDigest digest = MessageDigest.getInstance("SHA-512"); + updateDigest(digest, new File(parsedPackage.getBaseApkPath())); + if (!ArrayUtils.isEmpty(parsedPackage.getSplitCodePaths())) { + for (String path : parsedPackage.getSplitCodePaths()) { + updateDigest(digest, new File(path)); + } + } + digestBytes = digest.digest(); + } catch (NoSuchAlgorithmException | IOException e) { + throw new PrepareFailure(INSTALL_FAILED_INVALID_APK, + "Could not compute hash: " + pkgName11); + } + if (!Arrays.equals(oldPackage.getRestrictUpdateHash(), digestBytes)) { + throw new PrepareFailure(INSTALL_FAILED_INVALID_APK, + "New package fails restrict-update check: " + pkgName11); + } + // retain upgrade restriction + parsedPackage.setRestrictUpdateHash(oldPackage.getRestrictUpdateHash()); + } + + // Check for shared user id changes + if (!Objects.equals(oldPackage.getSharedUserId(), + parsedPackage.getSharedUserId()) + // Don't mark as invalid if the app is trying to + // leave a sharedUserId + && parsedPackage.getSharedUserId() != null) { + throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED, + "Package " + parsedPackage.getPackageName() + + " shared user changed from " + + (oldPackage.getSharedUserId() != null + ? oldPackage.getSharedUserId() : "") + + " to " + parsedPackage.getSharedUserId()); + } + + // In case of rollback, remember per-user/profile install state + allUsers = mPm.mUserManager.getUserIds(); + installedUsers = ps.queryInstalledUsers(allUsers, true); + uninstalledUsers = ps.queryInstalledUsers(allUsers, false); + + + // don't allow an upgrade from full to ephemeral + if (isInstantApp) { + if (args.mUser == null + || args.mUser.getIdentifier() == UserHandle.USER_ALL) { + for (int currentUser : allUsers) { + if (!ps.getInstantApp(currentUser)) { + // can't downgrade from full to instant + Slog.w(TAG, + "Can't replace full app with instant app: " + pkgName11 + + " for user: " + currentUser); + throw new PrepareFailure( + PackageManager.INSTALL_FAILED_SESSION_INVALID); + } + } + } else if (!ps.getInstantApp(args.mUser.getIdentifier())) { + // can't downgrade from full to instant + Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11 + + " for user: " + args.mUser.getIdentifier()); + throw new PrepareFailure( + PackageManager.INSTALL_FAILED_SESSION_INVALID); + } + } + } + + // Update what is removed + res.mRemovedInfo = new PackageRemovedInfo(mPm); + res.mRemovedInfo.mUid = oldPackage.getUid(); + res.mRemovedInfo.mRemovedPackage = oldPackage.getPackageName(); + res.mRemovedInfo.mInstallerPackageName = ps.getInstallSource().installerPackageName; + res.mRemovedInfo.mIsStaticSharedLib = + parsedPackage.getStaticSharedLibName() != null; + res.mRemovedInfo.mIsUpdate = true; + res.mRemovedInfo.mOrigUsers = installedUsers; + res.mRemovedInfo.mInstallReasons = new SparseArray<>(installedUsers.length); + for (int i = 0; i < installedUsers.length; i++) { + final int userId = installedUsers[i]; + res.mRemovedInfo.mInstallReasons.put(userId, ps.getInstallReason(userId)); + } + res.mRemovedInfo.mUninstallReasons = new SparseArray<>(uninstalledUsers.length); + for (int i = 0; i < uninstalledUsers.length; i++) { + final int userId = uninstalledUsers[i]; + res.mRemovedInfo.mUninstallReasons.put(userId, ps.getUninstallReason(userId)); + } + + sysPkg = oldPackage.isSystem(); + if (sysPkg) { + // Set the system/privileged/oem/vendor/product flags as needed + final boolean privileged = oldPackage.isPrivileged(); + final boolean oem = oldPackage.isOem(); + final boolean vendor = oldPackage.isVendor(); + final boolean product = oldPackage.isProduct(); + final boolean odm = oldPackage.isOdm(); + final boolean systemExt = oldPackage.isSystemExt(); + final @ParsingPackageUtils.ParseFlags int systemParseFlags = parseFlags; + final @PackageManagerService.ScanFlags int systemScanFlags = scanFlags + | SCAN_AS_SYSTEM + | (privileged ? SCAN_AS_PRIVILEGED : 0) + | (oem ? SCAN_AS_OEM : 0) + | (vendor ? SCAN_AS_VENDOR : 0) + | (product ? SCAN_AS_PRODUCT : 0) + | (odm ? SCAN_AS_ODM : 0) + | (systemExt ? SCAN_AS_SYSTEM_EXT : 0); + + if (DEBUG_INSTALL) { + Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage + + ", old=" + oldPackage); + } + res.setReturnCode(PackageManager.INSTALL_SUCCEEDED); + targetParseFlags = systemParseFlags; + targetScanFlags = systemScanFlags; + } else { // non system replace + replace = true; + if (DEBUG_INSTALL) { + Slog.d(TAG, + "replaceNonSystemPackageLI: new=" + parsedPackage + ", old=" + + oldPackage); + } + } + } else { // new package install + ps = null; + disabledPs = null; + replace = false; + oldPackage = null; + // Remember this for later, in case we need to rollback this install + String pkgName1 = parsedPackage.getPackageName(); + + if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + parsedPackage); + + // TODO(b/194319951): MOVE TO RECONCILE + synchronized (mPm.mLock) { + renamedPackage = mPm.mSettings.getRenamedPackageLPr(pkgName1); + if (renamedPackage != null) { + // A package with the same name is already installed, though + // it has been renamed to an older name. The package we + // are trying to install should be installed as an update to + // the existing one, but that has not been requested, so bail. + throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS, + "Attempt to re-install " + pkgName1 + + " without first uninstalling package running as " + + renamedPackage); + } + if (mPm.mPackages.containsKey(pkgName1)) { + // Don't allow installation over an existing package with the same name. + throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS, + "Attempt to re-install " + pkgName1 + + " without first uninstalling."); + } + } + } + // we're passing the freezer back to be closed in a later phase of install + shouldCloseFreezerBeforeReturn = false; + + return new PrepareResult(replace, targetScanFlags, targetParseFlags, + oldPackage, parsedPackage, replace /* clearCodeCache */, sysPkg, + ps, disabledPs); + } finally { + res.mFreezer = freezer; + if (shouldCloseFreezerBeforeReturn) { + freezer.close(); + } + } + } + + /* + * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges + * as this only works for packages that are installed + * + * TODO: Move logic for permission group compatibility into PermissionManagerService + */ + @SuppressWarnings("AndroidFrameworkCompatChange") + private static boolean cannotInstallWithBadPermissionGroups(ParsedPackage parsedPackage) { + return parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S; + } + + private boolean doesSignatureMatchForPermissions(@NonNull String sourcePackageName, + @NonNull ParsedPackage parsedPackage, int scanFlags) { + // If the defining package is signed with our cert, it's okay. This + // also includes the "updating the same package" case, of course. + // "updating same package" could also involve key-rotation. + + final PackageSetting sourcePackageSetting; + final KeySetManagerService ksms; + synchronized (mPm.mLock) { + sourcePackageSetting = mPm.mSettings.getPackageLPr(sourcePackageName); + ksms = mPm.mSettings.getKeySetManagerService(); + } + + final SigningDetails sourceSigningDetails = (sourcePackageSetting == null + ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails()); + if (sourcePackageName.equals(parsedPackage.getPackageName()) + && (ksms.shouldCheckUpgradeKeySetLocked( + sourcePackageSetting, scanFlags))) { + return ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage); + } else { + + // in the event of signing certificate rotation, we need to see if the + // package's certificate has rotated from the current one, or if it is an + // older certificate with which the current is ok with sharing permissions + if (sourceSigningDetails.checkCapability( + parsedPackage.getSigningDetails(), + SigningDetails.CertCapabilities.PERMISSION)) { + return true; + } else if (parsedPackage.getSigningDetails().checkCapability( + sourceSigningDetails, + SigningDetails.CertCapabilities.PERMISSION)) { + // the scanned package checks out, has signing certificate rotation + // history, and is newer; bring it over + synchronized (mPm.mLock) { + sourcePackageSetting.setSigningDetails(parsedPackage.getSigningDetails()); + } + return true; + } else { + return false; + } + } + } + + /** + * Set up fs-verity for the given package if possible. This requires a feature flag of system + * property to be enabled only if the kernel supports fs-verity. + * + *

When the feature flag is set to legacy mode, only APK is supported (with some experimental + * kernel patches). In normal mode, all file format can be supported. + */ + private void setUpFsVerityIfPossible(AndroidPackage pkg) throws Installer.InstallerException, + PrepareFailure, IOException, DigestException, NoSuchAlgorithmException { + final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled(); + final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled(); + if (!standardMode && !legacyMode) { + return; + } + + if (isIncrementalPath(pkg.getPath()) && IncrementalManager.getVersion() + < IncrementalManager.MIN_VERSION_TO_SUPPORT_FSVERITY) { + return; + } + + // Collect files we care for fs-verity setup. + ArrayMap fsverityCandidates = new ArrayMap<>(); + if (legacyMode) { + synchronized (mPm.mLock) { + final PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName()); + if (ps != null && ps.isPrivileged()) { + fsverityCandidates.put(pkg.getBaseApkPath(), null); + if (pkg.getSplitCodePaths() != null) { + for (String splitPath : pkg.getSplitCodePaths()) { + fsverityCandidates.put(splitPath, null); + } + } + } + } + } else { + // NB: These files will become only accessible if the signing key is loaded in kernel's + // .fs-verity keyring. + fsverityCandidates.put(pkg.getBaseApkPath(), + VerityUtils.getFsveritySignatureFilePath(pkg.getBaseApkPath())); + + final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk( + pkg.getBaseApkPath()); + if (new File(dmPath).exists()) { + fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath)); + } + + if (pkg.getSplitCodePaths() != null) { + for (String path : pkg.getSplitCodePaths()) { + fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path)); + + final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path); + if (new File(splitDmPath).exists()) { + fsverityCandidates.put(splitDmPath, + VerityUtils.getFsveritySignatureFilePath(splitDmPath)); + } + } + } + } + + for (Map.Entry entry : fsverityCandidates.entrySet()) { + final String filePath = entry.getKey(); + final String signaturePath = entry.getValue(); + + if (!legacyMode) { + // fs-verity is optional for now. Only set up if signature is provided. + if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) { + try { + VerityUtils.setUpFsverity(filePath, signaturePath); + } catch (IOException e) { + throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE, + "Failed to enable fs-verity: " + e); + } + } + continue; + } + + // In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN. + final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(filePath); + if (result.isOk()) { + if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling verity to " + filePath); + final FileDescriptor fd = result.getUnownedFileDescriptor(); + try { + final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath); + try { + // A file may already have fs-verity, e.g. when reused during a split + // install. If the measurement succeeds, no need to attempt to set up. + mPm.mInstaller.assertFsverityRootHashMatches(filePath, rootHash); + } catch (Installer.InstallerException e) { + mPm.mInstaller.installApkVerity(filePath, fd, result.getContentSize()); + mPm.mInstaller.assertFsverityRootHashMatches(filePath, rootHash); + } + } finally { + IoUtils.closeQuietly(fd); + } + } else if (result.isFailed()) { + throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE, + "Failed to generate verity"); + } + } + } + + private PackageFreezer freezePackageForInstall(String packageName, int installFlags, + String killReason) { + return freezePackageForInstall(packageName, UserHandle.USER_ALL, installFlags, killReason); + } + + private PackageFreezer freezePackageForInstall(String packageName, int userId, int installFlags, + String killReason) { + if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) { + return new PackageFreezer(mPm); + } else { + return mPm.freezePackage(packageName, userId, killReason); + } + } + + private static void updateDigest(MessageDigest digest, File file) throws IOException { + try (DigestInputStream digestStream = + new DigestInputStream(new FileInputStream(file), digest)) { + int length, total = 0; + while ((length = digestStream.read()) != -1) { + total += length; + } // just plow through the file + } + } + + @GuardedBy("mLock") + private void commitPackagesLocked(final CommitRequest request) { + // TODO: remove any expected failures from this method; this should only be able to fail due + // to unavoidable errors (I/O, etc.) + for (ReconciledPackage reconciledPkg : request.mReconciledPackages.values()) { + final ScanResult scanResult = reconciledPkg.mScanResult; + final ScanRequest scanRequest = scanResult.mRequest; + final ParsedPackage parsedPackage = scanRequest.mParsedPackage; + final String packageName = parsedPackage.getPackageName(); + final PackageInstalledInfo res = reconciledPkg.mInstallResult; + final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm); + final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm); + + if (reconciledPkg.mPrepareResult.mReplace) { + AndroidPackage oldPackage = mPm.mPackages.get(packageName); + + // Set the update and install times + PackageSetting deletedPkgSetting = mPm.getPackageSetting( + oldPackage.getPackageName()); + reconciledPkg.mPkgSetting + .setFirstInstallTime(deletedPkgSetting.getFirstInstallTime()) + .setLastUpdateTime(System.currentTimeMillis()); + + res.mRemovedInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList( + reconciledPkg.mPkgSetting, request.mAllUsers, + mPm.mSettings.getPackagesLocked()); + if (reconciledPkg.mPrepareResult.mSystem) { + // Remove existing system package + removePackageHelper.removePackageLI(oldPackage, true); + if (!disableSystemPackageLPw(oldPackage)) { + // We didn't need to disable the .apk as a current system package, + // which means we are replacing another update that is already + // installed. We need to make sure to delete the older one's .apk. + res.mRemovedInfo.mArgs = new FileInstallArgs( + oldPackage.getPath(), + getAppDexInstructionSets( + AndroidPackageUtils.getPrimaryCpuAbi(oldPackage, + deletedPkgSetting), + AndroidPackageUtils.getSecondaryCpuAbi(oldPackage, + deletedPkgSetting)), mPm); + } else { + res.mRemovedInfo.mArgs = null; + } + } else { + try { + // Settings will be written during the call to updateSettingsLI(). + deletePackageHelper.executeDeletePackageLIF( + reconciledPkg.mDeletePackageAction, packageName, + true, request.mAllUsers, false); + } catch (SystemDeleteException e) { + if (mPm.mIsEngBuild) { + throw new RuntimeException("Unexpected failure", e); + // ignore; not possible for non-system app + } + } + // Successfully deleted the old package; proceed with replace. + + // If deleted package lived in a container, give users a chance to + // relinquish resources before killing. + if (oldPackage.isExternalStorage()) { + if (DEBUG_INSTALL) { + Slog.i(TAG, "upgrading pkg " + oldPackage + + " is ASEC-hosted -> UNAVAILABLE"); + } + final int[] uidArray = new int[]{oldPackage.getUid()}; + final ArrayList pkgList = new ArrayList<>(1); + pkgList.add(oldPackage.getPackageName()); + mBroadcastHelper.sendResourcesChangedBroadcast( + false, true, pkgList, uidArray, null); + } + + // Update the in-memory copy of the previous code paths. + PackageSetting ps1 = mPm.mSettings.getPackageLPr( + reconciledPkg.mPrepareResult.mExistingPackage.getPackageName()); + if ((reconciledPkg.mInstallArgs.mInstallFlags & PackageManager.DONT_KILL_APP) + == 0) { + if (ps1.mOldCodePaths == null) { + ps1.mOldCodePaths = new ArraySet<>(); + } + Collections.addAll(ps1.mOldCodePaths, oldPackage.getBaseApkPath()); + if (oldPackage.getSplitCodePaths() != null) { + Collections.addAll(ps1.mOldCodePaths, oldPackage.getSplitCodePaths()); + } + } else { + ps1.mOldCodePaths = null; + } + + if (reconciledPkg.mInstallResult.mReturnCode + == PackageManager.INSTALL_SUCCEEDED) { + PackageSetting ps2 = mPm.mSettings.getPackageLPr( + parsedPackage.getPackageName()); + if (ps2 != null) { + res.mRemovedInfo.mRemovedForAllUsers = + mPm.mPackages.get(ps2.getPackageName()) == null; + } + } + } + } + + AndroidPackage pkg = commitReconciledScanResultLocked( + reconciledPkg, request.mAllUsers); + updateSettingsLI(pkg, reconciledPkg, request.mAllUsers, res); + + final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); + if (ps != null) { + res.mNewUsers = ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true); + ps.setUpdateAvailable(false /*updateAvailable*/); + } + if (res.mReturnCode == PackageManager.INSTALL_SUCCEEDED) { + mPm.updateSequenceNumberLP(ps, res.mNewUsers); + mPm.updateInstantAppInstallerLocked(packageName); + } + } + ApplicationPackageManager.invalidateGetPackagesForUidCache(); + } + + @GuardedBy("mLock") + private boolean disableSystemPackageLPw(AndroidPackage oldPkg) { + return mPm.mSettings.disableSystemPackageLPw(oldPkg.getPackageName(), true); + } + + private void updateSettingsLI(AndroidPackage newPackage, ReconciledPackage reconciledPkg, + int[] allUsers, PackageInstalledInfo res) { + updateSettingsInternalLI(newPackage, reconciledPkg, allUsers, res); + } + + private void updateSettingsInternalLI(AndroidPackage pkg, ReconciledPackage reconciledPkg, + int[] allUsers, PackageInstalledInfo res) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings"); + + final String pkgName = pkg.getPackageName(); + final int[] installedForUsers = res.mOrigUsers; + final InstallArgs installArgs = reconciledPkg.mInstallArgs; + final int installReason = installArgs.mInstallReason; + InstallSource installSource = installArgs.mInstallSource; + final String installerPackageName = installSource.installerPackageName; + + if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath()); + synchronized (mPm.mLock) { + // For system-bundled packages, we assume that installing an upgraded version + // of the package implies that the user actually wants to run that new code, + // so we enable the package. + final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName); + final int userId = installArgs.mUser.getIdentifier(); + if (ps != null) { + if (pkg.isSystem()) { + if (DEBUG_INSTALL) { + Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName); + } + // Enable system package for requested users + if (res.mOrigUsers != null) { + for (int origUserId : res.mOrigUsers) { + if (userId == UserHandle.USER_ALL || userId == origUserId) { + ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, + origUserId, installerPackageName); + } + } + } + // Also convey the prior install/uninstall state + if (allUsers != null && installedForUsers != null) { + for (int currentUserId : allUsers) { + final boolean installed = ArrayUtils.contains( + installedForUsers, currentUserId); + if (DEBUG_INSTALL) { + Slog.d(TAG, " user " + currentUserId + " => " + installed); + } + ps.setInstalled(installed, currentUserId); + } + // these install state changes will be persisted in the + // upcoming call to mSettings.writeLPr(). + } + + if (allUsers != null) { + for (int currentUserId : allUsers) { + ps.resetOverrideComponentLabelIcon(currentUserId); + } + } + } + + // Retrieve the overlays for shared libraries of the package. + if (!ps.getPkgState().getUsesLibraryInfos().isEmpty()) { + for (SharedLibraryInfo sharedLib : ps.getPkgState().getUsesLibraryInfos()) { + for (int currentUserId : UserManagerService.getInstance().getUserIds()) { + if (!sharedLib.isDynamic()) { + // TODO(146804378): Support overlaying static shared libraries + continue; + } + final PackageSetting libPs = mPm.mSettings.getPackageLPr( + sharedLib.getPackageName()); + if (libPs == null) { + continue; + } + ps.setOverlayPathsForLibrary(sharedLib.getName(), + libPs.getOverlayPaths(currentUserId), currentUserId); + } + } + } + + // It's implied that when a user requests installation, they want the app to be + // installed and enabled. (This does not apply to USER_ALL, which here means only + // install on users for which the app is already installed). + if (userId != UserHandle.USER_ALL) { + ps.setInstalled(true, userId); + ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName); + } + + mPm.mSettings.addInstallerPackageNames(ps.getInstallSource()); + + // When replacing an existing package, preserve the original install reason for all + // users that had the package installed before. Similarly for uninstall reasons. + final Set previousUserIds = new ArraySet<>(); + if (res.mRemovedInfo != null && res.mRemovedInfo.mInstallReasons != null) { + final int installReasonCount = res.mRemovedInfo.mInstallReasons.size(); + for (int i = 0; i < installReasonCount; i++) { + final int previousUserId = res.mRemovedInfo.mInstallReasons.keyAt(i); + final int previousInstallReason = + res.mRemovedInfo.mInstallReasons.valueAt(i); + ps.setInstallReason(previousInstallReason, previousUserId); + previousUserIds.add(previousUserId); + } + } + if (res.mRemovedInfo != null && res.mRemovedInfo.mUninstallReasons != null) { + for (int i = 0; i < res.mRemovedInfo.mUninstallReasons.size(); i++) { + final int previousUserId = res.mRemovedInfo.mUninstallReasons.keyAt(i); + final int previousReason = res.mRemovedInfo.mUninstallReasons.valueAt(i); + ps.setUninstallReason(previousReason, previousUserId); + } + } + + // Set install reason for users that are having the package newly installed. + final int[] allUsersList = mPm.mUserManager.getUserIds(); + if (userId == UserHandle.USER_ALL) { + // TODO(b/152629990): It appears that the package doesn't actually get newly + // installed in this case, so the installReason shouldn't get modified? + for (int currentUserId : allUsersList) { + if (!previousUserIds.contains(currentUserId)) { + ps.setInstallReason(installReason, currentUserId); + } + } + } else if (!previousUserIds.contains(userId)) { + ps.setInstallReason(installReason, userId); + } + + // TODO(b/169721400): generalize Incremental States and create a Callback object + // that can be used for all the packages. + final String codePath = ps.getPathString(); + if (IncrementalManager.isIncrementalPath(codePath) + && mPm.mIncrementalManager != null) { + mPm.mIncrementalManager.registerLoadingProgressCallback(codePath, + new IncrementalProgressListener(ps.getPackageName(), mPm)); + } + + // Ensure that the uninstall reason is UNKNOWN for users with the package installed. + for (int currentUserId : allUsersList) { + if (ps.getInstalled(currentUserId)) { + ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, currentUserId); + } + } + + mPm.mSettings.writeKernelMappingLPr(ps); + + final PermissionManagerServiceInternal.PackageInstalledParams.Builder + permissionParamsBuilder = + new PermissionManagerServiceInternal.PackageInstalledParams.Builder(); + final boolean grantPermissions = (installArgs.mInstallFlags + & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0; + if (grantPermissions) { + final List grantedPermissions = + installArgs.mInstallGrantPermissions != null + ? Arrays.asList(installArgs.mInstallGrantPermissions) + : pkg.getRequestedPermissions(); + permissionParamsBuilder.setGrantedPermissions(grantedPermissions); + } + final boolean allowlistAllRestrictedPermissions = + (installArgs.mInstallFlags + & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0; + final List allowlistedRestrictedPermissions = + allowlistAllRestrictedPermissions ? pkg.getRequestedPermissions() + : installArgs.mAllowlistedRestrictedPermissions; + if (allowlistedRestrictedPermissions != null) { + permissionParamsBuilder.setAllowlistedRestrictedPermissions( + allowlistedRestrictedPermissions); + } + final int autoRevokePermissionsMode = installArgs.mAutoRevokePermissionsMode; + permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode); + final ScanResult scanResult = reconciledPkg.mScanResult; + mPm.mPermissionManager.onPackageInstalled(pkg, scanResult.mPreviousAppId, + permissionParamsBuilder.build(), userId); + } + res.mName = pkgName; + res.mUid = pkg.getUid(); + res.mPkg = pkg; + res.setReturnCode(PackageManager.INSTALL_SUCCEEDED); + //to update install status + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings"); + mPm.writeSettingsLPrTEMP(); + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + /** + * 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 com.android.server.pm.PackageManagerService.mLock}. + */ + private void executePostCommitSteps(CommitRequest commitRequest) { + final ArraySet incrementalStorages = new ArraySet<>(); + final AppDataHelper appDataHelper = new AppDataHelper(mPm); + for (ReconciledPackage reconciledPkg : commitRequest.mReconciledPackages.values()) { + final boolean instantApp = ((reconciledPkg.mScanResult.mRequest.mScanFlags + & SCAN_AS_INSTANT_APP) != 0); + final AndroidPackage pkg = reconciledPkg.mPkgSetting.getPkg(); + final String packageName = pkg.getPackageName(); + final String codePath = pkg.getPath(); + final boolean onIncremental = mPm.mIncrementalManager != null + && isIncrementalPath(codePath); + if (onIncremental) { + IncrementalStorage storage = mPm.mIncrementalManager.openStorage(codePath); + if (storage == null) { + throw new IllegalArgumentException( + "Install: null storage for incremental package " + packageName); + } + incrementalStorages.add(storage); + } + appDataHelper.prepareAppDataAfterInstallLIF(pkg); + if (reconciledPkg.mPrepareResult.mClearCodeCache) { + appDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL, + FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL + | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); + } + if (reconciledPkg.mPrepareResult.mReplace) { + mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(), + pkg.getBaseApkPath(), pkg.getSplitCodePaths()); + } + + // Prepare the application profiles for the new code paths. + // This needs to be done before invoking dexopt so that any install-time profile + // can be used for optimizations. + mPm.mArtManagerService.prepareAppProfiles( + pkg, + mPm.resolveUserIds(reconciledPkg.mInstallArgs.mUser.getIdentifier()), + /* updateReferenceProfileContent= */ true); + + // Compute the compilation reason from the installation scenario. + final int compilationReason = + mPm.getDexManager().getCompilationReasonForInstallScenario( + reconciledPkg.mInstallArgs.mInstallScenario); + + // Construct the DexoptOptions early to see if we should skip running 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. + final boolean isBackupOrRestore = + reconciledPkg.mInstallArgs.mInstallReason == INSTALL_REASON_DEVICE_RESTORE + || reconciledPkg.mInstallArgs.mInstallReason + == INSTALL_REASON_DEVICE_SETUP; + + final int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE + | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE + | (isBackupOrRestore ? DexoptOptions.DEXOPT_FOR_RESTORE : 0); + DexoptOptions dexoptOptions = + new DexoptOptions(packageName, compilationReason, dexoptFlags); + + // Check whether we need to dexopt the app. + // + // NOTE: it is IMPORTANT to call dexopt: + // - after doRename which will sync the package data from AndroidPackage and + // its corresponding ApplicationInfo. + // - after installNewPackageLIF or replacePackageLIF which will update result with the + // uid of the application (pkg.applicationInfo.uid). + // This update happens in place! + // + // We only need to dexopt if the package meets ALL of the following conditions: + // 1) it is not an instant app or if it is then dexopt is enabled via gservices. + // 2) it is not debuggable. + // 3) it is not on Incremental File System. + // + // Note that we do not dexopt instant apps by default. dexopt can take some time to + // complete, so we skip this step during installation. Instead, we'll take extra time + // the first time the instant app starts. It's preferred to do it this way to provide + // continuous progress to the useur instead of mysteriously blocking somewhere in the + // middle of running an instant app. The default behaviour can be overridden + // via gservices. + // + // Furthermore, dexopt may be skipped, depending on the install scenario and current + // state of the device. + // + // TODO(b/174695087): instantApp and onIncremental should be removed and their install + // path moved to SCENARIO_FAST. + final boolean performDexopt = + (!instantApp || android.provider.Settings.Global.getInt( + mPm.mContext.getContentResolver(), + android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) + && !pkg.isDebuggable() + && (!onIncremental) + && dexoptOptions.isCompilationEnabled(); + + if (performDexopt) { + // Compile the layout resources. + if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts"); + mPm.mViewCompiler.compileLayouts(pkg); + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); + ScanResult result = reconciledPkg.mScanResult; + + // This mirrors logic from commitReconciledScanResultLocked, where the library files + // needed for dexopt are assigned. + // TODO: Fix this to have 1 mutable PackageSetting for scan/install. If the previous + // setting needs to be passed to have a comparison, hide it behind an immutable + // interface. There's no good reason to have 3 different ways to access the real + // PackageSetting object, only one of which is actually correct. + PackageSetting realPkgSetting = result.mExistingSettingCopied + ? result.mRequest.mPkgSetting : result.mPkgSetting; + if (realPkgSetting == null) { + realPkgSetting = reconciledPkg.mPkgSetting; + } + + // Unfortunately, the updated system app flag is only tracked on this PackageSetting + boolean isUpdatedSystemApp = reconciledPkg.mPkgSetting.getPkgState() + .isUpdatedSystemApp(); + + realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp); + + mPm.mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting, + null /* instructionSets */, + mPm.getOrCreateCompilerPackageStats(pkg), + mPm.getDexManager().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.getService().notifyPackageChanged(packageName); + + notifyPackageChangeObserversOnUpdate(reconciledPkg); + } + waitForNativeBinariesExtraction(incrementalStorages); + } + + private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) { + final PackageSetting pkgSetting = reconciledPkg.mPkgSetting; + final PackageInstalledInfo pkgInstalledInfo = reconciledPkg.mInstallResult; + final PackageRemovedInfo pkgRemovedInfo = pkgInstalledInfo.mRemovedInfo; + + PackageChangeEvent pkgChangeEvent = new PackageChangeEvent(); + pkgChangeEvent.packageName = pkgSetting.getPkg().getPackageName(); + pkgChangeEvent.version = pkgSetting.getLongVersionCode(); + pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.getLastUpdateTime(); + pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.mIsUpdate); + pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.mDataRemoved); + pkgChangeEvent.isDeleted = false; + + mPm.notifyPackageChangeObservers(pkgChangeEvent); + } + + private static void waitForNativeBinariesExtraction( + ArraySet incrementalStorages) { + if (incrementalStorages.isEmpty()) { + return; + } + try { + // Native library extraction may take very long time: each page could potentially + // wait for either 10s or 100ms (adb vs non-adb data loader), and that easily adds + // up to a full watchdog timeout of 1 min, killing the system after that. It doesn't + // make much sense as blocking here doesn't lock up the framework, but only blocks + // the installation session and the following ones. + Watchdog.getInstance().pauseWatchingCurrentThread("native_lib_extract"); + for (int i = 0; i < incrementalStorages.size(); ++i) { + IncrementalStorage storage = incrementalStorages.valueAtUnchecked(i); + storage.waitForNativeBinariesExtraction(); + } + } finally { + Watchdog.getInstance().resumeWatchingCurrentThread("native_lib_extract"); + } + } + + public int installLocationPolicy(PackageInfoLite pkgLite, int installFlags) { + String packageName = pkgLite.packageName; + int installLocation = pkgLite.installLocation; + // reader + synchronized (mPm.mLock) { + // Currently installed package which the new package is attempting to replace or + // null if no such package is installed. + AndroidPackage installedPkg = mPm.mPackages.get(packageName); + + if (installedPkg != null) { + if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { + // Check for updated system application. + if (installedPkg.isSystem()) { + return PackageHelper.RECOMMEND_INSTALL_INTERNAL; + } else { + // If current upgrade specifies particular preference + if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { + // Application explicitly specified internal. + return PackageHelper.RECOMMEND_INSTALL_INTERNAL; + } else if ( + installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) { + // App explicitly prefers external. Let policy decide + } else { + // Prefer previous location + if (installedPkg.isExternalStorage()) { + return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; + } + return PackageHelper.RECOMMEND_INSTALL_INTERNAL; + } + } + } else { + // Invalid install. Return error code + return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS; + } + } + } + return pkgLite.recommendedInstallLocation; + } + + Pair verifyReplacingVersionCode(PackageInfoLite pkgLite, + long requiredInstalledVersionCode, int installFlags) { + if ((installFlags & PackageManager.INSTALL_APEX) != 0) { + return verifyReplacingVersionCodeForApex( + pkgLite, requiredInstalledVersionCode, installFlags); + } + + String packageName = pkgLite.packageName; + synchronized (mPm.mLock) { + // Package which currently owns the data that the new package will own if installed. + // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg + // will be null whereas dataOwnerPkg will contain information about the package + // which was uninstalled while keeping its data. + AndroidPackage dataOwnerPkg = mPm.mPackages.get(packageName); + if (dataOwnerPkg == null) { + PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); + if (ps != null) { + dataOwnerPkg = ps.getPkg(); + } + } + + if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) { + if (dataOwnerPkg == null) { + String errorMsg = "Required installed version code was " + + requiredInstalledVersionCode + + " but package is not installed"; + Slog.w(TAG, errorMsg); + return Pair.create( + PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg); + } + + if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) { + String errorMsg = "Required installed version code was " + + requiredInstalledVersionCode + + " but actual installed version is " + + dataOwnerPkg.getLongVersionCode(); + Slog.w(TAG, errorMsg); + return Pair.create( + PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg); + } + } + + if (dataOwnerPkg != null) { + if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, + dataOwnerPkg.isDebuggable())) { + try { + checkDowngrade(dataOwnerPkg, pkgLite); + } catch (PackageManagerException e) { + String errorMsg = "Downgrade detected: " + e.getMessage(); + Slog.w(TAG, errorMsg); + return Pair.create( + PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg); + } + } + } + } + return Pair.create(PackageManager.INSTALL_SUCCEEDED, null); + } + + private Pair verifyReplacingVersionCodeForApex(PackageInfoLite pkgLite, + long requiredInstalledVersionCode, int installFlags) { + String packageName = pkgLite.packageName; + + final PackageInfo activePackage = mPm.mApexManager.getPackageInfo(packageName, + ApexManager.MATCH_ACTIVE_PACKAGE); + if (activePackage == null) { + String errorMsg = "Attempting to install new APEX package " + packageName; + Slog.w(TAG, errorMsg); + return Pair.create(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED, errorMsg); + } + + final long activeVersion = activePackage.getLongVersionCode(); + if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST + && activeVersion != requiredInstalledVersionCode) { + String errorMsg = "Installed version of APEX package " + packageName + + " does not match required. Active version: " + activeVersion + + " required: " + requiredInstalledVersionCode; + Slog.w(TAG, errorMsg); + return Pair.create(PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg); + } + + final boolean isAppDebuggable = (activePackage.applicationInfo.flags + & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + final long newVersionCode = pkgLite.getLongVersionCode(); + if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, isAppDebuggable) + && newVersionCode < activeVersion) { + String errorMsg = "Downgrade of APEX package " + packageName + + " is not allowed. Active version: " + activeVersion + + " attempted: " + newVersionCode; + Slog.w(TAG, errorMsg); + return Pair.create(PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg); + } + + return Pair.create(PackageManager.INSTALL_SUCCEEDED, null); + } + + /** + * Check and throw if the given before/after packages would be considered a + * downgrade. + */ + private static void checkDowngrade(AndroidPackage before, PackageInfoLite after) + throws PackageManagerException { + if (after.getLongVersionCode() < before.getLongVersionCode()) { + throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE, + "Update version code " + after.versionCode + " is older than current " + + before.getLongVersionCode()); + } else if (after.getLongVersionCode() == before.getLongVersionCode()) { + if (after.baseRevisionCode < before.getBaseRevisionCode()) { + throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE, + "Update base revision code " + after.baseRevisionCode + + " is older than current " + before.getBaseRevisionCode()); + } + + if (!ArrayUtils.isEmpty(after.splitNames)) { + for (int i = 0; i < after.splitNames.length; i++) { + final String splitName = after.splitNames[i]; + final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName); + if (j != -1) { + if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) { + throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE, + "Update split " + splitName + " revision code " + + after.splitRevisionCodes[i] + + " is older than current " + + before.getSplitRevisionCodes()[j]); + } + } + } + } + } + } + + int getUidForVerifier(VerifierInfo verifierInfo) { + synchronized (mPm.mLock) { + final AndroidPackage pkg = mPm.mPackages.get(verifierInfo.packageName); + if (pkg == null) { + return -1; + } else if (pkg.getSigningDetails().getSignatures().length != 1) { + Slog.i(TAG, "Verifier package " + verifierInfo.packageName + + " has more than one signature; ignoring"); + return -1; + } + + /* + * If the public key of the package's signature does not match + * our expected public key, then this is a different package and + * we should skip. + */ + + final byte[] expectedPublicKey; + try { + final Signature verifierSig = pkg.getSigningDetails().getSignatures()[0]; + final PublicKey publicKey = verifierSig.getPublicKey(); + expectedPublicKey = publicKey.getEncoded(); + } catch (CertificateException e) { + return -1; + } + + final byte[] actualPublicKey = verifierInfo.publicKey.getEncoded(); + + if (!Arrays.equals(actualPublicKey, expectedPublicKey)) { + Slog.i(TAG, "Verifier package " + verifierInfo.packageName + + " does not have the expected public key; ignoring"); + return -1; + } + + return pkg.getUid(); + } + } + + /** + * Check whether or not package verification has been enabled. + * + * @return true if verification should be performed + */ + boolean isVerificationEnabled(PackageInfoLite pkgInfoLite, int userId, int installFlags, + int installerUid) { + if (!DEFAULT_VERIFY_ENABLE) { + return false; + } + + // Check if installing from ADB + if ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0) { + if (mPm.isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS)) { + return true; + } + // Check if the developer wants to skip verification for ADB installs + if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) { + synchronized (mPm.mLock) { + if (mPm.mSettings.getPackageLPr(pkgInfoLite.packageName) == null) { + // Always verify fresh install + return true; + } + } + // Only skip when apk is debuggable + return !pkgInfoLite.debuggable; + } + return android.provider.Settings.Global.getInt(mPm.mContext.getContentResolver(), + android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0; + } + + // only when not installed from ADB, skip verification for instant apps when + // the installer and verifier are the same. + if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) { + if (mPm.mInstantAppInstallerActivity != null + && mPm.mInstantAppInstallerActivity.packageName.equals( + mPm.mRequiredVerifierPackage)) { + try { + mPm.mInjector.getSystemService(AppOpsManager.class) + .checkPackage(installerUid, mPm.mRequiredVerifierPackage); + if (DEBUG_VERIFY) { + Slog.i(TAG, "disable verification for instant app"); + } + return false; + } catch (SecurityException ignore) { } + } + } + return true; + } + + public void sendPendingBroadcasts() { + String[] packages; + ArrayList[] components; + int size = 0; + int[] uids; + Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); + + synchronized (mPm.mLock) { + size = mPm.mPendingBroadcasts.size(); + if (size <= 0) { + // Nothing to be done. Just return + return; + } + packages = new String[size]; + components = new ArrayList[size]; + uids = new int[size]; + int i = 0; // filling out the above arrays + + for (int n = 0; n < mPm.mPendingBroadcasts.userIdCount(); n++) { + final int packageUserId = mPm.mPendingBroadcasts.userIdAt(n); + final ArrayMap> componentsToBroadcast = + mPm.mPendingBroadcasts.packagesForUserId(packageUserId); + final int numComponents = componentsToBroadcast.size(); + for (int index = 0; i < size && index < numComponents; index++) { + packages[i] = componentsToBroadcast.keyAt(index); + components[i] = componentsToBroadcast.valueAt(index); + final PackageSetting ps = mPm.mSettings.getPackageLPr(packages[i]); + uids[i] = (ps != null) + ? UserHandle.getUid(packageUserId, ps.getAppId()) + : -1; + i++; + } + } + size = i; + mPm.mPendingBroadcasts.clear(); + } + // Send broadcasts + for (int i = 0; i < size; i++) { + mPm.sendPackageChangedBroadcast(packages[i], true /* dontKillApp */, + components[i], uids[i], null /* reason */); + } + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + } + + void handlePackagePostInstall(PackageInstalledInfo res, InstallArgs installArgs, + boolean launchedForRestore) { + final boolean killApp = + (installArgs.mInstallFlags & PackageManager.INSTALL_DONT_KILL_APP) == 0; + final boolean virtualPreload = + ((installArgs.mInstallFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0); + final String installerPackage = installArgs.mInstallSource.installerPackageName; + final IPackageInstallObserver2 installObserver = installArgs.mObserver; + final int dataLoaderType = installArgs.mDataLoaderType; + final boolean succeeded = res.mReturnCode == PackageManager.INSTALL_SUCCEEDED; + final boolean update = res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null; + final String packageName = res.mName; + final PackageSetting pkgSetting = succeeded ? mPm.getPackageSetting(packageName) : null; + final boolean removedBeforeUpdate = (pkgSetting == null) + || (pkgSetting.isSystem() && !pkgSetting.getPathString().equals( + res.mPkg.getPath())); + if (succeeded && removedBeforeUpdate) { + Slog.e(TAG, packageName + " was removed before handlePackagePostInstall " + + "could be executed"); + res.mReturnCode = INSTALL_FAILED_PACKAGE_CHANGED; + res.mReturnMsg = "Package was removed before install could complete."; + + // Remove the update failed package's older resources safely now + InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null; + if (args != null) { + synchronized (mPm.mInstallLock) { + args.doPostDeleteLI(true); + } + } + mPm.notifyInstallObserver(res, installObserver); + return; + } + + if (succeeded) { + // Clear the uid cache after we installed a new package. + mPm.mPerUidReadTimeoutsCache = null; + + // Send the removed broadcasts + if (res.mRemovedInfo != null) { + res.mRemovedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/); + } + + final String installerPackageName = + res.mInstallerPackageName != null + ? res.mInstallerPackageName + : res.mRemovedInfo != null + ? res.mRemovedInfo.mInstallerPackageName + : null; + + synchronized (mPm.mLock) { + mPm.mInstantAppRegistry.onPackageInstalledLPw(res.mPkg, res.mNewUsers); + } + + // Determine the set of users who are adding this package for + // the first time vs. those who are seeing an update. + int[] firstUserIds = EMPTY_INT_ARRAY; + int[] firstInstantUserIds = EMPTY_INT_ARRAY; + int[] updateUserIds = EMPTY_INT_ARRAY; + int[] instantUserIds = EMPTY_INT_ARRAY; + final boolean allNewUsers = res.mOrigUsers == null || res.mOrigUsers.length == 0; + for (int newUser : res.mNewUsers) { + final boolean isInstantApp = pkgSetting.getInstantApp(newUser); + if (allNewUsers) { + if (isInstantApp) { + firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser); + } else { + firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser); + } + continue; + } + boolean isNew = true; + for (int origUser : res.mOrigUsers) { + if (origUser == newUser) { + isNew = false; + break; + } + } + if (isNew) { + if (isInstantApp) { + firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser); + } else { + firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser); + } + } else { + if (isInstantApp) { + instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser); + } else { + updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser); + } + } + } + + // Send installed broadcasts if the package is not a static shared lib. + if (res.mPkg.getStaticSharedLibName() == null) { + mPm.mProcessLoggingHandler.invalidateBaseApkHash(res.mPkg.getBaseApkPath()); + + // Send added for users that see the package for the first time + // sendPackageAddedForNewUsers also deals with system apps + int appId = UserHandle.getAppId(res.mUid); + boolean isSystem = res.mPkg.isSystem(); + mPm.sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload, + virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds, + dataLoaderType); + + // Send added for users that don't see the package for the first time + Bundle extras = new Bundle(1); + extras.putInt(Intent.EXTRA_UID, res.mUid); + if (update) { + extras.putBoolean(Intent.EXTRA_REPLACING, true); + } + extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType); + // Send to all running apps. + final SparseArray newBroadcastAllowList; + synchronized (mPm.mLock) { + newBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList( + mPm.getPackageSettingInternal(packageName, Process.SYSTEM_UID), + updateUserIds, mPm.mSettings.getPackagesLocked()); + } + mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, + extras, 0 /*flags*/, + null /*targetPackage*/, null /*finishedReceiver*/, + updateUserIds, instantUserIds, newBroadcastAllowList, null); + if (installerPackageName != null) { + // Send to the installer, even if it's not running. + mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, + extras, 0 /*flags*/, + installerPackageName, null /*finishedReceiver*/, + updateUserIds, instantUserIds, null /* broadcastAllowList */, null); + } + // if the required verifier is defined, but, is not the installer of record + // for the package, it gets notified + final boolean notifyVerifier = mPm.mRequiredVerifierPackage != null + && !mPm.mRequiredVerifierPackage.equals(installerPackageName); + if (notifyVerifier) { + mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, + extras, 0 /*flags*/, + mPm.mRequiredVerifierPackage, null /*finishedReceiver*/, + updateUserIds, instantUserIds, null /* broadcastAllowList */, null); + } + // If package installer is defined, notify package installer about new + // app installed + if (mPm.mRequiredInstallerPackage != null) { + mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, + extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/, + mPm.mRequiredInstallerPackage, null /*finishedReceiver*/, + firstUserIds, instantUserIds, null /* broadcastAllowList */, null); + } + + // Send replaced for users that don't see the package for the first time + if (update) { + mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, + packageName, extras, 0 /*flags*/, + null /*targetPackage*/, null /*finishedReceiver*/, + updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList, + null); + if (installerPackageName != null) { + mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, + extras, 0 /*flags*/, + installerPackageName, null /*finishedReceiver*/, + updateUserIds, instantUserIds, null /*broadcastAllowList*/, null); + } + if (notifyVerifier) { + mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, + extras, 0 /*flags*/, + mPm.mRequiredVerifierPackage, null /*finishedReceiver*/, + updateUserIds, instantUserIds, null /*broadcastAllowList*/, null); + } + mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, + null /*package*/, null /*extras*/, 0 /*flags*/, + packageName /*targetPackage*/, + null /*finishedReceiver*/, updateUserIds, instantUserIds, + null /*broadcastAllowList*/, + mBroadcastHelper.getTemporaryAppAllowlistBroadcastOptions( + REASON_PACKAGE_REPLACED).toBundle()); + } else if (launchedForRestore && !res.mPkg.isSystem()) { + // First-install and we did a restore, so we're responsible for the + // first-launch broadcast. + if (DEBUG_BACKUP) { + Slog.i(TAG, "Post-restore of " + packageName + + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds)); + } + mBroadcastHelper.sendFirstLaunchBroadcast(packageName, installerPackage, + firstUserIds, firstInstantUserIds); + } + + // Send broadcast package appeared if external for all users + if (res.mPkg.isExternalStorage()) { + if (!update) { + final StorageManager storage = mPm.mInjector.getSystemService( + StorageManager.class); + VolumeInfo volume = + storage.findVolumeByUuid( + StorageManager.convert( + res.mPkg.getVolumeUuid()).toString()); + int packageExternalStorageType = + PackageManagerServiceUtils.getPackageExternalStorageType(volume, + res.mPkg.isExternalStorage()); + // If the package was installed externally, log it. + if (packageExternalStorageType != StorageEnums.UNKNOWN) { + FrameworkStatsLog.write( + FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED, + packageExternalStorageType, packageName); + } + } + if (DEBUG_INSTALL) { + Slog.i(TAG, "upgrading pkg " + res.mPkg + " is external"); + } + final int[] uidArray = new int[]{res.mPkg.getUid()}; + ArrayList pkgList = new ArrayList<>(1); + pkgList.add(packageName); + mBroadcastHelper.sendResourcesChangedBroadcast( + true, true, pkgList, uidArray, null); + } + } else if (!ArrayUtils.isEmpty(res.mLibraryConsumers)) { // if static shared lib + for (int i = 0; i < res.mLibraryConsumers.size(); i++) { + AndroidPackage pkg = res.mLibraryConsumers.get(i); + // send broadcast that all consumers of the static shared library have changed + mPm.sendPackageChangedBroadcast(pkg.getPackageName(), false /* dontKillApp */, + new ArrayList<>(Collections.singletonList(pkg.getPackageName())), + pkg.getUid(), null); + } + } + + // Work that needs to happen on first install within each user + if (firstUserIds.length > 0) { + for (int userId : firstUserIds) { + mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName, + userId); + } + } + + if (allNewUsers && !update) { + mPm.notifyPackageAdded(packageName, res.mUid); + } else { + mPm.notifyPackageChanged(packageName, res.mUid); + } + + // Log current value of "unknown sources" setting + EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED, + getUnknownSourcesSettings()); + + // Remove the replaced package's older resources safely now + InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null; + if (args != null) { + if (!killApp) { + // If we didn't kill the app, defer the deletion of code/resource files, since + // they may still be in use by the running application. This mitigates problems + // in cases where resources or code is loaded by a new Activity before + // ApplicationInfo changes have propagated to all application threads. + mPm.scheduleDeferredNoKillPostDelete(args); + } else { + synchronized (mPm.mInstallLock) { + args.doPostDeleteLI(true); + } + } + } else { + // Force a gc to clear up things. Ask for a background one, it's fine to go on + // and not block here. + VMRuntime.getRuntime().requestConcurrentGC(); + } + + // Notify DexManager that the package was installed for new users. + // The updated users should already be indexed and the package code paths + // should not change. + // Don't notify the manager for ephemeral apps as they are not expected to + // survive long enough to benefit of background optimizations. + for (int userId : firstUserIds) { + PackageInfo info = mPm.getPackageInfo(packageName, /*flags*/ 0, userId); + // There's a race currently where some install events may interleave with an + // uninstall. This can lead to package info being null (b/36642664). + if (info != null) { + mPm.getDexManager().notifyPackageInstalled(info, userId); + } + } + } + + final boolean deferInstallObserver = succeeded && update && !killApp; + if (deferInstallObserver) { + mPm.scheduleDeferredNoKillInstallObserver(res, installObserver); + } else { + mPm.notifyInstallObserver(res, installObserver); + } + + // Log tracing if needed + if (installArgs.mTraceMethod != null) { + Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, installArgs.mTraceMethod, + installArgs.mTraceCookie); + } + } + + /** + * Get the "allow unknown sources" setting. + * + * @return the current "allow unknown sources" setting + */ + private int getUnknownSourcesSettings() { + return android.provider.Settings.Secure.getIntForUser(mPm.mContext.getContentResolver(), + android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS, + -1, UserHandle.USER_SYSTEM); + } + + /** + * Uncompress and install stub applications. + *

In order to save space on the system partition, some applications are shipped in a + * compressed form. In addition the compressed bits for the full application, the + * system image contains a tiny stub comprised of only the Android manifest. + *

During the first boot, attempt to uncompress and install the full application. If + * the application can't be installed for any reason, disable the stub and prevent + * uncompressing the full application during future boots. + *

In order to forcefully attempt an installation of a full application, go to app + * settings and enable the application. + */ + @GuardedBy({"mPm.mLock", "mPm.mInstallLock"}) + void installSystemStubPackages(@NonNull List systemStubPackageNames, + @PackageManagerService.ScanFlags int scanFlags) { + for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) { + final String packageName = systemStubPackageNames.get(i); + // skip if the system package is already disabled + if (mPm.mSettings.isDisabledSystemPackageLPr(packageName)) { + systemStubPackageNames.remove(i); + continue; + } + // skip if the package isn't installed (?!); this should never happen + final AndroidPackage pkg = mPm.mPackages.get(packageName); + if (pkg == null) { + systemStubPackageNames.remove(i); + continue; + } + // skip if the package has been disabled by the user + final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); + if (ps != null) { + final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM); + if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { + systemStubPackageNames.remove(i); + continue; + } + } + + // install the package to replace the stub on /system + try { + installStubPackageLI(pkg, 0, scanFlags); + ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, + UserHandle.USER_SYSTEM, "android"); + systemStubPackageNames.remove(i); + } catch (PackageManagerException e) { + Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage()); + } + + // any failed attempt to install the package will be cleaned up later + } + + // disable any stub still left; these failed to install the full application + for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) { + final String pkgName = systemStubPackageNames.get(i); + final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName); + ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + UserHandle.USER_SYSTEM, "android"); + logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName); + } + } + + /** + * Extract, install and enable a stub package. + *

If the compressed file can not be extracted / installed for any reason, the stub + * APK will be installed and the package will be disabled. To recover from this situation, + * the user will need to go into system settings and re-enable the package. + */ + @GuardedBy({"mPm.mLock", "mPm.mInstallLock"}) + boolean enableCompressedPackage(AndroidPackage stubPkg, + @NonNull PackageSetting stubPkgSetting) { + final int parseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY + | ParsingPackageUtils.PARSE_ENFORCE_CODE; + synchronized (mPm.mInstallLock) { + final AndroidPackage pkg; + try (PackageFreezer freezer = + mPm.freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) { + pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/); + synchronized (mPm.mLock) { + mAppDataHelper.prepareAppDataAfterInstallLIF(pkg); + try { + mPm.updateSharedLibrariesLocked(pkg, stubPkgSetting, null, null, + Collections.unmodifiableMap(mPm.mPackages)); + } catch (PackageManagerException e) { + Slog.w(TAG, "updateAllSharedLibrariesLPw failed: ", e); + } + mPm.mPermissionManager.onPackageInstalled(pkg, + Process.INVALID_UID /* previousAppId */, + PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT, + UserHandle.USER_ALL); + mPm.writeSettingsLPrTEMP(); + } + } catch (PackageManagerException e) { + // Whoops! Something went very wrong; roll back to the stub and disable the package + try (PackageFreezer freezer = + mPm.freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) { + synchronized (mPm.mLock) { + // NOTE: Ensure the system package is enabled; even for a compressed stub. + // If we don't, installing the system package fails during scan + mPm.mSettings.enableSystemPackageLPw(stubPkg.getPackageName()); + } + installPackageFromSystemLIF(stubPkg.getPath(), + mPm.mUserManager.getUserIds() /*allUserHandles*/, + null /*origUserHandles*/, + true /*writeSettings*/); + } catch (PackageManagerException pme) { + // Serious WTF; we have to be able to install the stub + Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(), + pme); + } finally { + // Disable the package; the stub by itself is not runnable + synchronized (mPm.mLock) { + final PackageSetting stubPs = mPm.mSettings.getPackageLPr( + stubPkg.getPackageName()); + if (stubPs != null) { + stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, + UserHandle.USER_SYSTEM, "android"); + } + mPm.writeSettingsLPrTEMP(); + } + } + return false; + } + mAppDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL, + FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL + | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); + mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(), + pkg.getBaseApkPath(), pkg.getSplitCodePaths()); + } + return true; + } + + @GuardedBy({"mPm.mLock", "mPm.mInstallLock"}) + private AndroidPackage installStubPackageLI(AndroidPackage stubPkg, + @ParsingPackageUtils.ParseFlags int parseFlags, + @PackageManagerService.ScanFlags int scanFlags) + throws PackageManagerException { + if (DEBUG_COMPRESSION) { + Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.getPackageName()); + } + // uncompress the binary to its eventual destination on /data + final File scanFile = decompressPackage(stubPkg.getPackageName(), stubPkg.getPath()); + if (scanFile == null) { + throw new PackageManagerException( + "Unable to decompress stub at " + stubPkg.getPath()); + } + synchronized (mPm.mLock) { + mPm.mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/); + } + final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm); + removePackageHelper.removePackageLI(stubPkg, true /*chatty*/); + final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm); + try { + return scanPackageHelper.scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null); + } catch (PackageManagerException e) { + Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(), + e); + // Remove the failed install + removePackageHelper.removeCodePathLI(scanFile); + throw e; + } + } + + /** + * Decompresses the given package on the system image onto + * the /data partition. + * @return The directory the package was decompressed into. Otherwise, {@code null}. + */ + @GuardedBy("mPm.mInstallLock") + private File decompressPackage(String packageName, String codePath) { + if (!compressedFileExists(codePath)) { + if (DEBUG_COMPRESSION) { + Slog.i(TAG, "No files to decompress at: " + codePath); + } + return null; + } + final File dstCodePath = + PackageManagerServiceUtils.getNextCodePath(Environment.getDataAppDirectory(null), + packageName); + int ret = PackageManagerServiceUtils.decompressFiles(codePath, dstCodePath, packageName); + if (ret == PackageManager.INSTALL_SUCCEEDED) { + ret = extractNativeBinaries(dstCodePath, packageName); + } + if (ret == PackageManager.INSTALL_SUCCEEDED) { + // NOTE: During boot, we have to delay releasing cblocks for no other reason than + // we cannot retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}. + // When we no longer need to read that setting, cblock release can occur always + // occur here directly + if (!mPm.isSystemReady()) { + if (mPm.mReleaseOnSystemReady == null) { + mPm.mReleaseOnSystemReady = new ArrayList<>(); + } + mPm.mReleaseOnSystemReady.add(dstCodePath); + } else { + final ContentResolver resolver = mPm.mContext.getContentResolver(); + F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath); + } + } else { + if (!dstCodePath.exists()) { + return null; + } + new RemovePackageHelper(mPm).removeCodePathLI(dstCodePath); + return null; + } + + return dstCodePath; + } + + private static int extractNativeBinaries(File dstCodePath, String packageName) { + final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME); + NativeLibraryHelper.Handle handle = null; + try { + handle = NativeLibraryHelper.Handle.create(dstCodePath); + return NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot, + null /*abiOverride*/, false /*isIncremental*/); + } catch (IOException e) { + logCriticalInfo(Log.ERROR, "Failed to extract native libraries" + + "; pkg: " + packageName); + return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + } finally { + IoUtils.closeQuietly(handle); + } + } + + /** + * Tries to restore the disabled system package after an update has been deleted. + */ + @GuardedBy({"mPm.mLock", "mPm.mInstallLock"}) + public void restoreDisabledSystemPackageLIF(DeletePackageAction action, + PackageSetting deletedPs, @NonNull int[] allUserHandles, + @Nullable PackageRemovedInfo outInfo, + boolean writeSettings, + PackageSetting disabledPs) + throws SystemDeleteException { + // writer + synchronized (mPm.mLock) { + // NOTE: The system package always needs to be enabled; even if it's for + // a compressed stub. If we don't, installing the system package fails + // during scan [scanning checks the disabled packages]. We will reverse + // this later, after we've "installed" the stub. + // Reinstate the old system package + mPm.mSettings.enableSystemPackageLPw(disabledPs.getPkg().getPackageName()); + // Remove any native libraries from the upgraded package. + removeNativeBinariesLI(deletedPs); + } + + // Install the system package + if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs); + try { + installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles, + outInfo == null ? null : outInfo.mOrigUsers, writeSettings); + } catch (PackageManagerException e) { + Slog.w(TAG, "Failed to restore system package:" + deletedPs.getPackageName() + ": " + + e.getMessage()); + // TODO(b/194319951): can we avoid this; throw would come from scan... + throw new SystemDeleteException(e); + } finally { + if (disabledPs.getPkg().isStub()) { + // We've re-installed the stub; make sure it's disabled here. If package was + // originally enabled, we'll install the compressed version of the application + // and re-enable it afterward. + disableStubPackage(action, deletedPs, allUserHandles); + } + } + } + + @GuardedBy("mPm.mLock") + private void disableStubPackage(DeletePackageAction action, PackageSetting deletedPs, + @NonNull int[] allUserHandles) { + final PackageSetting stubPs = mPm.mSettings.getPackageLPr( + deletedPs.getPackageName()); + if (stubPs != null) { + int userId = action.mUser == null + ? UserHandle.USER_ALL : action.mUser.getIdentifier(); + if (userId == UserHandle.USER_ALL) { + for (int aUserId : allUserHandles) { + stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android"); + } + } else if (userId >= UserHandle.USER_SYSTEM) { + stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android"); + } + } + } + + private static void removeNativeBinariesLI(PackageSetting ps) { + if (ps != null) { + NativeLibraryHelper.removeNativeBinariesLI(ps.getLegacyNativeLibraryPath()); + } + } + + /** + * Installs a package that's already on the system partition. + */ + @GuardedBy({"mPm.mLock", "mPm.mInstallLock"}) + private void installPackageFromSystemLIF(@NonNull String codePathString, + @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings) + throws PackageManagerException { + final File codePath = new File(codePathString); + @ParsingPackageUtils.ParseFlags int parseFlags = + mPm.getDefParseFlags() + | ParsingPackageUtils.PARSE_MUST_BE_APK + | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR; + @PackageManagerService.ScanFlags int scanFlags = mPm.getSystemPackageScanFlags(codePath); + final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm); + final AndroidPackage pkg = + scanPackageHelper.scanPackageTracedLI( + codePath, parseFlags, scanFlags, 0 /*currentTime*/, null); + + PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName()); + + try { + // update shared libraries for the newly re-installed system package + mPm.updateSharedLibrariesLocked(pkg, pkgSetting, null, null, + Collections.unmodifiableMap(mPm.mPackages)); + } catch (PackageManagerException e) { + Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage()); + } + + mAppDataHelper.prepareAppDataAfterInstallLIF(pkg); + + setPackageInstalledForSystemPackage(pkg, allUserHandles, + origUserHandles, writeSettings); + } + + private void setPackageInstalledForSystemPackage(@NonNull AndroidPackage pkg, + @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings) { + // writer + synchronized (mPm.mLock) { + PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName()); + + final boolean applyUserRestrictions = origUserHandles != null; + if (applyUserRestrictions) { + boolean installedStateChanged = false; + if (DEBUG_REMOVE) { + Slog.d(TAG, "Propagating install state across reinstall"); + } + for (int userId : allUserHandles) { + final boolean installed = ArrayUtils.contains(origUserHandles, userId); + if (DEBUG_REMOVE) { + Slog.d(TAG, " user " + userId + " => " + installed); + } + if (installed != ps.getInstalled(userId)) { + installedStateChanged = true; + } + ps.setInstalled(installed, userId); + if (installed) { + ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId); + } + } + // Regardless of writeSettings we need to ensure that this restriction + // state propagation is persisted + mPm.mSettings.writeAllUsersPackageRestrictionsLPr(); + if (installedStateChanged) { + mPm.mSettings.writeKernelMappingLPr(ps); + } + } + + // The method below will take care of removing obsolete permissions and granting + // install permissions. + mPm.mPermissionManager.onPackageInstalled(pkg, + Process.INVALID_UID /* previousAppId */, + PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT, + UserHandle.USER_ALL); + for (final int userId : allUserHandles) { + if (applyUserRestrictions) { + mPm.mSettings.writePermissionStateForUserLPr(userId, false); + } + } + + // can downgrade to reader here + if (writeSettings) { + mPm.writeSettingsLPrTEMP(); + } + } + } + } diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java index e138188e2ccf6..0dff7d12ce74e 100644 --- a/services/core/java/com/android/server/pm/InstallParams.java +++ b/services/core/java/com/android/server/pm/InstallParams.java @@ -17,130 +17,42 @@ package com.android.server.pm; import static android.app.AppOpsManager.MODE_DEFAULT; -import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; -import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS; -import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GROUP; -import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; -import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION; -import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP; -import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; -import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; -import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; -import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; -import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID; -import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY; -import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED; -import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; -import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE; -import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP; import static android.content.pm.PackageManager.INSTALL_STAGED; import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; -import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; -import static android.os.incremental.IncrementalManager.isIncrementalPath; -import static android.os.storage.StorageManager.FLAG_STORAGE_CE; -import static android.os.storage.StorageManager.FLAG_STORAGE_DE; -import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL; -import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; -import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; import static com.android.server.pm.PackageManagerService.DEBUG_INSTANT; import static com.android.server.pm.PackageManagerService.INIT_COPY; -import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; -import static com.android.server.pm.PackageManagerService.PRECOMPILE_LAYOUTS; -import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP; -import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP; -import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM; -import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM; -import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED; -import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT; -import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM; -import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT; -import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR; -import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD; -import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP; -import static com.android.server.pm.PackageManagerService.SCAN_INITIAL; -import static com.android.server.pm.PackageManagerService.SCAN_MOVE; -import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL; -import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX; -import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE; import static com.android.server.pm.PackageManagerService.TAG; -import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride; -import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ApplicationPackageManager; import android.content.pm.DataLoaderType; import android.content.pm.IPackageInstallObserver2; -import android.content.pm.PackageChangeEvent; -import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; -import android.content.pm.PermissionGroupInfo; -import android.content.pm.PermissionInfo; -import android.content.pm.SharedLibraryInfo; import android.content.pm.SigningDetails; -import android.content.pm.dex.DexMetadataHelper; import android.content.pm.parsing.PackageLite; -import android.content.pm.parsing.ParsingPackageUtils; -import android.content.pm.parsing.component.ComponentMutateUtils; -import android.content.pm.parsing.component.ParsedPermission; -import android.content.pm.parsing.component.ParsedPermissionGroup; -import android.content.pm.parsing.result.ParseResult; -import android.content.pm.parsing.result.ParseTypeImpl; -import android.net.Uri; -import android.os.Build; import android.os.Environment; import android.os.Message; -import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; -import android.os.incremental.IncrementalManager; -import android.os.incremental.IncrementalStorage; import android.os.storage.StorageManager; import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.EventLog; import android.util.Pair; import android.util.Slog; -import android.util.SparseArray; -import com.android.internal.annotations.GuardedBy; import com.android.internal.content.F2fsUtils; import com.android.internal.content.PackageHelper; -import com.android.internal.security.VerityUtils; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; -import com.android.server.Watchdog; -import com.android.server.pm.dex.DexoptOptions; -import com.android.server.pm.parsing.PackageParser2; -import com.android.server.pm.parsing.pkg.AndroidPackage; -import com.android.server.pm.parsing.pkg.AndroidPackageUtils; -import com.android.server.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.permission.Permission; -import com.android.server.pm.permission.PermissionManagerServiceInternal; - -import libcore.io.IoUtils; import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.IOException; -import java.security.DigestException; -import java.security.DigestInputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.Set; final class InstallParams extends HandlerParams { final OriginInfo mOriginInfo; @@ -164,7 +76,6 @@ final class InstallParams extends HandlerParams { final int mDataLoaderType; final long mRequiredInstalledVersionCode; final PackageLite mPackageLite; - final InstallPackageHelper mInstallPackageHelper; InstallParams(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer, int installFlags, InstallSource installSource, String volumeUuid, @@ -189,7 +100,6 @@ final class InstallParams extends HandlerParams { mDataLoaderType = DataLoaderType.NONE; mRequiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST; mPackageLite = packageLite; - mInstallPackageHelper = new InstallPackageHelper(mPm); } InstallParams(File stagedDir, IPackageInstallObserver2 observer, @@ -216,7 +126,6 @@ final class InstallParams extends HandlerParams { ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE; mRequiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode; mPackageLite = packageLite; - mInstallPackageHelper = new InstallPackageHelper(mPm); } @Override @@ -225,45 +134,6 @@ final class InstallParams extends HandlerParams { + " file=" + mOriginInfo.mFile + "}"; } - private int installLocationPolicy(PackageInfoLite pkgLite) { - String packageName = pkgLite.packageName; - int installLocation = pkgLite.installLocation; - // reader - synchronized (mPm.mLock) { - // Currently installed package which the new package is attempting to replace or - // null if no such package is installed. - AndroidPackage installedPkg = mPm.mPackages.get(packageName); - - if (installedPkg != null) { - if ((mInstallFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { - // Check for updated system application. - if (installedPkg.isSystem()) { - return PackageHelper.RECOMMEND_INSTALL_INTERNAL; - } else { - // If current upgrade specifies particular preference - if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { - // Application explicitly specified internal. - return PackageHelper.RECOMMEND_INSTALL_INTERNAL; - } else if ( - installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) { - // App explicitly prefers external. Let policy decide - } else { - // Prefer previous location - if (installedPkg.isExternalStorage()) { - return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; - } - return PackageHelper.RECOMMEND_INSTALL_INTERNAL; - } - } - } else { - // Invalid install. Return error code - return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS; - } - } - } - return pkgLite.recommendedInstallLocation; - } - /** * Override install location based on default policy if needed. * @@ -337,7 +207,7 @@ final class InstallParams extends HandlerParams { ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE; } else { // Override with defaults if needed. - loc = installLocationPolicy(pkgLite); + loc = mInstallPackageHelper.installLocationPolicy(pkgLite, mInstallFlags); final boolean onInt = (mInstallFlags & PackageManager.INSTALL_INTERNAL) != 0; @@ -374,7 +244,7 @@ final class InstallParams extends HandlerParams { // state can change within this delay and hence we need to re-verify certain conditions. boolean isStaged = (mInstallFlags & INSTALL_STAGED) != 0; if (isStaged) { - Pair ret = verifyReplacingVersionCode( + Pair ret = mInstallPackageHelper.verifyReplacingVersionCode( pkgLite, mRequiredInstalledVersionCode, mInstallFlags); mRet = ret.first; if (mRet != INSTALL_SUCCEEDED) { @@ -421,1604 +291,10 @@ final class InstallParams extends HandlerParams { private void processInstallRequestsAsync(boolean success, List installRequests) { mPm.mHandler.post(() -> { - List apexInstallRequests = new ArrayList<>(); - List apkInstallRequests = new ArrayList<>(); - for (InstallRequest request : installRequests) { - if ((request.mArgs.mInstallFlags & PackageManager.INSTALL_APEX) != 0) { - apexInstallRequests.add(request); - } else { - apkInstallRequests.add(request); - } - } - // Note: supporting multi package install of both APEXes and APKs might requir some - // thinking to ensure atomicity of the install. - if (!apexInstallRequests.isEmpty() && !apkInstallRequests.isEmpty()) { - // This should've been caught at the validation step, but for some reason wasn't. - throw new IllegalStateException( - "Attempted to do a multi package install of both APEXes and APKs"); - } - if (!apexInstallRequests.isEmpty()) { - if (success) { - // Since installApexPackages requires talking to external service (apexd), we - // schedule to run it async. Once it finishes, it will resume the install. - Thread t = new Thread(() -> installApexPackagesTraced(apexInstallRequests), - "installApexPackages"); - t.start(); - } else { - // Non-staged APEX installation failed somewhere before - // processInstallRequestAsync. In that case just notify the observer about the - // failure. - InstallRequest request = apexInstallRequests.get(0); - mPm.notifyInstallObserver(request.mInstallResult, - request.mArgs.mObserver); - } - return; - } - if (success) { - for (InstallRequest request : apkInstallRequests) { - request.mArgs.doPreInstall(request.mInstallResult.mReturnCode); - } - synchronized (mPm.mInstallLock) { - installPackagesTracedLI(apkInstallRequests); - } - for (InstallRequest request : apkInstallRequests) { - request.mArgs.doPostInstall( - request.mInstallResult.mReturnCode, request.mInstallResult.mUid); - } - } - for (InstallRequest request : apkInstallRequests) { - mInstallPackageHelper.restoreAndPostInstall(request.mArgs.mUser.getIdentifier(), - request.mInstallResult, - new PostInstallData(request.mArgs, - request.mInstallResult, null)); - } + mInstallPackageHelper.processInstallRequests(success, installRequests); }); } - private void installApexPackagesTraced(List requests) { - try { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installApexPackages"); - installApexPackages(requests); - } finally { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - } - - private void installApexPackages(List requests) { - if (requests.isEmpty()) { - return; - } - if (requests.size() != 1) { - throw new IllegalStateException( - "Only a non-staged install of a single APEX is supported"); - } - InstallRequest request = requests.get(0); - try { - // Should directory scanning logic be moved to ApexManager for better test coverage? - final File dir = request.mArgs.mOriginInfo.mResolvedFile; - final File[] apexes = dir.listFiles(); - if (apexes == null) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - dir.getAbsolutePath() + " is not a directory"); - } - if (apexes.length != 1) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Expected exactly one .apex file under " + dir.getAbsolutePath() - + " got: " + apexes.length); - } - try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) { - mPm.mApexManager.installPackage(apexes[0], packageParser); - } - } catch (PackageManagerException e) { - request.mInstallResult.setError("APEX installation failed", e); - } - PackageManagerService.invalidatePackageInfoCache(); - mPm.notifyInstallObserver(request.mInstallResult, request.mArgs.mObserver); - } - - @GuardedBy("mInstallLock") - private void installPackagesTracedLI(List requests) { - try { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages"); - installPackagesLI(requests); - } finally { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - } - - /** - * Installs one or more packages atomically. This operation is broken up into four phases: - *

    - *
  • Prepare - *
    Analyzes any current install state, parses the package and does initial - * validation on it.
  • - *
  • Scan - *
    Interrogates the parsed packages given the context collected in prepare.
  • - *
  • Reconcile - *
    Validates scanned packages in the context of each other and the current system - * state to ensure that the install will be successful. - *
  • Commit - *
    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.
  • - *
- * - * Failure at any phase will result in a full failure to install all packages. - */ - @GuardedBy("mInstallLock") - private void installPackagesLI(List requests) { - final Map preparedScans = new ArrayMap<>(requests.size()); - final Map installArgs = new ArrayMap<>(requests.size()); - final Map installResults = new ArrayMap<>(requests.size()); - final Map prepareResults = new ArrayMap<>(requests.size()); - final Map versionInfos = new ArrayMap<>(requests.size()); - final Map lastStaticSharedLibSettings = - new ArrayMap<>(requests.size()); - final Map createdAppId = new ArrayMap<>(requests.size()); - boolean success = false; - final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm); - 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; - try { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage"); - prepareResult = - preparePackageLI(request.mArgs, request.mInstallResult); - } catch (PrepareFailure prepareFailure) { - request.mInstallResult.setError(prepareFailure.error, - prepareFailure.getMessage()); - request.mInstallResult.mOrigPackage = prepareFailure.mConflictingPackage; - request.mInstallResult.mOrigPermission = prepareFailure.mConflictingPermission; - return; - } finally { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - request.mInstallResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED); - request.mInstallResult.mInstallerPackageName = - request.mArgs.mInstallSource.installerPackageName; - - final String packageName = prepareResult.mPackageToScan.getPackageName(); - prepareResults.put(packageName, prepareResult); - installResults.put(packageName, request.mInstallResult); - installArgs.put(packageName, request.mArgs); - try { - final ScanResult result = scanPackageHelper.scanPackageTracedLI( - prepareResult.mPackageToScan, prepareResult.mParseFlags, - prepareResult.mScanFlags, System.currentTimeMillis(), - request.mArgs.mUser, request.mArgs.mAbiOverride); - if (null != preparedScans.put(result.mPkgSetting.getPkg().getPackageName(), - result)) { - request.mInstallResult.setError( - PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE, - "Duplicate package " - + result.mPkgSetting.getPkg().getPackageName() - + " in multi-package install request."); - return; - } - createdAppId.put(packageName, - scanPackageHelper.optimisticallyRegisterAppId(result)); - versionInfos.put(result.mPkgSetting.getPkg().getPackageName(), - mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg())); - if (result.mStaticSharedLibraryInfo != null) { - final PackageSetting sharedLibLatestVersionSetting = - mPm.getSharedLibLatestVersionSetting(result); - if (sharedLibLatestVersionSetting != null) { - lastStaticSharedLibSettings.put( - result.mPkgSetting.getPkg().getPackageName(), - sharedLibLatestVersionSetting); - } - } - } catch (PackageManagerException e) { - request.mInstallResult.setError("Scanning Failed.", e); - return; - } - } - ReconcileRequest - reconcileRequest = new ReconcileRequest(preparedScans, installArgs, - installResults, - prepareResults, - mPm.mSharedLibraries, - Collections.unmodifiableMap(mPm.mPackages), versionInfos, - lastStaticSharedLibSettings); - CommitRequest commitRequest = null; - synchronized (mPm.mLock) { - Map reconciledPackages; - try { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages"); - reconciledPackages = mInstallPackageHelper.reconcilePackagesLocked( - reconcileRequest, mPm.mSettings.getKeySetManagerService(), - mPm.mInjector); - } catch (ReconcileFailure e) { - for (InstallRequest request : requests) { - request.mInstallResult.setError("Reconciliation failed...", e); - } - return; - } finally { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - try { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages"); - commitRequest = new CommitRequest(reconciledPackages, - mPm.mUserManager.getUserIds()); - commitPackagesLocked(commitRequest); - success = true; - } finally { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - } - executePostCommitSteps(commitRequest); - } finally { - if (success) { - for (InstallRequest request : requests) { - final InstallArgs args = request.mArgs; - if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) { - continue; - } - if (args.mSigningDetails.getSignatureSchemeVersion() != SIGNING_BLOCK_V4) { - continue; - } - // For incremental installs, we bypass the verifier prior to install. Now - // that we know the package is valid, send a notice to the verifier with - // the root hash of the base.apk. - final String baseCodePath = request.mInstallResult.mPkg.getBaseApkPath(); - final String[] splitCodePaths = request.mInstallResult.mPkg.getSplitCodePaths(); - final Uri originUri = Uri.fromFile(args.mOriginInfo.mResolvedFile); - final int verificationId = mPm.mPendingVerificationToken++; - final String rootHashString = PackageManagerServiceUtils - .buildVerificationRootHashString(baseCodePath, splitCodePaths); - mVerificationHelper.broadcastPackageVerified(verificationId, originUri, - PackageManager.VERIFICATION_ALLOW, rootHashString, - args.mDataLoaderType, args.getUser()); - } - } else { - for (ScanResult result : preparedScans.values()) { - if (createdAppId.getOrDefault(result.mRequest.mParsedPackage.getPackageName(), - false)) { - scanPackageHelper.cleanUpAppIdCreation(result); - } - } - // TODO(b/194319951): create a more descriptive reason than unknown - // mark all non-failure installs as UNKNOWN so we do not treat them as success - for (InstallRequest request : requests) { - if (request.mInstallResult.mFreezer != null) { - request.mInstallResult.mFreezer.close(); - } - if (request.mInstallResult.mReturnCode == PackageManager.INSTALL_SUCCEEDED) { - request.mInstallResult.mReturnCode = PackageManager.INSTALL_UNKNOWN; - } - } - } - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - } - - @GuardedBy("mInstallLock") - private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res) - throws PrepareFailure { - final int installFlags = args.mInstallFlags; - final File tmpPackageFile = new File(args.getCodePath()); - final boolean onExternal = args.mVolumeUuid != null; - final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0); - final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0); - final boolean virtualPreload = - ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0); - final boolean isRollback = args.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK; - @PackageManagerService.ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE; - if (args.mMoveInfo != null) { - // moving a complete application; perform an initial scan on the new install location - scanFlags |= SCAN_INITIAL; - } - if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) { - scanFlags |= SCAN_DONT_KILL_APP; - } - if (instantApp) { - scanFlags |= SCAN_AS_INSTANT_APP; - } - if (fullApp) { - scanFlags |= SCAN_AS_FULL_APP; - } - if (virtualPreload) { - scanFlags |= SCAN_AS_VIRTUAL_PRELOAD; - } - - if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile); - - // Validity check - if (instantApp && onExternal) { - Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal); - throw new PrepareFailure(PackageManager.INSTALL_FAILED_SESSION_INVALID); - } - - // Retrieve PackageSettings and parse package - @ParsingPackageUtils.ParseFlags final int parseFlags = - mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY - | ParsingPackageUtils.PARSE_ENFORCE_CODE - | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0); - - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); - final ParsedPackage parsedPackage; - try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) { - parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false); - AndroidPackageUtils.validatePackageDexMetadata(parsedPackage); - } catch (PackageManagerException e) { - throw new PrepareFailure("Failed parse during installPackageLI", e); - } finally { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - - // Instant apps have several additional install-time checks. - if (instantApp) { - if (parsedPackage.getTargetSdkVersion() < Build.VERSION_CODES.O) { - Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName() - + " does not target at least O"); - throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID, - "Instant app package must target at least O"); - } - if (parsedPackage.getSharedUserId() != null) { - Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName() - + " may not declare sharedUserId."); - throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID, - "Instant app package may not declare a sharedUserId"); - } - } - - if (parsedPackage.isStaticSharedLibrary()) { - // Static shared libraries have synthetic package names - PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage); - - // No static shared libs on external storage - if (onExternal) { - Slog.i(TAG, "Static shared libs can only be installed on internal storage."); - throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION, - "Packages declaring static-shared libs cannot be updated"); - } - } - - String pkgName = res.mName = parsedPackage.getPackageName(); - if (parsedPackage.isTestOnly()) { - if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) { - throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI"); - } - } - - // either use what we've been given or parse directly from the APK - if (args.mSigningDetails != SigningDetails.UNKNOWN) { - parsedPackage.setSigningDetails(args.mSigningDetails); - } else { - final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); - final ParseResult result = ParsingPackageUtils.getSigningDetails( - input, parsedPackage, false /*skipVerify*/); - if (result.isError()) { - throw new PrepareFailure("Failed collect during installPackageLI", - result.getException()); - } - parsedPackage.setSigningDetails(result.getResult()); - } - - if (instantApp && parsedPackage.getSigningDetails().getSignatureSchemeVersion() - < SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2) { - Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName() - + " is not signed with at least APK Signature Scheme v2"); - throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID, - "Instant app package must be signed with APK Signature Scheme v2 or greater"); - } - - boolean systemApp = false; - boolean replace = false; - synchronized (mPm.mLock) { - // Check if installing already existing package - if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { - String oldName = mPm.mSettings.getRenamedPackageLPr(pkgName); - if (parsedPackage.getOriginalPackages().contains(oldName) - && mPm.mPackages.containsKey(oldName)) { - // This package is derived from an original package, - // and this device has been updating from that original - // name. We must continue using the original name, so - // rename the new package here. - parsedPackage.setPackageName(oldName); - pkgName = parsedPackage.getPackageName(); - replace = true; - if (DEBUG_INSTALL) { - Slog.d(TAG, "Replacing existing renamed package: oldName=" - + oldName + " pkgName=" + pkgName); - } - } else if (mPm.mPackages.containsKey(pkgName)) { - // This package, under its official name, already exists - // on the device; we should replace it. - replace = true; - if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing package: " + pkgName); - } - - if (replace) { - // Prevent apps opting out from runtime permissions - AndroidPackage oldPackage = mPm.mPackages.get(pkgName); - final int oldTargetSdk = oldPackage.getTargetSdkVersion(); - final int newTargetSdk = parsedPackage.getTargetSdkVersion(); - if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1 - && newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) { - throw new PrepareFailure( - PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE, - "Package " + parsedPackage.getPackageName() - + " new target SDK " + newTargetSdk - + " doesn't support runtime permissions but the old" - + " target SDK " + oldTargetSdk + " does."); - } - // Prevent persistent apps from being updated - if (oldPackage.isPersistent() - && ((installFlags & PackageManager.INSTALL_STAGED) == 0)) { - throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK, - "Package " + oldPackage.getPackageName() + " is a persistent app. " - + "Persistent apps are not updateable."); - } - } - } - - PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName); - if (ps != null) { - if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps); - - // Static shared libs have same package with different versions where - // we internally use a synthetic package name to allow multiple versions - // of the same package, therefore we need to compare signatures against - // the package setting for the latest library version. - PackageSetting signatureCheckPs = ps; - if (parsedPackage.isStaticSharedLibrary()) { - SharedLibraryInfo libraryInfo = mPm.getLatestSharedLibraVersionLPr( - parsedPackage); - if (libraryInfo != null) { - signatureCheckPs = mPm.mSettings.getPackageLPr( - libraryInfo.getPackageName()); - } - } - - // Quick validity 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. - final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService(); - if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) { - if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) { - throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " - + parsedPackage.getPackageName() + " upgrade keys do not match the " - + "previously installed version"); - } - } else { - try { - final boolean compareCompat = - mInstallPackageHelper.isCompatSignatureUpdateNeeded(parsedPackage); - final boolean compareRecover = - mInstallPackageHelper.isRecoverSignatureUpdateNeeded(parsedPackage); - // We don't care about disabledPkgSetting on install for now. - final boolean compatMatch = verifySignatures(signatureCheckPs, null, - parsedPackage.getSigningDetails(), compareCompat, compareRecover, - isRollback); - // The new KeySets will be re-added later in the scanning process. - if (compatMatch) { - synchronized (mPm.mLock) { - ksms.removeAppKeySetDataLPw(parsedPackage.getPackageName()); - } - } - } catch (PackageManagerException e) { - throw new PrepareFailure(e.error, e.getMessage()); - } - } - - if (ps.getPkg() != null) { - systemApp = ps.getPkg().isSystem(); - } - res.mOrigUsers = ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true); - } - - final int numGroups = ArrayUtils.size(parsedPackage.getPermissionGroups()); - for (int groupNum = 0; groupNum < numGroups; groupNum++) { - final ParsedPermissionGroup group = - parsedPackage.getPermissionGroups().get(groupNum); - final PermissionGroupInfo sourceGroup = mPm.getPermissionGroupInfo(group.getName(), - 0); - - if (sourceGroup != null && cannotInstallWithBadPermissionGroups(parsedPackage)) { - final String sourcePackageName = sourceGroup.packageName; - - if ((replace || !parsedPackage.getPackageName().equals(sourcePackageName)) - && !doesSignatureMatchForPermissions(sourcePackageName, parsedPackage, - scanFlags)) { - EventLog.writeEvent(0x534e4554, "146211400", -1, - parsedPackage.getPackageName()); - - throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP, - "Package " - + parsedPackage.getPackageName() - + " attempting to redeclare permission group " - + group.getName() + " already owned by " - + sourcePackageName); - } - } - } - - // TODO: Move logic for checking permission compatibility into PermissionManagerService - final int n = ArrayUtils.size(parsedPackage.getPermissions()); - for (int i = n - 1; i >= 0; i--) { - final ParsedPermission perm = parsedPackage.getPermissions().get(i); - final Permission bp = mPm.mPermissionManager.getPermissionTEMP(perm.getName()); - - // Don't allow anyone but the system to define ephemeral permissions. - if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0 - && !systemApp) { - Slog.w(TAG, "Non-System package " + parsedPackage.getPackageName() - + " attempting to delcare ephemeral permission " - + perm.getName() + "; Removing ephemeral."); - ComponentMutateUtils.setProtectionLevel(perm, - perm.getProtectionLevel() & ~PermissionInfo.PROTECTION_FLAG_INSTANT); - } - - // Check whether the newly-scanned package wants to define an already-defined perm - if (bp != null) { - final String sourcePackageName = bp.getPackageName(); - - if (!doesSignatureMatchForPermissions(sourcePackageName, parsedPackage, - scanFlags)) { - // If the owning package is the system itself, we log but allow - // install to proceed; we fail the install on all other permission - // redefinitions. - if (!sourcePackageName.equals("android")) { - throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION, - "Package " - + parsedPackage.getPackageName() - + " attempting to redeclare permission " - + perm.getName() + " already owned by " - + sourcePackageName) - .conflictsWithExistingPermission(perm.getName(), - sourcePackageName); - } else { - Slog.w(TAG, "Package " + parsedPackage.getPackageName() - + " attempting to redeclare system permission " - + perm.getName() + "; ignoring new declaration"); - parsedPackage.removePermission(i); - } - } else if (!PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())) { - // Prevent apps to change protection level to dangerous from any other - // type as this would allow a privilege escalation where an app adds a - // normal/signature permission in other app's group and later redefines - // it as dangerous leading to the group auto-grant. - if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_MASK_BASE) - == PermissionInfo.PROTECTION_DANGEROUS) { - if (bp != null && !bp.isRuntime()) { - Slog.w(TAG, "Package " + parsedPackage.getPackageName() - + " trying to change a non-runtime permission " - + perm.getName() - + " to runtime; keeping old protection level"); - ComponentMutateUtils.setProtectionLevel(perm, - bp.getProtectionLevel()); - } - } - } - } - - if (perm.getGroup() != null - && cannotInstallWithBadPermissionGroups(parsedPackage)) { - boolean isPermGroupDefinedByPackage = false; - for (int groupNum = 0; groupNum < numGroups; groupNum++) { - if (parsedPackage.getPermissionGroups().get(groupNum).getName() - .equals(perm.getGroup())) { - isPermGroupDefinedByPackage = true; - break; - } - } - - if (!isPermGroupDefinedByPackage) { - final PermissionGroupInfo sourceGroup = - mPm.getPermissionGroupInfo(perm.getGroup(), 0); - - if (sourceGroup == null) { - EventLog.writeEvent(0x534e4554, "146211400", -1, - parsedPackage.getPackageName()); - - throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP, - "Package " - + parsedPackage.getPackageName() - + " attempting to declare permission " - + perm.getName() + " in non-existing group " - + perm.getGroup()); - } else { - String groupSourcePackageName = sourceGroup.packageName; - - if (!PLATFORM_PACKAGE_NAME.equals(groupSourcePackageName) - && !doesSignatureMatchForPermissions(groupSourcePackageName, - parsedPackage, scanFlags)) { - EventLog.writeEvent(0x534e4554, "146211400", -1, - parsedPackage.getPackageName()); - - throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP, - "Package " - + parsedPackage.getPackageName() - + " attempting to declare permission " - + perm.getName() + " in group " - + perm.getGroup() + " owned by package " - + groupSourcePackageName - + " with incompatible certificate"); - } - } - } - } - } - } - - if (systemApp) { - if (onExternal) { - // Abort update; system app can't be replaced with app on sdcard - throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION, - "Cannot install updates to system apps on sdcard"); - } else if (instantApp) { - // Abort update; system app can't be replaced with an instant app - throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID, - "Cannot update a system app with an instant app"); - } - } - - if (args.mMoveInfo != null) { - // We did an in-place move, so dex is ready to roll - scanFlags |= SCAN_NO_DEX; - scanFlags |= SCAN_MOVE; - - synchronized (mPm.mLock) { - final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName); - if (ps == null) { - res.setError(INSTALL_FAILED_INTERNAL_ERROR, - "Missing settings for moved package " + pkgName); - } - - // We moved the entire application as-is, so bring over the - // previously derived ABI information. - parsedPackage.setPrimaryCpuAbi(ps.getPrimaryCpuAbi()) - .setSecondaryCpuAbi(ps.getSecondaryCpuAbi()); - } - - } else { - // Enable SCAN_NO_DEX flag to skip dexopt at a later stage - scanFlags |= SCAN_NO_DEX; - - try { - PackageSetting pkgSetting; - AndroidPackage oldPackage; - synchronized (mPm.mLock) { - pkgSetting = mPm.mSettings.getPackageLPr(pkgName); - oldPackage = mPm.mPackages.get(pkgName); - } - boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null - && pkgSetting.getPkgState().isUpdatedSystemApp(); - final String abiOverride = deriveAbiOverride(args.mAbiOverride); - boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem(); - final Pair - derivedAbi = mPm.mInjector.getAbiHelper().derivePackageAbi(parsedPackage, - isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred, - abiOverride, mPm.mAppLib32InstallDir); - derivedAbi.first.applyTo(parsedPackage); - derivedAbi.second.applyTo(parsedPackage); - } catch (PackageManagerException pme) { - Slog.e(TAG, "Error deriving application ABI", pme); - throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, - "Error deriving application ABI: " + pme.getMessage()); - } - } - - if (!args.doRename(res.mReturnCode, parsedPackage)) { - throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename"); - } - - try { - setUpFsVerityIfPossible(parsedPackage); - } catch (Installer.InstallerException | IOException | DigestException - | NoSuchAlgorithmException e) { - throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, - "Failed to set up verity: " + e); - } - - final PackageFreezer freezer = - freezePackageForInstall(pkgName, installFlags, "installPackageLI"); - boolean shouldCloseFreezerBeforeReturn = true; - try { - final AndroidPackage oldPackage; - String renamedPackage; - boolean sysPkg = false; - int targetScanFlags = scanFlags; - int targetParseFlags = parseFlags; - final PackageSetting ps; - final PackageSetting disabledPs; - if (replace) { - final String pkgName11 = parsedPackage.getPackageName(); - synchronized (mPm.mLock) { - oldPackage = mPm.mPackages.get(pkgName11); - } - if (parsedPackage.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 installed from adb which is useful for development. - if (oldPackage != null - && (installFlags & PackageManager.INSTALL_FROM_ADB) == 0) { - throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PACKAGE, - "Packages declaring " - + "static-shared libs cannot be updated"); - } - } - - final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0; - - final int[] allUsers; - final int[] installedUsers; - final int[] uninstalledUsers; - - synchronized (mPm.mLock) { - if (DEBUG_INSTALL) { - Slog.d(TAG, - "replacePackageLI: new=" + parsedPackage + ", old=" + oldPackage); - } - - ps = mPm.mSettings.getPackageLPr(pkgName11); - disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(ps); - - // verify signatures are valid - final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService(); - if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) { - if (!ksms.checkUpgradeKeySetLocked(ps, parsedPackage)) { - throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, - "New package not signed by keys specified by upgrade-keysets: " - + pkgName11); - } - } else { - SigningDetails parsedPkgSigningDetails = parsedPackage.getSigningDetails(); - SigningDetails oldPkgSigningDetails = oldPackage.getSigningDetails(); - // default to original signature matching - if (!parsedPkgSigningDetails.checkCapability(oldPkgSigningDetails, - SigningDetails.CertCapabilities.INSTALLED_DATA) - && !oldPkgSigningDetails.checkCapability(parsedPkgSigningDetails, - SigningDetails.CertCapabilities.ROLLBACK)) { - // Allow the update to proceed if this is a rollback and the parsed - // package's current signing key is the current signer or in the lineage - // of the old package; this allows a rollback to a previously installed - // version after an app's signing key has been rotated without requiring - // the rollback capability on the previous signing key. - if (!isRollback || !oldPkgSigningDetails.hasAncestorOrSelf( - parsedPkgSigningDetails)) { - throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, - "New package has a different signature: " + pkgName11); - } - } - } - - // don't allow a system upgrade unless the upgrade hash matches - if (oldPackage.getRestrictUpdateHash() != null && oldPackage.isSystem()) { - final byte[] digestBytes; - try { - final MessageDigest digest = MessageDigest.getInstance("SHA-512"); - updateDigest(digest, new File(parsedPackage.getBaseApkPath())); - if (!ArrayUtils.isEmpty(parsedPackage.getSplitCodePaths())) { - for (String path : parsedPackage.getSplitCodePaths()) { - updateDigest(digest, new File(path)); - } - } - digestBytes = digest.digest(); - } catch (NoSuchAlgorithmException | IOException e) { - throw new PrepareFailure(INSTALL_FAILED_INVALID_APK, - "Could not compute hash: " + pkgName11); - } - if (!Arrays.equals(oldPackage.getRestrictUpdateHash(), digestBytes)) { - throw new PrepareFailure(INSTALL_FAILED_INVALID_APK, - "New package fails restrict-update check: " + pkgName11); - } - // retain upgrade restriction - parsedPackage.setRestrictUpdateHash(oldPackage.getRestrictUpdateHash()); - } - - // Check for shared user id changes - if (!Objects.equals(oldPackage.getSharedUserId(), - parsedPackage.getSharedUserId()) - // Don't mark as invalid if the app is trying to - // leave a sharedUserId - && parsedPackage.getSharedUserId() != null) { - throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED, - "Package " + parsedPackage.getPackageName() - + " shared user changed from " - + (oldPackage.getSharedUserId() != null - ? oldPackage.getSharedUserId() : "") - + " to " + parsedPackage.getSharedUserId()); - } - - // In case of rollback, remember per-user/profile install state - allUsers = mPm.mUserManager.getUserIds(); - installedUsers = ps.queryInstalledUsers(allUsers, true); - uninstalledUsers = ps.queryInstalledUsers(allUsers, false); - - - // don't allow an upgrade from full to ephemeral - if (isInstantApp) { - if (args.mUser == null - || args.mUser.getIdentifier() == UserHandle.USER_ALL) { - for (int currentUser : allUsers) { - if (!ps.getInstantApp(currentUser)) { - // can't downgrade from full to instant - Slog.w(TAG, - "Can't replace full app with instant app: " + pkgName11 - + " for user: " + currentUser); - throw new PrepareFailure( - PackageManager.INSTALL_FAILED_SESSION_INVALID); - } - } - } else if (!ps.getInstantApp(args.mUser.getIdentifier())) { - // can't downgrade from full to instant - Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11 - + " for user: " + args.mUser.getIdentifier()); - throw new PrepareFailure( - PackageManager.INSTALL_FAILED_SESSION_INVALID); - } - } - } - - // Update what is removed - res.mRemovedInfo = new PackageRemovedInfo(mPm); - res.mRemovedInfo.mUid = oldPackage.getUid(); - res.mRemovedInfo.mRemovedPackage = oldPackage.getPackageName(); - res.mRemovedInfo.mInstallerPackageName = ps.getInstallSource().installerPackageName; - res.mRemovedInfo.mIsStaticSharedLib = - parsedPackage.getStaticSharedLibName() != null; - res.mRemovedInfo.mIsUpdate = true; - res.mRemovedInfo.mOrigUsers = installedUsers; - res.mRemovedInfo.mInstallReasons = new SparseArray<>(installedUsers.length); - for (int i = 0; i < installedUsers.length; i++) { - final int userId = installedUsers[i]; - res.mRemovedInfo.mInstallReasons.put(userId, ps.getInstallReason(userId)); - } - res.mRemovedInfo.mUninstallReasons = new SparseArray<>(uninstalledUsers.length); - for (int i = 0; i < uninstalledUsers.length; i++) { - final int userId = uninstalledUsers[i]; - res.mRemovedInfo.mUninstallReasons.put(userId, ps.getUninstallReason(userId)); - } - - sysPkg = oldPackage.isSystem(); - if (sysPkg) { - // Set the system/privileged/oem/vendor/product flags as needed - final boolean privileged = oldPackage.isPrivileged(); - final boolean oem = oldPackage.isOem(); - final boolean vendor = oldPackage.isVendor(); - final boolean product = oldPackage.isProduct(); - final boolean odm = oldPackage.isOdm(); - final boolean systemExt = oldPackage.isSystemExt(); - final @ParsingPackageUtils.ParseFlags int systemParseFlags = parseFlags; - final @PackageManagerService.ScanFlags int systemScanFlags = scanFlags - | SCAN_AS_SYSTEM - | (privileged ? SCAN_AS_PRIVILEGED : 0) - | (oem ? SCAN_AS_OEM : 0) - | (vendor ? SCAN_AS_VENDOR : 0) - | (product ? SCAN_AS_PRODUCT : 0) - | (odm ? SCAN_AS_ODM : 0) - | (systemExt ? SCAN_AS_SYSTEM_EXT : 0); - - if (DEBUG_INSTALL) { - Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage - + ", old=" + oldPackage); - } - res.setReturnCode(PackageManager.INSTALL_SUCCEEDED); - targetParseFlags = systemParseFlags; - targetScanFlags = systemScanFlags; - } else { // non system replace - replace = true; - if (DEBUG_INSTALL) { - Slog.d(TAG, - "replaceNonSystemPackageLI: new=" + parsedPackage + ", old=" - + oldPackage); - } - } - } else { // new package install - ps = null; - disabledPs = null; - replace = false; - oldPackage = null; - // Remember this for later, in case we need to rollback this install - String pkgName1 = parsedPackage.getPackageName(); - - if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + parsedPackage); - - // TODO(b/194319951): MOVE TO RECONCILE - synchronized (mPm.mLock) { - renamedPackage = mPm.mSettings.getRenamedPackageLPr(pkgName1); - if (renamedPackage != null) { - // A package with the same name is already installed, though - // it has been renamed to an older name. The package we - // are trying to install should be installed as an update to - // the existing one, but that has not been requested, so bail. - throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS, - "Attempt to re-install " + pkgName1 - + " without first uninstalling package running as " - + renamedPackage); - } - if (mPm.mPackages.containsKey(pkgName1)) { - // Don't allow installation over an existing package with the same name. - throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS, - "Attempt to re-install " + pkgName1 - + " without first uninstalling."); - } - } - } - // we're passing the freezer back to be closed in a later phase of install - shouldCloseFreezerBeforeReturn = false; - - return new PrepareResult(replace, targetScanFlags, targetParseFlags, - oldPackage, parsedPackage, replace /* clearCodeCache */, sysPkg, - ps, disabledPs); - } finally { - res.mFreezer = freezer; - if (shouldCloseFreezerBeforeReturn) { - freezer.close(); - } - } - } - - /* - * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges - * as this only works for packages that are installed - * - * TODO: Move logic for permission group compatibility into PermissionManagerService - */ - @SuppressWarnings("AndroidFrameworkCompatChange") - private static boolean cannotInstallWithBadPermissionGroups(ParsedPackage parsedPackage) { - return parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S; - } - - private boolean doesSignatureMatchForPermissions(@NonNull String sourcePackageName, - @NonNull ParsedPackage parsedPackage, int scanFlags) { - // If the defining package is signed with our cert, it's okay. This - // also includes the "updating the same package" case, of course. - // "updating same package" could also involve key-rotation. - - final PackageSetting sourcePackageSetting; - final KeySetManagerService ksms; - synchronized (mPm.mLock) { - sourcePackageSetting = mPm.mSettings.getPackageLPr(sourcePackageName); - ksms = mPm.mSettings.getKeySetManagerService(); - } - - final SigningDetails sourceSigningDetails = (sourcePackageSetting == null - ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails()); - if (sourcePackageName.equals(parsedPackage.getPackageName()) - && (ksms.shouldCheckUpgradeKeySetLocked( - sourcePackageSetting, scanFlags))) { - return ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage); - } else { - - // in the event of signing certificate rotation, we need to see if the - // package's certificate has rotated from the current one, or if it is an - // older certificate with which the current is ok with sharing permissions - if (sourceSigningDetails.checkCapability( - parsedPackage.getSigningDetails(), - SigningDetails.CertCapabilities.PERMISSION)) { - return true; - } else if (parsedPackage.getSigningDetails().checkCapability( - sourceSigningDetails, - SigningDetails.CertCapabilities.PERMISSION)) { - // the scanned package checks out, has signing certificate rotation - // history, and is newer; bring it over - synchronized (mPm.mLock) { - sourcePackageSetting.setSigningDetails(parsedPackage.getSigningDetails()); - } - return true; - } else { - return false; - } - } - } - - /** - * Set up fs-verity for the given package if possible. This requires a feature flag of system - * property to be enabled only if the kernel supports fs-verity. - * - *

When the feature flag is set to legacy mode, only APK is supported (with some experimental - * kernel patches). In normal mode, all file format can be supported. - */ - private void setUpFsVerityIfPossible(AndroidPackage pkg) throws Installer.InstallerException, - PrepareFailure, IOException, DigestException, NoSuchAlgorithmException { - final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled(); - final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled(); - if (!standardMode && !legacyMode) { - return; - } - - if (isIncrementalPath(pkg.getPath()) && IncrementalManager.getVersion() - < IncrementalManager.MIN_VERSION_TO_SUPPORT_FSVERITY) { - return; - } - - // Collect files we care for fs-verity setup. - ArrayMap fsverityCandidates = new ArrayMap<>(); - if (legacyMode) { - synchronized (mPm.mLock) { - final PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName()); - if (ps != null && ps.isPrivileged()) { - fsverityCandidates.put(pkg.getBaseApkPath(), null); - if (pkg.getSplitCodePaths() != null) { - for (String splitPath : pkg.getSplitCodePaths()) { - fsverityCandidates.put(splitPath, null); - } - } - } - } - } else { - // NB: These files will become only accessible if the signing key is loaded in kernel's - // .fs-verity keyring. - fsverityCandidates.put(pkg.getBaseApkPath(), - VerityUtils.getFsveritySignatureFilePath(pkg.getBaseApkPath())); - - final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk( - pkg.getBaseApkPath()); - if (new File(dmPath).exists()) { - fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath)); - } - - if (pkg.getSplitCodePaths() != null) { - for (String path : pkg.getSplitCodePaths()) { - fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path)); - - final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path); - if (new File(splitDmPath).exists()) { - fsverityCandidates.put(splitDmPath, - VerityUtils.getFsveritySignatureFilePath(splitDmPath)); - } - } - } - } - - for (Map.Entry entry : fsverityCandidates.entrySet()) { - final String filePath = entry.getKey(); - final String signaturePath = entry.getValue(); - - if (!legacyMode) { - // fs-verity is optional for now. Only set up if signature is provided. - if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) { - try { - VerityUtils.setUpFsverity(filePath, signaturePath); - } catch (IOException e) { - throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE, - "Failed to enable fs-verity: " + e); - } - } - continue; - } - - // In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN. - final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(filePath); - if (result.isOk()) { - if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling verity to " + filePath); - final FileDescriptor fd = result.getUnownedFileDescriptor(); - try { - final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath); - try { - // A file may already have fs-verity, e.g. when reused during a split - // install. If the measurement succeeds, no need to attempt to set up. - mPm.mInstaller.assertFsverityRootHashMatches(filePath, rootHash); - } catch (Installer.InstallerException e) { - mPm.mInstaller.installApkVerity(filePath, fd, result.getContentSize()); - mPm.mInstaller.assertFsverityRootHashMatches(filePath, rootHash); - } - } finally { - IoUtils.closeQuietly(fd); - } - } else if (result.isFailed()) { - throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE, - "Failed to generate verity"); - } - } - } - - private PackageFreezer freezePackageForInstall(String packageName, int installFlags, - String killReason) { - return freezePackageForInstall(packageName, UserHandle.USER_ALL, installFlags, killReason); - } - - private PackageFreezer freezePackageForInstall(String packageName, int userId, int installFlags, - String killReason) { - if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) { - return new PackageFreezer(mPm); - } else { - return mPm.freezePackage(packageName, userId, killReason); - } - } - - private static void updateDigest(MessageDigest digest, File file) throws IOException { - try (DigestInputStream digestStream = - new DigestInputStream(new FileInputStream(file), digest)) { - int length, total = 0; - while ((length = digestStream.read()) != -1) { - total += length; - } // just plow through the file - } - } - - @GuardedBy("mLock") - private void commitPackagesLocked(final CommitRequest request) { - // TODO: remove any expected failures from this method; this should only be able to fail due - // to unavoidable errors (I/O, etc.) - for (ReconciledPackage reconciledPkg : request.mReconciledPackages.values()) { - final ScanResult scanResult = reconciledPkg.mScanResult; - final ScanRequest scanRequest = scanResult.mRequest; - final ParsedPackage parsedPackage = scanRequest.mParsedPackage; - final String packageName = parsedPackage.getPackageName(); - final PackageInstalledInfo res = reconciledPkg.mInstallResult; - final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm); - final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm); - - if (reconciledPkg.mPrepareResult.mReplace) { - AndroidPackage oldPackage = mPm.mPackages.get(packageName); - - // Set the update and install times - PackageSetting deletedPkgSetting = mPm.getPackageSetting( - oldPackage.getPackageName()); - reconciledPkg.mPkgSetting - .setFirstInstallTime(deletedPkgSetting.getFirstInstallTime()) - .setLastUpdateTime(System.currentTimeMillis()); - - res.mRemovedInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList( - reconciledPkg.mPkgSetting, request.mAllUsers, - mPm.mSettings.getPackagesLocked()); - if (reconciledPkg.mPrepareResult.mSystem) { - // Remove existing system package - removePackageHelper.removePackageLI(oldPackage, true); - if (!disableSystemPackageLPw(oldPackage)) { - // We didn't need to disable the .apk as a current system package, - // which means we are replacing another update that is already - // installed. We need to make sure to delete the older one's .apk. - res.mRemovedInfo.mArgs = new FileInstallArgs( - oldPackage.getPath(), - getAppDexInstructionSets( - AndroidPackageUtils.getPrimaryCpuAbi(oldPackage, - deletedPkgSetting), - AndroidPackageUtils.getSecondaryCpuAbi(oldPackage, - deletedPkgSetting)), mPm); - } else { - res.mRemovedInfo.mArgs = null; - } - } else { - try { - // Settings will be written during the call to updateSettingsLI(). - deletePackageHelper.executeDeletePackageLIF( - reconciledPkg.mDeletePackageAction, packageName, - true, request.mAllUsers, false); - } catch (SystemDeleteException e) { - if (mPm.mIsEngBuild) { - throw new RuntimeException("Unexpected failure", e); - // ignore; not possible for non-system app - } - } - // Successfully deleted the old package; proceed with replace. - - // If deleted package lived in a container, give users a chance to - // relinquish resources before killing. - if (oldPackage.isExternalStorage()) { - if (DEBUG_INSTALL) { - Slog.i(TAG, "upgrading pkg " + oldPackage - + " is ASEC-hosted -> UNAVAILABLE"); - } - final int[] uidArray = new int[]{oldPackage.getUid()}; - final ArrayList pkgList = new ArrayList<>(1); - pkgList.add(oldPackage.getPackageName()); - mBroadcastHelper.sendResourcesChangedBroadcast( - false, true, pkgList, uidArray, null); - } - - // Update the in-memory copy of the previous code paths. - PackageSetting ps1 = mPm.mSettings.getPackageLPr( - reconciledPkg.mPrepareResult.mExistingPackage.getPackageName()); - if ((reconciledPkg.mInstallArgs.mInstallFlags & PackageManager.DONT_KILL_APP) - == 0) { - if (ps1.mOldCodePaths == null) { - ps1.mOldCodePaths = new ArraySet<>(); - } - Collections.addAll(ps1.mOldCodePaths, oldPackage.getBaseApkPath()); - if (oldPackage.getSplitCodePaths() != null) { - Collections.addAll(ps1.mOldCodePaths, oldPackage.getSplitCodePaths()); - } - } else { - ps1.mOldCodePaths = null; - } - - if (reconciledPkg.mInstallResult.mReturnCode - == PackageManager.INSTALL_SUCCEEDED) { - PackageSetting ps2 = mPm.mSettings.getPackageLPr( - parsedPackage.getPackageName()); - if (ps2 != null) { - res.mRemovedInfo.mRemovedForAllUsers = - mPm.mPackages.get(ps2.getPackageName()) == null; - } - } - } - } - - AndroidPackage pkg = mInstallPackageHelper.commitReconciledScanResultLocked( - reconciledPkg, request.mAllUsers); - updateSettingsLI(pkg, reconciledPkg, request.mAllUsers, res); - - final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); - if (ps != null) { - res.mNewUsers = ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true); - ps.setUpdateAvailable(false /*updateAvailable*/); - } - if (res.mReturnCode == PackageManager.INSTALL_SUCCEEDED) { - mPm.updateSequenceNumberLP(ps, res.mNewUsers); - mPm.updateInstantAppInstallerLocked(packageName); - } - } - ApplicationPackageManager.invalidateGetPackagesForUidCache(); - } - - @GuardedBy("mLock") - private boolean disableSystemPackageLPw(AndroidPackage oldPkg) { - return mPm.mSettings.disableSystemPackageLPw(oldPkg.getPackageName(), true); - } - - private void updateSettingsLI(AndroidPackage newPackage, ReconciledPackage reconciledPkg, - int[] allUsers, PackageInstalledInfo res) { - updateSettingsInternalLI(newPackage, reconciledPkg, allUsers, res); - } - - private void updateSettingsInternalLI(AndroidPackage pkg, ReconciledPackage reconciledPkg, - int[] allUsers, PackageInstalledInfo res) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings"); - - final String pkgName = pkg.getPackageName(); - final int[] installedForUsers = res.mOrigUsers; - final InstallArgs installArgs = reconciledPkg.mInstallArgs; - final int installReason = installArgs.mInstallReason; - InstallSource installSource = installArgs.mInstallSource; - final String installerPackageName = installSource.installerPackageName; - - if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath()); - synchronized (mPm.mLock) { - // For system-bundled packages, we assume that installing an upgraded version - // of the package implies that the user actually wants to run that new code, - // so we enable the package. - final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName); - final int userId = installArgs.mUser.getIdentifier(); - if (ps != null) { - if (pkg.isSystem()) { - if (DEBUG_INSTALL) { - Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName); - } - // Enable system package for requested users - if (res.mOrigUsers != null) { - for (int origUserId : res.mOrigUsers) { - if (userId == UserHandle.USER_ALL || userId == origUserId) { - ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, - origUserId, installerPackageName); - } - } - } - // Also convey the prior install/uninstall state - if (allUsers != null && installedForUsers != null) { - for (int currentUserId : allUsers) { - final boolean installed = ArrayUtils.contains( - installedForUsers, currentUserId); - if (DEBUG_INSTALL) { - Slog.d(TAG, " user " + currentUserId + " => " + installed); - } - ps.setInstalled(installed, currentUserId); - } - // these install state changes will be persisted in the - // upcoming call to mSettings.writeLPr(). - } - - if (allUsers != null) { - for (int currentUserId : allUsers) { - ps.resetOverrideComponentLabelIcon(currentUserId); - } - } - } - - // Retrieve the overlays for shared libraries of the package. - if (!ps.getPkgState().getUsesLibraryInfos().isEmpty()) { - for (SharedLibraryInfo sharedLib : ps.getPkgState().getUsesLibraryInfos()) { - for (int currentUserId : UserManagerService.getInstance().getUserIds()) { - if (!sharedLib.isDynamic()) { - // TODO(146804378): Support overlaying static shared libraries - continue; - } - final PackageSetting libPs = mPm.mSettings.getPackageLPr( - sharedLib.getPackageName()); - if (libPs == null) { - continue; - } - ps.setOverlayPathsForLibrary(sharedLib.getName(), - libPs.getOverlayPaths(currentUserId), currentUserId); - } - } - } - - // It's implied that when a user requests installation, they want the app to be - // installed and enabled. (This does not apply to USER_ALL, which here means only - // install on users for which the app is already installed). - if (userId != UserHandle.USER_ALL) { - ps.setInstalled(true, userId); - ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName); - } - - mPm.mSettings.addInstallerPackageNames(ps.getInstallSource()); - - // When replacing an existing package, preserve the original install reason for all - // users that had the package installed before. Similarly for uninstall reasons. - final Set previousUserIds = new ArraySet<>(); - if (res.mRemovedInfo != null && res.mRemovedInfo.mInstallReasons != null) { - final int installReasonCount = res.mRemovedInfo.mInstallReasons.size(); - for (int i = 0; i < installReasonCount; i++) { - final int previousUserId = res.mRemovedInfo.mInstallReasons.keyAt(i); - final int previousInstallReason = - res.mRemovedInfo.mInstallReasons.valueAt(i); - ps.setInstallReason(previousInstallReason, previousUserId); - previousUserIds.add(previousUserId); - } - } - if (res.mRemovedInfo != null && res.mRemovedInfo.mUninstallReasons != null) { - for (int i = 0; i < res.mRemovedInfo.mUninstallReasons.size(); i++) { - final int previousUserId = res.mRemovedInfo.mUninstallReasons.keyAt(i); - final int previousReason = res.mRemovedInfo.mUninstallReasons.valueAt(i); - ps.setUninstallReason(previousReason, previousUserId); - } - } - - // Set install reason for users that are having the package newly installed. - final int[] allUsersList = mPm.mUserManager.getUserIds(); - if (userId == UserHandle.USER_ALL) { - // TODO(b/152629990): It appears that the package doesn't actually get newly - // installed in this case, so the installReason shouldn't get modified? - for (int currentUserId : allUsersList) { - if (!previousUserIds.contains(currentUserId)) { - ps.setInstallReason(installReason, currentUserId); - } - } - } else if (!previousUserIds.contains(userId)) { - ps.setInstallReason(installReason, userId); - } - - // TODO(b/169721400): generalize Incremental States and create a Callback object - // that can be used for all the packages. - final String codePath = ps.getPathString(); - if (IncrementalManager.isIncrementalPath(codePath) - && mPm.mIncrementalManager != null) { - mPm.mIncrementalManager.registerLoadingProgressCallback(codePath, - new IncrementalProgressListener(ps.getPackageName(), mPm)); - } - - // Ensure that the uninstall reason is UNKNOWN for users with the package installed. - for (int currentUserId : allUsersList) { - if (ps.getInstalled(currentUserId)) { - ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, currentUserId); - } - } - - mPm.mSettings.writeKernelMappingLPr(ps); - - final PermissionManagerServiceInternal.PackageInstalledParams.Builder - permissionParamsBuilder = - new PermissionManagerServiceInternal.PackageInstalledParams.Builder(); - final boolean grantPermissions = (installArgs.mInstallFlags - & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0; - if (grantPermissions) { - final List grantedPermissions = - installArgs.mInstallGrantPermissions != null - ? Arrays.asList(installArgs.mInstallGrantPermissions) - : pkg.getRequestedPermissions(); - permissionParamsBuilder.setGrantedPermissions(grantedPermissions); - } - final boolean allowlistAllRestrictedPermissions = - (installArgs.mInstallFlags - & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0; - final List allowlistedRestrictedPermissions = - allowlistAllRestrictedPermissions ? pkg.getRequestedPermissions() - : installArgs.mAllowlistedRestrictedPermissions; - if (allowlistedRestrictedPermissions != null) { - permissionParamsBuilder.setAllowlistedRestrictedPermissions( - allowlistedRestrictedPermissions); - } - final int autoRevokePermissionsMode = installArgs.mAutoRevokePermissionsMode; - permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode); - final ScanResult scanResult = reconciledPkg.mScanResult; - mPm.mPermissionManager.onPackageInstalled(pkg, scanResult.mPreviousAppId, - permissionParamsBuilder.build(), userId); - } - res.mName = pkgName; - res.mUid = pkg.getUid(); - res.mPkg = pkg; - res.setReturnCode(PackageManager.INSTALL_SUCCEEDED); - //to update install status - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings"); - mPm.writeSettingsLPrTEMP(); - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - - /** - * 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 com.android.server.pm.PackageManagerService.mLock}. - */ - private void executePostCommitSteps(CommitRequest commitRequest) { - final ArraySet incrementalStorages = new ArraySet<>(); - final AppDataHelper appDataHelper = new AppDataHelper(mPm); - for (ReconciledPackage reconciledPkg : commitRequest.mReconciledPackages.values()) { - final boolean instantApp = ((reconciledPkg.mScanResult.mRequest.mScanFlags - & SCAN_AS_INSTANT_APP) != 0); - final AndroidPackage pkg = reconciledPkg.mPkgSetting.getPkg(); - final String packageName = pkg.getPackageName(); - final String codePath = pkg.getPath(); - final boolean onIncremental = mPm.mIncrementalManager != null - && isIncrementalPath(codePath); - if (onIncremental) { - IncrementalStorage storage = mPm.mIncrementalManager.openStorage(codePath); - if (storage == null) { - throw new IllegalArgumentException( - "Install: null storage for incremental package " + packageName); - } - incrementalStorages.add(storage); - } - appDataHelper.prepareAppDataAfterInstallLIF(pkg); - if (reconciledPkg.mPrepareResult.mClearCodeCache) { - appDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL, - FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL - | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); - } - if (reconciledPkg.mPrepareResult.mReplace) { - mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(), - pkg.getBaseApkPath(), pkg.getSplitCodePaths()); - } - - // Prepare the application profiles for the new code paths. - // This needs to be done before invoking dexopt so that any install-time profile - // can be used for optimizations. - mPm.mArtManagerService.prepareAppProfiles( - pkg, - mPm.resolveUserIds(reconciledPkg.mInstallArgs.mUser.getIdentifier()), - /* updateReferenceProfileContent= */ true); - - // Compute the compilation reason from the installation scenario. - final int compilationReason = - mPm.getDexManager().getCompilationReasonForInstallScenario( - reconciledPkg.mInstallArgs.mInstallScenario); - - // Construct the DexoptOptions early to see if we should skip running 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. - final boolean isBackupOrRestore = - reconciledPkg.mInstallArgs.mInstallReason == INSTALL_REASON_DEVICE_RESTORE - || reconciledPkg.mInstallArgs.mInstallReason - == INSTALL_REASON_DEVICE_SETUP; - - final int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE - | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE - | (isBackupOrRestore ? DexoptOptions.DEXOPT_FOR_RESTORE : 0); - DexoptOptions dexoptOptions = - new DexoptOptions(packageName, compilationReason, dexoptFlags); - - // Check whether we need to dexopt the app. - // - // NOTE: it is IMPORTANT to call dexopt: - // - after doRename which will sync the package data from AndroidPackage and - // its corresponding ApplicationInfo. - // - after installNewPackageLIF or replacePackageLIF which will update result with the - // uid of the application (pkg.applicationInfo.uid). - // This update happens in place! - // - // We only need to dexopt if the package meets ALL of the following conditions: - // 1) it is not an instant app or if it is then dexopt is enabled via gservices. - // 2) it is not debuggable. - // 3) it is not on Incremental File System. - // - // Note that we do not dexopt instant apps by default. dexopt can take some time to - // complete, so we skip this step during installation. Instead, we'll take extra time - // the first time the instant app starts. It's preferred to do it this way to provide - // continuous progress to the useur instead of mysteriously blocking somewhere in the - // middle of running an instant app. The default behaviour can be overridden - // via gservices. - // - // Furthermore, dexopt may be skipped, depending on the install scenario and current - // state of the device. - // - // TODO(b/174695087): instantApp and onIncremental should be removed and their install - // path moved to SCENARIO_FAST. - final boolean performDexopt = - (!instantApp || android.provider.Settings.Global.getInt( - mPm.mContext.getContentResolver(), - android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) - && !pkg.isDebuggable() - && (!onIncremental) - && dexoptOptions.isCompilationEnabled(); - - if (performDexopt) { - // Compile the layout resources. - if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts"); - mPm.mViewCompiler.compileLayouts(pkg); - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); - ScanResult result = reconciledPkg.mScanResult; - - // This mirrors logic from commitReconciledScanResultLocked, where the library files - // needed for dexopt are assigned. - // TODO: Fix this to have 1 mutable PackageSetting for scan/install. If the previous - // setting needs to be passed to have a comparison, hide it behind an immutable - // interface. There's no good reason to have 3 different ways to access the real - // PackageSetting object, only one of which is actually correct. - PackageSetting realPkgSetting = result.mExistingSettingCopied - ? result.mRequest.mPkgSetting : result.mPkgSetting; - if (realPkgSetting == null) { - realPkgSetting = reconciledPkg.mPkgSetting; - } - - // Unfortunately, the updated system app flag is only tracked on this PackageSetting - boolean isUpdatedSystemApp = reconciledPkg.mPkgSetting.getPkgState() - .isUpdatedSystemApp(); - - realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp); - - mPm.mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting, - null /* instructionSets */, - mPm.getOrCreateCompilerPackageStats(pkg), - mPm.getDexManager().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.getService().notifyPackageChanged(packageName); - - notifyPackageChangeObserversOnUpdate(reconciledPkg); - } - waitForNativeBinariesExtraction(incrementalStorages); - } - - private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) { - final PackageSetting pkgSetting = reconciledPkg.mPkgSetting; - final PackageInstalledInfo pkgInstalledInfo = reconciledPkg.mInstallResult; - final PackageRemovedInfo pkgRemovedInfo = pkgInstalledInfo.mRemovedInfo; - - PackageChangeEvent pkgChangeEvent = new PackageChangeEvent(); - pkgChangeEvent.packageName = pkgSetting.getPkg().getPackageName(); - pkgChangeEvent.version = pkgSetting.getLongVersionCode(); - pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.getLastUpdateTime(); - pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.mIsUpdate); - pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.mDataRemoved); - pkgChangeEvent.isDeleted = false; - - mPm.notifyPackageChangeObservers(pkgChangeEvent); - } - - static void waitForNativeBinariesExtraction( - ArraySet incrementalStorages) { - if (incrementalStorages.isEmpty()) { - return; - } - try { - // Native library extraction may take very long time: each page could potentially - // wait for either 10s or 100ms (adb vs non-adb data loader), and that easily adds - // up to a full watchdog timeout of 1 min, killing the system after that. It doesn't - // make much sense as blocking here doesn't lock up the framework, but only blocks - // the installation session and the following ones. - Watchdog.getInstance().pauseWatchingCurrentThread("native_lib_extract"); - for (int i = 0; i < incrementalStorages.size(); ++i) { - IncrementalStorage storage = incrementalStorages.valueAtUnchecked(i); - storage.waitForNativeBinariesExtraction(); - } - } finally { - Watchdog.getInstance().resumeWatchingCurrentThread("native_lib_extract"); - } - } - /** * Ensure that the install reason matches what we know about the package installer (e.g. whether * it is acting on behalf on an enterprise or the user). diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java index 3c3fb561bba5f..0c47f7d60979b 100644 --- a/services/core/java/com/android/server/pm/PackageHandler.java +++ b/services/core/java/com/android/server/pm/PackageHandler.java @@ -16,20 +16,15 @@ package com.android.server.pm; -import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED; -import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static com.android.server.pm.PackageManagerService.CHECK_PENDING_INTEGRITY_VERIFICATION; import static com.android.server.pm.PackageManagerService.CHECK_PENDING_VERIFICATION; -import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP; import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; import static com.android.server.pm.PackageManagerService.DEFAULT_VERIFICATION_RESPONSE; import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_INSTALL_OBSERVER; import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_POST_DELETE; -import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_POST_DELETE_DELAY_MS; import static com.android.server.pm.PackageManagerService.DOMAIN_VERIFICATION; -import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY; import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_STATUS; import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_TIMEOUT; import static com.android.server.pm.PackageManagerService.INIT_COPY; @@ -46,15 +41,11 @@ import static com.android.server.pm.PackageManagerService.WRITE_PACKAGE_RESTRICT import static com.android.server.pm.PackageManagerService.WRITE_SETTINGS; import android.content.Intent; -import android.content.pm.IPackageInstallObserver2; import android.content.pm.InstantAppRequest; -import android.content.pm.PackageInfo; -import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.net.Uri; import android.os.Binder; -import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -62,39 +53,20 @@ import android.os.Process; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; -import android.os.storage.StorageManager; -import android.os.storage.VolumeInfo; -import android.stats.storage.StorageEnums; -import android.util.ArrayMap; -import android.util.EventLog; import android.util.Log; import android.util.Slog; -import android.util.SparseArray; - -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.FrameworkStatsLog; -import com.android.server.EventLogTags; -import com.android.server.pm.parsing.pkg.AndroidPackage; - -import dalvik.system.VMRuntime; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; /** * Part of PackageManagerService that handles events. */ -public final class PackageHandler extends Handler { +final class PackageHandler extends Handler { private final PackageManagerService mPm; - private final VerificationHelper mVerificationHelper; - private final BroadcastHelper mBroadcastHelper; + private final InstallPackageHelper mInstallPackageHelper; PackageHandler(Looper looper, PackageManagerService pm) { super(looper); mPm = pm; - mVerificationHelper = new VerificationHelper(mPm.mContext); - mBroadcastHelper = new BroadcastHelper(mPm.mInjector); + mInstallPackageHelper = new InstallPackageHelper(mPm); } @Override @@ -121,46 +93,7 @@ public final class PackageHandler extends Handler { break; } case SEND_PENDING_BROADCAST: { - String[] packages; - ArrayList[] components; - int size = 0; - int[] uids; - Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); - synchronized (mPm.mLock) { - size = mPm.mPendingBroadcasts.size(); - if (size <= 0) { - // Nothing to be done. Just return - return; - } - packages = new String[size]; - components = new ArrayList[size]; - uids = new int[size]; - int i = 0; // filling out the above arrays - - for (int n = 0; n < mPm.mPendingBroadcasts.userIdCount(); n++) { - final int packageUserId = mPm.mPendingBroadcasts.userIdAt(n); - final ArrayMap> componentsToBroadcast = - mPm.mPendingBroadcasts.packagesForUserId(packageUserId); - final int numComponents = componentsToBroadcast.size(); - for (int index = 0; i < size && index < numComponents; index++) { - packages[i] = componentsToBroadcast.keyAt(index); - components[i] = componentsToBroadcast.valueAt(index); - final PackageSetting ps = mPm.mSettings.getPackageLPr(packages[i]); - uids[i] = (ps != null) - ? UserHandle.getUid(packageUserId, ps.getAppId()) - : -1; - i++; - } - } - size = i; - mPm.mPendingBroadcasts.clear(); - } - // Send broadcasts - for (int i = 0; i < size; i++) { - mPm.sendPackageChangedBroadcast(packages[i], true /* dontKillApp */, - components[i], uids[i], null /* reason */); - } - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + mInstallPackageHelper.sendPendingBroadcasts(); break; } case POST_INSTALL: { @@ -177,23 +110,7 @@ public final class PackageHandler extends Handler { if (data != null && data.mPostInstallRunnable != null) { data.mPostInstallRunnable.run(); } else if (data != null && data.args != null) { - InstallArgs args = data.args; - PackageInstalledInfo parentRes = data.res; - - final boolean killApp = (args.mInstallFlags - & PackageManager.INSTALL_DONT_KILL_APP) == 0; - final boolean virtualPreload = ((args.mInstallFlags - & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0); - - handlePackagePostInstall(parentRes, killApp, virtualPreload, - didRestore, args.mInstallSource.installerPackageName, - args.mObserver, args.mDataLoaderType); - - // Log tracing if needed - if (args.mTraceMethod != null) { - Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.mTraceMethod, - args.mTraceCookie); - } + mInstallPackageHelper.handlePackagePostInstall(data.res, data.args, didRestore); } else if (DEBUG_INSTALL) { // No post-install when we run restore from installExistingPackageForUser Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1); @@ -217,31 +134,17 @@ public final class PackageHandler extends Handler { } break; case WRITE_SETTINGS: { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); - synchronized (mPm.mLock) { - removeMessages(WRITE_SETTINGS); - removeMessages(WRITE_PACKAGE_RESTRICTIONS); - mPm.writeSettingsLPrTEMP(); - mPm.mDirtyUsers.clear(); - } + mPm.writeSettings(); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } break; case WRITE_PACKAGE_RESTRICTIONS: { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); - synchronized (mPm.mLock) { - removeMessages(WRITE_PACKAGE_RESTRICTIONS); - for (int userId : mPm.mDirtyUsers) { - mPm.mSettings.writePackageRestrictionsLPr(userId); - } - mPm.mDirtyUsers.clear(); - } + mPm.writePendingRestrictions(); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } break; case WRITE_PACKAGE_LIST: { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); - synchronized (mPm.mLock) { - removeMessages(WRITE_PACKAGE_LIST); - mPm.mSettings.writePackageListLPr(msg.arg1); - } + mPm.writePackageList(msg.arg1); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } break; case CHECK_PENDING_VERIFICATION: { @@ -262,13 +165,13 @@ public final class PackageHandler extends Handler { Slog.i(TAG, "Continuing with installation of " + originUri); state.setVerifierResponse(Binder.getCallingUid(), PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT); - mVerificationHelper.broadcastPackageVerified(verificationId, originUri, + VerificationUtils.broadcastPackageVerified(verificationId, originUri, PackageManager.VERIFICATION_ALLOW, null, params.mDataLoaderType, - user); + user, mPm.mContext); } else { - mVerificationHelper.broadcastPackageVerified(verificationId, originUri, + VerificationUtils.broadcastPackageVerified(verificationId, originUri, PackageManager.VERIFICATION_REJECT, null, - params.mDataLoaderType, user); + params.mDataLoaderType, user, mPm.mContext); params.setReturnCode( PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, errorMsg); state.setVerifierResponse(Binder.getCallingUid(), @@ -343,8 +246,9 @@ public final class PackageHandler extends Handler { final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile); if (state.isInstallAllowed()) { - mVerificationHelper.broadcastPackageVerified(verificationId, originUri, - response.code, null, params.mDataLoaderType, params.getUser()); + VerificationUtils.broadcastPackageVerified(verificationId, originUri, + response.code, null, params.mDataLoaderType, params.getUser(), + mPm.mContext); } else { params.setReturnCode( PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, @@ -475,285 +379,6 @@ public final class PackageHandler extends Handler { } } - private void handlePackagePostInstall(PackageInstalledInfo res, boolean killApp, - boolean virtualPreload, boolean launchedForRestore, String installerPackage, - IPackageInstallObserver2 installObserver, int dataLoaderType) { - boolean succeeded = res.mReturnCode == PackageManager.INSTALL_SUCCEEDED; - final boolean update = res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null; - final String packageName = res.mName; - final PackageSetting pkgSetting = succeeded ? mPm.getPackageSetting(packageName) : null; - final boolean removedBeforeUpdate = (pkgSetting == null) - || (pkgSetting.isSystem() && !pkgSetting.getPathString().equals( - res.mPkg.getPath())); - if (succeeded && removedBeforeUpdate) { - Slog.e(TAG, packageName + " was removed before handlePackagePostInstall " - + "could be executed"); - res.mReturnCode = INSTALL_FAILED_PACKAGE_CHANGED; - res.mReturnMsg = "Package was removed before install could complete."; - - // Remove the update failed package's older resources safely now - InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null; - if (args != null) { - synchronized (mPm.mInstallLock) { - args.doPostDeleteLI(true); - } - } - mPm.notifyInstallObserver(res, installObserver); - return; - } - - if (succeeded) { - // Clear the uid cache after we installed a new package. - mPm.mPerUidReadTimeoutsCache = null; - - // Send the removed broadcasts - if (res.mRemovedInfo != null) { - res.mRemovedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/); - } - - final String installerPackageName = - res.mInstallerPackageName != null - ? res.mInstallerPackageName - : res.mRemovedInfo != null - ? res.mRemovedInfo.mInstallerPackageName - : null; - - synchronized (mPm.mLock) { - mPm.mInstantAppRegistry.onPackageInstalledLPw(res.mPkg, res.mNewUsers); - } - - // Determine the set of users who are adding this package for - // the first time vs. those who are seeing an update. - int[] firstUserIds = EMPTY_INT_ARRAY; - int[] firstInstantUserIds = EMPTY_INT_ARRAY; - int[] updateUserIds = EMPTY_INT_ARRAY; - int[] instantUserIds = EMPTY_INT_ARRAY; - final boolean allNewUsers = res.mOrigUsers == null || res.mOrigUsers.length == 0; - for (int newUser : res.mNewUsers) { - final boolean isInstantApp = pkgSetting.getInstantApp(newUser); - if (allNewUsers) { - if (isInstantApp) { - firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser); - } else { - firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser); - } - continue; - } - boolean isNew = true; - for (int origUser : res.mOrigUsers) { - if (origUser == newUser) { - isNew = false; - break; - } - } - if (isNew) { - if (isInstantApp) { - firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser); - } else { - firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser); - } - } else { - if (isInstantApp) { - instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser); - } else { - updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser); - } - } - } - - // Send installed broadcasts if the package is not a static shared lib. - if (res.mPkg.getStaticSharedLibName() == null) { - mPm.mProcessLoggingHandler.invalidateBaseApkHash(res.mPkg.getBaseApkPath()); - - // Send added for users that see the package for the first time - // sendPackageAddedForNewUsers also deals with system apps - int appId = UserHandle.getAppId(res.mUid); - boolean isSystem = res.mPkg.isSystem(); - mPm.sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload, - virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds, - dataLoaderType); - - // Send added for users that don't see the package for the first time - Bundle extras = new Bundle(1); - extras.putInt(Intent.EXTRA_UID, res.mUid); - if (update) { - extras.putBoolean(Intent.EXTRA_REPLACING, true); - } - extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType); - // Send to all running apps. - final SparseArray newBroadcastAllowList; - - synchronized (mPm.mLock) { - newBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList( - mPm.getPackageSettingInternal(res.mName, Process.SYSTEM_UID), - updateUserIds, mPm.mSettings.getPackagesLocked()); - } - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, - extras, 0 /*flags*/, - null /*targetPackage*/, null /*finishedReceiver*/, - updateUserIds, instantUserIds, newBroadcastAllowList, null); - if (installerPackageName != null) { - // Send to the installer, even if it's not running. - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, - extras, 0 /*flags*/, - installerPackageName, null /*finishedReceiver*/, - updateUserIds, instantUserIds, null /* broadcastAllowList */, null); - } - // if the required verifier is defined, but, is not the installer of record - // for the package, it gets notified - final boolean notifyVerifier = mPm.mRequiredVerifierPackage != null - && !mPm.mRequiredVerifierPackage.equals(installerPackageName); - if (notifyVerifier) { - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, - extras, 0 /*flags*/, - mPm.mRequiredVerifierPackage, null /*finishedReceiver*/, - updateUserIds, instantUserIds, null /* broadcastAllowList */, null); - } - // If package installer is defined, notify package installer about new - // app installed - if (mPm.mRequiredInstallerPackage != null) { - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, - extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/, - mPm.mRequiredInstallerPackage, null /*finishedReceiver*/, - firstUserIds, instantUserIds, null /* broadcastAllowList */, null); - } - - // Send replaced for users that don't see the package for the first time - if (update) { - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, - packageName, extras, 0 /*flags*/, - null /*targetPackage*/, null /*finishedReceiver*/, - updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList, - null); - if (installerPackageName != null) { - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, - extras, 0 /*flags*/, - installerPackageName, null /*finishedReceiver*/, - updateUserIds, instantUserIds, null /*broadcastAllowList*/, null); - } - if (notifyVerifier) { - mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, - extras, 0 /*flags*/, - mPm.mRequiredVerifierPackage, null /*finishedReceiver*/, - updateUserIds, instantUserIds, null /*broadcastAllowList*/, null); - } - mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, - null /*package*/, null /*extras*/, 0 /*flags*/, - packageName /*targetPackage*/, - null /*finishedReceiver*/, updateUserIds, instantUserIds, - null /*broadcastAllowList*/, - mBroadcastHelper.getTemporaryAppAllowlistBroadcastOptions( - REASON_PACKAGE_REPLACED).toBundle()); - } else if (launchedForRestore && !res.mPkg.isSystem()) { - // First-install and we did a restore, so we're responsible for the - // first-launch broadcast. - if (DEBUG_BACKUP) { - Slog.i(TAG, "Post-restore of " + packageName - + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds)); - } - mBroadcastHelper.sendFirstLaunchBroadcast(packageName, installerPackage, - firstUserIds, firstInstantUserIds); - } - - // Send broadcast package appeared if external for all users - if (res.mPkg.isExternalStorage()) { - if (!update) { - final StorageManager storage = mPm.mInjector.getSystemService( - StorageManager.class); - VolumeInfo volume = - storage.findVolumeByUuid( - StorageManager.convert( - res.mPkg.getVolumeUuid()).toString()); - int packageExternalStorageType = - PackageManagerServiceUtils.getPackageExternalStorageType(volume, - res.mPkg.isExternalStorage()); - // If the package was installed externally, log it. - if (packageExternalStorageType != StorageEnums.UNKNOWN) { - FrameworkStatsLog.write( - FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED, - packageExternalStorageType, packageName); - } - } - if (DEBUG_INSTALL) { - Slog.i(TAG, "upgrading pkg " + res.mPkg + " is external"); - } - final int[] uidArray = new int[]{res.mPkg.getUid()}; - ArrayList pkgList = new ArrayList<>(1); - pkgList.add(packageName); - mBroadcastHelper.sendResourcesChangedBroadcast( - true, true, pkgList, uidArray, null); - } - } else if (!ArrayUtils.isEmpty(res.mLibraryConsumers)) { // if static shared lib - for (int i = 0; i < res.mLibraryConsumers.size(); i++) { - AndroidPackage pkg = res.mLibraryConsumers.get(i); - // send broadcast that all consumers of the static shared library have changed - mPm.sendPackageChangedBroadcast(pkg.getPackageName(), false /* dontKillApp */, - new ArrayList<>(Collections.singletonList(pkg.getPackageName())), - pkg.getUid(), null); - } - } - - // Work that needs to happen on first install within each user - if (firstUserIds != null && firstUserIds.length > 0) { - for (int userId : firstUserIds) { - mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName, - userId); - } - } - - if (allNewUsers && !update) { - mPm.notifyPackageAdded(packageName, res.mUid); - } else { - mPm.notifyPackageChanged(packageName, res.mUid); - } - - // Log current value of "unknown sources" setting - EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED, - getUnknownSourcesSettings()); - - // Remove the replaced package's older resources safely now - InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null; - if (args != null) { - if (!killApp) { - // If we didn't kill the app, defer the deletion of code/resource files, since - // they may still be in use by the running application. This mitigates problems - // in cases where resources or code is loaded by a new Activity before - // ApplicationInfo changes have propagated to all application threads. - scheduleDeferredNoKillPostDelete(args); - } else { - synchronized (mPm.mInstallLock) { - args.doPostDeleteLI(true); - } - } - } else { - // Force a gc to clear up things. Ask for a background one, it's fine to go on - // and not block here. - VMRuntime.getRuntime().requestConcurrentGC(); - } - - // Notify DexManager that the package was installed for new users. - // The updated users should already be indexed and the package code paths - // should not change. - // Don't notify the manager for ephemeral apps as they are not expected to - // survive long enough to benefit of background optimizations. - for (int userId : firstUserIds) { - PackageInfo info = mPm.getPackageInfo(packageName, /*flags*/ 0, userId); - // There's a race currently where some install events may interleave with an - // uninstall. This can lead to package info being null (b/36642664). - if (info != null) { - mPm.getDexManager().notifyPackageInstalled(info, userId); - } - } - } - - final boolean deferInstallObserver = succeeded && update && !killApp; - if (deferInstallObserver) { - mPm.scheduleDeferredNoKillInstallObserver(res, installObserver); - } else { - mPm.notifyInstallObserver(res, installObserver); - } - } - /** * Get the default verification agent response code. * @@ -777,20 +402,4 @@ public final class PackageHandler extends Handler { // an easy way to get around the integrity check. return PackageManager.VERIFICATION_REJECT; } - - /** - * Get the "allow unknown sources" setting. - * - * @return the current "allow unknown sources" setting - */ - private int getUnknownSourcesSettings() { - return android.provider.Settings.Secure.getIntForUser(mPm.mContext.getContentResolver(), - android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS, - -1, UserHandle.USER_SYSTEM); - } - - private void scheduleDeferredNoKillPostDelete(InstallArgs args) { - Message message = obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args); - sendMessageDelayed(message, DEFERRED_NO_KILL_POST_DELETE_DELAY_MS); - } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 3d916ae464128..7f1ac15f2ba73 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1250,6 +1250,11 @@ public class PackageManagerService extends IPackageManager.Stub mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS); } + void scheduleDeferredNoKillPostDelete(InstallArgs args) { + Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args); + mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_POST_DELETE_DELAY_MS); + } + @Override public void requestChecksums(@NonNull String packageName, boolean includeSplits, @Checksum.TypeMask int optional, @@ -1380,6 +1385,38 @@ public class PackageManagerService extends IPackageManager.Stub } } + private void writePendingRestrictionsLocked() { + if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) { + mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS); + for (int userId : mDirtyUsers) { + mSettings.writePackageRestrictionsLPr(userId); + } + mDirtyUsers.clear(); + } + } + + void writePendingRestrictions() { + synchronized (mLock) { + writePendingRestrictionsLocked(); + } + } + + void writeSettings() { + synchronized (mLock) { + mHandler.removeMessages(WRITE_SETTINGS); + mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS); + writeSettingsLPrTEMP(); + mDirtyUsers.clear(); + } + } + + void writePackageList(int userId) { + synchronized (mLock) { + mHandler.removeMessages(WRITE_PACKAGE_LIST); + mSettings.writePackageListLPr(userId); + } + } + public static PackageManagerService main(Context context, Installer installer, @NonNull DomainVerificationService domainVerificationService, boolean factoryTest, boolean onlyCore) { @@ -1772,7 +1809,7 @@ public class PackageManagerService extends IPackageManager.Stub mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this, mRemovePackageHelper, mAppDataHelper); mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper, - mInitAndSystemPackageHelper, mAppDataHelper); + mAppDataHelper); mPreferredActivityHelper = new PreferredActivityHelper(this); mResolveIntentHelper = new ResolveIntentHelper(this, mPreferredActivityHelper); mDexOptHelper = new DexOptHelper(this); @@ -5059,13 +5096,7 @@ public class PackageManagerService extends IPackageManager.Stub mPackageUsage.writeNow(mSettings.getPackagesLocked()); // This is the last chance to write out pending restriction settings - if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) { - mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS); - for (int userId : mDirtyUsers) { - mSettings.writePackageRestrictionsLPr(userId); - } - mDirtyUsers.clear(); - } + writePendingRestrictionsLocked(); } } @@ -7808,7 +7839,8 @@ public class PackageManagerService extends IPackageManager.Stub if (isSystemStub && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) { - if (!mInitAndSystemPackageHelper.enableCompressedPackage(deletedPkg, pkgSetting)) { + if (!new InstallPackageHelper(this).enableCompressedPackage(deletedPkg, + pkgSetting)) { Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable " + "commpressed package " + setting.getPackageName()); updateAllowed[i] = false; @@ -11083,11 +11115,24 @@ public class PackageManagerService extends IPackageManager.Stub return mIsPreNMR1Upgrade; } - InitAndSystemPackageHelper getInitAndSystemPackageHelper() { - return mInitAndSystemPackageHelper; - } - boolean isOverlayMutable(String packageName) { return mOverlayConfig.isMutable(packageName); } + + @ScanFlags int getSystemPackageScanFlags(File codePath) { + List dirsToScanAsSystem = + mInitAndSystemPackageHelper.getDirsToScanAsSystem(); + @PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM; + for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) { + ScanPartition partition = dirsToScanAsSystem.get(i); + if (partition.containsFile(codePath)) { + scanFlags |= partition.scanFlag; + if (partition.containsPrivApp(codePath)) { + scanFlags |= SCAN_AS_PRIVILEGED; + } + break; + } + } + return scanFlags; + } } diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index e124e04603dfe..b8b2db238d905 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -643,6 +643,35 @@ public class PackageManagerServiceUtils { return compatMatch; } + /** + * Decompress files stored in codePath to dstCodePath for a certain package. + */ + public static int decompressFiles(String codePath, File dstCodePath, String packageName) { + final File[] compressedFiles = getCompressedFiles(codePath); + int ret = PackageManager.INSTALL_SUCCEEDED; + try { + makeDirRecursive(dstCodePath, 0755); + for (File srcFile : compressedFiles) { + final String srcFileName = srcFile.getName(); + final String dstFileName = srcFileName.substring( + 0, srcFileName.length() - COMPRESSED_EXTENSION.length()); + final File dstFile = new File(dstCodePath, dstFileName); + ret = decompressFile(srcFile, dstFile); + if (ret != PackageManager.INSTALL_SUCCEEDED) { + logCriticalInfo(Log.ERROR, "Failed to decompress" + + "; pkg: " + packageName + + ", file: " + dstFileName); + break; + } + } + } catch (ErrnoException e) { + logCriticalInfo(Log.ERROR, "Failed to decompress" + + "; pkg: " + packageName + + ", err: " + e.errno); + } + return ret; + } + public static int decompressFile(File srcFile, File dstFile) throws ErrnoException { if (DEBUG_COMPRESSION) { Slog.i(TAG, "Decompress file" diff --git a/services/core/java/com/android/server/pm/VerificationParams.java b/services/core/java/com/android/server/pm/VerificationParams.java index c5569cd53da10..fc1bb45c55977 100644 --- a/services/core/java/com/android/server/pm/VerificationParams.java +++ b/services/core/java/com/android/server/pm/VerificationParams.java @@ -51,7 +51,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; -import android.content.pm.Signature; import android.content.pm.SigningDetails; import android.content.pm.VerifierInfo; import android.content.pm.parsing.PackageLite; @@ -62,7 +61,6 @@ import android.os.Process; import android.os.RemoteException; import android.os.Trace; import android.os.UserHandle; -import android.os.UserManager; import android.provider.DeviceConfig; import android.provider.Settings; import android.util.ArraySet; @@ -70,13 +68,9 @@ import android.util.Pair; import android.util.Slog; import com.android.server.DeviceIdleInternal; -import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.File; -import java.security.PublicKey; -import java.security.cert.CertificateException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Set; @@ -90,10 +84,6 @@ final class VerificationParams extends HandlerParams { * milliseconds. */ private static final long DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT = 30 * 1000; - /** - * Whether verification is enabled by default. - */ - private static final boolean DEFAULT_VERIFY_ENABLE = true; /** * Timeout duration in milliseconds for enabling package rollback. If we fail to enable * rollback within that period, the install will proceed without rollback enabled. @@ -166,7 +156,7 @@ final class VerificationParams extends HandlerParams { PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext, mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride); - Pair ret = verifyReplacingVersionCode( + Pair ret = mInstallPackageHelper.verifyReplacingVersionCode( pkgLite, mRequiredInstalledVersionCode, mInstallFlags); setReturnCode(ret.first, ret.second); if (mRet != INSTALL_SUCCEEDED) { @@ -186,7 +176,7 @@ final class VerificationParams extends HandlerParams { } } - void sendApkVerificationRequest(PackageInfoLite pkgLite) { + private void sendApkVerificationRequest(PackageInfoLite pkgLite) { final int verificationId = mPm.mPendingVerificationToken++; PackageVerificationState verificationState = @@ -332,7 +322,7 @@ final class VerificationParams extends HandlerParams { /** * Send a request to verifier(s) to verify the package if necessary. */ - void sendPackageVerificationRequest( + private void sendPackageVerificationRequest( int verificationId, PackageInfoLite pkgLite, PackageVerificationState verificationState) { @@ -354,7 +344,7 @@ final class VerificationParams extends HandlerParams { verificationState.setRequiredVerifierUid(requiredUid); final int installerUid = mVerificationInfo == null ? -1 : mVerificationInfo.mInstallerUid; - final boolean isVerificationEnabled = isVerificationEnabled( + final boolean isVerificationEnabled = mInstallPackageHelper.isVerificationEnabled( pkgLite, verifierUser.getIdentifier(), mInstallFlags, installerUid); final boolean isV4Signed = (mSigningDetails.getSignatureSchemeVersion() == SIGNING_BLOCK_V4); @@ -404,7 +394,7 @@ final class VerificationParams extends HandlerParams { DeviceIdleInternal idleController = mPm.mInjector.getLocalService(DeviceIdleInternal.class); - final long idleDuration = mVerificationHelper.getVerificationTimeout(); + final long idleDuration = VerificationUtils.getVerificationTimeout(mPm.mContext); final BroadcastOptions options = BroadcastOptions.makeBasic(); options.setTemporaryAppAllowlist(idleDuration, TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, @@ -461,7 +451,7 @@ final class VerificationParams extends HandlerParams { .obtainMessage(CHECK_PENDING_VERIFICATION); msg.arg1 = verificationId; mPm.mHandler.sendMessageDelayed(msg, - mVerificationHelper.getVerificationTimeout()); + VerificationUtils.getVerificationTimeout(mPm.mContext)); } }, null, 0, null, null); @@ -498,7 +488,7 @@ final class VerificationParams extends HandlerParams { continue; } - final int verifierUid = getUidForVerifier(verifierInfo); + final int verifierUid = mInstallPackageHelper.getUidForVerifier(verifierInfo); if (verifierUid == -1) { continue; } @@ -514,44 +504,6 @@ final class VerificationParams extends HandlerParams { return sufficientVerifiers; } - private int getUidForVerifier(VerifierInfo verifierInfo) { - synchronized (mPm.mLock) { - final AndroidPackage pkg = mPm.mPackages.get(verifierInfo.packageName); - if (pkg == null) { - return -1; - } else if (pkg.getSigningDetails().getSignatures().length != 1) { - Slog.i(TAG, "Verifier package " + verifierInfo.packageName - + " has more than one signature; ignoring"); - return -1; - } - - /* - * If the public key of the package's signature does not match - * our expected public key, then this is a different package and - * we should skip. - */ - - final byte[] expectedPublicKey; - try { - final Signature verifierSig = pkg.getSigningDetails().getSignatures()[0]; - final PublicKey publicKey = verifierSig.getPublicKey(); - expectedPublicKey = publicKey.getEncoded(); - } catch (CertificateException e) { - return -1; - } - - final byte[] actualPublicKey = verifierInfo.publicKey.getEncoded(); - - if (!Arrays.equals(actualPublicKey, expectedPublicKey)) { - Slog.i(TAG, "Verifier package " + verifierInfo.packageName - + " does not have the expected public key; ignoring"); - return -1; - } - - return pkg.getUid(); - } - } - private static ComponentName matchComponentForVerifier(String packageName, List receivers) { ActivityInfo targetReceiver = null; @@ -576,56 +528,6 @@ final class VerificationParams extends HandlerParams { return new ComponentName(targetReceiver.packageName, targetReceiver.name); } - /** - * Check whether or not package verification has been enabled. - * - * @return true if verification should be performed - */ - private boolean isVerificationEnabled( - PackageInfoLite pkgInfoLite, int userId, int installFlags, int installerUid) { - if (!DEFAULT_VERIFY_ENABLE) { - return false; - } - - // Check if installing from ADB - if ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0) { - if (mPm.isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS)) { - return true; - } - // Check if the developer wants to skip verification for ADB installs - if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) { - synchronized (mPm.mLock) { - if (mPm.mSettings.getPackageLPr(pkgInfoLite.packageName) == null) { - // Always verify fresh install - return true; - } - } - // Only skip when apk is debuggable - return !pkgInfoLite.debuggable; - } - return Settings.Global.getInt(mPm.mContext.getContentResolver(), - Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0; - } - - // only when not installed from ADB, skip verification for instant apps when - // the installer and verifier are the same. - if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) { - if (mPm.mInstantAppInstallerActivity != null - && mPm.mInstantAppInstallerActivity.packageName.equals( - mPm.mRequiredVerifierPackage)) { - try { - mPm.mInjector.getSystemService(AppOpsManager.class) - .checkPackage(installerUid, mPm.mRequiredVerifierPackage); - if (DEBUG_VERIFY) { - Slog.i(TAG, "disable verification for instant app"); - } - return false; - } catch (SecurityException ignore) { } - } - } - return true; - } - void populateInstallerExtras(Intent intent) { intent.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE, mInstallSource.initiatingPackageName); diff --git a/services/core/java/com/android/server/pm/VerificationHelper.java b/services/core/java/com/android/server/pm/VerificationUtils.java similarity index 85% rename from services/core/java/com/android/server/pm/VerificationHelper.java rename to services/core/java/com/android/server/pm/VerificationUtils.java index b04d6de2340db..4392b4720fcb8 100644 --- a/services/core/java/com/android/server/pm/VerificationHelper.java +++ b/services/core/java/com/android/server/pm/VerificationUtils.java @@ -27,36 +27,30 @@ import android.net.Uri; import android.os.UserHandle; import android.provider.Settings; -public final class VerificationHelper { +final class VerificationUtils { /** * The default maximum time to wait for the verification agent to return in * milliseconds. */ private static final long DEFAULT_VERIFICATION_TIMEOUT = 10 * 1000; - Context mContext; - - public VerificationHelper(Context context) { - mContext = context; - } - /** * Get the verification agent timeout. Used for both the APK verifier and the * intent filter verifier. * * @return verification timeout in milliseconds */ - public long getVerificationTimeout() { - long timeout = Settings.Global.getLong(mContext.getContentResolver(), + public static long getVerificationTimeout(Context context) { + long timeout = Settings.Global.getLong(context.getContentResolver(), Settings.Global.PACKAGE_VERIFIER_TIMEOUT, DEFAULT_VERIFICATION_TIMEOUT); // The setting can be used to increase the timeout but not decrease it, since that is // equivalent to disabling the verifier. return Math.max(timeout, DEFAULT_VERIFICATION_TIMEOUT); } - public void broadcastPackageVerified(int verificationId, Uri packageUri, + public static void broadcastPackageVerified(int verificationId, Uri packageUri, int verificationCode, @Nullable String rootHashString, int dataLoaderType, - UserHandle user) { + UserHandle user, Context context) { final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED); intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); @@ -67,7 +61,7 @@ public final class VerificationHelper { } intent.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType); - mContext.sendBroadcastAsUser(intent, user, + context.sendBroadcastAsUser(intent, user, android.Manifest.permission.PACKAGE_VERIFICATION_AGENT); } }