Merge "Add a way to record causePackages for rollbacks."
This commit is contained in:
committed by
Android (Google) Code Review
commit
66a23d0504
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user