Merge "Add a way to record causePackages for rollbacks."
diff --git a/api/system-current.txt b/api/system-current.txt
index 82bafab..90191b6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1698,6 +1698,7 @@
public final class RollbackInfo implements android.os.Parcelable {
method public int describeContents();
+ method public java.util.List<android.content.pm.VersionedPackage> getCausePackages();
method public java.util.List<android.content.rollback.PackageRollbackInfo> getPackages();
method public int getRollbackId();
method public int getSessionId();
@@ -1707,7 +1708,7 @@
}
public final class RollbackManager {
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void commitRollback(int, @NonNull android.content.IntentSender);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void commitRollback(int, @NonNull java.util.List<android.content.pm.VersionedPackage>, @NonNull android.content.IntentSender);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void expireRollbackForPackage(@NonNull String);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public java.util.List<android.content.rollback.RollbackInfo> getAvailableRollbacks();
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) @NonNull public java.util.List<android.content.rollback.RollbackInfo> getRecentlyCommittedRollbacks();
diff --git a/core/java/android/content/rollback/IRollbackManager.aidl b/core/java/android/content/rollback/IRollbackManager.aidl
index 226d76a..32e2198 100644
--- a/core/java/android/content/rollback/IRollbackManager.aidl
+++ b/core/java/android/content/rollback/IRollbackManager.aidl
@@ -26,8 +26,8 @@
ParceledListSlice getAvailableRollbacks();
ParceledListSlice getRecentlyExecutedRollbacks();
- void commitRollback(int rollbackId, String callerPackageName,
- in IntentSender statusReceiver);
+ void commitRollback(int rollbackId, in ParceledListSlice causePackages,
+ String callerPackageName, in IntentSender statusReceiver);
// Exposed for use from the system server only. Callback from the package
// manager during the install flow when user data can be restored for a given
diff --git a/core/java/android/content/rollback/RollbackInfo.java b/core/java/android/content/rollback/RollbackInfo.java
index 812f995..1111b43 100644
--- a/core/java/android/content/rollback/RollbackInfo.java
+++ b/core/java/android/content/rollback/RollbackInfo.java
@@ -18,6 +18,7 @@
import android.annotation.SystemApi;
import android.content.pm.PackageInstaller;
+import android.content.pm.VersionedPackage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -39,15 +40,20 @@
private final List<PackageRollbackInfo> mPackages;
+ private final List<VersionedPackage> mCausePackages;
+
/** @hide */
- public RollbackInfo(int rollbackId, List<PackageRollbackInfo> packages) {
+ public RollbackInfo(int rollbackId, List<PackageRollbackInfo> packages,
+ List<VersionedPackage> causePackages) {
this.mRollbackId = rollbackId;
this.mPackages = packages;
+ this.mCausePackages = causePackages;
}
private RollbackInfo(Parcel in) {
mRollbackId = in.readInt();
mPackages = in.createTypedArrayList(PackageRollbackInfo.CREATOR);
+ mCausePackages = in.createTypedArrayList(VersionedPackage.CREATOR);
}
/**
@@ -82,6 +88,15 @@
return PackageInstaller.SessionInfo.INVALID_ID;
}
+ /**
+ * Gets the list of package versions that motivated this rollback.
+ * As provided to {@link #commitRollback} when the rollback was committed.
+ * This is only applicable for rollbacks that have been committed.
+ */
+ public List<VersionedPackage> getCausePackages() {
+ return mCausePackages;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -91,6 +106,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mRollbackId);
out.writeTypedList(mPackages);
+ out.writeTypedList(mCausePackages);
}
public static final Parcelable.Creator<RollbackInfo> CREATOR =
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
index f8abcfc..7795df5 100644
--- a/core/java/android/content/rollback/RollbackManager.java
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -22,6 +22,8 @@
import android.annotation.SystemService;
import android.content.Context;
import android.content.IntentSender;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.VersionedPackage;
import android.os.RemoteException;
import java.util.List;
@@ -103,14 +105,18 @@
* TODO: Specify the returns status codes.
*
* @param rollbackId ID of the rollback to commit
+ * @param causePackages package versions to record as the motivation for this
+ * rollback.
* @param statusReceiver where to deliver the results
* @throws SecurityException if the caller does not have the
* MANAGE_ROLLBACKS permission.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
- public void commitRollback(int rollbackId, @NonNull IntentSender statusReceiver) {
+ public void commitRollback(int rollbackId, @NonNull List<VersionedPackage> causePackages,
+ @NonNull IntentSender statusReceiver) {
try {
- mBinder.commitRollback(rollbackId, mCallerPackageName, statusReceiver);
+ mBinder.commitRollback(rollbackId, new ParceledListSlice(causePackages),
+ mCallerPackageName, statusReceiver);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index e59228a..14d7442 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -54,6 +54,7 @@
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -207,7 +208,8 @@
List<RollbackInfo> rollbacks = new ArrayList<>();
for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
RollbackData data = mAvailableRollbacks.get(i);
- rollbacks.add(new RollbackInfo(data.rollbackId, data.packages));
+ rollbacks.add(new RollbackInfo(data.rollbackId, data.packages,
+ Collections.emptyList()));
}
return new ParceledListSlice<>(rollbacks);
}
@@ -227,8 +229,8 @@
}
@Override
- public void commitRollback(int rollbackId, String callerPackageName,
- IntentSender statusReceiver) {
+ public void commitRollback(int rollbackId, ParceledListSlice causePackages,
+ String callerPackageName, IntentSender statusReceiver) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_ROLLBACKS,
"executeRollback");
@@ -238,7 +240,8 @@
appOps.checkPackage(callingUid, callerPackageName);
getHandler().post(() ->
- commitRollbackInternal(rollbackId, callerPackageName, statusReceiver));
+ commitRollbackInternal(rollbackId, causePackages.getList(),
+ callerPackageName, statusReceiver));
}
/**
@@ -246,7 +249,7 @@
* The work is done on the current thread. This may be a long running
* operation.
*/
- private void commitRollbackInternal(int rollbackId,
+ private void commitRollbackInternal(int rollbackId, List<VersionedPackage> causePackages,
String callerPackageName, IntentSender statusReceiver) {
Log.i(TAG, "Initiating rollback");
@@ -350,8 +353,8 @@
return;
}
- addRecentlyExecutedRollback(
- new RollbackInfo(data.rollbackId, data.packages));
+ addRecentlyExecutedRollback(new RollbackInfo(
+ data.rollbackId, data.packages, causePackages));
sendSuccess(statusReceiver);
Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index fcae618..2880103 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -29,6 +29,7 @@
import com.android.server.PackageWatchdog.PackageHealthObserver;
import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
+import java.util.Collections;
import java.util.List;
/**
@@ -85,8 +86,12 @@
});
// TODO(zezeozue): Log initiated metrics
+ // TODO: Pass the package as a cause package instead of using
+ // Collections.emptyList once the version of the failing package is
+ // easily available.
mHandler.post(() ->
mRollbackManager.commitRollback(rollback.getRollbackId(),
+ Collections.emptyList(),
rollbackReceiver.getIntentSender()));
// Assume rollback executed successfully
return true;
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 3b24b3e..98ebb09 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -116,7 +116,9 @@
int rollbackId = element.getInt("rollbackId");
List<PackageRollbackInfo> packages = packageRollbackInfosFromJson(
element.getJSONArray("packages"));
- RollbackInfo rollback = new RollbackInfo(rollbackId, packages);
+ List<VersionedPackage> causePackages = versionedPackagesFromJson(
+ element.getJSONArray("causePackages"));
+ RollbackInfo rollback = new RollbackInfo(rollbackId, packages, causePackages);
recentlyExecutedRollbacks.add(rollback);
}
} catch (IOException | JSONException e) {
@@ -187,6 +189,7 @@
JSONObject element = new JSONObject();
element.put("rollbackId", rollback.getRollbackId());
element.put("packages", toJson(rollback.getPackages()));
+ element.put("causePackages", versionedPackagesToJson(rollback.getCausePackages()));
array.put(element);
}
@@ -219,21 +222,49 @@
}
}
+ private JSONObject toJson(VersionedPackage pkg) throws JSONException {
+ JSONObject json = new JSONObject();
+ json.put("packageName", pkg.getPackageName());
+ json.put("longVersionCode", pkg.getLongVersionCode());
+ return json;
+ }
+
+ private VersionedPackage versionedPackageFromJson(JSONObject json) throws JSONException {
+ String packageName = json.getString("packageName");
+ long longVersionCode = json.getLong("longVersionCode");
+ return new VersionedPackage(packageName, longVersionCode);
+ }
+
private JSONObject toJson(PackageRollbackInfo info) throws JSONException {
JSONObject json = new JSONObject();
- json.put("packageName", info.getPackageName());
- json.put("higherVersionCode", info.getVersionRolledBackFrom().getLongVersionCode());
- json.put("lowerVersionCode", info.getVersionRolledBackTo().getLongVersionCode());
+ json.put("versionRolledBackFrom", toJson(info.getVersionRolledBackFrom()));
+ json.put("versionRolledBackTo", toJson(info.getVersionRolledBackTo()));
return json;
}
private PackageRollbackInfo packageRollbackInfoFromJson(JSONObject json) throws JSONException {
- String packageName = json.getString("packageName");
- long higherVersionCode = json.getLong("higherVersionCode");
- long lowerVersionCode = json.getLong("lowerVersionCode");
- return new PackageRollbackInfo(
- new VersionedPackage(packageName, higherVersionCode),
- new VersionedPackage(packageName, lowerVersionCode));
+ VersionedPackage versionRolledBackFrom = versionedPackageFromJson(
+ json.getJSONObject("versionRolledBackFrom"));
+ VersionedPackage versionRolledBackTo = versionedPackageFromJson(
+ json.getJSONObject("versionRolledBackTo"));
+ return new PackageRollbackInfo(versionRolledBackFrom, versionRolledBackTo);
+ }
+
+ private JSONArray versionedPackagesToJson(List<VersionedPackage> packages)
+ throws JSONException {
+ JSONArray json = new JSONArray();
+ for (VersionedPackage pkg : packages) {
+ json.put(toJson(pkg));
+ }
+ return json;
+ }
+
+ private List<VersionedPackage> versionedPackagesFromJson(JSONArray json) throws JSONException {
+ List<VersionedPackage> packages = new ArrayList<>();
+ for (int i = 0; i < json.length(); ++i) {
+ packages.add(versionedPackageFromJson(json.getJSONObject(i)));
+ }
+ return packages;
}
private JSONArray toJson(List<PackageRollbackInfo> infos) throws JSONException {
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 0493ef8..e128a6c 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
@@ -40,6 +41,7 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
@@ -303,14 +305,15 @@
rm.getAvailableRollbacks(), TEST_APP_A);
// Roll back the app.
- RollbackTestUtils.rollback(rollback.getRollbackId());
+ VersionedPackage cause = new VersionedPackage(
+ "com.android.tests.rollback.testapp.Foo", 42);
+ RollbackTestUtils.rollback(rollback.getRollbackId(), cause);
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
// Verify the recent rollback has been recorded.
rollback = getUniqueRollbackInfoForPackage(
rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
- assertNotNull(rollback);
- assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+ assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback, cause);
// Reload the persisted data.
rm.reloadPersistedData();
@@ -318,8 +321,7 @@
// Verify the recent rollback is still recorded.
rollback = getUniqueRollbackInfoForPackage(
rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
- assertNotNull(rollback);
- assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+ assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback, cause);
} finally {
RollbackTestUtils.dropShellPermissionIdentity();
}
@@ -544,7 +546,7 @@
try {
// TODO: What if the implementation checks arguments for non-null
// first? Then this test isn't valid.
- rm.commitRollback(0, null);
+ rm.commitRollback(0, Collections.emptyList(), null);
fail("expected SecurityException");
} catch (SecurityException e) {
// Expected.
@@ -690,11 +692,18 @@
// Helper function to test the value of a RollbackInfo with single package
private void assertRollbackInfoEquals(String packageName,
long versionRolledBackFrom, long versionRolledBackTo,
- RollbackInfo info) {
+ RollbackInfo info, VersionedPackage... causePackages) {
assertNotNull(info);
assertEquals(1, info.getPackages().size());
assertPackageRollbackInfoEquals(packageName, versionRolledBackFrom, versionRolledBackTo,
info.getPackages().get(0));
+ assertEquals(causePackages.length, info.getCausePackages().size());
+ for (int i = 0; i < causePackages.length; ++i) {
+ assertEquals(causePackages[i].getPackageName(),
+ info.getCausePackages().get(i).getPackageName());
+ assertEquals(causePackages[i].getLongVersionCode(),
+ info.getCausePackages().get(i).getLongVersionCode());
+ }
}
// Helper function to test that the given rollback info is a rollback for
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
index 1478657..ad560f3 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -21,12 +21,14 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
import android.content.rollback.RollbackManager;
import android.support.test.InstrumentationRegistry;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.Arrays;
/**
* Utilities to facilitate testing rollbacks.
@@ -92,9 +94,11 @@
* Commit the given rollback.
* @throws AssertionError if the rollback fails.
*/
- static void rollback(int rollbackId) throws InterruptedException {
+ static void rollback(int rollbackId, VersionedPackage... causePackages)
+ throws InterruptedException {
RollbackManager rm = getRollbackManager();
- rm.commitRollback(rollbackId, LocalIntentSender.getIntentSender());
+ rm.commitRollback(rollbackId, Arrays.asList(causePackages),
+ LocalIntentSender.getIntentSender());
assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
}