diff options
| author | 2019-01-29 13:17:22 +0000 | |
|---|---|---|
| committer | 2019-01-29 13:17:22 +0000 | |
| commit | 66a23d050451e2548fa73f31f274ff59bc5c2fcb (patch) | |
| tree | e39354476ab5e2f4062cc6ae7523267797a99b7e | |
| parent | 7fc3aa0a0ef6459c0900bea608fc67b83011149e (diff) | |
| parent | bf5b5c44614399ccf8759a23e61af518f8f54f2e (diff) | |
Merge "Add a way to record causePackages for rollbacks."
9 files changed, 107 insertions, 32 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 82bafab9aacc..90191b603718 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1698,6 +1698,7 @@ package android.content.rollback { 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 @@ package android.content.rollback { } 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 226d76abc01c..32e2198fa2df 100644 --- a/core/java/android/content/rollback/IRollbackManager.aidl +++ b/core/java/android/content/rollback/IRollbackManager.aidl @@ -26,8 +26,8 @@ interface IRollbackManager { 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 812f995675fc..1111b4320eb9 100644 --- a/core/java/android/content/rollback/RollbackInfo.java +++ b/core/java/android/content/rollback/RollbackInfo.java @@ -18,6 +18,7 @@ package android.content.rollback; 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 @@ public final class RollbackInfo implements Parcelable { 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 @@ public final class RollbackInfo implements Parcelable { 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 final class RollbackInfo implements Parcelable { 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 f8abcfc8fc76..7795df5e4249 100644 --- a/core/java/android/content/rollback/RollbackManager.java +++ b/core/java/android/content/rollback/RollbackManager.java @@ -22,6 +22,8 @@ import android.annotation.SystemApi; 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 @@ public final class RollbackManager { * 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 e59228a16fb1..14d74428d3e1 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.security.SecureRandom; 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 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { 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 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } @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 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { appOps.checkPackage(callingUid, callerPackageName); getHandler().post(() -> - commitRollbackInternal(rollbackId, callerPackageName, statusReceiver)); + commitRollbackInternal(rollbackId, causePackages.getList(), + callerPackageName, statusReceiver)); } /** @@ -246,7 +249,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { * 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 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { 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 fcae61895b36..28801038ebd1 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; import com.android.server.PackageWatchdog.PackageHealthObserver; import com.android.server.PackageWatchdog.PackageHealthObserverImpact; +import java.util.Collections; import java.util.List; /** @@ -85,8 +86,12 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve }); // 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 3b24b3e9a444..98ebb0943f43 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 @@ class RollbackStore { 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 @@ class RollbackStore { 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 @@ class RollbackStore { } } + 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 0493ef8ab062..e128a6ce9f7c 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.ComponentName; 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.Test; 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 @@ public class RollbackTest { 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 @@ public class RollbackTest { // 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 @@ public class RollbackTest { 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 @@ public class RollbackTest { // 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 14786577c814..ad560f33230c 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.Intent; 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 @@ class RollbackTestUtils { * 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()); } |