Merge changes Ic2bb4798,I2a409de2,I88a50a0e,Ia69342a4,Ia6548e4d, ...
* changes: Use "commit" instead of "execute" for rollbacks. Rename PACKAGE_ROLLBACK_EXECUTED to ROLLBACK_COMMITTED. Remove unused RollbackManager APIs. Use RollbackManager.getAvailableRollbacks in RollbackTest Use new RollbackManager API in RollbackPackageHealthObserver. Add RollbackManager.getAvailableRollbacks API. Include all relevant packages in RollbackInfo.
This commit is contained in:
committed by
Android (Google) Code Review
commit
efb4132ba3
@@ -1303,13 +1303,13 @@ package android.content {
|
||||
field public static final String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS";
|
||||
field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_SPECIAL_APP_ACCESSES = "android.intent.action.MANAGE_SPECIAL_APP_ACCESSES";
|
||||
field public static final String ACTION_MASTER_CLEAR_NOTIFICATION = "android.intent.action.MASTER_CLEAR_NOTIFICATION";
|
||||
field public static final String ACTION_PACKAGE_ROLLBACK_EXECUTED = "android.intent.action.PACKAGE_ROLLBACK_EXECUTED";
|
||||
field public static final String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
|
||||
field public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
|
||||
field public static final String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
|
||||
field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_APP_PERMISSION_USAGE = "android.intent.action.REVIEW_APP_PERMISSION_USAGE";
|
||||
field public static final String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
|
||||
field public static final String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE";
|
||||
field public static final String ACTION_ROLLBACK_COMMITTED = "android.intent.action.ROLLBACK_COMMITTED";
|
||||
field public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
|
||||
field @Deprecated public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
|
||||
field public static final String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
|
||||
@@ -1695,18 +1695,17 @@ package android.content.rollback {
|
||||
|
||||
public final class RollbackInfo implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public java.util.List<android.content.rollback.PackageRollbackInfo> getPackages();
|
||||
method public int getRollbackId();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.content.rollback.RollbackInfo> CREATOR;
|
||||
field public final android.content.rollback.PackageRollbackInfo targetPackage;
|
||||
}
|
||||
|
||||
public final class RollbackManager {
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void executeRollback(@NonNull android.content.rollback.RollbackInfo, @NonNull android.content.IntentSender);
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void commitRollback(@NonNull android.content.rollback.RollbackInfo, @NonNull android.content.IntentSender);
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void expireRollbackForPackage(@NonNull String);
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) @Nullable public android.content.rollback.RollbackInfo getAvailableRollback(@NonNull String);
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) @NonNull public java.util.List<java.lang.String> getPackagesWithAvailableRollbacks();
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) @NonNull public java.util.List<android.content.rollback.RollbackInfo> getRecentlyExecutedRollbacks();
|
||||
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();
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void reloadPersistedData();
|
||||
}
|
||||
|
||||
|
||||
@@ -2375,8 +2375,7 @@ public class Intent implements Parcelable, Cloneable {
|
||||
public static final String ACTION_PACKAGE_ENABLE_ROLLBACK =
|
||||
"android.intent.action.PACKAGE_ENABLE_ROLLBACK";
|
||||
/**
|
||||
* Broadcast Action: An existing version of an application package has been
|
||||
* rolled back to a previous version.
|
||||
* Broadcast Action: A rollback has been committed.
|
||||
*
|
||||
* <p class="note">This is a protected intent that can only be sent
|
||||
* by the system.
|
||||
@@ -2385,8 +2384,8 @@ public class Intent implements Parcelable, Cloneable {
|
||||
*/
|
||||
@SystemApi
|
||||
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
|
||||
public static final String ACTION_PACKAGE_ROLLBACK_EXECUTED =
|
||||
"android.intent.action.PACKAGE_ROLLBACK_EXECUTED";
|
||||
public static final String ACTION_ROLLBACK_COMMITTED =
|
||||
"android.intent.action.ROLLBACK_COMMITTED";
|
||||
/**
|
||||
* @hide
|
||||
* Broadcast Action: Ask system services if there is any reason to
|
||||
|
||||
@@ -17,17 +17,13 @@
|
||||
package android.content.rollback;
|
||||
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.content.pm.StringParceledListSlice;
|
||||
import android.content.rollback.RollbackInfo;
|
||||
import android.content.IntentSender;
|
||||
|
||||
/** {@hide} */
|
||||
interface IRollbackManager {
|
||||
|
||||
RollbackInfo getAvailableRollback(String packageName);
|
||||
|
||||
StringParceledListSlice getPackagesWithAvailableRollbacks();
|
||||
|
||||
ParceledListSlice getAvailableRollbacks();
|
||||
ParceledListSlice getRecentlyExecutedRollbacks();
|
||||
|
||||
void executeRollback(in RollbackInfo rollback, String callerPackageName,
|
||||
|
||||
@@ -20,6 +20,8 @@ import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Information about a set of packages that can be, or already have been
|
||||
* rolled back together.
|
||||
@@ -34,25 +36,20 @@ public final class RollbackInfo implements Parcelable {
|
||||
*/
|
||||
private final int mRollbackId;
|
||||
|
||||
/**
|
||||
* The package that needs to be rolled back.
|
||||
*/
|
||||
public final PackageRollbackInfo targetPackage;
|
||||
private final List<PackageRollbackInfo> mPackages;
|
||||
|
||||
// TODO: Add a list of additional packages rolled back due to atomic
|
||||
// install dependencies when rollback of atomic installs is supported.
|
||||
// TODO: Add a flag to indicate if reboot is required, when rollback of
|
||||
// staged installs is supported.
|
||||
|
||||
/** @hide */
|
||||
public RollbackInfo(int rollbackId, PackageRollbackInfo targetPackage) {
|
||||
public RollbackInfo(int rollbackId, List<PackageRollbackInfo> packages) {
|
||||
this.mRollbackId = rollbackId;
|
||||
this.targetPackage = targetPackage;
|
||||
this.mPackages = packages;
|
||||
}
|
||||
|
||||
private RollbackInfo(Parcel in) {
|
||||
mRollbackId = in.readInt();
|
||||
targetPackage = PackageRollbackInfo.CREATOR.createFromParcel(in);
|
||||
mPackages = in.createTypedArrayList(PackageRollbackInfo.CREATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,6 +59,13 @@ public final class RollbackInfo implements Parcelable {
|
||||
return mRollbackId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of package that are rolled back.
|
||||
*/
|
||||
public List<PackageRollbackInfo> getPackages() {
|
||||
return mPackages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
@@ -70,7 +74,7 @@ public final class RollbackInfo implements Parcelable {
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeInt(mRollbackId);
|
||||
targetPackage.writeToParcel(out, flags);
|
||||
out.writeTypedList(mPackages);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<RollbackInfo> CREATOR =
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package android.content.rollback;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.SystemService;
|
||||
@@ -50,55 +49,26 @@ public final class RollbackManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rollback currently available to be executed for the given
|
||||
* package.
|
||||
* <p>
|
||||
* The returned RollbackInfo describes what packages would be rolled back,
|
||||
* including package version codes before and after rollback. The rollback
|
||||
* can be initiated using {@link #executeRollback(RollbackInfo,IntentSender)}.
|
||||
* <p>
|
||||
* TODO: What if there is no package installed on device for packageName?
|
||||
* Returns a list of all currently available rollbacks.
|
||||
*
|
||||
* @param packageName name of the package to get the availble RollbackInfo for.
|
||||
* @return the rollback available for the package, or null if no rollback
|
||||
* is available for the package.
|
||||
* @throws SecurityException if the caller does not have the
|
||||
* MANAGE_ROLLBACKS permission.
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
|
||||
public @Nullable RollbackInfo getAvailableRollback(@NonNull String packageName) {
|
||||
public List<RollbackInfo> getAvailableRollbacks() {
|
||||
try {
|
||||
return mBinder.getAvailableRollback(packageName);
|
||||
return mBinder.getAvailableRollbacks().getList();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of packages that are available for rollback.
|
||||
* Call {@link #getAvailableRollback(String)} to get more information
|
||||
* about the rollback available for a particular package.
|
||||
*
|
||||
* @return the names of packages that are available for rollback.
|
||||
* @throws SecurityException if the caller does not have the
|
||||
* MANAGE_ROLLBACKS permission.
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
|
||||
public @NonNull List<String> getPackagesWithAvailableRollbacks() {
|
||||
try {
|
||||
return mBinder.getPackagesWithAvailableRollbacks().getList();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the list of all recently executed rollbacks.
|
||||
* Gets the list of all recently committed rollbacks.
|
||||
* This is for the purposes of preventing re-install of a bad version of a
|
||||
* package.
|
||||
* package and monitoring the status of a staged rollback.
|
||||
* <p>
|
||||
* Returns an empty list if there are no recently executed rollbacks.
|
||||
* Returns an empty list if there are no recently committed rollbacks.
|
||||
* <p>
|
||||
* To avoid having to keep around complete rollback history forever on a
|
||||
* device, the returned list of rollbacks is only guaranteed to include
|
||||
@@ -107,12 +77,12 @@ public final class RollbackManager {
|
||||
* (without the possibility of rollback) to a higher version code than was
|
||||
* rolled back from.
|
||||
*
|
||||
* @return the recently executed rollbacks
|
||||
* @return the recently committed rollbacks
|
||||
* @throws SecurityException if the caller does not have the
|
||||
* MANAGE_ROLLBACKS permission.
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
|
||||
public @NonNull List<RollbackInfo> getRecentlyExecutedRollbacks() {
|
||||
public @NonNull List<RollbackInfo> getRecentlyCommittedRollbacks() {
|
||||
try {
|
||||
return mBinder.getRecentlyExecutedRollbacks().getList();
|
||||
} catch (RemoteException e) {
|
||||
@@ -121,25 +91,24 @@ public final class RollbackManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given rollback, rolling back all versions of the packages
|
||||
* to the last good versions previously installed on the device as
|
||||
* specified in the given rollback object. The rollback will fail if any
|
||||
* of the installed packages or available rollbacks are inconsistent with
|
||||
* the versions specified in the given rollback object, which can happen
|
||||
* if a package has been updated or a rollback expired since the rollback
|
||||
* object was retrieved from {@link #getAvailableRollback(String)}.
|
||||
* Commit the rollback with given id, rolling back all versions of the
|
||||
* packages to the last good versions previously installed on the device
|
||||
* as specified in the corresponding RollbackInfo object. The
|
||||
* rollback will fail if any of the installed packages or available
|
||||
* rollbacks are inconsistent with the versions specified in the given
|
||||
* rollback object, which can happen if a package has been updated or a
|
||||
* rollback expired since the rollback object was retrieved from
|
||||
* {@link #getAvailableRollbacks()}.
|
||||
* <p>
|
||||
* TODO: Specify the returns status codes.
|
||||
* TODO: What happens in case reboot is required for the rollback to take
|
||||
* effect for staged installs?
|
||||
*
|
||||
* @param rollback to execute
|
||||
* @param rollback to commit
|
||||
* @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 executeRollback(@NonNull RollbackInfo rollback,
|
||||
public void commitRollback(@NonNull RollbackInfo rollback,
|
||||
@NonNull IntentSender statusReceiver) {
|
||||
try {
|
||||
mBinder.executeRollback(rollback, mCallerPackageName, statusReceiver);
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
|
||||
<protected-broadcast android:name="android.intent.action.PACKAGE_CHANGED" />
|
||||
<protected-broadcast android:name="android.intent.action.PACKAGE_ENABLE_ROLLBACK" />
|
||||
<protected-broadcast android:name="android.intent.action.PACKAGE_ROLLBACK_EXECUTED" />
|
||||
<protected-broadcast android:name="android.intent.action.ROLLBACK_COMMITTED" />
|
||||
<protected-broadcast android:name="android.intent.action.PACKAGE_RESTARTED" />
|
||||
<protected-broadcast android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
|
||||
<protected-broadcast android:name="android.intent.action.PACKAGE_FIRST_LAUNCH" />
|
||||
|
||||
@@ -28,7 +28,6 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.PackageParser;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.content.pm.StringParceledListSlice;
|
||||
import android.content.pm.VersionedPackage;
|
||||
import android.content.rollback.IRollbackManager;
|
||||
import android.content.rollback.PackageRollbackInfo;
|
||||
@@ -56,12 +55,10 @@ import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Implementation of service that manages APK level rollbacks.
|
||||
@@ -200,48 +197,20 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RollbackInfo getAvailableRollback(String packageName) {
|
||||
public ParceledListSlice getAvailableRollbacks() {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.MANAGE_ROLLBACKS,
|
||||
"getAvailableRollback");
|
||||
"getAvailableRollbacks");
|
||||
|
||||
RollbackData data = getRollbackForPackage(packageName);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Note: The rollback for the package ought to be for the currently
|
||||
// installed version, otherwise the rollback data is out of date. In
|
||||
// that rare case, we'll check when we execute the rollback whether
|
||||
// it's out of date or not, so no need to check package versions here.
|
||||
|
||||
for (PackageRollbackInfo info : data.packages) {
|
||||
if (info.getPackageName().equals(packageName)) {
|
||||
// TODO: Once the RollbackInfo API supports info about
|
||||
// dependant packages, add that info here.
|
||||
return new RollbackInfo(data.rollbackId, info);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringParceledListSlice getPackagesWithAvailableRollbacks() {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.MANAGE_ROLLBACKS,
|
||||
"getPackagesWithAvailableRollbacks");
|
||||
|
||||
final Set<String> packageNames = new HashSet<>();
|
||||
synchronized (mLock) {
|
||||
ensureRollbackDataLoadedLocked();
|
||||
List<RollbackInfo> rollbacks = new ArrayList<>();
|
||||
for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
|
||||
RollbackData data = mAvailableRollbacks.get(i);
|
||||
for (PackageRollbackInfo info : data.packages) {
|
||||
packageNames.add(info.getPackageName());
|
||||
}
|
||||
rollbacks.add(new RollbackInfo(data.rollbackId, data.packages));
|
||||
}
|
||||
return new ParceledListSlice<>(rollbacks);
|
||||
}
|
||||
return new StringParceledListSlice(new ArrayList<>(packageNames));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -279,18 +248,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
*/
|
||||
private void executeRollbackInternal(RollbackInfo rollback,
|
||||
String callerPackageName, IntentSender statusReceiver) {
|
||||
String targetPackageName = rollback.targetPackage.getPackageName();
|
||||
Log.i(TAG, "Initiating rollback of " + targetPackageName);
|
||||
Log.i(TAG, "Initiating rollback");
|
||||
|
||||
// Get the latest RollbackData for the target package.
|
||||
final RollbackData data = getRollbackForPackage(targetPackageName);
|
||||
RollbackData data = getRollbackForId(rollback.getRollbackId());
|
||||
if (data == null) {
|
||||
sendFailure(statusReceiver, "No rollback available for package.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.rollbackId != rollback.getRollbackId()) {
|
||||
sendFailure(statusReceiver, "Rollback for package is out of date.");
|
||||
sendFailure(statusReceiver, "Rollback unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -335,14 +297,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
try {
|
||||
PackageInstaller packageInstaller = pm.getPackageInstaller();
|
||||
String installerPackageName = pm.getInstallerPackageName(targetPackageName);
|
||||
if (installerPackageName == null) {
|
||||
sendFailure(statusReceiver, "Cannot find installer package");
|
||||
return;
|
||||
}
|
||||
PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
|
||||
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
|
||||
parentParams.setInstallerPackageName(installerPackageName);
|
||||
parentParams.setAllowDowngrade(true);
|
||||
parentParams.setMultiPackage();
|
||||
int parentSessionId = packageInstaller.createSession(parentParams);
|
||||
@@ -351,6 +307,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
for (PackageRollbackInfo info : data.packages) {
|
||||
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
|
||||
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
|
||||
String installerPackageName = pm.getInstallerPackageName(info.getPackageName());
|
||||
if (installerPackageName == null) {
|
||||
sendFailure(statusReceiver, "Cannot find installer package");
|
||||
return;
|
||||
}
|
||||
params.setInstallerPackageName(installerPackageName);
|
||||
params.setAllowDowngrade(true);
|
||||
int sessionId = packageInstaller.createSession(params);
|
||||
@@ -392,7 +353,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
addRecentlyExecutedRollback(rollback);
|
||||
sendSuccess(statusReceiver);
|
||||
|
||||
Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED);
|
||||
Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
|
||||
|
||||
// TODO: This call emits the warning "Calling a method in the
|
||||
// system process without a qualified user". Fix that.
|
||||
@@ -406,7 +367,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
data.inProgress = true;
|
||||
parentSession.commit(receiver.getIntentSender());
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to roll back " + targetPackageName, e);
|
||||
Log.e(TAG, "Rollback failed", e);
|
||||
sendFailure(statusReceiver, "IOException: " + e.toString());
|
||||
return;
|
||||
}
|
||||
@@ -537,9 +498,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
boolean changed = false;
|
||||
while (iter.hasNext()) {
|
||||
RollbackInfo rollback = iter.next();
|
||||
if (packageName.equals(rollback.targetPackage.getPackageName())) {
|
||||
iter.remove();
|
||||
changed = true;
|
||||
for (PackageRollbackInfo info : rollback.getPackages()) {
|
||||
if (packageName.equals(info.getPackageName())) {
|
||||
iter.remove();
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -935,6 +899,25 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the RollbackData, if any, for an available rollback with the
|
||||
* given rollbackId.
|
||||
*/
|
||||
private RollbackData getRollbackForId(int rollbackId) {
|
||||
synchronized (mLock) {
|
||||
// TODO: Have ensureRollbackDataLoadedLocked return the list of
|
||||
// available rollbacks, to hopefully avoid forgetting to call it?
|
||||
ensureRollbackDataLoadedLocked();
|
||||
for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
|
||||
RollbackData data = mAvailableRollbacks.get(i);
|
||||
if (data.rollbackId == rollbackId) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private int allocateRollbackIdLocked() throws IOException {
|
||||
int n = 0;
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.rollback;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.content.rollback.PackageRollbackInfo;
|
||||
import android.content.rollback.RollbackInfo;
|
||||
import android.content.rollback.RollbackManager;
|
||||
import android.os.Handler;
|
||||
@@ -51,11 +52,14 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
|
||||
@Override
|
||||
public boolean onHealthCheckFailed(String packageName) {
|
||||
RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
|
||||
RollbackInfo rollback = rollbackManager.getAvailableRollback(packageName);
|
||||
if (rollback != null) {
|
||||
// TODO(zezeozue): Only rollback if rollback version == failed package version
|
||||
mHandler.post(() -> executeRollback(rollbackManager, rollback));
|
||||
return true;
|
||||
for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) {
|
||||
for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
|
||||
if (packageName.equals(packageRollback.getPackageName())) {
|
||||
// TODO(zezeozue): Only rollback if rollback version == failed package version
|
||||
mHandler.post(() -> executeRollback(rollbackManager, rollback));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't handle the notification, no rollbacks available
|
||||
return false;
|
||||
@@ -84,7 +88,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
|
||||
}
|
||||
});
|
||||
});
|
||||
manager.executeRollback(rollback, rollbackReceiver.getIntentSender());
|
||||
manager.commitRollback(rollback, rollbackReceiver.getIntentSender());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -114,13 +114,9 @@ class RollbackStore {
|
||||
for (int i = 0; i < array.length(); ++i) {
|
||||
JSONObject element = array.getJSONObject(i);
|
||||
int rollbackId = element.getInt("rollbackId");
|
||||
String packageName = element.getString("packageName");
|
||||
long higherVersionCode = element.getLong("higherVersionCode");
|
||||
long lowerVersionCode = element.getLong("lowerVersionCode");
|
||||
PackageRollbackInfo target = new PackageRollbackInfo(
|
||||
new VersionedPackage(packageName, higherVersionCode),
|
||||
new VersionedPackage(packageName, lowerVersionCode));
|
||||
RollbackInfo rollback = new RollbackInfo(rollbackId, target);
|
||||
List<PackageRollbackInfo> packages = packageRollbackInfosFromJson(
|
||||
element.getJSONArray("packages"));
|
||||
RollbackInfo rollback = new RollbackInfo(rollbackId, packages);
|
||||
recentlyExecutedRollbacks.add(rollback);
|
||||
}
|
||||
} catch (IOException | JSONException e) {
|
||||
@@ -155,18 +151,8 @@ class RollbackStore {
|
||||
void saveAvailableRollback(RollbackData data) throws IOException {
|
||||
try {
|
||||
JSONObject dataJson = new JSONObject();
|
||||
JSONArray packagesJson = new JSONArray();
|
||||
for (PackageRollbackInfo info : data.packages) {
|
||||
JSONObject infoJson = new JSONObject();
|
||||
infoJson.put("packageName", info.getPackageName());
|
||||
infoJson.put("higherVersionCode",
|
||||
info.getVersionRolledBackFrom().getLongVersionCode());
|
||||
infoJson.put("lowerVersionCode",
|
||||
info.getVersionRolledBackTo().getVersionCode());
|
||||
packagesJson.put(infoJson);
|
||||
}
|
||||
dataJson.put("rollbackId", data.rollbackId);
|
||||
dataJson.put("packages", packagesJson);
|
||||
dataJson.put("packages", toJson(data.packages));
|
||||
dataJson.put("timestamp", data.timestamp.toString());
|
||||
|
||||
PrintWriter pw = new PrintWriter(new File(data.backupDir, "rollback.json"));
|
||||
@@ -200,11 +186,7 @@ class RollbackStore {
|
||||
RollbackInfo rollback = recentlyExecutedRollbacks.get(i);
|
||||
JSONObject element = new JSONObject();
|
||||
element.put("rollbackId", rollback.getRollbackId());
|
||||
element.put("packageName", rollback.targetPackage.getPackageName());
|
||||
element.put("higherVersionCode",
|
||||
rollback.targetPackage.getVersionRolledBackFrom().getLongVersionCode());
|
||||
element.put("lowerVersionCode",
|
||||
rollback.targetPackage.getVersionRolledBackTo().getLongVersionCode());
|
||||
element.put("packages", toJson(rollback.getPackages()));
|
||||
array.put(element);
|
||||
}
|
||||
|
||||
@@ -229,18 +211,7 @@ class RollbackStore {
|
||||
|
||||
int rollbackId = dataJson.getInt("rollbackId");
|
||||
RollbackData data = new RollbackData(rollbackId, backupDir);
|
||||
|
||||
JSONArray packagesJson = dataJson.getJSONArray("packages");
|
||||
for (int i = 0; i < packagesJson.length(); ++i) {
|
||||
JSONObject infoJson = packagesJson.getJSONObject(i);
|
||||
String packageName = infoJson.getString("packageName");
|
||||
long higherVersionCode = infoJson.getLong("higherVersionCode");
|
||||
long lowerVersionCode = infoJson.getLong("lowerVersionCode");
|
||||
data.packages.add(new PackageRollbackInfo(
|
||||
new VersionedPackage(packageName, higherVersionCode),
|
||||
new VersionedPackage(packageName, lowerVersionCode)));
|
||||
}
|
||||
|
||||
data.packages.addAll(packageRollbackInfosFromJson(dataJson.getJSONArray("packages")));
|
||||
data.timestamp = Instant.parse(dataJson.getString("timestamp"));
|
||||
return data;
|
||||
} catch (JSONException | DateTimeParseException e) {
|
||||
@@ -248,6 +219,40 @@ class RollbackStore {
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
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));
|
||||
}
|
||||
|
||||
private JSONArray toJson(List<PackageRollbackInfo> infos) throws JSONException {
|
||||
JSONArray json = new JSONArray();
|
||||
for (PackageRollbackInfo info : infos) {
|
||||
json.put(toJson(info));
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
private List<PackageRollbackInfo> packageRollbackInfosFromJson(JSONArray json)
|
||||
throws JSONException {
|
||||
List<PackageRollbackInfo> infos = new ArrayList<>();
|
||||
for (int i = 0; i < json.length(); ++i) {
|
||||
infos.add(packageRollbackInfoFromJson(json.getJSONObject(i)));
|
||||
}
|
||||
return infos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a file completely.
|
||||
* If the file is a directory, its contents are deleted as well.
|
||||
|
||||
@@ -29,7 +29,7 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A broadcast receiver that can be used to get
|
||||
* ACTION_PACKAGE_ROLLBACK_EXECUTED broadcasts.
|
||||
* ACTION_ROLLBACK_COMMITTED broadcasts.
|
||||
*/
|
||||
class RollbackBroadcastReceiver extends BroadcastReceiver {
|
||||
|
||||
@@ -43,7 +43,7 @@ class RollbackBroadcastReceiver extends BroadcastReceiver {
|
||||
*/
|
||||
RollbackBroadcastReceiver() {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED);
|
||||
filter.addAction(Intent.ACTION_ROLLBACK_COMMITTED);
|
||||
InstrumentationRegistry.getContext().registerReceiver(this, filter);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,8 @@ import android.support.test.InstrumentationRegistry;
|
||||
import android.util.Log;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Ignore;
|
||||
@@ -43,6 +40,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -86,8 +84,8 @@ public class RollbackTest {
|
||||
Manifest.permission.DELETE_PACKAGES,
|
||||
Manifest.permission.MANAGE_ROLLBACKS);
|
||||
|
||||
// Register a broadcast receiver for notification when the rollback is
|
||||
// done executing.
|
||||
// Register a broadcast receiver for notification when the
|
||||
// rollback has been committed.
|
||||
RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver();
|
||||
RollbackManager rm = RollbackTestUtils.getRollbackManager();
|
||||
|
||||
@@ -99,12 +97,11 @@ public class RollbackTest {
|
||||
// uninstalled and when rollback manager deletes the rollback. Fix it
|
||||
// so that's not the case!
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
|
||||
if (TEST_APP_A.equals(info.targetPackage.getPackageName())) {
|
||||
Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect.");
|
||||
Thread.sleep(1000);
|
||||
break;
|
||||
}
|
||||
RollbackInfo rollback = getUniqueRollbackInfoForPackage(
|
||||
rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
|
||||
if (rollback != null) {
|
||||
Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect.");
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,13 +110,11 @@ public class RollbackTest {
|
||||
// between when the app is uninstalled and when the previously
|
||||
// available rollback, if any, is removed.
|
||||
Thread.sleep(1000);
|
||||
assertNull(rm.getAvailableRollback(TEST_APP_A));
|
||||
assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
|
||||
assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A));
|
||||
|
||||
// There should be no recently executed rollbacks for this package.
|
||||
for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
|
||||
assertNotEquals(TEST_APP_A, info.targetPackage.getPackageName());
|
||||
}
|
||||
// There should be no recently committed rollbacks for this package.
|
||||
assertNull(getUniqueRollbackInfoForPackage(
|
||||
rm.getRecentlyCommittedRollbacks(), TEST_APP_A));
|
||||
|
||||
// Install v1 of the app (without rollbacks enabled).
|
||||
RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
|
||||
@@ -134,10 +129,9 @@ public class RollbackTest {
|
||||
// between when the app is installed and when the rollback
|
||||
// is made available.
|
||||
Thread.sleep(1000);
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
|
||||
RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollback);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.targetPackage);
|
||||
RollbackInfo rollback = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_A);
|
||||
assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
|
||||
|
||||
// We should not have received any rollback requests yet.
|
||||
// TODO: Possibly flaky if, by chance, some other app on device
|
||||
@@ -156,15 +150,9 @@ public class RollbackTest {
|
||||
assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
|
||||
|
||||
// Verify the recent rollback has been recorded.
|
||||
rollback = null;
|
||||
for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
|
||||
if (TEST_APP_A.equals(r.targetPackage.getPackageName())) {
|
||||
assertNull(rollback);
|
||||
rollback = r;
|
||||
}
|
||||
}
|
||||
assertNotNull(rollback);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.targetPackage);
|
||||
rollback = getUniqueRollbackInfoForPackage(
|
||||
rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
|
||||
assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
|
||||
|
||||
broadcastReceiver.unregister();
|
||||
context.unregisterReceiver(enableRollbackReceiver);
|
||||
@@ -202,28 +190,25 @@ public class RollbackTest {
|
||||
// is made available.
|
||||
Thread.sleep(1000);
|
||||
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
|
||||
RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollbackA);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA.targetPackage);
|
||||
RollbackInfo rollbackA = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_A);
|
||||
assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA);
|
||||
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B));
|
||||
RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B);
|
||||
assertNotNull(rollbackB);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB.targetPackage);
|
||||
RollbackInfo rollbackB = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_B);
|
||||
assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB);
|
||||
|
||||
// Reload the persisted data.
|
||||
rm.reloadPersistedData();
|
||||
|
||||
// The apps should still be available for rollback.
|
||||
rollbackA = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollbackA);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA.targetPackage);
|
||||
rollbackA = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_A);
|
||||
assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA);
|
||||
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B));
|
||||
rollbackB = rm.getAvailableRollback(TEST_APP_B);
|
||||
assertNotNull(rollbackB);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB.targetPackage);
|
||||
rollbackB = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_B);
|
||||
assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB);
|
||||
|
||||
// Rollback of B should not rollback A
|
||||
RollbackTestUtils.rollback(rollbackB);
|
||||
@@ -264,28 +249,23 @@ public class RollbackTest {
|
||||
// is made available.
|
||||
Thread.sleep(1000);
|
||||
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
|
||||
RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollbackA);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA.targetPackage);
|
||||
RollbackInfo rollbackA = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_A);
|
||||
assertRollbackInfoForAandB(rollbackA);
|
||||
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B));
|
||||
RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B);
|
||||
assertNotNull(rollbackB);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB.targetPackage);
|
||||
RollbackInfo rollbackB = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_B);
|
||||
assertRollbackInfoForAandB(rollbackB);
|
||||
|
||||
// Reload the persisted data.
|
||||
rm.reloadPersistedData();
|
||||
|
||||
// The apps should still be available for rollback.
|
||||
rollbackA = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollbackA);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA.targetPackage);
|
||||
rollbackA = getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A);
|
||||
assertRollbackInfoForAandB(rollbackA);
|
||||
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B));
|
||||
rollbackB = rm.getAvailableRollback(TEST_APP_B);
|
||||
assertNotNull(rollbackB);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB.targetPackage);
|
||||
rollbackB = getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_B);
|
||||
assertRollbackInfoForAandB(rollbackB);
|
||||
|
||||
// Rollback of B should rollback A as well
|
||||
RollbackTestUtils.rollback(rollbackB);
|
||||
@@ -297,10 +277,10 @@ public class RollbackTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that recently executed rollback data is properly persisted.
|
||||
* Test that recently committed rollback data is properly persisted.
|
||||
*/
|
||||
@Test
|
||||
public void testRecentlyExecutedRollbackPersistence() throws Exception {
|
||||
public void testRecentlyCommittedRollbackPersistence() throws Exception {
|
||||
try {
|
||||
RollbackTestUtils.adoptShellPermissionIdentity(
|
||||
Manifest.permission.INSTALL_PACKAGES,
|
||||
@@ -319,37 +299,27 @@ public class RollbackTest {
|
||||
// between when the app is installed and when the rollback
|
||||
// is made available.
|
||||
Thread.sleep(1000);
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
|
||||
RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
|
||||
RollbackInfo rollback = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_A);
|
||||
|
||||
// Roll back the app.
|
||||
RollbackTestUtils.rollback(rollback);
|
||||
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
|
||||
|
||||
// Verify the recent rollback has been recorded.
|
||||
rollback = null;
|
||||
for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
|
||||
if (TEST_APP_A.equals(r.targetPackage.getPackageName())) {
|
||||
assertNull(rollback);
|
||||
rollback = r;
|
||||
}
|
||||
}
|
||||
rollback = getUniqueRollbackInfoForPackage(
|
||||
rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
|
||||
assertNotNull(rollback);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.targetPackage);
|
||||
assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
|
||||
|
||||
// Reload the persisted data.
|
||||
rm.reloadPersistedData();
|
||||
|
||||
// Verify the recent rollback is still recorded.
|
||||
rollback = null;
|
||||
for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
|
||||
if (TEST_APP_A.equals(r.targetPackage.getPackageName())) {
|
||||
assertNull(rollback);
|
||||
rollback = r;
|
||||
}
|
||||
}
|
||||
rollback = getUniqueRollbackInfoForPackage(
|
||||
rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
|
||||
assertNotNull(rollback);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.targetPackage);
|
||||
assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
|
||||
} finally {
|
||||
RollbackTestUtils.dropShellPermissionIdentity();
|
||||
}
|
||||
@@ -378,17 +348,16 @@ public class RollbackTest {
|
||||
// between when the app is installed and when the rollback
|
||||
// is made available.
|
||||
Thread.sleep(1000);
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
|
||||
RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollback);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.targetPackage);
|
||||
RollbackInfo rollback = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_A);
|
||||
assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
|
||||
|
||||
// Expire the rollback.
|
||||
rm.expireRollbackForPackage(TEST_APP_A);
|
||||
|
||||
// The rollback should no longer be available.
|
||||
assertNull(rm.getAvailableRollback(TEST_APP_A));
|
||||
assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
|
||||
assertNull(getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_A));
|
||||
} finally {
|
||||
RollbackTestUtils.dropShellPermissionIdentity();
|
||||
}
|
||||
@@ -459,7 +428,8 @@ public class RollbackTest {
|
||||
processUserData(TEST_APP_A);
|
||||
|
||||
RollbackManager rm = RollbackTestUtils.getRollbackManager();
|
||||
RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
|
||||
RollbackInfo rollback = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_A);
|
||||
RollbackTestUtils.rollback(rollback);
|
||||
processUserData(TEST_APP_A);
|
||||
} finally {
|
||||
@@ -469,12 +439,12 @@ public class RollbackTest {
|
||||
|
||||
/**
|
||||
* Test restrictions on rollback broadcast sender.
|
||||
* A random app should not be able to send a PACKAGE_ROLLBACK_EXECUTED broadcast.
|
||||
* A random app should not be able to send a ROLLBACK_COMMITTED broadcast.
|
||||
*/
|
||||
@Test
|
||||
public void testRollbackBroadcastRestrictions() throws Exception {
|
||||
RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver();
|
||||
Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED);
|
||||
Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
|
||||
try {
|
||||
InstrumentationRegistry.getContext().sendBroadcast(broadcast);
|
||||
fail("Succeeded in sending restricted broadcast from app context.");
|
||||
@@ -516,18 +486,18 @@ public class RollbackTest {
|
||||
assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
|
||||
|
||||
// Both test apps should now be available for rollback, and the
|
||||
// targetPackage returned for rollback should be correct.
|
||||
// RollbackInfo returned for the rollbacks should be correct.
|
||||
// TODO: See if there is a way to remove this race condition
|
||||
// between when the app is installed and when the rollback
|
||||
// is made available.
|
||||
Thread.sleep(1000);
|
||||
RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollbackA);
|
||||
assertEquals(TEST_APP_A, rollbackA.targetPackage.getPackageName());
|
||||
RollbackInfo rollbackA = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_A);
|
||||
assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA);
|
||||
|
||||
RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B);
|
||||
assertNotNull(rollbackB);
|
||||
assertEquals(TEST_APP_B, rollbackB.targetPackage.getPackageName());
|
||||
RollbackInfo rollbackB = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_B);
|
||||
assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB);
|
||||
|
||||
// Executing rollback should roll back the correct package.
|
||||
RollbackTestUtils.rollback(rollbackA);
|
||||
@@ -558,21 +528,14 @@ public class RollbackTest {
|
||||
RollbackManager rm = RollbackTestUtils.getRollbackManager();
|
||||
|
||||
try {
|
||||
rm.getAvailableRollback(TEST_APP_A);
|
||||
rm.getAvailableRollbacks();
|
||||
fail("expected SecurityException");
|
||||
} catch (SecurityException e) {
|
||||
// Expected.
|
||||
}
|
||||
|
||||
try {
|
||||
rm.getPackagesWithAvailableRollbacks();
|
||||
fail("expected SecurityException");
|
||||
} catch (SecurityException e) {
|
||||
// Expected.
|
||||
}
|
||||
|
||||
try {
|
||||
rm.getRecentlyExecutedRollbacks();
|
||||
rm.getRecentlyCommittedRollbacks();
|
||||
fail("expected SecurityException");
|
||||
} catch (SecurityException e) {
|
||||
// Expected.
|
||||
@@ -581,7 +544,7 @@ public class RollbackTest {
|
||||
try {
|
||||
// TODO: What if the implementation checks arguments for non-null
|
||||
// first? Then this test isn't valid.
|
||||
rm.executeRollback(null, null);
|
||||
rm.commitRollback(null, null);
|
||||
fail("expected SecurityException");
|
||||
} catch (SecurityException e) {
|
||||
// Expected.
|
||||
@@ -629,12 +592,9 @@ public class RollbackTest {
|
||||
assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
|
||||
|
||||
// TEST_APP_A should now be available for rollback.
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
|
||||
RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollback);
|
||||
|
||||
// TODO: Test the dependent apps for rollback are correct once we
|
||||
// support that in the RollbackInfo API.
|
||||
RollbackInfo rollback = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_A);
|
||||
assertRollbackInfoForAandB(rollback);
|
||||
|
||||
// Rollback the app. It should cause both test apps to be rolled
|
||||
// back.
|
||||
@@ -642,14 +602,16 @@ public class RollbackTest {
|
||||
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
|
||||
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
|
||||
|
||||
// We should not see a recent rollback listed for TEST_APP_B
|
||||
for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
|
||||
assertNotEquals(TEST_APP_B, r.targetPackage.getPackageName());
|
||||
}
|
||||
// We should see recent rollbacks listed for both A and B.
|
||||
Thread.sleep(1000);
|
||||
RollbackInfo rollbackA = getUniqueRollbackInfoForPackage(
|
||||
rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
|
||||
|
||||
// TODO: Test the listed dependent apps for the recently executed
|
||||
// rollback are correct once we support that in the RollbackInfo
|
||||
// API.
|
||||
RollbackInfo rollbackB = getUniqueRollbackInfoForPackage(
|
||||
rm.getRecentlyCommittedRollbacks(), TEST_APP_B);
|
||||
assertRollbackInfoForAandB(rollbackB);
|
||||
|
||||
assertEquals(rollbackA.getRollbackId(), rollbackB.getRollbackId());
|
||||
} finally {
|
||||
RollbackTestUtils.dropShellPermissionIdentity();
|
||||
}
|
||||
@@ -697,13 +659,13 @@ public class RollbackTest {
|
||||
// between when the app is installed and when the rollback
|
||||
// is made available.
|
||||
Thread.sleep(1000);
|
||||
RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollbackA);
|
||||
assertEquals(TEST_APP_A, rollbackA.targetPackage.getPackageName());
|
||||
RollbackInfo rollbackA = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_A);
|
||||
assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA);
|
||||
|
||||
RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B);
|
||||
assertNotNull(rollbackB);
|
||||
assertEquals(TEST_APP_B, rollbackB.targetPackage.getPackageName());
|
||||
RollbackInfo rollbackB = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_B);
|
||||
assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB);
|
||||
|
||||
// Start apps PackageWatchdog#TRIGGER_FAILURE_COUNT times so TEST_APP_A crashes
|
||||
for (int i = 0; i < 5; i++) {
|
||||
@@ -724,4 +686,47 @@ public class RollbackTest {
|
||||
RollbackTestUtils.dropShellPermissionIdentity();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to test the value of a RollbackInfo with single package
|
||||
private void assertRollbackInfoEquals(String packageName,
|
||||
long versionRolledBackFrom, long versionRolledBackTo,
|
||||
RollbackInfo info) {
|
||||
assertNotNull(info);
|
||||
assertEquals(1, info.getPackages().size());
|
||||
assertPackageRollbackInfoEquals(packageName, versionRolledBackFrom, versionRolledBackTo,
|
||||
info.getPackages().get(0));
|
||||
}
|
||||
|
||||
// Helper function to test that the given rollback info is a rollback for
|
||||
// the atomic set {A2, B2} -> {A1, B1}.
|
||||
private void assertRollbackInfoForAandB(RollbackInfo rollback) {
|
||||
assertNotNull(rollback);
|
||||
assertEquals(2, rollback.getPackages().size());
|
||||
if (TEST_APP_A.equals(rollback.getPackages().get(0).getPackageName())) {
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.getPackages().get(0));
|
||||
assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollback.getPackages().get(1));
|
||||
} else {
|
||||
assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollback.getPackages().get(0));
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.getPackages().get(1));
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to return the RollbackInfo with a given package in the
|
||||
// list of rollbacks. Throws an assertion failure if there is more than
|
||||
// one such rollback info. Returns null if there are no such rollback
|
||||
// infos.
|
||||
private RollbackInfo getUniqueRollbackInfoForPackage(List<RollbackInfo> rollbacks,
|
||||
String packageName) {
|
||||
RollbackInfo found = null;
|
||||
for (RollbackInfo rollback : rollbacks) {
|
||||
for (PackageRollbackInfo info : rollback.getPackages()) {
|
||||
if (packageName.equals(info.getPackageName())) {
|
||||
assertNull(found);
|
||||
found = rollback;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,12 +90,12 @@ class RollbackTestUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given rollback.
|
||||
* Commit the given rollback.
|
||||
* @throws AssertionError if the rollback fails.
|
||||
*/
|
||||
static void rollback(RollbackInfo rollback) throws InterruptedException {
|
||||
RollbackManager rm = getRollbackManager();
|
||||
rm.executeRollback(rollback, LocalIntentSender.getIntentSender());
|
||||
rm.commitRollback(rollback, LocalIntentSender.getIntentSender());
|
||||
assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user