Merge changes Ic8db00b6,I68a91e07,I860ad443
* changes: Remove package name from ROLLBACK_EXECUTED broadcast. Use VersionedPackage in PackageRollbackInfo. Assign a rollbackId to all rollbacks.
This commit is contained in:
committed by
Android (Google) Code Review
commit
3a02bebb34
@@ -1672,22 +1672,17 @@ package android.content.pm.permission {
|
||||
package android.content.rollback {
|
||||
|
||||
public final class PackageRollbackInfo implements android.os.Parcelable {
|
||||
ctor public PackageRollbackInfo(String, android.content.rollback.PackageRollbackInfo.PackageVersion, android.content.rollback.PackageRollbackInfo.PackageVersion);
|
||||
method public int describeContents();
|
||||
method public String getPackageName();
|
||||
method public android.content.pm.VersionedPackage getVersionRolledBackFrom();
|
||||
method public android.content.pm.VersionedPackage getVersionRolledBackTo();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.content.rollback.PackageRollbackInfo> CREATOR;
|
||||
field public final android.content.rollback.PackageRollbackInfo.PackageVersion higherVersion;
|
||||
field public final android.content.rollback.PackageRollbackInfo.PackageVersion lowerVersion;
|
||||
field public final String packageName;
|
||||
}
|
||||
|
||||
public static class PackageRollbackInfo.PackageVersion {
|
||||
ctor public PackageRollbackInfo.PackageVersion(long);
|
||||
field public final long versionCode;
|
||||
}
|
||||
|
||||
public final class RollbackInfo implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
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;
|
||||
|
||||
@@ -2377,7 +2377,6 @@ public class Intent implements Parcelable, Cloneable {
|
||||
/**
|
||||
* Broadcast Action: An existing version of an application package has been
|
||||
* rolled back to a previous version.
|
||||
* The data contains the name of the package.
|
||||
*
|
||||
* <p class="note">This is a protected intent that can only be sent
|
||||
* by the system.
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
package android.content.rollback;
|
||||
|
||||
import android.annotation.SystemApi;
|
||||
import android.content.pm.VersionedPackage;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Information about a rollback available for a particular package.
|
||||
*
|
||||
@@ -29,59 +28,41 @@ import java.util.Objects;
|
||||
*/
|
||||
@SystemApi
|
||||
public final class PackageRollbackInfo implements Parcelable {
|
||||
/**
|
||||
* The name of a package being rolled back.
|
||||
*/
|
||||
public final String packageName;
|
||||
|
||||
private final VersionedPackage mVersionRolledBackFrom;
|
||||
private final VersionedPackage mVersionRolledBackTo;
|
||||
|
||||
/**
|
||||
* The version the package was rolled back from.
|
||||
* Returns the name of the package to roll back from.
|
||||
*/
|
||||
public final PackageVersion higherVersion;
|
||||
|
||||
/**
|
||||
* The version the package was rolled back to.
|
||||
*/
|
||||
public final PackageVersion lowerVersion;
|
||||
|
||||
/**
|
||||
* Represents a version of a package.
|
||||
*/
|
||||
public static class PackageVersion {
|
||||
public final long versionCode;
|
||||
|
||||
// TODO(b/120200473): Include apk sha or some other way to distinguish
|
||||
// between two different apks with the same version code.
|
||||
public PackageVersion(long versionCode) {
|
||||
this.versionCode = versionCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof PackageVersion) {
|
||||
PackageVersion otherVersion = (PackageVersion) other;
|
||||
return versionCode == otherVersion.versionCode;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(versionCode);
|
||||
}
|
||||
public String getPackageName() {
|
||||
return mVersionRolledBackFrom.getPackageName();
|
||||
}
|
||||
|
||||
public PackageRollbackInfo(String packageName,
|
||||
PackageVersion higherVersion, PackageVersion lowerVersion) {
|
||||
this.packageName = packageName;
|
||||
this.higherVersion = higherVersion;
|
||||
this.lowerVersion = lowerVersion;
|
||||
/**
|
||||
* Returns the version of the package rolled back from.
|
||||
*/
|
||||
public VersionedPackage getVersionRolledBackFrom() {
|
||||
return mVersionRolledBackFrom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the package rolled back to.
|
||||
*/
|
||||
public VersionedPackage getVersionRolledBackTo() {
|
||||
return mVersionRolledBackTo;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public PackageRollbackInfo(VersionedPackage packageRolledBackFrom,
|
||||
VersionedPackage packageRolledBackTo) {
|
||||
this.mVersionRolledBackFrom = packageRolledBackFrom;
|
||||
this.mVersionRolledBackTo = packageRolledBackTo;
|
||||
}
|
||||
|
||||
private PackageRollbackInfo(Parcel in) {
|
||||
this.packageName = in.readString();
|
||||
this.higherVersion = new PackageVersion(in.readLong());
|
||||
this.lowerVersion = new PackageVersion(in.readLong());
|
||||
this.mVersionRolledBackFrom = VersionedPackage.CREATOR.createFromParcel(in);
|
||||
this.mVersionRolledBackTo = VersionedPackage.CREATOR.createFromParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,9 +72,8 @@ public final class PackageRollbackInfo implements Parcelable {
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeString(packageName);
|
||||
out.writeLong(higherVersion.versionCode);
|
||||
out.writeLong(lowerVersion.versionCode);
|
||||
mVersionRolledBackFrom.writeToParcel(out, flags);
|
||||
mVersionRolledBackTo.writeToParcel(out, flags);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<PackageRollbackInfo> CREATOR =
|
||||
|
||||
@@ -29,6 +29,11 @@ import android.os.Parcelable;
|
||||
@SystemApi
|
||||
public final class RollbackInfo implements Parcelable {
|
||||
|
||||
/**
|
||||
* A unique identifier for the rollback.
|
||||
*/
|
||||
private final int mRollbackId;
|
||||
|
||||
/**
|
||||
* The package that needs to be rolled back.
|
||||
*/
|
||||
@@ -40,12 +45,21 @@ public final class RollbackInfo implements Parcelable {
|
||||
// staged installs is supported.
|
||||
|
||||
/** @hide */
|
||||
public RollbackInfo(PackageRollbackInfo targetPackage) {
|
||||
public RollbackInfo(int rollbackId, PackageRollbackInfo targetPackage) {
|
||||
this.mRollbackId = rollbackId;
|
||||
this.targetPackage = targetPackage;
|
||||
}
|
||||
|
||||
private RollbackInfo(Parcel in) {
|
||||
this.targetPackage = PackageRollbackInfo.CREATOR.createFromParcel(in);
|
||||
mRollbackId = in.readInt();
|
||||
targetPackage = PackageRollbackInfo.CREATOR.createFromParcel(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a unique identifier for this rollback.
|
||||
*/
|
||||
public int getRollbackId() {
|
||||
return mRollbackId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,6 +69,7 @@ public final class RollbackInfo implements Parcelable {
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeInt(mRollbackId);
|
||||
targetPackage.writeToParcel(out, flags);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,11 @@ import java.util.List;
|
||||
* packages.
|
||||
*/
|
||||
class RollbackData {
|
||||
/**
|
||||
* A unique identifier for this rollback.
|
||||
*/
|
||||
public final int rollbackId;
|
||||
|
||||
/**
|
||||
* The per-package rollback information.
|
||||
*/
|
||||
@@ -44,7 +49,8 @@ class RollbackData {
|
||||
*/
|
||||
public Instant timestamp;
|
||||
|
||||
RollbackData(File backupDir) {
|
||||
RollbackData(int rollbackId, File backupDir) {
|
||||
this.rollbackId = rollbackId;
|
||||
this.backupDir = backupDir;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.server.rollback;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
@@ -32,10 +31,10 @@ 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;
|
||||
import android.content.rollback.RollbackInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
@@ -46,6 +45,7 @@ import android.os.ParcelFileDescriptor;
|
||||
import android.os.Process;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.util.Log;
|
||||
import android.util.SparseBooleanArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.server.LocalServices;
|
||||
@@ -55,14 +55,16 @@ import com.android.server.pm.PackageManagerServiceUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -82,6 +84,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
// mLock is held when they are called.
|
||||
private final Object mLock = new Object();
|
||||
|
||||
// Used for generating rollback IDs.
|
||||
private final Random mRandom = new SecureRandom();
|
||||
|
||||
// Set of allocated rollback ids
|
||||
@GuardedBy("mLock")
|
||||
private final SparseBooleanArray mAllocatedRollbackIds = new SparseBooleanArray();
|
||||
|
||||
// Package rollback data for rollback-enabled installs that have not yet
|
||||
// been committed. Maps from sessionId to rollback data.
|
||||
@GuardedBy("mLock")
|
||||
@@ -209,10 +218,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
// it's out of date or not, so no need to check package versions here.
|
||||
|
||||
for (PackageRollbackInfo info : data.packages) {
|
||||
if (info.packageName.equals(packageName)) {
|
||||
if (info.getPackageName().equals(packageName)) {
|
||||
// TODO: Once the RollbackInfo API supports info about
|
||||
// dependant packages, add that info here.
|
||||
return new RollbackInfo(info);
|
||||
return new RollbackInfo(data.rollbackId, info);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -230,7 +239,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
|
||||
RollbackData data = mAvailableRollbacks.get(i);
|
||||
for (PackageRollbackInfo info : data.packages) {
|
||||
packageNames.add(info.packageName);
|
||||
packageNames.add(info.getPackageName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,7 +281,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
*/
|
||||
private void executeRollbackInternal(RollbackInfo rollback,
|
||||
String callerPackageName, IntentSender statusReceiver) {
|
||||
String targetPackageName = rollback.targetPackage.packageName;
|
||||
String targetPackageName = rollback.targetPackage.getPackageName();
|
||||
Log.i(TAG, "Initiating rollback of " + targetPackageName);
|
||||
|
||||
// Get the latest RollbackData for the target package.
|
||||
@@ -282,15 +291,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify the latest rollback matches the version requested.
|
||||
// TODO: Check dependant packages too once RollbackInfo includes that
|
||||
// information.
|
||||
for (PackageRollbackInfo info : data.packages) {
|
||||
if (info.packageName.equals(targetPackageName)
|
||||
&& !rollback.targetPackage.higherVersion.equals(info.higherVersion)) {
|
||||
sendFailure(statusReceiver, "Rollback is out of date.");
|
||||
return;
|
||||
}
|
||||
if (data.rollbackId != rollback.getRollbackId()) {
|
||||
sendFailure(statusReceiver, "Rollback for package is out of date");
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify the RollbackData is up to date with what's installed on
|
||||
@@ -302,15 +305,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
// Figure out how to ensure we don't commit the rollback if
|
||||
// roll forward happens at the same time.
|
||||
for (PackageRollbackInfo info : data.packages) {
|
||||
PackageRollbackInfo.PackageVersion installedVersion =
|
||||
getInstalledPackageVersion(info.packageName);
|
||||
VersionedPackage installedVersion = getInstalledPackageVersion(info.getPackageName());
|
||||
if (installedVersion == null) {
|
||||
// TODO: Test this case
|
||||
sendFailure(statusReceiver, "Package to roll back is not installed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!info.higherVersion.equals(installedVersion)) {
|
||||
if (!packageVersionsEqual(info.getVersionRolledBackFrom(), installedVersion)) {
|
||||
// TODO: Test this case
|
||||
sendFailure(statusReceiver, "Package version to roll back not installed.");
|
||||
return;
|
||||
@@ -353,7 +355,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
|
||||
// TODO: Will it always be called "base.apk"? What about splits?
|
||||
// What about apex?
|
||||
File packageDir = new File(data.backupDir, info.packageName);
|
||||
File packageDir = new File(data.backupDir, info.getPackageName());
|
||||
File baseApk = new File(packageDir, "base.apk");
|
||||
try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(baseApk,
|
||||
ParcelFileDescriptor.MODE_READ_ONLY)) {
|
||||
@@ -380,12 +382,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
addRecentlyExecutedRollback(rollback);
|
||||
sendSuccess(statusReceiver);
|
||||
|
||||
Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED,
|
||||
Uri.fromParts("package", targetPackageName,
|
||||
Manifest.permission.MANAGE_ROLLBACKS));
|
||||
Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED);
|
||||
|
||||
// TODO: This call emits the warning "Calling a method in the
|
||||
// system process without a qualified user". Fix that.
|
||||
// TODO: Limit this to receivers holding the
|
||||
// MANAGE_ROLLBACKS permission?
|
||||
mContext.sendBroadcast(broadcast);
|
||||
}
|
||||
);
|
||||
@@ -427,7 +429,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
while (iter.hasNext()) {
|
||||
RollbackData data = iter.next();
|
||||
for (PackageRollbackInfo info : data.packages) {
|
||||
if (info.packageName.equals(packageName)) {
|
||||
if (info.getPackageName().equals(packageName)) {
|
||||
iter.remove();
|
||||
mRollbackStore.deleteAvailableRollback(data);
|
||||
break;
|
||||
@@ -469,7 +471,15 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
@GuardedBy("mLock")
|
||||
private void loadAllRollbackDataLocked() {
|
||||
mAvailableRollbacks = mRollbackStore.loadAvailableRollbacks();
|
||||
for (RollbackData data : mAvailableRollbacks) {
|
||||
mAllocatedRollbackIds.put(data.rollbackId, true);
|
||||
}
|
||||
|
||||
mRecentlyExecutedRollbacks = mRollbackStore.loadRecentlyExecutedRollbacks();
|
||||
for (RollbackInfo info : mRecentlyExecutedRollbacks) {
|
||||
mAllocatedRollbackIds.put(info.getRollbackId(), true);
|
||||
}
|
||||
|
||||
scheduleExpiration(0);
|
||||
}
|
||||
|
||||
@@ -481,8 +491,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
private void onPackageReplaced(String packageName) {
|
||||
// TODO: Could this end up incorrectly deleting a rollback for a
|
||||
// package that is about to be installed?
|
||||
PackageRollbackInfo.PackageVersion installedVersion =
|
||||
getInstalledPackageVersion(packageName);
|
||||
VersionedPackage installedVersion = getInstalledPackageVersion(packageName);
|
||||
|
||||
synchronized (mLock) {
|
||||
ensureRollbackDataLoadedLocked();
|
||||
@@ -490,8 +499,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
while (iter.hasNext()) {
|
||||
RollbackData data = iter.next();
|
||||
for (PackageRollbackInfo info : data.packages) {
|
||||
if (info.packageName.equals(packageName)
|
||||
&& !info.higherVersion.equals(installedVersion)) {
|
||||
if (info.getPackageName().equals(packageName)
|
||||
&& !packageVersionsEqual(
|
||||
info.getVersionRolledBackFrom(),
|
||||
installedVersion)) {
|
||||
iter.remove();
|
||||
mRollbackStore.deleteAvailableRollback(data);
|
||||
break;
|
||||
@@ -514,7 +525,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
boolean changed = false;
|
||||
while (iter.hasNext()) {
|
||||
RollbackInfo rollback = iter.next();
|
||||
if (packageName.equals(rollback.targetPackage.packageName)) {
|
||||
if (packageName.equals(rollback.targetPackage.getPackageName())) {
|
||||
iter.remove();
|
||||
changed = true;
|
||||
}
|
||||
@@ -689,8 +700,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
return false;
|
||||
}
|
||||
|
||||
PackageRollbackInfo.PackageVersion newVersion =
|
||||
new PackageRollbackInfo.PackageVersion(newPackage.versionCode);
|
||||
VersionedPackage newVersion = new VersionedPackage(packageName, newPackage.versionCode);
|
||||
|
||||
// Get information about the currently installed package.
|
||||
PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
|
||||
@@ -701,8 +711,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
Log.e(TAG, packageName + " is not installed");
|
||||
return false;
|
||||
}
|
||||
PackageRollbackInfo.PackageVersion installedVersion =
|
||||
new PackageRollbackInfo.PackageVersion(installedPackage.getLongVersionCode());
|
||||
VersionedPackage installedVersion = new VersionedPackage(packageName,
|
||||
installedPackage.getLongVersionCode());
|
||||
|
||||
for (int user : installedUsers) {
|
||||
final int storageFlags;
|
||||
@@ -723,8 +733,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
PackageRollbackInfo info = new PackageRollbackInfo(
|
||||
packageName, newVersion, installedVersion);
|
||||
PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion);
|
||||
|
||||
RollbackData data;
|
||||
try {
|
||||
@@ -732,7 +741,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
mChildSessions.put(childSessionId, parentSessionId);
|
||||
data = mPendingRollbacks.get(parentSessionId);
|
||||
if (data == null) {
|
||||
data = mRollbackStore.createAvailableRollback();
|
||||
int rollbackId = allocateRollbackIdLocked();
|
||||
data = mRollbackStore.createAvailableRollback(rollbackId);
|
||||
mPendingRollbacks.put(parentSessionId, data);
|
||||
}
|
||||
data.packages.add(info);
|
||||
@@ -819,7 +829,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
* Gets the version of the package currently installed.
|
||||
* Returns null if the package is not currently installed.
|
||||
*/
|
||||
private PackageRollbackInfo.PackageVersion getInstalledPackageVersion(String packageName) {
|
||||
private VersionedPackage getInstalledPackageVersion(String packageName) {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
PackageInfo pkgInfo = null;
|
||||
try {
|
||||
@@ -828,7 +838,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PackageRollbackInfo.PackageVersion(pkgInfo.getLongVersionCode());
|
||||
return new VersionedPackage(packageName, pkgInfo.getLongVersionCode());
|
||||
}
|
||||
|
||||
private boolean packageVersionsEqual(VersionedPackage a, VersionedPackage b) {
|
||||
return a.getPackageName().equals(b.getPackageName())
|
||||
&& a.getLongVersionCode() == b.getLongVersionCode();
|
||||
}
|
||||
|
||||
private class SessionCallback extends PackageInstaller.SessionCallback {
|
||||
@@ -904,7 +919,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
|
||||
RollbackData data = mAvailableRollbacks.get(i);
|
||||
for (PackageRollbackInfo info : data.packages) {
|
||||
if (info.packageName.equals(packageName)) {
|
||||
if (info.getPackageName().equals(packageName)) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -912,4 +927,19 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private int allocateRollbackIdLocked() throws IOException {
|
||||
int n = 0;
|
||||
int rollbackId;
|
||||
do {
|
||||
rollbackId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
|
||||
if (!mAllocatedRollbackIds.get(rollbackId, false)) {
|
||||
mAllocatedRollbackIds.put(rollbackId, true);
|
||||
return rollbackId;
|
||||
}
|
||||
} while (n++ < 32);
|
||||
|
||||
throw new IOException("Failed to allocate rollback ID");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.rollback;
|
||||
|
||||
import android.content.pm.VersionedPackage;
|
||||
import android.content.rollback.PackageRollbackInfo;
|
||||
import android.content.rollback.RollbackInfo;
|
||||
import android.util.Log;
|
||||
@@ -29,7 +30,6 @@ import org.json.JSONObject;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
@@ -58,7 +58,7 @@ class RollbackStore {
|
||||
// base.apk
|
||||
// recently_executed.json
|
||||
//
|
||||
// * XXX, YYY are random strings from Files.createTempDirectory
|
||||
// * XXX, YYY are the rollbackIds for the corresponding rollbacks.
|
||||
// * rollback.json contains all relevant metadata for the rollback. This
|
||||
// file is not written until the rollback is made available.
|
||||
//
|
||||
@@ -113,13 +113,14 @@ class RollbackStore {
|
||||
JSONArray array = object.getJSONArray("recentlyExecuted");
|
||||
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(packageName,
|
||||
new PackageRollbackInfo.PackageVersion(higherVersionCode),
|
||||
new PackageRollbackInfo.PackageVersion(lowerVersionCode));
|
||||
RollbackInfo rollback = new RollbackInfo(target);
|
||||
PackageRollbackInfo target = new PackageRollbackInfo(
|
||||
new VersionedPackage(packageName, higherVersionCode),
|
||||
new VersionedPackage(packageName, lowerVersionCode));
|
||||
RollbackInfo rollback = new RollbackInfo(rollbackId, target);
|
||||
recentlyExecutedRollbacks.add(rollback);
|
||||
}
|
||||
} catch (IOException | JSONException e) {
|
||||
@@ -135,9 +136,9 @@ class RollbackStore {
|
||||
/**
|
||||
* Creates a new RollbackData instance with backupDir assigned.
|
||||
*/
|
||||
RollbackData createAvailableRollback() throws IOException {
|
||||
File backupDir = Files.createTempDirectory(mAvailableRollbacksDir.toPath(), null).toFile();
|
||||
return new RollbackData(backupDir);
|
||||
RollbackData createAvailableRollback(int rollbackId) throws IOException {
|
||||
File backupDir = new File(mAvailableRollbacksDir, Integer.toString(rollbackId));
|
||||
return new RollbackData(rollbackId, backupDir);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,11 +158,14 @@ class RollbackStore {
|
||||
JSONArray packagesJson = new JSONArray();
|
||||
for (PackageRollbackInfo info : data.packages) {
|
||||
JSONObject infoJson = new JSONObject();
|
||||
infoJson.put("packageName", info.packageName);
|
||||
infoJson.put("higherVersionCode", info.higherVersion.versionCode);
|
||||
infoJson.put("lowerVersionCode", info.lowerVersion.versionCode);
|
||||
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("timestamp", data.timestamp.toString());
|
||||
|
||||
@@ -195,9 +199,12 @@ class RollbackStore {
|
||||
for (int i = 0; i < recentlyExecutedRollbacks.size(); ++i) {
|
||||
RollbackInfo rollback = recentlyExecutedRollbacks.get(i);
|
||||
JSONObject element = new JSONObject();
|
||||
element.put("packageName", rollback.targetPackage.packageName);
|
||||
element.put("higherVersionCode", rollback.targetPackage.higherVersion.versionCode);
|
||||
element.put("lowerVersionCode", rollback.targetPackage.lowerVersion.versionCode);
|
||||
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());
|
||||
array.put(element);
|
||||
}
|
||||
|
||||
@@ -216,19 +223,22 @@ class RollbackStore {
|
||||
*/
|
||||
private RollbackData loadRollbackData(File backupDir) throws IOException {
|
||||
try {
|
||||
RollbackData data = new RollbackData(backupDir);
|
||||
File rollbackJsonFile = new File(backupDir, "rollback.json");
|
||||
JSONObject dataJson = new JSONObject(
|
||||
IoUtils.readFileAsString(rollbackJsonFile.getAbsolutePath()));
|
||||
|
||||
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(packageName,
|
||||
new PackageRollbackInfo.PackageVersion(higherVersionCode),
|
||||
new PackageRollbackInfo.PackageVersion(lowerVersionCode)));
|
||||
data.packages.add(new PackageRollbackInfo(
|
||||
new VersionedPackage(packageName, higherVersionCode),
|
||||
new VersionedPackage(packageName, lowerVersionCode)));
|
||||
}
|
||||
|
||||
data.timestamp = Instant.parse(dataJson.getString("timestamp"));
|
||||
|
||||
@@ -44,7 +44,6 @@ class RollbackBroadcastReceiver extends BroadcastReceiver {
|
||||
RollbackBroadcastReceiver() {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED);
|
||||
filter.addDataScheme("package");
|
||||
InstrumentationRegistry.getContext().registerReceiver(this, filter);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.rollback.PackageRollbackInfo;
|
||||
import android.content.rollback.RollbackInfo;
|
||||
import android.content.rollback.RollbackManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
@@ -98,7 +98,7 @@ public class RollbackTest {
|
||||
// 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.packageName)) {
|
||||
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;
|
||||
@@ -116,7 +116,7 @@ public class RollbackTest {
|
||||
|
||||
// There should be no recently executed rollbacks for this package.
|
||||
for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
|
||||
assertNotEquals(TEST_APP_A, info.targetPackage.packageName);
|
||||
assertNotEquals(TEST_APP_A, info.targetPackage.getPackageName());
|
||||
}
|
||||
|
||||
// Install v1 of the app (without rollbacks enabled).
|
||||
@@ -135,9 +135,7 @@ public class RollbackTest {
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
|
||||
RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollback);
|
||||
assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
|
||||
assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
|
||||
assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.targetPackage);
|
||||
|
||||
// We should not have received any rollback requests yet.
|
||||
// TODO: Possibly flaky if, by chance, some other app on device
|
||||
@@ -153,21 +151,18 @@ public class RollbackTest {
|
||||
// received could lead to test flakiness.
|
||||
Intent broadcast = broadcastReceiver.poll(5, TimeUnit.SECONDS);
|
||||
assertNotNull(broadcast);
|
||||
assertEquals(TEST_APP_A, broadcast.getData().getSchemeSpecificPart());
|
||||
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.packageName)) {
|
||||
if (TEST_APP_A.equals(r.targetPackage.getPackageName())) {
|
||||
assertNull(rollback);
|
||||
rollback = r;
|
||||
}
|
||||
}
|
||||
assertNotNull(rollback);
|
||||
assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
|
||||
assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
|
||||
assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.targetPackage);
|
||||
|
||||
broadcastReceiver.unregister();
|
||||
context.unregisterReceiver(enableRollbackReceiver);
|
||||
@@ -208,16 +203,12 @@ public class RollbackTest {
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
|
||||
RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollbackA);
|
||||
assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName);
|
||||
assertEquals(2, rollbackA.targetPackage.higherVersion.versionCode);
|
||||
assertEquals(1, rollbackA.targetPackage.lowerVersion.versionCode);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA.targetPackage);
|
||||
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B));
|
||||
RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B);
|
||||
assertNotNull(rollbackB);
|
||||
assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName);
|
||||
assertEquals(2, rollbackB.targetPackage.higherVersion.versionCode);
|
||||
assertEquals(1, rollbackB.targetPackage.lowerVersion.versionCode);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB.targetPackage);
|
||||
|
||||
// Reload the persisted data.
|
||||
rm.reloadPersistedData();
|
||||
@@ -225,16 +216,12 @@ public class RollbackTest {
|
||||
// The apps should still be available for rollback.
|
||||
rollbackA = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollbackA);
|
||||
assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName);
|
||||
assertEquals(2, rollbackA.targetPackage.higherVersion.versionCode);
|
||||
assertEquals(1, rollbackA.targetPackage.lowerVersion.versionCode);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA.targetPackage);
|
||||
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B));
|
||||
rollbackB = rm.getAvailableRollback(TEST_APP_B);
|
||||
assertNotNull(rollbackB);
|
||||
assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName);
|
||||
assertEquals(2, rollbackB.targetPackage.higherVersion.versionCode);
|
||||
assertEquals(1, rollbackB.targetPackage.lowerVersion.versionCode);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB.targetPackage);
|
||||
|
||||
// Rollback of B should not rollback A
|
||||
RollbackTestUtils.rollback(rollbackB);
|
||||
@@ -278,16 +265,12 @@ public class RollbackTest {
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
|
||||
RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollbackA);
|
||||
assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName);
|
||||
assertEquals(2, rollbackA.targetPackage.higherVersion.versionCode);
|
||||
assertEquals(1, rollbackA.targetPackage.lowerVersion.versionCode);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA.targetPackage);
|
||||
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B));
|
||||
RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B);
|
||||
assertNotNull(rollbackB);
|
||||
assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName);
|
||||
assertEquals(2, rollbackB.targetPackage.higherVersion.versionCode);
|
||||
assertEquals(1, rollbackB.targetPackage.lowerVersion.versionCode);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB.targetPackage);
|
||||
|
||||
// Reload the persisted data.
|
||||
rm.reloadPersistedData();
|
||||
@@ -295,16 +278,12 @@ public class RollbackTest {
|
||||
// The apps should still be available for rollback.
|
||||
rollbackA = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollbackA);
|
||||
assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName);
|
||||
assertEquals(2, rollbackA.targetPackage.higherVersion.versionCode);
|
||||
assertEquals(1, rollbackA.targetPackage.lowerVersion.versionCode);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA.targetPackage);
|
||||
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_B));
|
||||
rollbackB = rm.getAvailableRollback(TEST_APP_B);
|
||||
assertNotNull(rollbackB);
|
||||
assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName);
|
||||
assertEquals(2, rollbackB.targetPackage.higherVersion.versionCode);
|
||||
assertEquals(1, rollbackB.targetPackage.lowerVersion.versionCode);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB.targetPackage);
|
||||
|
||||
// Rollback of B should rollback A as well
|
||||
RollbackTestUtils.rollback(rollbackB);
|
||||
@@ -348,15 +327,13 @@ public class RollbackTest {
|
||||
// Verify the recent rollback has been recorded.
|
||||
rollback = null;
|
||||
for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
|
||||
if (TEST_APP_A.equals(r.targetPackage.packageName)) {
|
||||
if (TEST_APP_A.equals(r.targetPackage.getPackageName())) {
|
||||
assertNull(rollback);
|
||||
rollback = r;
|
||||
}
|
||||
}
|
||||
assertNotNull(rollback);
|
||||
assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
|
||||
assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
|
||||
assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.targetPackage);
|
||||
|
||||
// Reload the persisted data.
|
||||
rm.reloadPersistedData();
|
||||
@@ -364,15 +341,13 @@ public class RollbackTest {
|
||||
// Verify the recent rollback is still recorded.
|
||||
rollback = null;
|
||||
for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
|
||||
if (TEST_APP_A.equals(r.targetPackage.packageName)) {
|
||||
if (TEST_APP_A.equals(r.targetPackage.getPackageName())) {
|
||||
assertNull(rollback);
|
||||
rollback = r;
|
||||
}
|
||||
}
|
||||
assertNotNull(rollback);
|
||||
assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
|
||||
assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
|
||||
assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.targetPackage);
|
||||
} finally {
|
||||
RollbackTestUtils.dropShellPermissionIdentity();
|
||||
}
|
||||
@@ -404,9 +379,7 @@ public class RollbackTest {
|
||||
assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
|
||||
RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollback);
|
||||
assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
|
||||
assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
|
||||
assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
|
||||
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.targetPackage);
|
||||
|
||||
// Expire the rollback.
|
||||
rm.expireRollbackForPackage(TEST_APP_A);
|
||||
@@ -499,8 +472,7 @@ public class RollbackTest {
|
||||
@Test
|
||||
public void testRollbackBroadcastRestrictions() throws Exception {
|
||||
RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver();
|
||||
Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED,
|
||||
Uri.fromParts("package", "com.android.tests.rollback.bogus", null));
|
||||
Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED);
|
||||
try {
|
||||
InstrumentationRegistry.getContext().sendBroadcast(broadcast);
|
||||
fail("Succeeded in sending restricted broadcast from app context.");
|
||||
@@ -549,11 +521,11 @@ public class RollbackTest {
|
||||
Thread.sleep(1000);
|
||||
RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
|
||||
assertNotNull(rollbackA);
|
||||
assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName);
|
||||
assertEquals(TEST_APP_A, rollbackA.targetPackage.getPackageName());
|
||||
|
||||
RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B);
|
||||
assertNotNull(rollbackB);
|
||||
assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName);
|
||||
assertEquals(TEST_APP_B, rollbackB.targetPackage.getPackageName());
|
||||
|
||||
// Executing rollback should roll back the correct package.
|
||||
RollbackTestUtils.rollback(rollbackA);
|
||||
@@ -670,7 +642,7 @@ public class RollbackTest {
|
||||
|
||||
// We should not see a recent rollback listed for TEST_APP_B
|
||||
for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
|
||||
assertNotEquals(TEST_APP_B, r.targetPackage.packageName);
|
||||
assertNotEquals(TEST_APP_B, r.targetPackage.getPackageName());
|
||||
}
|
||||
|
||||
// TODO: Test the listed dependent apps for the recently executed
|
||||
@@ -680,4 +652,15 @@ public class RollbackTest {
|
||||
RollbackTestUtils.dropShellPermissionIdentity();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to test the value of a PackageRollbackInfo
|
||||
private void assertPackageRollbackInfoEquals(String packageName,
|
||||
long versionRolledBackFrom, long versionRolledBackTo,
|
||||
PackageRollbackInfo info) {
|
||||
assertEquals(packageName, info.getPackageName());
|
||||
assertEquals(packageName, info.getVersionRolledBackFrom().getPackageName());
|
||||
assertEquals(versionRolledBackFrom, info.getVersionRolledBackFrom().getLongVersionCode());
|
||||
assertEquals(packageName, info.getVersionRolledBackTo().getPackageName());
|
||||
assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user