Merge "Break install up into phases"
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 0053745..5bf323a 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -1048,11 +1048,14 @@
final String otherPackageName =
(other != null && other.getComponentName() != null)
? other.getComponentName().getPackageName() : "?";
- throw new PackageManagerException(
- INSTALL_FAILED_CONFLICTING_PROVIDER,
- "Can't install because provider name " + names[j]
- + " (in package " + pkg.applicationInfo.packageName
- + ") is already used by " + otherPackageName);
+ // if we're installing over the same already-installed package, this is ok
+ if (otherPackageName != pkg.packageName) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_CONFLICTING_PROVIDER,
+ "Can't install because provider name " + names[j]
+ + " (in package " + pkg.applicationInfo.packageName
+ + ") is already used by " + otherPackageName);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index db7a109..91af0ec 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -111,7 +111,8 @@
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
-import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+import static com.android.server.pm.permission.PermissionsState
+ .PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
import android.Manifest;
import android.annotation.IntDef;
@@ -303,7 +304,8 @@
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.permission.BasePermission;
import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
-import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy
+ .DefaultPermissionGrantedCallback;
import com.android.server.pm.permission.PermissionManagerInternal;
import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
import com.android.server.pm.permission.PermissionManagerService;
@@ -8584,7 +8586,10 @@
null /* originalPkgSetting */, null, parseFlags, scanFlags,
(pkg == mPlatformPackage), user);
applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
- scanPackageOnlyLI(request, mFactoryTest, -1L);
+ final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L);
+ if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) {
+ scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting);
+ }
}
}
}
@@ -9807,6 +9812,11 @@
/** Whether or not the package scan was successful */
public final boolean success;
/**
+ * Whether or not the original PackageSetting needs to be updated with this result on
+ * commit.
+ */
+ public final boolean existingSettingCopied;
+ /**
* The final package settings. This may be the same object passed in
* the {@link ScanRequest}, but, with modified values.
*/
@@ -9816,11 +9826,12 @@
public ScanResult(
ScanRequest request, boolean success,
@Nullable PackageSetting pkgSetting,
- @Nullable List<String> changedAbiCodePath) {
+ @Nullable List<String> changedAbiCodePath, boolean existingSettingCopied) {
this.request = request;
this.success = success;
this.pkgSetting = pkgSetting;
this.changedAbiCodePath = changedAbiCodePath;
+ this.existingSettingCopied = existingSettingCopied;
}
}
@@ -9897,26 +9908,34 @@
private @ScanFlags int adjustScanFlags(@ScanFlags int scanFlags,
PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
PackageParser.Package pkg) {
- if (disabledPkgSetting != null) {
+
+ // TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain
+ // the correct isSystem value now that we don't disable system packages before scan.
+ final PackageSetting systemPkgSetting =
+ (scanFlags & SCAN_NEW_INSTALL) != 0 && disabledPkgSetting == null
+ && pkgSetting != null && pkgSetting.isSystem()
+ ? pkgSetting
+ : disabledPkgSetting;
+ if (systemPkgSetting != null) {
// updated system application, must at least have SCAN_AS_SYSTEM
scanFlags |= SCAN_AS_SYSTEM;
- if ((disabledPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
- if ((disabledPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
scanFlags |= SCAN_AS_OEM;
}
- if ((disabledPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
scanFlags |= SCAN_AS_VENDOR;
}
- if ((disabledPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
scanFlags |= SCAN_AS_PRODUCT;
}
- if ((disabledPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.pkgPrivateFlags
& ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0) {
scanFlags |= SCAN_AS_PRODUCT_SERVICES;
}
@@ -10046,11 +10065,18 @@
final PackageSetting disabledPkgSetting = request.disabledPkgSetting;
final UserHandle user = request.user;
final String realPkgName = request.realPkgName;
- final PackageSetting pkgSetting = result.pkgSetting;
final List<String> changedAbiCodePath = result.changedAbiCodePath;
- final boolean newPkgSettingCreated = (result.pkgSetting != request.pkgSetting);
-
- if (newPkgSettingCreated) {
+ final PackageSetting pkgSetting;
+ if (result.existingSettingCopied) {
+ pkgSetting = request.pkgSetting;
+ pkgSetting.updateFrom(result.pkgSetting);
+ pkg.mExtras = pkgSetting;
+ if (pkgSetting.sharedUser != null
+ && pkgSetting.sharedUser.removePackage(result.pkgSetting)) {
+ pkgSetting.sharedUser.addPackage(pkgSetting);
+ }
+ } else {
+ pkgSetting = result.pkgSetting;
if (originalPkgSetting != null) {
mSettings.addRenamedPackageLPw(pkg.packageName, originalPkgSetting.name);
}
@@ -10357,7 +10383,6 @@
String primaryCpuAbiFromSettings = null;
String secondaryCpuAbiFromSettings = null;
boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
-
if (!needToDeriveAbi) {
if (pkgSetting != null) {
primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString;
@@ -10401,6 +10426,11 @@
UserManagerService.getInstance(), usesStaticLibraries,
pkg.usesStaticLibrariesVersions);
} else {
+ if (!createNewPackage) {
+ // make a deep copy to avoid modifying any existing system state.
+ pkgSetting = new PackageSetting(pkgSetting);
+ pkgSetting.pkg = pkg;
+ }
// REMOVE SharedUserSetting from method; update in a separate call.
//
// TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
@@ -10433,8 +10463,10 @@
final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
setInstantAppForUser(pkgSetting, userId, instantApp, fullApp);
}
-
- if (disabledPkgSetting != null) {
+ // TODO(patb): see if we can do away with disabled check here.
+ if (disabledPkgSetting != null
+ || (0 != (scanFlags & SCAN_NEW_INSTALL)
+ && pkgSetting != null && pkgSetting.isSystem())) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
@@ -10617,7 +10649,8 @@
pkgSetting.volumeUuid = volumeUuid;
}
- return new ScanResult(request, true, pkgSetting, changedAbiCodePath);
+ return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
+ !createNewPackage /* existingSettingCopied */);
}
/**
@@ -10832,7 +10865,7 @@
}
// A package name must be unique; don't allow duplicates
- if (mPackages.containsKey(pkg.packageName)) {
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0 && mPackages.containsKey(pkg.packageName)) {
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
"Application package " + pkg.packageName
+ " already installed. Skipping duplicate.");
@@ -10841,7 +10874,8 @@
if (pkg.applicationInfo.isStaticSharedLibrary()) {
// Static libs have a synthetic package name containing the version
// but we still want the base name to be unique.
- if (mPackages.containsKey(pkg.manifestPackageName)) {
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0
+ && mPackages.containsKey(pkg.manifestPackageName)) {
throw new PackageManagerException(
"Duplicate static shared lib provider package");
}
@@ -11930,7 +11964,9 @@
// Remove the parent package setting
PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps != null) {
- removePackageLI(ps, chatty);
+ removePackageLI(ps.name, chatty);
+ } else if (DEBUG_REMOVE && chatty) {
+ Log.d(TAG, "Not removing package " + pkg.packageName + "; mExtras == null");
}
// Remove the child package setting
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
@@ -11938,23 +11974,22 @@
PackageParser.Package childPkg = pkg.childPackages.get(i);
ps = (PackageSetting) childPkg.mExtras;
if (ps != null) {
- removePackageLI(ps, chatty);
+ removePackageLI(ps.name, chatty);
}
}
}
- void removePackageLI(PackageSetting ps, boolean chatty) {
+ void removePackageLI(String packageName, boolean chatty) {
if (DEBUG_INSTALL) {
if (chatty)
- Log.d(TAG, "Removing package " + ps.name);
+ Log.d(TAG, "Removing package " + packageName);
}
// writer
synchronized (mPackages) {
- mPackages.remove(ps.name);
- final PackageParser.Package pkg = ps.pkg;
- if (pkg != null) {
- cleanPackageDataStructuresLILPw(pkg, chatty);
+ final PackageParser.Package removedPackage = mPackages.remove(packageName);
+ if (removedPackage != null) {
+ cleanPackageDataStructuresLILPw(removedPackage, chatty);
}
}
}
@@ -14655,68 +14690,6 @@
String origPermission;
}
- /*
- * Install a non-existing package.
- */
- private void installNewPackageLIF(PackageParser.Package pkg, final @ParseFlags int parseFlags,
- final @ScanFlags int scanFlags, UserHandle user, String installerPackageName,
- String volumeUuid, PackageInstalledInfo res, int installReason) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage");
-
- // Remember this for later, in case we need to rollback this install
- String pkgName = pkg.packageName;
-
- if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
-
- synchronized(mPackages) {
- final String renamedPackage = mSettings.getRenamedPackageLPr(pkgName);
- if (renamedPackage != null) {
- // 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.
- res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
- + " without first uninstalling package running as "
- + renamedPackage);
- return;
- }
- if (mPackages.containsKey(pkgName)) {
- // Don't allow installation over an existing package with the same name.
- res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
- + " without first uninstalling.");
- return;
- }
- }
-
- try {
- final PackageParser.Package newPackage;
- List<ScanResult> scanResults = scanPackageTracedLI(pkg, parseFlags, scanFlags,
- System.currentTimeMillis(), user);
- commitSuccessfulScanResults(scanResults);
- // TODO(b/109941548): Child packages may return >1 result with the first being the base;
- // we need to treat child packages as an atomic install and remove this hack
- final ScanResult basePackageScanResult = scanResults.get(0);
- newPackage = basePackageScanResult.pkgSetting.pkg;
- updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);
-
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- prepareAppDataAfterInstallLIF(newPackage);
- } else {
- // Remove package from internal structures, but keep around any
- // data that might have already existed
- deletePackageLIF(pkgName, UserHandle.ALL, false, null,
- PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
- }
- } catch (PackageManagerException e) {
- destroyAppDataLIF(pkg, UserHandle.USER_ALL,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
- destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
- res.setError("Package couldn't be installed in " + pkg.codePath, e);
- }
-
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
private static void updateDigest(MessageDigest digest, File file) throws IOException {
try (DigestInputStream digestStream =
new DigestInputStream(new FileInputStream(file), digest)) {
@@ -14724,482 +14697,6 @@
}
}
- private void replacePackageLIF(PackageParser.Package pkg, final @ParseFlags int parseFlags,
- final @ScanFlags int scanFlags, UserHandle user, String installerPackageName,
- PackageInstalledInfo res, int installReason) {
- final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
-
- final PackageParser.Package oldPackage;
- final PackageSetting ps;
- final String pkgName = pkg.packageName;
- final int[] allUsers;
- final int[] installedUsers;
-
- synchronized(mPackages) {
- oldPackage = mPackages.get(pkgName);
- if (DEBUG_INSTALL) Slog.d(TAG, "replacePackageLI: new=" + pkg + ", old=" + oldPackage);
-
- // don't allow upgrade to target a release SDK from a pre-release SDK
- final boolean oldTargetsPreRelease = oldPackage.applicationInfo.targetSdkVersion
- == android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
- final boolean newTargetsPreRelease = pkg.applicationInfo.targetSdkVersion
- == android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
- if (oldTargetsPreRelease
- && !newTargetsPreRelease
- && ((parseFlags & PackageParser.PARSE_FORCE_SDK) == 0)) {
- Slog.w(TAG, "Can't install package targeting released sdk");
- res.setReturnCode(PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE);
- return;
- }
-
- ps = mSettings.mPackages.get(pkgName);
-
- // verify signatures are valid
- final KeySetManagerService ksms = mSettings.mKeySetManagerService;
- if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {
- if (!ksms.checkUpgradeKeySetLocked(ps, pkg)) {
- res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "New package not signed by keys specified by upgrade-keysets: "
- + pkgName);
- return;
- }
- } else {
-
- // default to original signature matching
- if (!pkg.mSigningDetails.checkCapability(oldPackage.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
- && !oldPackage.mSigningDetails.checkCapability(
- pkg.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
- res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "New package has a different signature: " + pkgName);
- return;
- }
- }
-
- // don't allow a system upgrade unless the upgrade hash matches
- if (oldPackage.restrictUpdateHash != null && oldPackage.isSystem()) {
- final byte[] digestBytes;
- try {
- final MessageDigest digest = MessageDigest.getInstance("SHA-512");
- updateDigest(digest, new File(pkg.baseCodePath));
- if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
- for (String path : pkg.splitCodePaths) {
- updateDigest(digest, new File(path));
- }
- }
- digestBytes = digest.digest();
- } catch (NoSuchAlgorithmException | IOException e) {
- res.setError(INSTALL_FAILED_INVALID_APK,
- "Could not compute hash: " + pkgName);
- return;
- }
- if (!Arrays.equals(oldPackage.restrictUpdateHash, digestBytes)) {
- res.setError(INSTALL_FAILED_INVALID_APK,
- "New package fails restrict-update check: " + pkgName);
- return;
- }
- // retain upgrade restriction
- pkg.restrictUpdateHash = oldPackage.restrictUpdateHash;
- }
-
- // Check for shared user id changes
- String invalidPackageName =
- getParentOrChildPackageChangedSharedUser(oldPackage, pkg);
- if (invalidPackageName != null) {
- res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
- "Package " + invalidPackageName + " tried to change user "
- + oldPackage.mSharedUserId);
- return;
- }
-
- // check if the new package supports all of the abis which the old package supports
- boolean oldPkgSupportMultiArch = oldPackage.applicationInfo.secondaryCpuAbi != null;
- boolean newPkgSupportMultiArch = pkg.applicationInfo.secondaryCpuAbi != null;
- if (isSystemApp(oldPackage) && oldPkgSupportMultiArch && !newPkgSupportMultiArch) {
- res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "Update to package " + pkgName + " doesn't support multi arch");
- return;
- }
-
- // In case of rollback, remember per-user/profile install state
- allUsers = sUserManager.getUserIds();
- installedUsers = ps.queryInstalledUsers(allUsers, true);
-
- // don't allow an upgrade from full to ephemeral
- if (isInstantApp) {
- if (user == null || user.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: " + pkgName
- + " for user: " + currentUser);
- res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
- return;
- }
- }
- } else if (!ps.getInstantApp(user.getIdentifier())) {
- // can't downgrade from full to instant
- Slog.w(TAG, "Can't replace full app with instant app: " + pkgName
- + " for user: " + user.getIdentifier());
- res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
- return;
- }
- }
- }
-
- // Update what is removed
- res.removedInfo = new PackageRemovedInfo(this);
- res.removedInfo.uid = oldPackage.applicationInfo.uid;
- res.removedInfo.removedPackage = oldPackage.packageName;
- res.removedInfo.installerPackageName = ps.installerPackageName;
- res.removedInfo.isStaticSharedLib = pkg.staticSharedLibName != null;
- res.removedInfo.isUpdate = true;
- res.removedInfo.origUsers = installedUsers;
- res.removedInfo.installReasons = new SparseArray<>(installedUsers.length);
- for (int i = 0; i < installedUsers.length; i++) {
- final int userId = installedUsers[i];
- res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));
- }
-
- final int childCount = (oldPackage.childPackages != null)
- ? oldPackage.childPackages.size() : 0;
- for (int i = 0; i < childCount; i++) {
- boolean childPackageUpdated = false;
- PackageParser.Package childPkg = oldPackage.childPackages.get(i);
- final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
- if (res.addedChildPackages != null) {
- PackageInstalledInfo childRes = res.addedChildPackages.get(childPkg.packageName);
- if (childRes != null) {
- childRes.removedInfo.uid = childPkg.applicationInfo.uid;
- childRes.removedInfo.removedPackage = childPkg.packageName;
- if (childPs != null) {
- childRes.removedInfo.installerPackageName = childPs.installerPackageName;
- }
- childRes.removedInfo.isUpdate = true;
- childRes.removedInfo.installReasons = res.removedInfo.installReasons;
- childPackageUpdated = true;
- }
- }
- if (!childPackageUpdated) {
- PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
- childRemovedRes.removedPackage = childPkg.packageName;
- if (childPs != null) {
- childRemovedRes.installerPackageName = childPs.installerPackageName;
- }
- childRemovedRes.isUpdate = false;
- childRemovedRes.dataRemoved = true;
- synchronized (mPackages) {
- if (childPs != null) {
- childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers, true);
- }
- }
- if (res.removedInfo.removedChildPackages == null) {
- res.removedInfo.removedChildPackages = new ArrayMap<>();
- }
- res.removedInfo.removedChildPackages.put(childPkg.packageName, childRemovedRes);
- }
- }
-
- boolean sysPkg = (isSystemApp(oldPackage));
- if (sysPkg) {
- // Set the system/privileged/oem/vendor/product flags as needed
- final boolean privileged = isPrivilegedApp(oldPackage);
- final boolean oem = isOemApp(oldPackage);
- final boolean vendor = isVendorApp(oldPackage);
- final boolean product = isProductApp(oldPackage);
- final boolean productServices = isProductServicesApp(oldPackage);
-
- final @ParseFlags int systemParseFlags = parseFlags;
- final @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)
- | (productServices ? SCAN_AS_PRODUCT_SERVICES : 0);
-
- replaceSystemPackageLIF(oldPackage, pkg, systemParseFlags, systemScanFlags,
- user, allUsers, installerPackageName, res, installReason);
- } else {
- replaceNonSystemPackageLIF(oldPackage, pkg, parseFlags, scanFlags,
- user, allUsers, installerPackageName, res, installReason);
- }
- }
-
- private void replaceNonSystemPackageLIF(PackageParser.Package deletedPackage,
- PackageParser.Package pkg, final @ParseFlags int parseFlags,
- final @ScanFlags int scanFlags, UserHandle user, int[] allUsers,
- String installerPackageName, PackageInstalledInfo res, int installReason) {
- if (DEBUG_INSTALL) Slog.d(TAG, "replaceNonSystemPackageLI: new=" + pkg + ", old="
- + deletedPackage);
-
- String pkgName = deletedPackage.packageName;
- boolean deletedPkg = true;
- boolean addedPkg = false;
- boolean updatedSettings = false;
- final boolean killApp = (scanFlags & SCAN_DONT_KILL_APP) == 0;
- final int deleteFlags = PackageManager.DELETE_KEEP_DATA
- | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
-
- final long origUpdateTime = (pkg.mExtras != null)
- ? ((PackageSetting)pkg.mExtras).lastUpdateTime : 0;
-
- // First delete the existing package while retaining the data directory
- if (!deletePackageLIF(pkgName, null, true, allUsers, deleteFlags,
- res.removedInfo, true, pkg)) {
- // If the existing package wasn't successfully deleted
- res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE, "replaceNonSystemPackageLI");
- deletedPkg = false;
- } else {
- // 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 (deletedPackage.isForwardLocked() || isExternal(deletedPackage)) {
- if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + deletedPackage + " is ASEC-hosted -> UNAVAILABLE");
- }
- final int[] uidArray = new int[] { deletedPackage.applicationInfo.uid };
- final ArrayList<String> pkgList = new ArrayList<>(1);
- pkgList.add(deletedPackage.applicationInfo.packageName);
- sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
- }
-
- clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
- | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-
- try {
- final List<ScanResult> scanResults = scanPackageTracedLI(pkg, parseFlags,
- scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);
- commitSuccessfulScanResults(scanResults);
- final PackageParser.Package newPackage = scanResults.get(0).pkgSetting.pkg;
- updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,
- installReason);
-
- // Update the in-memory copy of the previous code paths.
- PackageSetting ps = mSettings.mPackages.get(pkgName);
- if (!killApp) {
- if (ps.oldCodePaths == null) {
- ps.oldCodePaths = new ArraySet<>();
- }
- Collections.addAll(ps.oldCodePaths, deletedPackage.baseCodePath);
- if (deletedPackage.splitCodePaths != null) {
- Collections.addAll(ps.oldCodePaths, deletedPackage.splitCodePaths);
- }
- } else {
- ps.oldCodePaths = null;
- }
- if (ps.childPackageNames != null) {
- for (int i = ps.childPackageNames.size() - 1; i >= 0; --i) {
- final String childPkgName = ps.childPackageNames.get(i);
- final PackageSetting childPs = mSettings.mPackages.get(childPkgName);
- childPs.oldCodePaths = ps.oldCodePaths;
- }
- }
- prepareAppDataAfterInstallLIF(newPackage);
- addedPkg = true;
- mDexManager.notifyPackageUpdated(newPackage.packageName,
- newPackage.baseCodePath, newPackage.splitCodePaths);
- } catch (PackageManagerException e) {
- res.setError("Package couldn't be installed in " + pkg.codePath, e);
- }
- }
-
- if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
- if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName);
-
- // Revert all internal state mutations and added folders for the failed install
- if (addedPkg) {
- deletePackageLIF(pkgName, null, true, allUsers, deleteFlags,
- res.removedInfo, true, null);
- }
-
- // Restore the old package
- if (deletedPkg) {
- if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);
- File restoreFile = new File(deletedPackage.codePath);
- // Parse old package
- boolean oldExternal = isExternal(deletedPackage);
- int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY |
- (deletedPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0) |
- (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
- int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
- try {
- scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime,
- null);
- } catch (PackageManagerException e) {
- Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade: "
- + e.getMessage());
- return;
- }
-
- synchronized (mPackages) {
- // Ensure the installer package name up to date
- setInstallerPackageNameLPw(deletedPackage, installerPackageName);
-
- // Update permissions for restored package
- mPermissionManager.updatePermissions(
- deletedPackage.packageName, deletedPackage, false, mPackages.values(),
- mPermissionCallback);
-
- mSettings.writeLPr();
- }
-
- Slog.i(TAG, "Successfully restored package : " + pkgName + " after failed upgrade");
- }
- } else {
- synchronized (mPackages) {
- PackageSetting ps = mSettings.getPackageLPr(pkg.packageName);
- if (ps != null) {
- res.removedInfo.removedForAllUsers = mPackages.get(ps.name) == null;
- if (res.removedInfo.removedChildPackages != null) {
- final int childCount = res.removedInfo.removedChildPackages.size();
- // Iterate in reverse as we may modify the collection
- for (int i = childCount - 1; i >= 0; i--) {
- String childPackageName = res.removedInfo.removedChildPackages.keyAt(i);
- if (res.addedChildPackages.containsKey(childPackageName)) {
- res.removedInfo.removedChildPackages.removeAt(i);
- } else {
- PackageRemovedInfo childInfo = res.removedInfo
- .removedChildPackages.valueAt(i);
- childInfo.removedForAllUsers = mPackages.get(
- childInfo.removedPackage) == null;
- }
- }
- }
- }
- }
- }
- }
-
- private void replaceSystemPackageLIF(PackageParser.Package deletedPackage,
- PackageParser.Package pkg, final @ParseFlags int parseFlags,
- final @ScanFlags int scanFlags, UserHandle user,
- int[] allUsers, String installerPackageName, PackageInstalledInfo res,
- int installReason) {
- if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
- + ", old=" + deletedPackage);
-
- final boolean disabledSystem;
-
- // Remove existing system package
- removePackageLI(deletedPackage, true);
-
- synchronized (mPackages) {
- disabledSystem = disableSystemPackageLPw(deletedPackage, pkg);
- }
- if (!disabledSystem) {
- // 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.removedInfo.args = createInstallArgsForExisting(0,
- deletedPackage.applicationInfo.getCodePath(),
- deletedPackage.applicationInfo.getResourcePath(),
- getAppDexInstructionSets(deletedPackage.applicationInfo));
- } else {
- res.removedInfo.args = null;
- }
-
- // Successfully disabled the old package. Now proceed with re-installation
- clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
- | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-
- res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
- pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
- ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
-
- PackageParser.Package newPackage = null;
- try {
- final List<ScanResult> scanResults =
- scanPackageTracedLI(pkg, parseFlags, scanFlags, 0, user);
- // Add the package to the internal data structures
- commitSuccessfulScanResults(scanResults);
- newPackage = scanResults.get(0).pkgSetting.pkg;
-
- // Set the update and install times
- PackageSetting deletedPkgSetting = (PackageSetting) deletedPackage.mExtras;
- setInstallAndUpdateTime(newPackage, deletedPkgSetting.firstInstallTime,
- System.currentTimeMillis());
-
- // Update the package dynamic state if succeeded
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- // Now that the install succeeded make sure we remove data
- // directories for any child package the update removed.
- final int deletedChildCount = (deletedPackage.childPackages != null)
- ? deletedPackage.childPackages.size() : 0;
- final int newChildCount = (newPackage.childPackages != null)
- ? newPackage.childPackages.size() : 0;
- for (int i = 0; i < deletedChildCount; i++) {
- PackageParser.Package deletedChildPkg = deletedPackage.childPackages.get(i);
- boolean childPackageDeleted = true;
- for (int j = 0; j < newChildCount; j++) {
- PackageParser.Package newChildPkg = newPackage.childPackages.get(j);
- if (deletedChildPkg.packageName.equals(newChildPkg.packageName)) {
- childPackageDeleted = false;
- break;
- }
- }
- if (childPackageDeleted) {
- PackageSetting ps = mSettings.getDisabledSystemPkgLPr(
- deletedChildPkg.packageName);
- if (ps != null && res.removedInfo.removedChildPackages != null) {
- PackageRemovedInfo removedChildRes = res.removedInfo
- .removedChildPackages.get(deletedChildPkg.packageName);
- removePackageDataLIF(ps, allUsers, removedChildRes, 0, false);
- removedChildRes.removedForAllUsers = mPackages.get(ps.name) == null;
- }
- }
- }
-
- updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,
- installReason);
- prepareAppDataAfterInstallLIF(newPackage);
-
- mDexManager.notifyPackageUpdated(newPackage.packageName,
- newPackage.baseCodePath, newPackage.splitCodePaths);
- }
- } catch (PackageManagerException e) {
- res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
- res.setError("Package couldn't be installed in " + pkg.codePath, e);
- }
-
- if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
- // Re installation failed. Restore old information
- // Remove new pkg information
- if (newPackage != null) {
- removeInstalledPackageLI(newPackage, true);
- }
- // Add back the old system package
- try {
- final List<ScanResult> scanResults = scanPackageTracedLI(
- deletedPackage, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);
- commitSuccessfulScanResults(scanResults);
- } catch (PackageManagerException e) {
- Slog.e(TAG, "Failed to restore original package: " + e.getMessage());
- }
-
- synchronized (mPackages) {
- if (disabledSystem) {
- enableSystemPackageLPw(deletedPackage);
- }
-
- // Ensure the installer package name up to date
- setInstallerPackageNameLPw(deletedPackage, installerPackageName);
-
- // Update permissions for restored package
- mPermissionManager.updatePermissions(
- deletedPackage.packageName, deletedPackage, false, mPackages.values(),
- mPermissionCallback);
-
- mSettings.writeLPr();
- }
-
- Slog.i(TAG, "Successfully restored package : " + deletedPackage.packageName
- + " after failed upgrade");
- }
- }
-
/**
* Checks whether the parent or any of the child packages have a change shared
* user. For a package to be a valid update the shred users of the parent and
@@ -15396,92 +14893,683 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
+
private static class InstallRequest {
- final InstallArgs args;
- final PackageInstalledInfo res;
+ public final InstallArgs args;
+ public final PackageInstalledInfo installResult;
private InstallRequest(InstallArgs args, PackageInstalledInfo res) {
this.args = args;
- this.res = res;
+ this.installResult = res;
}
}
@GuardedBy({"mInstallLock", "mPackages"})
- private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {
+ private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo installResult) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage");
- installPackagesLI(Collections.singletonList(new InstallRequest(args, res)));
+ installPackagesLI(Collections.singletonList(new InstallRequest(args, installResult)));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
+ /**
+ * Package state to commit to memory and disk after reconciliation has completed.
+ */
private static class CommitRequest {
final Map<String, ReconciledPackage> reconciledPackages;
+ final int[] mAllUsers;
- private CommitRequest(Map<String, ReconciledPackage> reconciledPackages) {
+ private CommitRequest(Map<String, ReconciledPackage> reconciledPackages, int[] allUsers) {
this.reconciledPackages = reconciledPackages;
+ this.mAllUsers = allUsers;
}
}
- private static class ReconcileRequest {
- final Map<String, ScanResult> scannedPackages;
- private ReconcileRequest(Map<String, ScanResult> scannedPackages) {
+ /**
+ * Package scan results and related request details used to reconcile the potential addition of
+ * one or more packages to the system.
+ *
+ * Reconcile will take a set of package details that need to be committed to the system and make
+ * sure that they are valid in the context of the system and the other installing apps. Any
+ * invalid state or app will result in a failed reconciliation and thus whatever operation (such
+ * as install) led to the request.
+ */
+ private static class ReconcileRequest {
+ public final Map<String, ScanResult> scannedPackages;
+
+ // TODO: Remove install-specific details from reconcile request; make them generic types
+ // that can be used for scanDir for example.
+ public final Map<String, InstallArgs> installArgs;
+ public final Map<String, PackageInstalledInfo> installResults;
+ public final Map<String, PrepareResult> preparedPackages;
+
+ private ReconcileRequest(Map<String, ScanResult> scannedPackages,
+ Map<String, InstallArgs> installArgs,
+ Map<String, PackageInstalledInfo> installResults,
+ Map<String, PrepareResult> preparedPackages) {
this.scannedPackages = scannedPackages;
+ this.installArgs = installArgs;
+ this.installResults = installResults;
+ this.preparedPackages = preparedPackages;
}
}
private static class ReconcileFailure extends PackageManagerException {
public ReconcileFailure(String message) {
super("Invalid reconcile request: " + message);
}
- };
+ }
/**
* A container of all data needed to commit a package to in-memory data structures and to disk.
- * Ideally most of the data contained in this class will move into a PackageSetting it contains.
+ * TODO: move most of the data contained her into a PackageSetting for commit.
*/
- private static class ReconciledPackage {}
+ private static class ReconciledPackage {
+ public final PackageSetting pkgSetting;
+ public final ScanResult scanResult;
+ public final UserHandle installForUser;
+ public final String volumeUuid;
+ // TODO: Remove install-specific details from the reconcile result
+ public final PackageInstalledInfo installResult;
+ public final PrepareResult prepareResult;
+ @PackageManager.InstallFlags
+ public final int installFlags;
+ public final InstallArgs installArgs;
+
+ private ReconciledPackage(InstallArgs installArgs, PackageSetting pkgSetting,
+ UserHandle installForUser, PackageInstalledInfo installResult, int installFlags,
+ String volumeUuid, PrepareResult prepareResult, ScanResult scanResult) {
+ this.installArgs = installArgs;
+ this.pkgSetting = pkgSetting;
+ this.installForUser = installForUser;
+ this.installResult = installResult;
+ this.installFlags = installFlags;
+ this.volumeUuid = volumeUuid;
+ this.prepareResult = prepareResult;
+ this.scanResult = scanResult;
+ }
+ }
@GuardedBy("mPackages")
private static Map<String, ReconciledPackage> reconcilePackagesLocked(
- final ReconcileRequest request) throws ReconcileFailure {
- return Collections.emptyMap();
+ final ReconcileRequest request)
+ throws ReconcileFailure {
+ Map<String, ReconciledPackage> result = new ArrayMap<>(request.scannedPackages.size());
+ for (String installPackageName : request.installArgs.keySet()) {
+ final ScanResult scanResult = request.scannedPackages.get(installPackageName);
+ final InstallArgs installArgs = request.installArgs.get(installPackageName);
+ final PackageInstalledInfo res = request.installResults.get(installPackageName);
+ if (scanResult == null || installArgs == null || res == null) {
+ throw new ReconcileFailure(
+ "inputs not balanced; missing argument for " + installPackageName);
+ }
+ result.put(installPackageName,
+ new ReconciledPackage(installArgs, scanResult.pkgSetting, installArgs.getUser(),
+ res, installArgs.installFlags, installArgs.volumeUuid,
+ request.preparedPackages.get(installPackageName), scanResult));
+ }
+ return result;
}
@GuardedBy("mPackages")
private boolean 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.reconciledPackages.values()) {
+ final ScanResult scanResult = reconciledPkg.scanResult;
+ final ScanRequest scanRequest = scanResult.request;
+ final PackageParser.Package pkg = scanRequest.pkg;
+ final String packageName = pkg.packageName;
+ final PackageInstalledInfo res = reconciledPkg.installResult;
+
+ if (reconciledPkg.prepareResult.replace) {
+ PackageParser.Package oldPackage = mPackages.get(packageName);
+ if (reconciledPkg.prepareResult.system) {
+ // Remove existing system package
+ removePackageLI(oldPackage, true);
+ if (!disableSystemPackageLPw(oldPackage, pkg)) {
+ // 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.removedInfo.args = createInstallArgsForExisting(0,
+ oldPackage.applicationInfo.getCodePath(),
+ oldPackage.applicationInfo.getResourcePath(),
+ getAppDexInstructionSets(oldPackage.applicationInfo));
+ } else {
+ res.removedInfo.args = null;
+ }
+
+ // Set the update and install times
+ PackageSetting deletedPkgSetting = (PackageSetting) oldPackage.mExtras;
+ setInstallAndUpdateTime(pkg, deletedPkgSetting.firstInstallTime,
+ System.currentTimeMillis());
+
+ // Update the package dynamic state if succeeded
+ // Now that the install succeeded make sure we remove data
+ // directories for any child package the update removed.
+ final int deletedChildCount = (oldPackage.childPackages != null)
+ ? oldPackage.childPackages.size() : 0;
+ final int newChildCount = (pkg.childPackages != null)
+ ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < deletedChildCount; i++) {
+ PackageParser.Package deletedChildPkg = oldPackage.childPackages.get(i);
+ boolean childPackageDeleted = true;
+ for (int j = 0; j < newChildCount; j++) {
+ PackageParser.Package newChildPkg = pkg.childPackages.get(j);
+ if (deletedChildPkg.packageName.equals(newChildPkg.packageName)) {
+ childPackageDeleted = false;
+ break;
+ }
+ }
+ if (childPackageDeleted) {
+ PackageSetting ps1 = mSettings.getDisabledSystemPkgLPr(
+ deletedChildPkg.packageName);
+ if (ps1 != null && res.removedInfo.removedChildPackages != null) {
+ PackageRemovedInfo removedChildRes = res.removedInfo
+ .removedChildPackages.get(deletedChildPkg.packageName);
+ removePackageDataLIF(ps1, request.mAllUsers, removedChildRes, 0,
+ false);
+ removedChildRes.removedForAllUsers = mPackages.get(ps1.name)
+ == null;
+ }
+ }
+ }
+ } else {
+ final boolean killApp = (scanRequest.scanFlags & SCAN_DONT_KILL_APP) == 0;
+ final int deleteFlags = PackageManager.DELETE_KEEP_DATA
+ | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
+ // First delete the existing package while retaining the data directory
+ if (!deletePackageLIF(packageName, null, true, request.mAllUsers, deleteFlags,
+ res.removedInfo, true, pkg)) {
+ // If the existing package wasn't successfully deleted
+ res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE,
+ "replaceNonSystemPackageLI");
+ return false;
+ } else {
+ // 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.isForwardLocked() || isExternal(oldPackage)) {
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + oldPackage
+ + " is ASEC-hosted -> UNAVAILABLE");
+ }
+ final int[] uidArray = new int[]{oldPackage.applicationInfo.uid};
+ final ArrayList<String> pkgList = new ArrayList<>(1);
+ pkgList.add(oldPackage.applicationInfo.packageName);
+ sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
+ }
+
+ clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
+ | StorageManager.FLAG_STORAGE_CE
+ | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ }
+
+
+
+ // Update the in-memory copy of the previous code paths.
+ PackageSetting ps1 = mSettings.mPackages.get(
+ reconciledPkg.prepareResult.existingPackage.packageName);
+ if ((reconciledPkg.installArgs.installFlags & PackageManager.DONT_KILL_APP)
+ == 0) {
+ if (ps1.mOldCodePaths == null) {
+ ps1.mOldCodePaths = new ArraySet<>();
+ }
+ Collections.addAll(ps1.mOldCodePaths, oldPackage.baseCodePath);
+ if (oldPackage.splitCodePaths != null) {
+ Collections.addAll(ps1.mOldCodePaths, oldPackage.splitCodePaths);
+ }
+ } else {
+ ps1.mOldCodePaths = null;
+ }
+ if (ps1.childPackageNames != null) {
+ for (int i = ps1.childPackageNames.size() - 1; i >= 0; --i) {
+ final String childPkgName = ps1.childPackageNames.get(i);
+ final PackageSetting childPs = mSettings.mPackages.get(childPkgName);
+ childPs.mOldCodePaths = ps1.mOldCodePaths;
+ }
+ }
+
+ if (reconciledPkg.installResult.returnCode
+ == PackageManager.INSTALL_SUCCEEDED) {
+ PackageSetting ps2 = mSettings.getPackageLPr(pkg.packageName);
+ if (ps2 != null) {
+ res.removedInfo.removedForAllUsers = mPackages.get(ps2.name) == null;
+ if (res.removedInfo.removedChildPackages != null) {
+ final int childCount1 = res.removedInfo.removedChildPackages.size();
+ // Iterate in reverse as we may modify the collection
+ for (int i = childCount1 - 1; i >= 0; i--) {
+ String childPackageName =
+ res.removedInfo.removedChildPackages.keyAt(i);
+ if (res.addedChildPackages.containsKey(childPackageName)) {
+ res.removedInfo.removedChildPackages.removeAt(i);
+ } else {
+ PackageRemovedInfo childInfo = res.removedInfo
+ .removedChildPackages.valueAt(i);
+ childInfo.removedForAllUsers = mPackages.get(
+ childInfo.removedPackage) == null;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+
+ try {
+ commitScanResultLocked(scanResult);
+ } catch (PackageManagerException e) {
+ res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
+ res.setError("Package couldn't be installed in " + pkg.codePath, e);
+ return false;
+ }
+ updateSettingsLI(pkg, reconciledPkg.installArgs.installerPackageName, request.mAllUsers,
+ res, reconciledPkg.installForUser, reconciledPkg.installArgs.installReason);
+
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps != null) {
+ res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
+ ps.setUpdateAvailable(false /*updateAvailable*/);
+ }
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ PackageInstalledInfo childRes = res.addedChildPackages.get(
+ childPkg.packageName);
+ PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
+ if (childPs != null) {
+ childRes.newUsers = childPs.queryInstalledUsers(
+ sUserManager.getUserIds(), true);
+ }
+ }
+ if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ updateSequenceNumberLP(ps, res.newUsers);
+ updateInstantAppInstallerLocked(packageName);
+ }
+ }
return true;
}
- @GuardedBy({"mInstallLock", "mPackages", "PackageManagerService.mPackages"})
+ /**
+ * Installs one or more packages atomically. This operation is broken up into four phases:
+ * <ul>
+ * <li><b>Prepare</b>
+ * <br/>Analyzes any current install state, parses the package and does initial
+ * validation on it.</li>
+ * <li><b>Scan</b>
+ * <br/>Interrogates the parsed packages given the context collected in prepare.</li>
+ * <li><b>Reconcile</b>
+ * <br/>Validates scanned packages in the context of each other and the current system
+ * state to ensure that the install will be successful.
+ * <li><b>Commit</b>
+ * <br/>Commits all scanned packages and updates system state. This is the only place
+ * that system state may be modified in the install flow and all predictable errors
+ * must be determined before this phase.</li>
+ * </ul>
+ *
+ * Failure at any phase will result in a full failure to install all packages.
+ */
+ @GuardedBy({"mInstallLock", "mPackages"})
private void installPackagesLI(List<InstallRequest> requests) {
- Map<String, ScanResult> scans = new ArrayMap<>(requests.size());
- for (InstallRequest request : requests) {
- // TODO(b/109941548): remove this once we've pulled everything from it and into scan,
- // reconcile or commit.
- preparePackageLI(request.args, request.res);
-
- // TODO(b/109941548): scan package and get result
- }
- ReconcileRequest reconcileRequest = new ReconcileRequest(scans);
- Map<String, ReconciledPackage> reconciledPackages;
+ final Map<String, ScanResult> scans = new ArrayMap<>(requests.size());
+ final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());
+ final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
+ final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
try {
- reconciledPackages = reconcilePackagesLocked(reconcileRequest);
- } catch (ReconcileFailure e) {
- // TODO(b/109941548): set install args error
- return;
+ 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.args, request.installResult);
+ } catch (PrepareFailure prepareFailure) {
+ request.installResult.setError(prepareFailure.error,
+ prepareFailure.getMessage());
+ request.installResult.origPackage = prepareFailure.conflictingPackage;
+ request.installResult.origPermission = prepareFailure.conflictingPermission;
+ return;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ request.installResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ request.installResult.installerPackageName = request.args.installerPackageName;
+
+ final String packageName = prepareResult.packageToScan.packageName;
+ prepareResults.put(packageName, prepareResult);
+ installResults.put(packageName, request.installResult);
+ installArgs.put(packageName, request.args);
+ try {
+ final List<ScanResult> scanResults = scanPackageTracedLI(
+ prepareResult.packageToScan, prepareResult.parseFlags,
+ prepareResult.scanFlags, System.currentTimeMillis(),
+ request.args.user);
+ for (ScanResult result : scanResults) {
+ if (null != scans.put(result.pkgSetting.pkg.packageName, result)) {
+ request.installResult.setError(
+ PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Duplicate package " + result.pkgSetting.pkg.packageName
+ + " in atomic install request.");
+ return;
+ }
+ }
+ } catch (PackageManagerException e) {
+ request.installResult.setError("Scanning Failed.", e);
+ return;
+ }
+ }
+ ReconcileRequest reconcileRequest = new ReconcileRequest(scans, installArgs,
+ installResults,
+ prepareResults);
+ CommitRequest commitRequest = null;
+ synchronized (mPackages) {
+ Map<String, ReconciledPackage> reconciledPackages;
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
+ reconciledPackages = reconcilePackagesLocked(reconcileRequest);
+ } catch (ReconcileFailure e) {
+ for (InstallRequest request : requests) {
+ // TODO(b/109941548): add more concrete failure reasons
+ request.installResult.setError("Reconciliation failed...", e);
+ // TODO: return any used system resources
+ }
+ return;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
+ commitRequest = new CommitRequest(reconciledPackages,
+ sUserManager.getUserIds());
+ if (!commitPackagesLocked(commitRequest)) {
+ cleanUpCommitFailuresLocked(commitRequest);
+ return;
+ }
+ } finally {
+ for (PrepareResult result : prepareResults.values()) {
+ if (result.freezer != null) {
+ result.freezer.close();
+ }
+ }
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+ executePostCommitSteps(commitRequest);
+ } finally {
+ for (PrepareResult result : prepareResults.values()) {
+ if (result.freezer != null) {
+ result.freezer.close();
+ }
+ }
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- CommitRequest request = new CommitRequest(reconciledPackages);
- if (!commitPackagesLocked(request)) {
- // TODO(b/109941548): set install args error
- return;
- }
- // TODO(b/109941548) post-commit actions (dex-opt, etc.)
}
- @Deprecated
+ /**
+ * On successful install, executes remaining steps after commit completes and the package lock
+ * is released.
+ */
+ private void executePostCommitSteps(CommitRequest commitRequest) {
+ for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
+ final boolean forwardLocked =
+ ((reconciledPkg.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
+ final boolean instantApp =
+ ((reconciledPkg.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
+ final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg;
+ final String packageName = pkg.packageName;
+ prepareAppDataAfterInstallLIF(pkg);
+ if (reconciledPkg.prepareResult.clearCodeCache) {
+ clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
+ | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ }
+ if (reconciledPkg.prepareResult.replace) {
+ mDexManager.notifyPackageUpdated(pkg.packageName,
+ pkg.baseCodePath, pkg.splitCodePaths);
+ }
+
+ // 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.
+ mArtManagerService.prepareAppProfiles(
+ pkg, resolveUserIds(reconciledPkg.installForUser.getIdentifier()));
+
+ // Check whether we need to dexopt the app.
+ //
+ // NOTE: it is IMPORTANT to call dexopt:
+ // - after doRename which will sync the package data from PackageParser.Package 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 forward locked.
+ // 2) it is not on on an external ASEC container.
+ // 3) it is not an instant app or if it is then dexopt is enabled via gservices.
+ // 4) it is not debuggable.
+ //
+ // 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.
+ final boolean performDexopt = !forwardLocked
+ && !pkg.applicationInfo.isExternalAsec()
+ && (!instantApp || Global.getInt(mContext.getContentResolver(),
+ Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
+ && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
+
+ if (performDexopt) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+ // Do not run PackageDexOptimizer through the local performDexOpt
+ // method because `pkg` may not be in `mPackages` yet.
+ //
+ // Also, don't fail application installs if the dexopt step fails.
+ DexoptOptions dexoptOptions = new DexoptOptions(packageName,
+ REASON_INSTALL,
+ DexoptOptions.DEXOPT_BOOT_COMPLETE
+ | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
+ mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
+ null /* instructionSets */,
+ getOrCreateCompilerPackageStats(pkg),
+ mDexManager.getPackageUseInfoOrDefault(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 blacklist.
+ // TODO: Layering violation
+ BackgroundDexOptService.notifyPackageChanged(packageName);
+ }
+
+ }
+
+ private void cleanUpCommitFailuresLocked(CommitRequest request) {
+ final Map<String, ReconciledPackage> reconciledPackages = request.reconciledPackages;
+ final int[] allUsers = request.mAllUsers;
+ for (ReconciledPackage reconciledPackage : reconciledPackages.values()) {
+ final String pkgName1 = reconciledPackage.pkgSetting.pkg.packageName;
+ if (reconciledPackage.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ reconciledPackage.installResult.setError(
+ PackageManager.INSTALL_FAILED_INTERNAL_ERROR, "Commit failed...");
+ }
+ final PackageParser.Package oldPackage =
+ reconciledPackage.prepareResult.existingPackage;
+ final PackageParser.Package newPackage = reconciledPackage.pkgSetting.pkg;
+ if (reconciledPackage.prepareResult.system) {
+ // Re installation failed. Restore old information
+ // Remove new pkg information
+ if (newPackage != null) {
+ removeInstalledPackageLI(newPackage, true);
+ }
+ // Add back the old system package
+ PackageParser.Package restoredPkg = null;
+ try {
+ final List<ScanResult> restoreResults = scanPackageTracedLI(oldPackage,
+ reconciledPackage.prepareResult.parseFlags, SCAN_UPDATE_SIGNATURE, 0,
+ reconciledPackage.installForUser);
+ commitSuccessfulScanResults(restoreResults);
+ restoredPkg = restoreResults.get(0).pkgSetting.pkg;
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "Failed to restore original package: " + e.getMessage());
+ }
+ synchronized (mPackages) {
+ final boolean disabledSystem;
+ // Remove existing system package
+ removePackageLI(reconciledPackage.scanResult.pkgSetting.pkg, true);
+ disabledSystem = disableSystemPackageLPw(
+ reconciledPackage.scanResult.pkgSetting.pkg, restoredPkg);
+ if (disabledSystem) {
+ enableSystemPackageLPw(restoredPkg);
+ }
+ // Ensure the installer package name up to date
+ setInstallerPackageNameLPw(reconciledPackage.scanResult.pkgSetting.pkg,
+ reconciledPackage.installArgs.installerPackageName);
+ // Update permissions for restored package
+ mPermissionManager.updatePermissions(
+ restoredPkg.packageName, restoredPkg, false, mPackages.values(),
+ mPermissionCallback);
+ mSettings.writeLPr();
+ }
+ Slog.i(TAG, "Successfully restored package : " + restoredPkg.packageName
+ + " after failed upgrade");
+ } else if (reconciledPackage.prepareResult.replace) {
+ if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName1);
+
+ // Revert all internal state mutations and added folders for the failed install
+ boolean deletedPkg = deletePackageLIF(pkgName1, null, true,
+ allUsers, /*TODO: deleteFlags*/ 0,
+ reconciledPackage.installResult.removedInfo, true, null);
+
+ // Restore the old package
+ if (deletedPkg) {
+ if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + oldPackage);
+ File restoreFile = new File(oldPackage.codePath);
+ // Parse old package
+ boolean oldExternal = isExternal(oldPackage);
+ int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
+ | (oldPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0)
+ | (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
+ int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
+ try {
+ scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags,
+ /* origUpdateTime */ System.currentTimeMillis(), null);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "Failed to restore package : " + pkgName1
+ + " after failed upgrade: "
+ + e.getMessage());
+ return;
+ }
+
+ synchronized (mPackages) {
+ // Ensure the installer package name up to date
+ setInstallerPackageNameLPw(oldPackage,
+ reconciledPackage.installArgs.installerPackageName);
+
+ // Update permissions for restored package
+ mPermissionManager.updatePermissions(
+ oldPackage.packageName, oldPackage, false, mPackages.values(),
+ mPermissionCallback);
+
+ mSettings.writeLPr();
+ }
+
+ Slog.i(TAG, "Successfully restored package : " + pkgName1
+ + " after failed upgrade");
+ }
+ } else {
+ // Remove package from internal structures, but keep around any
+ // data that might have already existed
+ deletePackageLIF(pkgName1, UserHandle.ALL, false, null,
+ PackageManager.DELETE_KEEP_DATA,
+ reconciledPackage.installResult.removedInfo, true, null);
+ }
+ }
+ }
+
+ /**
+ * The set of data needed to successfully install the prepared package. This includes data that
+ * will be used to scan and reconcile the package.
+ */
+ private static class PrepareResult {
+ public final int installReason;
+ public final String volumeUuid;
+ public final String installerPackageName;
+ public final UserHandle user;
+ public final boolean replace;
+ public final int scanFlags;
+ public final int parseFlags;
+ @Nullable /* The original Package if it is being replaced, otherwise {@code null} */
+ public final PackageParser.Package existingPackage;
+ public final PackageParser.Package packageToScan;
+ public final boolean clearCodeCache;
+ public final boolean system;
+ /* The original package name if it was changed during an update, otherwise {@code null}. */
+ @Nullable
+ public final String renamedPackage;
+ public final PackageFreezer freezer;
+
+ private PrepareResult(int installReason, String volumeUuid,
+ String installerPackageName, UserHandle user, boolean replace, int scanFlags,
+ int parseFlags, PackageParser.Package existingPackage,
+ PackageParser.Package packageToScan, boolean clearCodeCache, boolean system,
+ String renamedPackage,
+ PackageFreezer freezer) {
+ this.installReason = installReason;
+ this.volumeUuid = volumeUuid;
+ this.installerPackageName = installerPackageName;
+ this.user = user;
+ this.replace = replace;
+ this.scanFlags = scanFlags;
+ this.parseFlags = parseFlags;
+ this.existingPackage = existingPackage;
+ this.packageToScan = packageToScan;
+ this.clearCodeCache = clearCodeCache;
+ this.system = system;
+ this.renamedPackage = renamedPackage;
+ this.freezer = freezer;
+ }
+ }
+
+ private static class PrepareFailure extends PackageManagerException {
+
+ public String conflictingPackage;
+ public String conflictingPermission;
+
+ PrepareFailure(int error) {
+ super(error, "Failed to prepare for install.");
+ }
+
+ PrepareFailure(int error, String detailMessage) {
+ super(error, detailMessage);
+ }
+
+ PrepareFailure(String message, Exception e) {
+ super(e instanceof PackageParserException
+ ? ((PackageParserException) e).error
+ : ((PackageManagerException) e).error,
+ ExceptionUtils.getCompleteMessage(message, e));
+ }
+
+ PrepareFailure conflictsWithExistingPermission(String conflictingPermission,
+ String conflictingPackage) {
+ this.conflictingPermission = conflictingPermission;
+ this.conflictingPackage = conflictingPackage;
+ return this;
+ }
+ }
+
@GuardedBy("mInstallLock")
- private void preparePackageLI(InstallArgs args, PackageInstalledInfo res) {
+ private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
+ throws PrepareFailure {
final int installFlags = args.installFlags;
final String installerPackageName = args.installerPackageName;
final String volumeUuid = args.volumeUuid;
@@ -15494,7 +15582,6 @@
final boolean forceSdk = ((installFlags & PackageManager.INSTALL_FORCE_SDK) != 0);
final boolean virtualPreload =
((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
- boolean replace = false;
@ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
if (args.move != null) {
// moving a complete application; perform an initial scan on the new install location
@@ -15513,18 +15600,13 @@
scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
}
- // Result object to be returned
- res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
- res.installerPackageName = installerPackageName;
-
if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
// Sanity check
if (instantApp && (forwardLocked || onExternal)) {
Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked
+ " external=" + onExternal);
- res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
- return;
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
}
// Retrieve PackageSettings and parse package
@@ -15533,6 +15615,7 @@
| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
| (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);
+
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
@@ -15544,8 +15627,7 @@
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
DexMetadataHelper.validatePackageDexMetadata(pkg);
} catch (PackageParserException e) {
- res.setError("Failed parse during installPackageLI", e);
- return;
+ throw new PrepareFailure("Failed parse during installPackageLI", e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -15555,16 +15637,14 @@
if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
Slog.w(TAG,
"Instant app package " + pkg.packageName + " does not target at least O");
- res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+ throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
"Instant app package must target at least O");
- return;
}
if (pkg.mSharedUserId != null) {
Slog.w(TAG, "Instant app package " + pkg.packageName
+ " may not declare sharedUserId.");
- res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+ throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
"Instant app package may not declare a sharedUserId");
- return;
}
}
@@ -15575,9 +15655,8 @@
// No static shared libs on external storage
if (onExternal) {
Slog.i(TAG, "Static shared libs can only be installed on internal storage.");
- res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Packages declaring static-shared libs cannot be updated");
- return;
}
}
@@ -15616,10 +15695,9 @@
}
String pkgName = res.name = pkg.packageName;
- if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
+ if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0) {
if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
- res.setError(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
- return;
+ throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
}
}
@@ -15631,22 +15709,21 @@
PackageParser.collectCertificates(pkg, false /* skipVerify */);
}
} catch (PackageParserException e) {
- res.setError("Failed collect during installPackageLI", e);
- return;
+ throw new PrepareFailure("Failed collect during installPackageLI", e);
}
if (instantApp && pkg.mSigningDetails.signatureSchemeVersion
< SignatureSchemeVersion.SIGNING_BLOCK_V2) {
Slog.w(TAG, "Instant app package " + pkg.packageName
+ " is not signed with at least APK Signature Scheme v2");
- res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+ throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
"Instant app package must be signed with APK Signature Scheme v2 or greater");
- return;
}
// Get rid of all references to package scan path via parser.
pp = null;
boolean systemApp = false;
+ boolean replace = false;
synchronized (mPackages) {
// Check if installing already existing package
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
@@ -15661,8 +15738,10 @@
pkg.setPackageName(oldName);
pkgName = pkg.packageName;
replace = true;
- if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="
- + oldName + " pkgName=" + pkgName);
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, "Replacing existing renamed package: oldName="
+ + oldName + " pkgName=" + pkgName);
+ }
} else if (mPackages.containsKey(pkgName)) {
// This package, under its official name, already exists
// on the device; we should replace it.
@@ -15672,11 +15751,11 @@
// Child packages are installed through the parent package
if (pkg.parentPackage != null) {
- res.setError(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+ throw new PrepareFailure(
+ PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
"Package " + pkg.packageName + " is child of package "
+ pkg.parentPackage.parentPackage + ". Child packages "
+ "can be updated only through the parent package.");
- return;
}
if (replace) {
@@ -15686,26 +15765,25 @@
final int newTargetSdk = pkg.applicationInfo.targetSdkVersion;
if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
&& newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {
- res.setError(PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
+ throw new PrepareFailure(
+ PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
"Package " + pkg.packageName + " new target SDK " + newTargetSdk
+ " doesn't support runtime permissions but the old"
+ " target SDK " + oldTargetSdk + " does.");
- return;
}
// Prevent persistent apps from being updated
if ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0) {
- res.setError(PackageManager.INSTALL_FAILED_INVALID_APK,
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,
"Package " + oldPackage.packageName + " is a persistent app. "
+ "Persistent apps are not updateable.");
- return;
}
// Prevent installing of child packages
if (oldPackage.parentPackage != null) {
- res.setError(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+ throw new PrepareFailure(
+ PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
"Package " + pkg.packageName + " is child of package "
+ oldPackage.parentPackage + ". Child packages "
+ "can be updated only through the parent package.");
- return;
}
}
}
@@ -15732,10 +15810,9 @@
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
- res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
- return;
}
} else {
try {
@@ -15752,8 +15829,7 @@
}
}
} catch (PackageManagerException e) {
- res.setError(e.error, e.getMessage());
- return;
+ throw new PrepareFailure(e.error, e.getMessage());
}
}
@@ -15764,8 +15840,9 @@
res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
+
int N = pkg.permissions.size();
- for (int i = N-1; i >= 0; i--) {
+ for (int i = N - 1; i >= 0; i--) {
final PackageParser.Permission perm = pkg.permissions.get(i);
final BasePermission bp =
(BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);
@@ -15790,7 +15867,7 @@
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
if (sourcePackageName.equals(pkg.packageName)
&& (ksms.shouldCheckUpgradeKeySetLocked(
- sourcePackageSetting, scanFlags))) {
+ sourcePackageSetting, scanFlags))) {
sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg);
} else {
@@ -15798,12 +15875,12 @@
// 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 (sourcePackageSetting.signatures.mSigningDetails.checkCapability(
- pkg.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+ pkg.mSigningDetails,
+ PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
sigsOk = true;
} else if (pkg.mSigningDetails.checkCapability(
- sourcePackageSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+ sourcePackageSetting.signatures.mSigningDetails,
+ PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
// the scanned package checks out, has signing certificate rotation
// history, and is newer; bring it over
@@ -15818,12 +15895,13 @@
// install to proceed; we fail the install on all other permission
// redefinitions.
if (!sourcePackageName.equals("android")) {
- res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
- + pkg.packageName + " attempting to redeclare permission "
- + perm.info.name + " already owned by " + sourcePackageName);
- res.origPermission = perm.info.name;
- res.origPackage = sourcePackageName;
- return;
+ throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
+ + pkg.packageName
+ + " attempting to redeclare permission "
+ + perm.info.name + " already owned by "
+ + sourcePackageName)
+ .conflictsWithExistingPermission(perm.info.name,
+ sourcePackageName);
} else {
Slog.w(TAG, "Package " + pkg.packageName
+ " attempting to redeclare system permission "
@@ -15852,14 +15930,12 @@
if (systemApp) {
if (onExternal) {
// Abort update; system app can't be replaced with app on sdcard
- res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Cannot install updates to system apps on sdcard");
- return;
} else if (instantApp) {
// Abort update; system app can't be replaced with an instant app
- res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+ throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
"Cannot update a system app with an instant app");
- return;
}
}
@@ -15887,13 +15963,13 @@
try {
String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?
- args.abiOverride : pkg.cpuAbiOverride);
+ args.abiOverride : pkg.cpuAbiOverride);
final boolean extractNativeLibs = !pkg.isLibrary();
derivePackageAbi(pkg, abiOverride, extractNativeLibs);
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
- res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
- return;
+ throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
+ "Error deriving application ABI");
}
// Shared libraries for the package need to be updated.
@@ -15907,8 +15983,7 @@
}
if (!args.doRename(res.returnCode, pkg)) {
- res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
- return;
+ throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
}
if (PackageManagerServiceUtils.isApkVerityEnabled()) {
@@ -15922,7 +15997,6 @@
apkPath = pkg.baseCodePath;
}
}
-
if (apkPath != null) {
final VerityUtils.SetupResult result =
VerityUtils.generateApkVeritySetupData(apkPath);
@@ -15934,16 +16008,15 @@
mInstaller.installApkVerity(apkPath, fd, result.getContentSize());
mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash);
} catch (InstallerException | IOException | DigestException |
- NoSuchAlgorithmException e) {
- res.setError(INSTALL_FAILED_INTERNAL_ERROR,
+ NoSuchAlgorithmException e) {
+ throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
"Failed to set up verity: " + e);
- return;
} finally {
IoUtils.closeQuietly(fd);
}
} else if (result.isFailed()) {
- res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Failed to generate verity");
- return;
+ throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
+ "Failed to generate verity");
} else {
// Do nothing if verity is skipped. Will fall back to full apk verification on
// reboot.
@@ -15958,109 +16031,293 @@
Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);
}
}
+ final PackageFreezer freezer =
+ freezePackageForInstall(pkgName, installFlags, "installPackageLI");
+ boolean shouldCloseFreezerBeforeReturn = true;
+ try {
+ final PackageParser.Package existingPackage;
+ String renamedPackage = null;
+ boolean clearCodeCache = false;
+ boolean sysPkg = false;
+ String targetVolumeUuid = volumeUuid;
+ int targetScanFlags = scanFlags;
+ int targetParseFlags = parseFlags;
- try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
- "installPackageLI")) {
if (replace) {
+ targetVolumeUuid = null;
if (pkg.applicationInfo.isStaticSharedLibrary()) {
// Static libs have a synthetic package name containing the version
// and cannot be updated as an update would get a new package name,
// unless this is the exact same version code which is useful for
// development.
PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
- if (existingPkg != null &&
- existingPkg.getLongVersionCode() != pkg.getLongVersionCode()) {
- res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
- + "static-shared libs cannot be updated");
- return;
+ if (existingPkg != null
+ && existingPkg.getLongVersionCode() != pkg.getLongVersionCode()) {
+ throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Packages declaring "
+ + "static-shared libs cannot be updated");
}
}
- replacePackageLIF(pkg, parseFlags, scanFlags, args.user,
- installerPackageName, res, args.installReason);
- } else {
- installNewPackageLIF(pkg, parseFlags, scanFlags,
- args.user, installerPackageName, volumeUuid, res, args.installReason);
- }
- }
- // 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.
- mArtManagerService.prepareAppProfiles(pkg, resolveUserIds(args.user.getIdentifier()));
+ final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
- // Check whether we need to dexopt the app.
- //
- // NOTE: it is IMPORTANT to call dexopt:
- // - after doRename which will sync the package data from PackageParser.Package 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 forward locked.
- // 2) it is not on on an external ASEC container.
- // 3) it is not an instant app or if it is then dexopt is enabled via gservices.
- // 4) it is not debuggable.
- //
- // 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.
- final boolean performDexopt = (res.returnCode == PackageManager.INSTALL_SUCCEEDED)
- && !forwardLocked
- && !pkg.applicationInfo.isExternalAsec()
- && (!instantApp || Global.getInt(mContext.getContentResolver(),
- Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
- && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
+ final PackageParser.Package oldPackage;
+ final PackageSetting ps;
+ final String pkgName11 = pkg.packageName;
+ final int[] allUsers;
+ final int[] installedUsers;
- if (performDexopt) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
- // Do not run PackageDexOptimizer through the local performDexOpt
- // method because `pkg` may not be in `mPackages` yet.
- //
- // Also, don't fail application installs if the dexopt step fails.
- DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
- REASON_INSTALL,
- DexoptOptions.DEXOPT_BOOT_COMPLETE |
- DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
- mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
- null /* instructionSets */,
- getOrCreateCompilerPackageStats(pkg),
- mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
- dexoptOptions);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
+ synchronized (mPackages) {
+ oldPackage = mPackages.get(pkgName11);
+ existingPackage = oldPackage;
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG,
+ "replacePackageLI: new=" + pkg + ", old=" + oldPackage);
+ }
- // 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 blacklist.
- // TODO: Layering violation
- BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
+ // don't allow upgrade to target a release SDK from a pre-release SDK
+ final boolean oldTargetsPreRelease = oldPackage.applicationInfo.targetSdkVersion
+ == Build.VERSION_CODES.CUR_DEVELOPMENT;
+ final boolean newTargetsPreRelease = pkg.applicationInfo.targetSdkVersion
+ == Build.VERSION_CODES.CUR_DEVELOPMENT;
+ if (oldTargetsPreRelease
+ && !newTargetsPreRelease
+ && ((parseFlags & PackageParser.PARSE_FORCE_SDK) == 0)) {
+ Slog.w(TAG, "Can't install package targeting released sdk");
+ throw new PrepareFailure(
+ PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE);
+ }
- synchronized (mPackages) {
- final PackageSetting ps = mSettings.mPackages.get(pkgName);
- if (ps != null) {
- res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
- ps.setUpdateAvailable(false /*updateAvailable*/);
- }
+ ps = mSettings.mPackages.get(pkgName11);
- final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
- for (int i = 0; i < childCount; i++) {
- PackageParser.Package childPkg = pkg.childPackages.get(i);
- PackageInstalledInfo childRes = res.addedChildPackages.get(childPkg.packageName);
- PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
- if (childPs != null) {
- childRes.newUsers = childPs.queryInstalledUsers(
- sUserManager.getUserIds(), true);
+ // verify signatures are valid
+ final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {
+ if (!ksms.checkUpgradeKeySetLocked(ps, pkg)) {
+ throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "New package not signed by keys specified by upgrade-keysets: "
+ + pkgName11);
+ }
+ } else {
+ // default to original signature matching
+ if (!pkg.mSigningDetails.checkCapability(oldPackage.mSigningDetails,
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
+ && !oldPackage.mSigningDetails.checkCapability(
+ pkg.mSigningDetails,
+ SigningDetails.CertCapabilities.ROLLBACK)) {
+ 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.restrictUpdateHash != null && oldPackage.isSystem()) {
+ final byte[] digestBytes;
+ try {
+ final MessageDigest digest = MessageDigest.getInstance("SHA-512");
+ updateDigest(digest, new File(pkg.baseCodePath));
+ if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
+ for (String path : pkg.splitCodePaths) {
+ 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.restrictUpdateHash, digestBytes)) {
+ throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
+ "New package fails restrict-update check: " + pkgName11);
+ }
+ // retain upgrade restriction
+ pkg.restrictUpdateHash = oldPackage.restrictUpdateHash;
+ }
+
+ // Check for shared user id changes
+ String invalidPackageName =
+ getParentOrChildPackageChangedSharedUser(oldPackage, pkg);
+ if (invalidPackageName != null) {
+ throw new PrepareFailure(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
+ "Package " + invalidPackageName + " tried to change user "
+ + oldPackage.mSharedUserId);
+ }
+
+ // check if the new package supports all of the abis which the old package
+ // supports
+ boolean oldPkgSupportMultiArch =
+ oldPackage.applicationInfo.secondaryCpuAbi != null;
+ boolean newPkgSupportMultiArch = pkg.applicationInfo.secondaryCpuAbi != null;
+ if (isSystemApp(oldPackage) && oldPkgSupportMultiArch
+ && !newPkgSupportMultiArch) {
+ throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "Update to package " + pkgName11 + " doesn't support multi arch");
+ }
+
+ // In case of rollback, remember per-user/profile install state
+ allUsers = sUserManager.getUserIds();
+ installedUsers = ps.queryInstalledUsers(allUsers, true);
+
+
+ // don't allow an upgrade from full to ephemeral
+ if (isInstantApp) {
+ if (args.user == null || args.user.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_INSTANT_APP_INVALID);
+ }
+ }
+ } else if (!ps.getInstantApp(args.user.getIdentifier())) {
+ // can't downgrade from full to instant
+ Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11
+ + " for user: " + args.user.getIdentifier());
+ throw new PrepareFailure(
+ PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+ }
+ }
+ }
+
+ // Update what is removed
+ res.removedInfo = new PackageRemovedInfo(this);
+ res.removedInfo.uid = oldPackage.applicationInfo.uid;
+ res.removedInfo.removedPackage = oldPackage.packageName;
+ res.removedInfo.installerPackageName = ps.installerPackageName;
+ res.removedInfo.isStaticSharedLib = pkg.staticSharedLibName != null;
+ res.removedInfo.isUpdate = true;
+ res.removedInfo.origUsers = installedUsers;
+ res.removedInfo.installReasons = new SparseArray<>(installedUsers.length);
+ for (int i = 0; i < installedUsers.length; i++) {
+ final int userId = installedUsers[i];
+ res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));
+ }
+
+ final int childCount = (oldPackage.childPackages != null)
+ ? oldPackage.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ boolean childPackageUpdated = false;
+ PackageParser.Package childPkg = oldPackage.childPackages.get(i);
+ final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
+ if (res.addedChildPackages != null) {
+ PackageInstalledInfo childRes = res.addedChildPackages.get(
+ childPkg.packageName);
+ if (childRes != null) {
+ childRes.removedInfo.uid = childPkg.applicationInfo.uid;
+ childRes.removedInfo.removedPackage = childPkg.packageName;
+ if (childPs != null) {
+ childRes.removedInfo.installerPackageName =
+ childPs.installerPackageName;
+ }
+ childRes.removedInfo.isUpdate = true;
+ childRes.removedInfo.installReasons = res.removedInfo.installReasons;
+ childPackageUpdated = true;
+ }
+ }
+ if (!childPackageUpdated) {
+ PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
+ childRemovedRes.removedPackage = childPkg.packageName;
+ if (childPs != null) {
+ childRemovedRes.installerPackageName = childPs.installerPackageName;
+ }
+ childRemovedRes.isUpdate = false;
+ childRemovedRes.dataRemoved = true;
+ synchronized (mPackages) {
+ if (childPs != null) {
+ childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers,
+ true);
+ }
+ }
+ if (res.removedInfo.removedChildPackages == null) {
+ res.removedInfo.removedChildPackages = new ArrayMap<>();
+ }
+ res.removedInfo.removedChildPackages.put(childPkg.packageName,
+ childRemovedRes);
+ }
+ }
+
+ sysPkg = (isSystemApp(oldPackage));
+ if (sysPkg) {
+ // Set the system/privileged/oem/vendor/product flags as needed
+ final boolean privileged = isPrivilegedApp(oldPackage);
+ final boolean oem = isOemApp(oldPackage);
+ final boolean vendor = isVendorApp(oldPackage);
+ final boolean product = isProductApp(oldPackage);
+ final @ParseFlags int systemParseFlags = parseFlags;
+ final @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);
+
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
+ + ", old=" + oldPackage);
+ }
+ clearCodeCache = true;
+ res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
+ ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
+ targetParseFlags = systemParseFlags;
+ targetScanFlags = systemScanFlags;
+ } else { // non system replace
+ replace = true;
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG,
+ "replaceNonSystemPackageLI: new=" + pkg + ", old="
+ + oldPackage);
+ }
+
+ String pkgName1 = oldPackage.packageName;
+ boolean deletedPkg = true;
+ boolean addedPkg = false;
+ boolean updatedSettings = false;
+
+ final long origUpdateTime = (pkg.mExtras != null)
+ ? ((PackageSetting) pkg.mExtras).lastUpdateTime : 0;
+
+ }
+ } else { // new package install
+ replace = false;
+ existingPackage = null;
+ // Remember this for later, in case we need to rollback this install
+ String pkgName1 = pkg.packageName;
+
+ if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
+
+ // TODO(patb): MOVE TO RECONCILE
+ synchronized (mPackages) {
+ renamedPackage = 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 (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.");
+ }
}
}
-
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- updateSequenceNumberLP(ps, res.newUsers);
- updateInstantAppInstallerLocked(pkgName);
+ // we're passing the freezer back to be closed in a later phase of install
+ shouldCloseFreezerBeforeReturn = false;
+ return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName,
+ args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg,
+ clearCodeCache, sysPkg, renamedPackage, freezer);
+ } finally {
+ if (shouldCloseFreezerBeforeReturn) {
+ freezer.close();
}
}
}
@@ -16870,7 +17127,7 @@
}
}
- removePackageLI(ps, (flags & PackageManager.DELETE_CHATTY) != 0);
+ removePackageLI(ps.name, (flags & PackageManager.DELETE_CHATTY) != 0);
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
final PackageParser.Package resolvedPkg;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 727fb15..b850613 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -207,4 +207,13 @@
writeUsersInfoToProto(proto, PackageProto.USERS);
proto.end(packageToken);
}
+
+ /** Updates all fields in the current setting from another. */
+ public void updateFrom(PackageSetting other) {
+ super.updateFrom(other);
+ appId = other.appId;
+ pkg = other.pkg;
+ sharedUserId = other.sharedUserId;
+ sharedUser = other.sharedUser;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 138594c..fd6aceb 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -26,7 +26,6 @@
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.Signature;
-import android.os.BaseBundle;
import android.os.PersistableBundle;
import android.service.pm.PackageProto;
import android.util.ArraySet;
@@ -109,7 +108,7 @@
// Whether this package is currently stopped, thus can not be
// started until explicitly launched by the user.
- private final SparseArray<PackageUserState> userState = new SparseArray<PackageUserState>();
+ private final SparseArray<PackageUserState> mUserState = new SparseArray<>();
/**
* Non-persisted value. During an "upgrade without restart", we need the set
@@ -118,7 +117,7 @@
* restart, this field will be cleared since the classloader would be created
* using the full set of code paths when the package's process is started.
*/
- Set<String> oldCodePaths;
+ Set<String> mOldCodePaths;
/** Package name of the app that installed this package */
String installerPackageName;
@@ -223,7 +222,7 @@
/**
* Makes a shallow copy of the given package settings.
*
- * NOTE: For some fields [such as keySetData, signatures, userState, verificationInfo, etc...],
+ * NOTE: For some fields [such as keySetData, signatures, mUserState, verificationInfo, etc...],
* the original object is copied and a new one is not created.
*/
public void copyFrom(PackageSettingBase orig) {
@@ -244,7 +243,7 @@
keySetData = orig.keySetData;
lastUpdateTime = orig.lastUpdateTime;
legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString;
- // Intentionally skip oldCodePaths; it's not relevant for copies
+ // Intentionally skip mOldCodePaths; it's not relevant for copies
parentPackageName = orig.parentPackageName;
primaryCpuAbiString = orig.primaryCpuAbiString;
resourcePath = orig.resourcePath;
@@ -253,9 +252,9 @@
signatures = orig.signatures;
timeStamp = orig.timeStamp;
uidError = orig.uidError;
- userState.clear();
- for (int i=0; i<orig.userState.size(); i++) {
- userState.put(orig.userState.keyAt(i), orig.userState.valueAt(i));
+ mUserState.clear();
+ for (int i = 0; i < orig.mUserState.size(); i++) {
+ mUserState.put(orig.mUserState.keyAt(i), orig.mUserState.valueAt(i));
}
verificationInfo = orig.verificationInfo;
versionCode = orig.versionCode;
@@ -271,16 +270,16 @@
}
private PackageUserState modifyUserState(int userId) {
- PackageUserState state = userState.get(userId);
+ PackageUserState state = mUserState.get(userId);
if (state == null) {
state = new PackageUserState();
- userState.put(userId, state);
+ mUserState.put(userId, state);
}
return state;
}
public PackageUserState readUserState(int userId) {
- PackageUserState state = userState.get(userId);
+ PackageUserState state = mUserState.get(userId);
if (state == null) {
return DEFAULT_USER_STATE;
}
@@ -330,7 +329,7 @@
/** Only use for testing. Do NOT use in production code. */
@VisibleForTesting
SparseArray<PackageUserState> getUserState() {
- return userState;
+ return mUserState;
}
boolean isAnyInstalled(int[] users) {
@@ -536,14 +535,14 @@
}
void removeUser(int userId) {
- userState.delete(userId);
+ mUserState.delete(userId);
}
public int[] getNotInstalledUserIds() {
int count = 0;
- int userStateCount = userState.size();
+ int userStateCount = mUserState.size();
for (int i = 0; i < userStateCount; i++) {
- if (userState.valueAt(i).installed == false) {
+ if (!mUserState.valueAt(i).installed) {
count++;
}
}
@@ -551,8 +550,8 @@
int[] excludedUserIds = new int[count];
int idx = 0;
for (int i = 0; i < userStateCount; i++) {
- if (userState.valueAt(i).installed == false) {
- excludedUserIds[idx++] = userState.keyAt(i);
+ if (!mUserState.valueAt(i).installed) {
+ excludedUserIds[idx++] = mUserState.keyAt(i);
}
}
return excludedUserIds;
@@ -591,11 +590,11 @@
}
protected void writeUsersInfoToProto(ProtoOutputStream proto, long fieldId) {
- int count = userState.size();
+ int count = mUserState.size();
for (int i = 0; i < count; i++) {
final long userToken = proto.start(fieldId);
- final int userId = userState.keyAt(i);
- final PackageUserState state = userState.valueAt(i);
+ final int userId = mUserState.keyAt(i);
+ final PackageUserState state = mUserState.valueAt(i);
proto.write(PackageProto.UserInfoProto.ID, userId);
final int installType;
if (state.instantApp) {
@@ -630,4 +629,48 @@
PackageUserState userState = readUserState(userId);
return userState.harmfulAppWarning;
}
+
+ protected PackageSettingBase updateFrom(PackageSettingBase other) {
+ super.copyFrom(other);
+ this.parentPackageName = other.parentPackageName;
+ this.childPackageNames = other.childPackageNames;
+ this.codePath = other.codePath;
+ this.codePathString = other.codePathString;
+ this.resourcePath = other.resourcePath;
+ this.resourcePathString = other.resourcePathString;
+ this.usesStaticLibraries = other.usesStaticLibraries;
+ this.usesStaticLibrariesVersions = other.usesStaticLibrariesVersions;
+ this.legacyNativeLibraryPathString = other.legacyNativeLibraryPathString;
+ this.primaryCpuAbiString = other.primaryCpuAbiString;
+ this.secondaryCpuAbiString = other.secondaryCpuAbiString;
+ this.cpuAbiOverrideString = other.cpuAbiOverrideString;
+ this.timeStamp = other.timeStamp;
+ this.firstInstallTime = other.firstInstallTime;
+ this.lastUpdateTime = other.lastUpdateTime;
+ this.versionCode = other.versionCode;
+ this.uidError = other.uidError;
+ this.signatures = other.signatures;
+ this.installPermissionsFixed = other.installPermissionsFixed;
+ this.keySetData = other.keySetData;
+ this.installerPackageName = other.installerPackageName;
+ this.isOrphaned = other.isOrphaned;
+ this.volumeUuid = other.volumeUuid;
+ this.categoryHint = other.categoryHint;
+ this.updateAvailable = other.updateAvailable;
+ this.verificationInfo = other.verificationInfo;
+
+ if (mOldCodePaths != null) {
+ if (other.mOldCodePaths != null) {
+ mOldCodePaths.clear();
+ mOldCodePaths.addAll(other.mOldCodePaths);
+ } else {
+ mOldCodePaths = null;
+ }
+ }
+ mUserState.clear();
+ for (int i = 0; i < other.mUserState.size(); i++) {
+ mUserState.put(other.mUserState.keyAt(i), other.mUserState.valueAt(i));
+ }
+ return this;
+ }
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6d242f4..5c88e06 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -539,16 +539,18 @@
if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
- mDisabledSysPackages.put(name, p);
-
+ final PackageSetting disabled;
if (replaced) {
// a little trick... when we install the new package, we don't
// want to modify the existing PackageSetting for the built-in
- // version. so at this point we need a new PackageSetting that
- // is okay to muck with.
- PackageSetting newp = new PackageSetting(p);
- replacePackageLPw(name, newp);
+ // version. so at this point we make a copy to place into the
+ // disabled set.
+ disabled = new PackageSetting(p);
+ } else {
+ disabled = p;
}
+ mDisabledSysPackages.put(name, disabled);
+
return true;
}
return false;
@@ -1105,19 +1107,6 @@
mInstallerPackages.remove(packageName);
}
- private void replacePackageLPw(String name, PackageSetting newp) {
- final PackageSetting p = mPackages.get(name);
- if (p != null) {
- if (p.sharedUser != null) {
- p.sharedUser.removePackage(p);
- p.sharedUser.addPackage(newp);
- } else {
- replaceUserIdLPw(p.appId, newp);
- }
- }
- mPackages.put(name, newp);
- }
-
private boolean addUserIdLPw(int uid, Object obj, Object name) {
if (uid > Process.LAST_APPLICATION_UID) {
return false;
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index c94d209..1a8b2af 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -24,7 +24,6 @@
import android.util.proto.ProtoOutputStream;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
/**
@@ -69,24 +68,26 @@
proto.end(token);
}
- void removePackage(PackageSetting packageSetting) {
- if (packages.remove(packageSetting)) {
- // recalculate the pkgFlags for this shared user if needed
- if ((this.pkgFlags & packageSetting.pkgFlags) != 0) {
- int aggregatedFlags = uidFlags;
- for (PackageSetting ps : packages) {
- aggregatedFlags |= ps.pkgFlags;
- }
- setFlags(aggregatedFlags);
- }
- if ((this.pkgPrivateFlags & packageSetting.pkgPrivateFlags) != 0) {
- int aggregatedPrivateFlags = uidPrivateFlags;
- for (PackageSetting ps : packages) {
- aggregatedPrivateFlags |= ps.pkgPrivateFlags;
- }
- setPrivateFlags(aggregatedPrivateFlags);
- }
+ boolean removePackage(PackageSetting packageSetting) {
+ if (!packages.remove(packageSetting)) {
+ return false;
}
+ // recalculate the pkgFlags for this shared user if needed
+ if ((this.pkgFlags & packageSetting.pkgFlags) != 0) {
+ int aggregatedFlags = uidFlags;
+ for (PackageSetting ps : packages) {
+ aggregatedFlags |= ps.pkgFlags;
+ }
+ setFlags(aggregatedFlags);
+ }
+ if ((this.pkgPrivateFlags & packageSetting.pkgPrivateFlags) != 0) {
+ int aggregatedPrivateFlags = uidPrivateFlags;
+ for (PackageSetting ps : packages) {
+ aggregatedPrivateFlags |= ps.pkgPrivateFlags;
+ }
+ setPrivateFlags(aggregatedPrivateFlags);
+ }
+ return true;
}
void addPackage(PackageSetting packageSetting) {
@@ -143,4 +144,16 @@
}
}
+ /** Updates all fields in this shared user setting from another. */
+ public SharedUserSetting updateFrom(SharedUserSetting sharedUser) {
+ copyFrom(sharedUser);
+ this.userId = sharedUser.userId;
+ this.uidFlags = sharedUser.uidFlags;
+ this.uidPrivateFlags = sharedUser.uidPrivateFlags;
+ this.seInfoTargetSdkVersion = sharedUser.seInfoTargetSdkVersion;
+ this.packages.clear();
+ this.packages.addAll(sharedUser.packages);
+ this.signaturesChanged = sharedUser.signaturesChanged;
+ return this;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index ee41c0b..c3c0788 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -716,9 +716,9 @@
assertNotSame(origPkgSetting.mPermissionsState, testPkgSetting.mPermissionsState);
assertThat(origPkgSetting.mPermissionsState, is(testPkgSetting.mPermissionsState));
assertThat(origPkgSetting.name, is(testPkgSetting.name));
- // oldCodePaths is _not_ copied
- // assertNotSame(origPkgSetting.oldCodePaths, testPkgSetting.oldCodePaths);
- // assertThat(origPkgSetting.oldCodePaths, is(not(testPkgSetting.oldCodePaths)));
+ // mOldCodePaths is _not_ copied
+ // assertNotSame(origPkgSetting.mOldCodePaths, testPkgSetting.mOldCodePaths);
+ // assertThat(origPkgSetting.mOldCodePaths, is(not(testPkgSetting.mOldCodePaths)));
assertSame(origPkgSetting.parentPackageName, testPkgSetting.parentPackageName);
assertThat(origPkgSetting.parentPackageName, is(testPkgSetting.parentPackageName));
assertSame(origPkgSetting.pkg, testPkgSetting.pkg);