Merge "[PM] Add downgrade check for split apps when data exists" into main
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 303371b..8d3f07e 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2261,6 +2261,12 @@
installRequest.getNewUsers());
mPm.updateSequenceNumberLP(ps, installRequest.getNewUsers());
mPm.updateInstantAppInstallerLocked(packageName);
+
+ // The installation is success, remove the split info copy stored in package
+ // setting for the downgrade version check of DELETE_KEEP_DATA and archived app
+ // cases.
+ ps.setSplitNames(null);
+ ps.setSplitRevisionCodes(null);
}
installRequest.onCommitFinished();
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index c3cac20..a1dffc6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -1423,11 +1423,8 @@
*/
public static void checkDowngrade(@NonNull PackageSetting before,
@NonNull PackageInfoLite after) throws PackageManagerException {
- if (after.getLongVersionCode() < before.getVersionCode()) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update version code " + after.versionCode + " is older than current "
- + before.getVersionCode());
- }
+ checkDowngrade(before.getVersionCode(), before.getBaseRevisionCode(),
+ before.getSplitNames(), before.getSplitRevisionCodes(), after);
}
/**
@@ -1436,28 +1433,35 @@
*/
public static void checkDowngrade(@NonNull AndroidPackage before,
@NonNull PackageInfoLite after) throws PackageManagerException {
- if (after.getLongVersionCode() < before.getLongVersionCode()) {
+ checkDowngrade(before.getLongVersionCode(), before.getBaseRevisionCode(),
+ before.getSplitNames(), before.getSplitRevisionCodes(), after);
+ }
+
+ private static void checkDowngrade(long beforeVersionCode, int beforeBaseRevisionCode,
+ @NonNull String[] beforeSplitNames, @NonNull int[] beforeSplitRevisionCodes,
+ @NonNull PackageInfoLite after) throws PackageManagerException {
+ if (after.getLongVersionCode() < beforeVersionCode) {
throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
"Update version code " + after.versionCode + " is older than current "
- + before.getLongVersionCode());
- } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
- if (after.baseRevisionCode < before.getBaseRevisionCode()) {
+ + beforeVersionCode);
+ } else if (after.getLongVersionCode() == beforeVersionCode) {
+ if (after.baseRevisionCode < beforeBaseRevisionCode) {
throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
"Update base revision code " + after.baseRevisionCode
- + " is older than current " + before.getBaseRevisionCode());
+ + " is older than current " + beforeBaseRevisionCode);
}
if (!ArrayUtils.isEmpty(after.splitNames)) {
for (int i = 0; i < after.splitNames.length; i++) {
final String splitName = after.splitNames[i];
- final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
+ final int j = ArrayUtils.indexOf(beforeSplitNames, splitName);
if (j != -1) {
- if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
+ if (after.splitRevisionCodes[i] < beforeSplitRevisionCodes[j]) {
throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
"Update split " + splitName + " revision code "
+ after.splitRevisionCodes[i]
+ " is older than current "
- + before.getSplitRevisionCodes()[j]);
+ + beforeSplitRevisionCodes[j]);
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 82df527..9f10e01 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -234,6 +234,22 @@
@Nullable
private byte[] mRestrictUpdateHash;
+ // This is the copy of the same data stored in AndroidPackage. It is not null if the
+ // AndroidPackage is deleted in cases of DELETE_KEEP_DATA. When AndroidPackage is not null,
+ // the field will be null, and the getter method will return the data from AndroidPackage
+ // instead.
+ @Nullable
+ private String[] mSplitNames;
+
+ // This is the copy of the same data stored in AndroidPackage. It is not null if the
+ // AndroidPackage is deleted in cases of DELETE_KEEP_DATA. When AndroidPackage is not null,
+ // the field will be null, and the getter method will return the data from AndroidPackage
+ // instead.
+ @Nullable
+ private int[] mSplitRevisionCodes;
+
+ private int mBaseRevisionCode;
+
/**
* Snapshot support.
*/
@@ -578,6 +594,62 @@
return getBoolean(Booleans.DEBUGGABLE);
}
+ /**
+ * @see AndroidPackage#getBaseRevisionCode
+ */
+ public PackageSetting setBaseRevisionCode(int value) {
+ mBaseRevisionCode = value;
+ onChanged();
+ return this;
+ }
+
+ /**
+ * @see AndroidPackage#getBaseRevisionCode
+ */
+ public int getBaseRevisionCode() {
+ return mBaseRevisionCode;
+ }
+
+ /**
+ * @see AndroidPackage#getSplitNames
+ */
+ public PackageSetting setSplitNames(String[] value) {
+ mSplitNames = value;
+ onChanged();
+ return this;
+ }
+
+ /**
+ * @see AndroidPackage#getSplitNames
+ */
+ @NonNull
+ public String[] getSplitNames() {
+ if (pkg != null) {
+ return pkg.getSplitNames();
+ }
+ return mSplitNames == null ? EmptyArray.STRING : mSplitNames;
+ }
+
+ /**
+ * @see AndroidPackage#getSplitRevisionCodes
+ */
+ public PackageSetting setSplitRevisionCodes(int[] value) {
+ mSplitRevisionCodes = value;
+ onChanged();
+ return this;
+ }
+
+ /**
+ * @see AndroidPackage#getSplitRevisionCodes
+ */
+ @NonNull
+ public int[] getSplitRevisionCodes() {
+ if (pkg != null) {
+ return pkg.getSplitRevisionCodes();
+ }
+ return mSplitRevisionCodes == null ? EmptyArray.INT : mSplitRevisionCodes;
+ }
+
@Override
public String toString() {
return "PackageSetting{"
@@ -739,6 +811,11 @@
mTargetSdkVersion = other.mTargetSdkVersion;
mRestrictUpdateHash = other.mRestrictUpdateHash == null
? null : other.mRestrictUpdateHash.clone();
+ mBaseRevisionCode = other.mBaseRevisionCode;
+ mSplitNames = other.mSplitNames != null
+ ? Arrays.copyOf(other.mSplitNames, other.mSplitNames.length) : null;
+ mSplitRevisionCodes = other.mSplitRevisionCodes != null
+ ? Arrays.copyOf(other.mSplitRevisionCodes, other.mSplitRevisionCodes.length) : null;
usesSdkLibraries = other.usesSdkLibraries != null
? Arrays.copyOf(other.usesSdkLibraries,
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 2f2c451..7afc358 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -432,6 +432,13 @@
}
deletedPs.setInstalled(/* installed= */ false, userId);
}
+
+ // Preserve split apk information for downgrade check with DELETE_KEEP_DATA and archived
+ // app cases
+ if (deletedPkg.getSplitNames() != null) {
+ deletedPs.setSplitNames(deletedPkg.getSplitNames());
+ deletedPs.setSplitRevisionCodes(deletedPkg.getSplitRevisionCodes());
+ }
}
// make sure to preserve per-user installed state if this removal was just
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index d8ce38e..95561f5f 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -437,8 +437,9 @@
pkgSetting.setIsOrphaned(true);
}
- // update debuggable to packageSetting
+ // update debuggable and BaseRevisionCode to packageSetting
pkgSetting.setDebuggable(parsedPackage.isDebuggable());
+ pkgSetting.setBaseRevisionCode(parsedPackage.getBaseRevisionCode());
// Take care of first install / last update times.
final long scanFileTime = getLastModifiedTime(parsedPackage);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 0d16b00..9177e2b 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -325,6 +325,7 @@
private static final String TAG_MIME_TYPE = "mime-type";
private static final String TAG_ARCHIVE_STATE = "archive-state";
private static final String TAG_ARCHIVE_ACTIVITY_INFO = "archive-activity-info";
+ private static final String TAG_SPLIT_VERSION = "split-version";
public static final String ATTR_NAME = "name";
public static final String ATTR_PACKAGE = "package";
@@ -3261,6 +3262,9 @@
if (pkg.isLoading()) {
serializer.attributeBoolean(null, "isLoading", true);
}
+ if (pkg.getBaseRevisionCode() != 0) {
+ serializer.attributeInt(null, "baseRevisionCode", pkg.getBaseRevisionCode());
+ }
serializer.attributeFloat(null, "loadingProgress", pkg.getLoadingProgress());
serializer.attributeLongHex(null, "loadingCompletedTime", pkg.getLoadingCompletedTime());
@@ -3289,7 +3293,12 @@
writeUpgradeKeySetsLPr(serializer, pkg.getKeySetData());
writeKeySetAliasesLPr(serializer, pkg.getKeySetData());
writeMimeGroupLPr(serializer, pkg.getMimeGroups());
-
+ // If getPkg is not NULL, these values are from the getPkg. And these values are preserved
+ // for the downgrade check for DELETE_KEEP_DATA and archived app cases. If the getPkg is
+ // not NULL, we don't need to preserve it.
+ if (pkg.getPkg() == null) {
+ writeSplitVersionsLPr(serializer, pkg.getSplitNames(), pkg.getSplitRevisionCodes());
+ }
serializer.endTag(null, "package");
}
@@ -4071,6 +4080,7 @@
byte[] restrictUpdateHash = null;
boolean isScannedAsStoppedSystemApp = false;
boolean isSdkLibrary = false;
+ int baseRevisionCode = 0;
try {
name = parser.getAttributeValue(null, ATTR_NAME);
realName = parser.getAttributeValue(null, "realName");
@@ -4116,6 +4126,7 @@
appMetadataFilePath = parser.getAttributeValue(null, "appMetadataFilePath");
appMetadataSource = parser.getAttributeInt(null, "appMetadataSource",
PackageManager.APP_METADATA_SOURCE_UNKNOWN);
+ baseRevisionCode = parser.getAttributeInt(null, "baseRevisionCode", 0);
isScannedAsStoppedSystemApp = parser.getAttributeBoolean(null,
"scannedAsStoppedSystemApp", false);
@@ -4269,6 +4280,7 @@
.setAppMetadataFilePath(appMetadataFilePath)
.setAppMetadataSource(appMetadataSource)
.setTargetSdkVersion(targetSdkVersion)
+ .setBaseRevisionCode(baseRevisionCode)
.setRestrictUpdateHash(restrictUpdateHash)
.setScannedAsStoppedSystemApp(isScannedAsStoppedSystemApp);
// Handle legacy string here for single-user mode
@@ -4374,6 +4386,8 @@
readUsesStaticLibLPw(parser, packageSetting);
} else if (tagName.equals(TAG_USES_SDK_LIB)) {
readUsesSdkLibLPw(parser, packageSetting);
+ } else if (tagName.equals(TAG_SPLIT_VERSION)) {
+ readSplitVersionsLPw(parser, packageSetting);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <package>: " + parser.getName());
@@ -4470,6 +4484,37 @@
}
}
+ private void readSplitVersionsLPw(TypedXmlPullParser parser, PackageSetting outPs)
+ throws IOException, XmlPullParserException {
+ String splitName = parser.getAttributeValue(null, ATTR_NAME);
+ int splitRevision = parser.getAttributeInt(null, ATTR_VERSION, -1);
+ if (splitName != null && splitRevision >= 0) {
+ outPs.setSplitNames(ArrayUtils.appendElement(String.class,
+ outPs.getSplitNames(), splitName));
+ outPs.setSplitRevisionCodes(ArrayUtils.appendInt(
+ outPs.getSplitRevisionCodes(), splitRevision));
+ }
+
+ XmlUtils.skipCurrentTag(parser);
+ }
+
+ private void writeSplitVersionsLPr(TypedXmlSerializer serializer, String[] splitNames,
+ int[] splitRevisionCodes) throws IOException {
+ if (ArrayUtils.isEmpty(splitNames) || ArrayUtils.isEmpty(splitRevisionCodes)
+ || splitNames.length != splitRevisionCodes.length) {
+ return;
+ }
+ final int libLength = splitNames.length;
+ for (int i = 0; i < libLength; i++) {
+ final String splitName = splitNames[i];
+ final int splitRevision = splitRevisionCodes[i];
+ serializer.startTag(null, TAG_SPLIT_VERSION);
+ serializer.attribute(null, ATTR_NAME, splitName);
+ serializer.attributeInt(null, ATTR_VERSION, splitRevision);
+ serializer.endTag(null, TAG_SPLIT_VERSION);
+ }
+ }
+
private void readDisabledComponentsLPw(PackageSetting packageSetting, TypedXmlPullParser parser,
int userId) throws IOException, XmlPullParserException {
int outerDepth = parser.getDepth();
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index 7138306..dec4634 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -1046,6 +1046,77 @@
}
@Test
+ public void testWriteReadBaseRevisionCode() {
+ Settings settings = makeSettings();
+ PackageSetting packageSetting = createPackageSetting(PACKAGE_NAME_1);
+ packageSetting.setAppId(Process.FIRST_APPLICATION_UID);
+ packageSetting.setPkg(PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed()
+ .setUid(packageSetting.getAppId())
+ .hideAsFinal());
+
+ final int revisionCode = 311;
+ packageSetting.setBaseRevisionCode(revisionCode);
+ settings.mPackages.put(PACKAGE_NAME_1, packageSetting);
+
+ settings.writeLPr(computer, /* sync= */ true);
+ settings.mPackages.clear();
+
+ assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
+ assertThat(settings.getPackageLPr(PACKAGE_NAME_1).getBaseRevisionCode(), is(revisionCode));
+ }
+
+ @Test
+ public void testHasPkg_writeReadSplitVersions() {
+ Settings settings = makeSettings();
+ PackageSetting packageSetting = createPackageSetting(PACKAGE_NAME_1);
+ packageSetting.setAppId(Process.FIRST_APPLICATION_UID);
+ packageSetting.setPkg(PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed()
+ .setUid(packageSetting.getAppId())
+ .hideAsFinal());
+
+ final String splitOne = "one";
+ final String splitTwo = "two";
+ final int revisionOne = 311;
+ final int revisionTwo = 330;
+ packageSetting.setSplitNames(new String[] { splitOne, splitTwo});
+ packageSetting.setSplitRevisionCodes(new int[] { revisionOne, revisionTwo});
+ settings.mPackages.put(PACKAGE_NAME_1, packageSetting);
+
+ settings.writeLPr(computer, /* sync= */ true);
+ settings.mPackages.clear();
+
+ assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
+ PackageSetting resultSetting = settings.getPackageLPr(PACKAGE_NAME_1);
+ assertThat(resultSetting.getSplitNames().length, is(0));
+ assertThat(resultSetting.getSplitRevisionCodes().length, is(0));
+ }
+
+ @Test
+ public void testNoPkg_writeReadSplitVersions() {
+ Settings settings = makeSettings();
+ PackageSetting packageSetting = createPackageSetting(PACKAGE_NAME_1);
+ packageSetting.setAppId(Process.FIRST_APPLICATION_UID);
+
+ final String splitOne = "one";
+ final String splitTwo = "two";
+ final int revisionOne = 311;
+ final int revisionTwo = 330;
+ packageSetting.setSplitNames(new String[] { splitOne, splitTwo});
+ packageSetting.setSplitRevisionCodes(new int[] { revisionOne, revisionTwo});
+ settings.mPackages.put(PACKAGE_NAME_1, packageSetting);
+
+ settings.writeLPr(computer, /* sync= */ true);
+ settings.mPackages.clear();
+
+ assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
+ PackageSetting resultSetting = settings.getPackageLPr(PACKAGE_NAME_1);
+ assertThat(resultSetting.getSplitNames()[0], is(splitOne));
+ assertThat(resultSetting.getSplitNames()[1], is(splitTwo));
+ assertThat(resultSetting.getSplitRevisionCodes()[0], is(revisionOne));
+ assertThat(resultSetting.getSplitRevisionCodes()[1], is(revisionTwo));
+ }
+
+ @Test
public void testWriteReadArchiveState() {
Settings settings = makeSettings();
PackageSetting packageSetting = createPackageSetting(PACKAGE_NAME_1);