summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java3
-rw-r--r--services/core/java/com/android/server/rollback/Rollback.java25
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java20
-rw-r--r--services/core/java/com/android/server/rollback/RollbackStore.java45
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java110
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java4
6 files changed, 189 insertions, 18 deletions
diff --git a/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
index c268ff4291e4..6c25f2849cea 100644
--- a/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
+++ b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
@@ -38,6 +38,9 @@ public class SdkExtensions {
private static final int R_EXTENSION_INT;
static {
+ // Note: when adding more extension versions, the logic that records current
+ // extension versions when saving a rollback must also be updated.
+ // At the time of writing this is in RollbackManagerServiceImpl#getExtensionVersions()
R_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.r", 0);
}
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 885f561d2a19..9171501270d0 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -39,6 +39,7 @@ import android.os.UserManager;
import android.text.TextUtils;
import android.util.IntArray;
import android.util.Slog;
+import android.util.SparseIntArray;
import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
@@ -174,6 +175,12 @@ class Rollback {
private int mNumPackageSessionsWithSuccess;
/**
+ * The extension versions supported at the time of rollback creation. May be null if not set
+ * at creation time.
+ */
+ @Nullable private final SparseIntArray mExtensionVersions;
+
+ /**
* Constructs a new, empty Rollback instance.
*
* @param rollbackId the id of the rollback.
@@ -182,9 +189,11 @@ class Rollback {
* @param userId the user that performed the install with rollback enabled.
* @param installerPackageName the installer package name from the original install session.
* @param packageSessionIds the session ids for all packages in the install.
+ * @param extensionVersions the extension versions supported at the time of rollback creation
*/
Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
- String installerPackageName, int[] packageSessionIds) {
+ String installerPackageName, int[] packageSessionIds,
+ SparseIntArray extensionVersions) {
this.info = new RollbackInfo(rollbackId,
/* packages */ new ArrayList<>(),
/* isStaged */ stagedSessionId != -1,
@@ -197,11 +206,12 @@ class Rollback {
mState = ROLLBACK_STATE_ENABLING;
mTimestamp = Instant.now();
mPackageSessionIds = packageSessionIds != null ? packageSessionIds : new int[0];
+ mExtensionVersions = extensionVersions;
}
Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
String installerPackageName) {
- this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null);
+ this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null, null);
}
/**
@@ -209,7 +219,7 @@ class Rollback {
*/
Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
@RollbackState int state, int apkSessionId, boolean restoreUserDataInProgress,
- int userId, String installerPackageName) {
+ int userId, String installerPackageName, SparseIntArray extensionVersions) {
this.info = info;
mUserId = userId;
mInstallerPackageName = installerPackageName;
@@ -219,6 +229,7 @@ class Rollback {
mState = state;
mApkSessionId = apkSessionId;
mRestoreUserDataInProgress = restoreUserDataInProgress;
+ mExtensionVersions = extensionVersions;
// TODO(b/120200473): Include this field during persistence. This field will be used to
// decide which rollback to expire when ACTION_PACKAGE_REPLACED is received. Note persisting
// this field is not backward compatible. We won't fix b/120200473 until S to minimize the
@@ -283,6 +294,14 @@ class Rollback {
}
/**
+ * Returns the extension versions that were supported at the time that the rollback was created,
+ * as a mapping from SdkVersion to ExtensionVersion.
+ */
+ @Nullable SparseIntArray getExtensionVersions() {
+ return mExtensionVersions;
+ }
+
+ /**
* Returns true if the rollback is in the ENABLING state.
*/
boolean isEnabling() {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 83e99b008b68..6726cc829c81 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -41,6 +41,7 @@ import android.content.rollback.IRollbackManager;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.os.Binder;
+import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -49,12 +50,14 @@ import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.ext.SdkExtensions;
import android.provider.DeviceConfig;
import android.util.IntArray;
import android.util.Log;
import android.util.LongArrayQueue;
import android.util.Slog;
import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
@@ -1274,16 +1277,29 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
if (parentSession.isStaged()) {
rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
- installerPackageName, packageSessionIds);
+ installerPackageName, packageSessionIds, getExtensionVersions());
} else {
rollback = mRollbackStore.createNonStagedRollback(rollbackId, userId,
- installerPackageName, packageSessionIds);
+ installerPackageName, packageSessionIds, getExtensionVersions());
}
mRollbacks.add(rollback);
return rollback;
}
+ private SparseIntArray getExtensionVersions() {
+ // This list must be updated whenever the current API level is increased, or should be
+ // replaced when we have another way of determining the relevant SDK versions.
+ final int[] relevantSdkVersions = { Build.VERSION_CODES.R };
+
+ SparseIntArray result = new SparseIntArray(relevantSdkVersions.length);
+ for (int i = 0; i < relevantSdkVersions.length; i++) {
+ result.put(relevantSdkVersions[i],
+ SdkExtensions.getExtensionVersion(relevantSdkVersions[i]));
+ }
+ return result;
+ }
+
/**
* Returns the Rollback associated with the given session if parent or child session id matches.
* Returns null if not found.
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 7b046c1aa3a6..f186d65baaa1 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -21,6 +21,7 @@ import static android.os.UserHandle.USER_SYSTEM;
import static com.android.server.rollback.Rollback.rollbackStateFromString;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
@@ -28,6 +29,7 @@ import android.content.rollback.PackageRollbackInfo.RestoreInfo;
import android.content.rollback.RollbackInfo;
import android.util.IntArray;
import android.util.Slog;
+import android.util.SparseIntArray;
import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
@@ -176,6 +178,35 @@ class RollbackStore {
return ceSnapshotInodes;
}
+ private static @Nullable JSONArray extensionVersionsToJson(
+ @Nullable SparseIntArray extensionVersions) throws JSONException {
+ if (extensionVersions == null) {
+ return null;
+ }
+ JSONArray array = new JSONArray();
+ for (int i = 0; i < extensionVersions.size(); i++) {
+ JSONObject entryJson = new JSONObject();
+ entryJson.put("sdkVersion", extensionVersions.keyAt(i));
+ entryJson.put("extensionVersion", extensionVersions.valueAt(i));
+ array.put(entryJson);
+ }
+ return array;
+ }
+
+ private static @Nullable SparseIntArray extensionVersionsFromJson(@Nullable JSONArray json)
+ throws JSONException {
+ if (json == null) {
+ return null;
+ }
+ SparseIntArray extensionVersions = new SparseIntArray(json.length());
+ for (int i = 0; i < json.length(); i++) {
+ JSONObject entry = json.getJSONObject(i);
+ extensionVersions.append(
+ entry.getInt("sdkVersion"), entry.getInt("extensionVersion"));
+ }
+ return extensionVersions;
+ }
+
private static JSONObject rollbackInfoToJson(RollbackInfo rollback) throws JSONException {
JSONObject json = new JSONObject();
json.put("rollbackId", rollback.getRollbackId());
@@ -200,10 +231,10 @@ class RollbackStore {
* backupDir assigned.
*/
Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName,
- int[] packageSessionIds) {
+ int[] packageSessionIds, SparseIntArray extensionVersions) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
return new Rollback(rollbackId, backupDir, -1, userId, installerPackageName,
- packageSessionIds);
+ packageSessionIds, extensionVersions);
}
/**
@@ -211,10 +242,11 @@ class RollbackStore {
* backupDir assigned.
*/
Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId,
- String installerPackageName, int[] packageSessionIds) {
+ String installerPackageName, int[] packageSessionIds,
+ SparseIntArray extensionVersions) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName,
- packageSessionIds);
+ packageSessionIds, extensionVersions);
}
/**
@@ -272,6 +304,8 @@ class RollbackStore {
dataJson.put("restoreUserDataInProgress", rollback.isRestoreUserDataInProgress());
dataJson.put("userId", rollback.getUserId());
dataJson.putOpt("installerPackageName", rollback.getInstallerPackageName());
+ dataJson.putOpt(
+ "extensionVersions", extensionVersionsToJson(rollback.getExtensionVersions()));
PrintWriter pw = new PrintWriter(new File(rollback.getBackupDir(), "rollback.json"));
pw.println(dataJson.toString());
@@ -316,7 +350,8 @@ class RollbackStore {
dataJson.getInt("apkSessionId"),
dataJson.getBoolean("restoreUserDataInProgress"),
dataJson.optInt("userId", USER_SYSTEM),
- dataJson.optString("installerPackageName", ""));
+ dataJson.optString("installerPackageName", ""),
+ extensionVersionsFromJson(dataJson.optJSONArray("extensionVersions")));
}
private static JSONObject toJson(VersionedPackage pkg) throws JSONException {
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
index 64d05f07e64e..6afdfba70f58 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.util.IntArray;
+import android.util.SparseIntArray;
import android.util.SparseLongArray;
import com.google.common.truth.Correspondence;
@@ -83,7 +84,7 @@ public class RollbackStoreTest {
}
};
- private static final String JSON_ROLLBACK = "{'info':{'rollbackId':123,'packages':"
+ private static final String JSON_ROLLBACK_NO_EXT = "{'info':{'rollbackId':123,'packages':"
+ "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55},"
+ "'versionRolledBackTo':{'packageName':'blah1','longVersionCode':50},'pendingBackups':"
+ "[59,1245,124544],'pendingRestores':[{'userId':498,'appId':32322,'seInfo':'wombles'},"
@@ -103,6 +104,28 @@ public class RollbackStoreTest {
+ "'restoreUserDataInProgress':true, 'userId':0,"
+ "'installerPackageName':'some.installer'}";
+ private static final String JSON_ROLLBACK = "{'info':{'rollbackId':123,'packages':"
+ + "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55},"
+ + "'versionRolledBackTo':{'packageName':'blah1','longVersionCode':50},'pendingBackups':"
+ + "[59,1245,124544],'pendingRestores':[{'userId':498,'appId':32322,'seInfo':'wombles'},"
+ + "{'userId':-895,'appId':1,'seInfo':'pingu'}],'isApex':false,'isApkInApex':false,"
+ + "'installedUsers':"
+ + "[498468432,1111,98464],'ceSnapshotInodes':[{'userId':1,'ceSnapshotInode':-6},"
+ + "{'userId':2222,'ceSnapshotInode':81641654445},{'userId':546546,"
+ + "'ceSnapshotInode':345689375}]},{'versionRolledBackFrom':{'packageName':'chips',"
+ + "'longVersionCode':28},'versionRolledBackTo':{'packageName':'com.chips.test',"
+ + "'longVersionCode':48},'pendingBackups':[5],'pendingRestores':[{'userId':18,"
+ + "'appId':-12,'seInfo':''}],'isApex':false,'isApkInApex':false,"
+ + "'installedUsers':[55,79],"
+ + "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello',"
+ + "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
+ + "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
+ + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1,"
+ + "'restoreUserDataInProgress':true, 'userId':0,"
+ + "'installerPackageName':'some.installer',"
+ + "'extensionVersions':[{'sdkVersion':5,'extensionVersion':25},"
+ + "{'sdkVersion':30,'extensionVersion':71}]}";
+
@Rule
public TemporaryFolder mFolder = new TemporaryFolder();
@@ -119,7 +142,10 @@ public class RollbackStoreTest {
@Test
public void createNonStaged() {
- Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
+ SparseIntArray extensionVersions = new SparseIntArray();
+ extensionVersions.put(30, 71);
+ Rollback rollback = mRollbackStore.createNonStagedRollback(
+ ID, USER, INSTALLER, null, extensionVersions);
assertThat(rollback.getBackupDir().getAbsolutePath())
.isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
@@ -128,11 +154,16 @@ public class RollbackStoreTest {
assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
assertThat(rollback.info.getPackages()).isEmpty();
assertThat(rollback.isEnabling()).isTrue();
+ assertThat(rollback.getExtensionVersions().toString())
+ .isEqualTo(extensionVersions.toString());
}
@Test
public void createStaged() {
- Rollback rollback = mRollbackStore.createStagedRollback(ID, 897, USER, INSTALLER, null);
+ SparseIntArray extensionVersions = new SparseIntArray();
+ extensionVersions.put(30, 71);
+ Rollback rollback = mRollbackStore.createStagedRollback(
+ ID, 897, USER, INSTALLER, null, extensionVersions);
assertThat(rollback.getBackupDir().getAbsolutePath())
.isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
@@ -143,11 +174,17 @@ public class RollbackStoreTest {
assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
assertThat(rollback.info.getPackages()).isEmpty();
assertThat(rollback.isEnabling()).isTrue();
+ assertThat(rollback.getExtensionVersions().toString())
+ .isEqualTo(extensionVersions.toString());
}
@Test
public void saveAndLoadRollback() {
- Rollback origRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
+ SparseIntArray extensionVersions = new SparseIntArray();
+ extensionVersions.put(5, 25);
+ extensionVersions.put(30, 71);
+ Rollback origRb = mRollbackStore.createNonStagedRollback(
+ ID, USER, INSTALLER, null, extensionVersions);
origRb.setRestoreUserDataInProgress(true);
origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2));
@@ -196,8 +233,62 @@ public class RollbackStoreTest {
}
@Test
+ public void loadFromJsonNoExtensionVersions() throws Exception {
+ Rollback expectedRb =
+ mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null, null);
+
+ expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
+ expectedRb.setRestoreUserDataInProgress(true);
+ expectedRb.info.getCausePackages().add(new VersionedPackage("hello", 23));
+ expectedRb.info.getCausePackages().add(new VersionedPackage("something", 999));
+ expectedRb.info.setCommittedSessionId(45654465);
+
+ PackageRollbackInfo pkgInfo1 = new PackageRollbackInfo(new VersionedPackage("blah", 55),
+ new VersionedPackage("blah1", 50), new IntArray(), new ArrayList<>(),
+ false, false, new IntArray(), new SparseLongArray());
+ pkgInfo1.getPendingBackups().add(59);
+ pkgInfo1.getPendingBackups().add(1245);
+ pkgInfo1.getPendingBackups().add(124544);
+ pkgInfo1.getCeSnapshotInodes().put(546546, 345689375);
+ pkgInfo1.getCeSnapshotInodes().put(2222, 81641654445L);
+ pkgInfo1.getCeSnapshotInodes().put(1, -6);
+
+ pkgInfo1.getPendingRestores().add(
+ new PackageRollbackInfo.RestoreInfo(498, 32322, "wombles"));
+ pkgInfo1.getPendingRestores().add(
+ new PackageRollbackInfo.RestoreInfo(-895, 1, "pingu"));
+
+ pkgInfo1.getSnapshottedUsers().add(498468432);
+ pkgInfo1.getSnapshottedUsers().add(1111);
+ pkgInfo1.getSnapshottedUsers().add(98464);
+
+ PackageRollbackInfo pkgInfo2 = new PackageRollbackInfo(new VersionedPackage("chips", 28),
+ new VersionedPackage("com.chips.test", 48), new IntArray(), new ArrayList<>(),
+ false, false, new IntArray(), new SparseLongArray());
+ pkgInfo2.getPendingBackups().add(5);
+
+ pkgInfo2.getPendingRestores().add(
+ new PackageRollbackInfo.RestoreInfo(18, -12, ""));
+
+ pkgInfo2.getSnapshottedUsers().add(55);
+ pkgInfo2.getSnapshottedUsers().add(79);
+
+ expectedRb.info.getPackages().add(pkgInfo1);
+ expectedRb.info.getPackages().add(pkgInfo2);
+
+ Rollback parsedRb = RollbackStore.rollbackFromJson(
+ new JSONObject(JSON_ROLLBACK_NO_EXT), expectedRb.getBackupDir());
+
+ assertRollbacksAreEquivalent(parsedRb, expectedRb);
+ }
+
+ @Test
public void loadFromJson() throws Exception {
- Rollback expectedRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
+ SparseIntArray extensionVersions = new SparseIntArray();
+ extensionVersions.put(5, 25);
+ extensionVersions.put(30, 71);
+ Rollback expectedRb = mRollbackStore.createNonStagedRollback(
+ ID, USER, INSTALLER, null, extensionVersions);
expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
expectedRb.setRestoreUserDataInProgress(true);
@@ -246,7 +337,7 @@ public class RollbackStoreTest {
@Test
public void saveAndDelete() {
- Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
+ Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null, null);
RollbackStore.saveRollback(rollback);
@@ -294,6 +385,13 @@ public class RollbackStoreTest {
assertThat(a.getUserId()).isEqualTo(b.getUserId());
assertThat(a.getInstallerPackageName()).isEqualTo(b.getInstallerPackageName());
+
+ if (a.getExtensionVersions() == null) {
+ assertThat(b.getExtensionVersions()).isNull();
+ } else {
+ assertThat(b.getExtensionVersions().toString())
+ .isEqualTo(a.getExtensionVersions().toString());
+ }
}
private void assertPackageRollbacksAreEquivalent(PackageRollbackInfo b, PackageRollbackInfo a) {
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index 1a6c6b4011cd..e74891c7b3f8 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -317,7 +317,7 @@ public class RollbackUnitTest {
public void notifySessionWithSuccess() {
int[] sessionIds = new int[]{ 7777, 8888 };
Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
- sessionIds);
+ sessionIds, null);
// The 1st invocation returns false because not all child sessions are notified.
assertThat(rollback.notifySessionWithSuccess()).isFalse();
// The 2nd invocation returns true because now all child sessions are notified.
@@ -328,7 +328,7 @@ public class RollbackUnitTest {
public void allPackagesEnabled() {
int[] sessionIds = new int[]{ 7777, 8888 };
Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
- sessionIds);
+ sessionIds, null);
// #allPackagesEnabled returns false when 1 out of 2 packages is enabled.
rollback.info.getPackages().add(newPkgInfoFor(PKG_1, 12, 10, false));
assertThat(rollback.allPackagesEnabled()).isFalse();