Merge "Add a way to record causePackages for rollbacks."

This commit is contained in:
TreeHugger Robot
2019-01-29 13:17:22 +00:00
committed by Android (Google) Code Review
9 changed files with 107 additions and 32 deletions

View File

@@ -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();

View File

@@ -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

View File

@@ -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 =

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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

View File

@@ -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());
}