Merge changes I7fd7d98a,If770a615 into rvc-dev
* changes: Block rollback commit if it may violate min extension constraint. Record supported extension versions at rollback creation.
This commit is contained in:
@@ -38,6 +38,9 @@ public class SdkExtensions {
|
|||||||
|
|
||||||
private static final int R_EXTENSION_INT;
|
private static final int R_EXTENSION_INT;
|
||||||
static {
|
static {
|
||||||
|
// Note: when adding more extension versions, the logic that records current
|
||||||
|
// extension versions when saving a rollback must also be updated.
|
||||||
|
// At the time of writing this is in RollbackManagerServiceImpl#getExtensionVersions()
|
||||||
R_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.r", 0);
|
R_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.r", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import android.content.Intent;
|
|||||||
import android.content.IntentSender;
|
import android.content.IntentSender;
|
||||||
import android.content.pm.PackageInstaller;
|
import android.content.pm.PackageInstaller;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.PackageManagerInternal;
|
||||||
import android.content.pm.UserInfo;
|
import android.content.pm.UserInfo;
|
||||||
import android.content.pm.VersionedPackage;
|
import android.content.pm.VersionedPackage;
|
||||||
import android.content.rollback.PackageRollbackInfo;
|
import android.content.rollback.PackageRollbackInfo;
|
||||||
@@ -36,14 +37,19 @@ import android.os.Binder;
|
|||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
|
import android.os.ext.SdkExtensions;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.IntArray;
|
import android.util.IntArray;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
import android.util.SparseIntArray;
|
||||||
import android.util.SparseLongArray;
|
import android.util.SparseLongArray;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
import com.android.internal.util.IndentingPrintWriter;
|
import com.android.internal.util.IndentingPrintWriter;
|
||||||
|
import com.android.server.LocalServices;
|
||||||
|
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -173,6 +179,11 @@ class Rollback {
|
|||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private int mNumPackageSessionsWithSuccess;
|
private int mNumPackageSessionsWithSuccess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The extension versions supported at the time of rollback creation.
|
||||||
|
*/
|
||||||
|
private final SparseIntArray mExtensionVersions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new, empty Rollback instance.
|
* Constructs a new, empty Rollback instance.
|
||||||
*
|
*
|
||||||
@@ -182,9 +193,11 @@ class Rollback {
|
|||||||
* @param userId the user that performed the install with rollback enabled.
|
* @param userId the user that performed the install with rollback enabled.
|
||||||
* @param installerPackageName the installer package name from the original install session.
|
* @param installerPackageName the installer package name from the original install session.
|
||||||
* @param packageSessionIds the session ids for all packages in the install.
|
* @param packageSessionIds the session ids for all packages in the install.
|
||||||
|
* @param extensionVersions the extension versions supported at the time of rollback creation
|
||||||
*/
|
*/
|
||||||
Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
|
Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
|
||||||
String installerPackageName, int[] packageSessionIds) {
|
String installerPackageName, int[] packageSessionIds,
|
||||||
|
SparseIntArray extensionVersions) {
|
||||||
this.info = new RollbackInfo(rollbackId,
|
this.info = new RollbackInfo(rollbackId,
|
||||||
/* packages */ new ArrayList<>(),
|
/* packages */ new ArrayList<>(),
|
||||||
/* isStaged */ stagedSessionId != -1,
|
/* isStaged */ stagedSessionId != -1,
|
||||||
@@ -197,11 +210,13 @@ class Rollback {
|
|||||||
mState = ROLLBACK_STATE_ENABLING;
|
mState = ROLLBACK_STATE_ENABLING;
|
||||||
mTimestamp = Instant.now();
|
mTimestamp = Instant.now();
|
||||||
mPackageSessionIds = packageSessionIds != null ? packageSessionIds : new int[0];
|
mPackageSessionIds = packageSessionIds != null ? packageSessionIds : new int[0];
|
||||||
|
mExtensionVersions = extensionVersions;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
|
Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
|
||||||
String installerPackageName) {
|
String installerPackageName) {
|
||||||
this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null);
|
this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null,
|
||||||
|
new SparseIntArray(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -209,7 +224,7 @@ class Rollback {
|
|||||||
*/
|
*/
|
||||||
Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
|
Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
|
||||||
@RollbackState int state, int apkSessionId, boolean restoreUserDataInProgress,
|
@RollbackState int state, int apkSessionId, boolean restoreUserDataInProgress,
|
||||||
int userId, String installerPackageName) {
|
int userId, String installerPackageName, SparseIntArray extensionVersions) {
|
||||||
this.info = info;
|
this.info = info;
|
||||||
mUserId = userId;
|
mUserId = userId;
|
||||||
mInstallerPackageName = installerPackageName;
|
mInstallerPackageName = installerPackageName;
|
||||||
@@ -219,6 +234,7 @@ class Rollback {
|
|||||||
mState = state;
|
mState = state;
|
||||||
mApkSessionId = apkSessionId;
|
mApkSessionId = apkSessionId;
|
||||||
mRestoreUserDataInProgress = restoreUserDataInProgress;
|
mRestoreUserDataInProgress = restoreUserDataInProgress;
|
||||||
|
mExtensionVersions = extensionVersions;
|
||||||
// TODO(b/120200473): Include this field during persistence. This field will be used to
|
// TODO(b/120200473): Include this field during persistence. This field will be used to
|
||||||
// decide which rollback to expire when ACTION_PACKAGE_REPLACED is received. Note persisting
|
// decide which rollback to expire when ACTION_PACKAGE_REPLACED is received. Note persisting
|
||||||
// this field is not backward compatible. We won't fix b/120200473 until S to minimize the
|
// this field is not backward compatible. We won't fix b/120200473 until S to minimize the
|
||||||
@@ -282,6 +298,14 @@ class Rollback {
|
|||||||
return mInstallerPackageName;
|
return mInstallerPackageName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the extension versions that were supported at the time that the rollback was created,
|
||||||
|
* as a mapping from SdkVersion to ExtensionVersion.
|
||||||
|
*/
|
||||||
|
SparseIntArray getExtensionVersions() {
|
||||||
|
return mExtensionVersions;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the rollback is in the ENABLING state.
|
* Returns true if the rollback is in the ENABLING state.
|
||||||
*/
|
*/
|
||||||
@@ -451,6 +475,15 @@ class Rollback {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (containsApex() && wasCreatedAtLowerExtensionVersion()) {
|
||||||
|
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
|
||||||
|
if (extensionVersionReductionWouldViolateConstraint(mExtensionVersions, pmi)) {
|
||||||
|
sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
|
||||||
|
"Rollback may violate a minExtensionVersion constraint");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get a context to use to install the downgraded version of the package.
|
// Get a context to use to install the downgraded version of the package.
|
||||||
Context pkgContext;
|
Context pkgContext;
|
||||||
try {
|
try {
|
||||||
@@ -826,6 +859,56 @@ class Rollback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there is an app installed that specifies a minExtensionVersion greater
|
||||||
|
* than what was present at the time this Rollback was created.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
static boolean extensionVersionReductionWouldViolateConstraint(
|
||||||
|
SparseIntArray rollbackExtVers, PackageManagerInternal pmi) {
|
||||||
|
if (rollbackExtVers.size() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<String> packages = pmi.getPackageList().getPackageNames();
|
||||||
|
for (int i = 0; i < packages.size(); i++) {
|
||||||
|
AndroidPackage pkg = pmi.getPackage(packages.get(i));
|
||||||
|
SparseIntArray minExtVers = pkg.getMinExtensionVersions();
|
||||||
|
if (minExtVers == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int j = 0; j < rollbackExtVers.size(); j++) {
|
||||||
|
int minExt = minExtVers.get(rollbackExtVers.keyAt(j), -1);
|
||||||
|
if (rollbackExtVers.valueAt(j) < minExt) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if for any SDK version, the extension version recorded at the time of rollback
|
||||||
|
* creation is lower than the current extension version.
|
||||||
|
*/
|
||||||
|
private boolean wasCreatedAtLowerExtensionVersion() {
|
||||||
|
for (int i = 0; i < mExtensionVersions.size(); i++) {
|
||||||
|
if (SdkExtensions.getExtensionVersion(mExtensionVersions.keyAt(i))
|
||||||
|
> mExtensionVersions.valueAt(i)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsApex() {
|
||||||
|
for (PackageRollbackInfo pkgInfo : info.getPackages()) {
|
||||||
|
if (pkgInfo.isApex()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void dump(IndentingPrintWriter ipw) {
|
void dump(IndentingPrintWriter ipw) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
ipw.println(info.getRollbackId() + ":");
|
ipw.println(info.getRollbackId() + ":");
|
||||||
@@ -852,6 +935,12 @@ class Rollback {
|
|||||||
ipw.decreaseIndent();
|
ipw.decreaseIndent();
|
||||||
ipw.println("-committedSessionId: " + info.getCommittedSessionId());
|
ipw.println("-committedSessionId: " + info.getCommittedSessionId());
|
||||||
}
|
}
|
||||||
|
if (mExtensionVersions.size() > 0) {
|
||||||
|
ipw.println("-extensionVersions:");
|
||||||
|
ipw.increaseIndent();
|
||||||
|
ipw.println(mExtensionVersions.toString());
|
||||||
|
ipw.decreaseIndent();
|
||||||
|
}
|
||||||
ipw.decreaseIndent();
|
ipw.decreaseIndent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import android.content.rollback.IRollbackManager;
|
|||||||
import android.content.rollback.RollbackInfo;
|
import android.content.rollback.RollbackInfo;
|
||||||
import android.content.rollback.RollbackManager;
|
import android.content.rollback.RollbackManager;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerExecutor;
|
import android.os.HandlerExecutor;
|
||||||
@@ -49,12 +50,14 @@ import android.os.Process;
|
|||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
|
import android.os.ext.SdkExtensions;
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
import android.util.IntArray;
|
import android.util.IntArray;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.LongArrayQueue;
|
import android.util.LongArrayQueue;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseBooleanArray;
|
import android.util.SparseBooleanArray;
|
||||||
|
import android.util.SparseIntArray;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.util.DumpUtils;
|
import com.android.internal.util.DumpUtils;
|
||||||
@@ -1274,16 +1277,29 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
|||||||
|
|
||||||
if (parentSession.isStaged()) {
|
if (parentSession.isStaged()) {
|
||||||
rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
|
rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
|
||||||
installerPackageName, packageSessionIds);
|
installerPackageName, packageSessionIds, getExtensionVersions());
|
||||||
} else {
|
} else {
|
||||||
rollback = mRollbackStore.createNonStagedRollback(rollbackId, userId,
|
rollback = mRollbackStore.createNonStagedRollback(rollbackId, userId,
|
||||||
installerPackageName, packageSessionIds);
|
installerPackageName, packageSessionIds, getExtensionVersions());
|
||||||
}
|
}
|
||||||
|
|
||||||
mRollbacks.add(rollback);
|
mRollbacks.add(rollback);
|
||||||
return rollback;
|
return rollback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SparseIntArray getExtensionVersions() {
|
||||||
|
// This list must be updated whenever the current API level is increased, or should be
|
||||||
|
// replaced when we have another way of determining the relevant SDK versions.
|
||||||
|
final int[] relevantSdkVersions = { Build.VERSION_CODES.R };
|
||||||
|
|
||||||
|
SparseIntArray result = new SparseIntArray(relevantSdkVersions.length);
|
||||||
|
for (int i = 0; i < relevantSdkVersions.length; i++) {
|
||||||
|
result.put(relevantSdkVersions[i],
|
||||||
|
SdkExtensions.getExtensionVersion(relevantSdkVersions[i]));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Rollback associated with the given session if parent or child session id matches.
|
* Returns the Rollback associated with the given session if parent or child session id matches.
|
||||||
* Returns null if not found.
|
* Returns null if not found.
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import static android.os.UserHandle.USER_SYSTEM;
|
|||||||
import static com.android.server.rollback.Rollback.rollbackStateFromString;
|
import static com.android.server.rollback.Rollback.rollbackStateFromString;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.VersionedPackage;
|
import android.content.pm.VersionedPackage;
|
||||||
import android.content.rollback.PackageRollbackInfo;
|
import android.content.rollback.PackageRollbackInfo;
|
||||||
@@ -28,6 +29,7 @@ import android.content.rollback.PackageRollbackInfo.RestoreInfo;
|
|||||||
import android.content.rollback.RollbackInfo;
|
import android.content.rollback.RollbackInfo;
|
||||||
import android.util.IntArray;
|
import android.util.IntArray;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
import android.util.SparseIntArray;
|
||||||
import android.util.SparseLongArray;
|
import android.util.SparseLongArray;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
@@ -176,6 +178,32 @@ class RollbackStore {
|
|||||||
return ceSnapshotInodes;
|
return ceSnapshotInodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static @Nullable JSONArray extensionVersionsToJson(
|
||||||
|
SparseIntArray extensionVersions) throws JSONException {
|
||||||
|
JSONArray array = new JSONArray();
|
||||||
|
for (int i = 0; i < extensionVersions.size(); i++) {
|
||||||
|
JSONObject entryJson = new JSONObject();
|
||||||
|
entryJson.put("sdkVersion", extensionVersions.keyAt(i));
|
||||||
|
entryJson.put("extensionVersion", extensionVersions.valueAt(i));
|
||||||
|
array.put(entryJson);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @Nullable SparseIntArray extensionVersionsFromJson(JSONArray json)
|
||||||
|
throws JSONException {
|
||||||
|
if (json == null) {
|
||||||
|
return new SparseIntArray(0);
|
||||||
|
}
|
||||||
|
SparseIntArray extensionVersions = new SparseIntArray(json.length());
|
||||||
|
for (int i = 0; i < json.length(); i++) {
|
||||||
|
JSONObject entry = json.getJSONObject(i);
|
||||||
|
extensionVersions.append(
|
||||||
|
entry.getInt("sdkVersion"), entry.getInt("extensionVersion"));
|
||||||
|
}
|
||||||
|
return extensionVersions;
|
||||||
|
}
|
||||||
|
|
||||||
private static JSONObject rollbackInfoToJson(RollbackInfo rollback) throws JSONException {
|
private static JSONObject rollbackInfoToJson(RollbackInfo rollback) throws JSONException {
|
||||||
JSONObject json = new JSONObject();
|
JSONObject json = new JSONObject();
|
||||||
json.put("rollbackId", rollback.getRollbackId());
|
json.put("rollbackId", rollback.getRollbackId());
|
||||||
@@ -200,10 +228,10 @@ class RollbackStore {
|
|||||||
* backupDir assigned.
|
* backupDir assigned.
|
||||||
*/
|
*/
|
||||||
Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName,
|
Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName,
|
||||||
int[] packageSessionIds) {
|
int[] packageSessionIds, SparseIntArray extensionVersions) {
|
||||||
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
|
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
|
||||||
return new Rollback(rollbackId, backupDir, -1, userId, installerPackageName,
|
return new Rollback(rollbackId, backupDir, -1, userId, installerPackageName,
|
||||||
packageSessionIds);
|
packageSessionIds, extensionVersions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -211,10 +239,11 @@ class RollbackStore {
|
|||||||
* backupDir assigned.
|
* backupDir assigned.
|
||||||
*/
|
*/
|
||||||
Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId,
|
Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId,
|
||||||
String installerPackageName, int[] packageSessionIds) {
|
String installerPackageName, int[] packageSessionIds,
|
||||||
|
SparseIntArray extensionVersions) {
|
||||||
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
|
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
|
||||||
return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName,
|
return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName,
|
||||||
packageSessionIds);
|
packageSessionIds, extensionVersions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -272,6 +301,8 @@ class RollbackStore {
|
|||||||
dataJson.put("restoreUserDataInProgress", rollback.isRestoreUserDataInProgress());
|
dataJson.put("restoreUserDataInProgress", rollback.isRestoreUserDataInProgress());
|
||||||
dataJson.put("userId", rollback.getUserId());
|
dataJson.put("userId", rollback.getUserId());
|
||||||
dataJson.putOpt("installerPackageName", rollback.getInstallerPackageName());
|
dataJson.putOpt("installerPackageName", rollback.getInstallerPackageName());
|
||||||
|
dataJson.putOpt(
|
||||||
|
"extensionVersions", extensionVersionsToJson(rollback.getExtensionVersions()));
|
||||||
|
|
||||||
PrintWriter pw = new PrintWriter(new File(rollback.getBackupDir(), "rollback.json"));
|
PrintWriter pw = new PrintWriter(new File(rollback.getBackupDir(), "rollback.json"));
|
||||||
pw.println(dataJson.toString());
|
pw.println(dataJson.toString());
|
||||||
@@ -316,7 +347,8 @@ class RollbackStore {
|
|||||||
dataJson.getInt("apkSessionId"),
|
dataJson.getInt("apkSessionId"),
|
||||||
dataJson.getBoolean("restoreUserDataInProgress"),
|
dataJson.getBoolean("restoreUserDataInProgress"),
|
||||||
dataJson.optInt("userId", USER_SYSTEM),
|
dataJson.optInt("userId", USER_SYSTEM),
|
||||||
dataJson.optString("installerPackageName", ""));
|
dataJson.optString("installerPackageName", ""),
|
||||||
|
extensionVersionsFromJson(dataJson.optJSONArray("extensionVersions")));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JSONObject toJson(VersionedPackage pkg) throws JSONException {
|
private static JSONObject toJson(VersionedPackage pkg) throws JSONException {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
import android.content.pm.VersionedPackage;
|
import android.content.pm.VersionedPackage;
|
||||||
import android.content.rollback.PackageRollbackInfo;
|
import android.content.rollback.PackageRollbackInfo;
|
||||||
import android.util.IntArray;
|
import android.util.IntArray;
|
||||||
|
import android.util.SparseIntArray;
|
||||||
import android.util.SparseLongArray;
|
import android.util.SparseLongArray;
|
||||||
|
|
||||||
import com.google.common.truth.Correspondence;
|
import com.google.common.truth.Correspondence;
|
||||||
@@ -83,7 +84,7 @@ public class RollbackStoreTest {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final String JSON_ROLLBACK = "{'info':{'rollbackId':123,'packages':"
|
private static final String JSON_ROLLBACK_NO_EXT = "{'info':{'rollbackId':123,'packages':"
|
||||||
+ "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55},"
|
+ "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55},"
|
||||||
+ "'versionRolledBackTo':{'packageName':'blah1','longVersionCode':50},'pendingBackups':"
|
+ "'versionRolledBackTo':{'packageName':'blah1','longVersionCode':50},'pendingBackups':"
|
||||||
+ "[59,1245,124544],'pendingRestores':[{'userId':498,'appId':32322,'seInfo':'wombles'},"
|
+ "[59,1245,124544],'pendingRestores':[{'userId':498,'appId':32322,'seInfo':'wombles'},"
|
||||||
@@ -103,6 +104,28 @@ public class RollbackStoreTest {
|
|||||||
+ "'restoreUserDataInProgress':true, 'userId':0,"
|
+ "'restoreUserDataInProgress':true, 'userId':0,"
|
||||||
+ "'installerPackageName':'some.installer'}";
|
+ "'installerPackageName':'some.installer'}";
|
||||||
|
|
||||||
|
private static final String JSON_ROLLBACK = "{'info':{'rollbackId':123,'packages':"
|
||||||
|
+ "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55},"
|
||||||
|
+ "'versionRolledBackTo':{'packageName':'blah1','longVersionCode':50},'pendingBackups':"
|
||||||
|
+ "[59,1245,124544],'pendingRestores':[{'userId':498,'appId':32322,'seInfo':'wombles'},"
|
||||||
|
+ "{'userId':-895,'appId':1,'seInfo':'pingu'}],'isApex':false,'isApkInApex':false,"
|
||||||
|
+ "'installedUsers':"
|
||||||
|
+ "[498468432,1111,98464],'ceSnapshotInodes':[{'userId':1,'ceSnapshotInode':-6},"
|
||||||
|
+ "{'userId':2222,'ceSnapshotInode':81641654445},{'userId':546546,"
|
||||||
|
+ "'ceSnapshotInode':345689375}]},{'versionRolledBackFrom':{'packageName':'chips',"
|
||||||
|
+ "'longVersionCode':28},'versionRolledBackTo':{'packageName':'com.chips.test',"
|
||||||
|
+ "'longVersionCode':48},'pendingBackups':[5],'pendingRestores':[{'userId':18,"
|
||||||
|
+ "'appId':-12,'seInfo':''}],'isApex':false,'isApkInApex':false,"
|
||||||
|
+ "'installedUsers':[55,79],"
|
||||||
|
+ "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello',"
|
||||||
|
+ "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
|
||||||
|
+ "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
|
||||||
|
+ "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1,"
|
||||||
|
+ "'restoreUserDataInProgress':true, 'userId':0,"
|
||||||
|
+ "'installerPackageName':'some.installer',"
|
||||||
|
+ "'extensionVersions':[{'sdkVersion':5,'extensionVersion':25},"
|
||||||
|
+ "{'sdkVersion':30,'extensionVersion':71}]}";
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public TemporaryFolder mFolder = new TemporaryFolder();
|
public TemporaryFolder mFolder = new TemporaryFolder();
|
||||||
|
|
||||||
@@ -119,7 +142,10 @@ public class RollbackStoreTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createNonStaged() {
|
public void createNonStaged() {
|
||||||
Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
|
SparseIntArray extensionVersions = new SparseIntArray();
|
||||||
|
extensionVersions.put(30, 71);
|
||||||
|
Rollback rollback = mRollbackStore.createNonStagedRollback(
|
||||||
|
ID, USER, INSTALLER, null, extensionVersions);
|
||||||
|
|
||||||
assertThat(rollback.getBackupDir().getAbsolutePath())
|
assertThat(rollback.getBackupDir().getAbsolutePath())
|
||||||
.isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
|
.isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
|
||||||
@@ -128,11 +154,16 @@ public class RollbackStoreTest {
|
|||||||
assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
|
assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
|
||||||
assertThat(rollback.info.getPackages()).isEmpty();
|
assertThat(rollback.info.getPackages()).isEmpty();
|
||||||
assertThat(rollback.isEnabling()).isTrue();
|
assertThat(rollback.isEnabling()).isTrue();
|
||||||
|
assertThat(rollback.getExtensionVersions().toString())
|
||||||
|
.isEqualTo(extensionVersions.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createStaged() {
|
public void createStaged() {
|
||||||
Rollback rollback = mRollbackStore.createStagedRollback(ID, 897, USER, INSTALLER, null);
|
SparseIntArray extensionVersions = new SparseIntArray();
|
||||||
|
extensionVersions.put(30, 71);
|
||||||
|
Rollback rollback = mRollbackStore.createStagedRollback(
|
||||||
|
ID, 897, USER, INSTALLER, null, extensionVersions);
|
||||||
|
|
||||||
assertThat(rollback.getBackupDir().getAbsolutePath())
|
assertThat(rollback.getBackupDir().getAbsolutePath())
|
||||||
.isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
|
.isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
|
||||||
@@ -143,11 +174,17 @@ public class RollbackStoreTest {
|
|||||||
assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
|
assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
|
||||||
assertThat(rollback.info.getPackages()).isEmpty();
|
assertThat(rollback.info.getPackages()).isEmpty();
|
||||||
assertThat(rollback.isEnabling()).isTrue();
|
assertThat(rollback.isEnabling()).isTrue();
|
||||||
|
assertThat(rollback.getExtensionVersions().toString())
|
||||||
|
.isEqualTo(extensionVersions.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void saveAndLoadRollback() {
|
public void saveAndLoadRollback() {
|
||||||
Rollback origRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
|
SparseIntArray extensionVersions = new SparseIntArray();
|
||||||
|
extensionVersions.put(5, 25);
|
||||||
|
extensionVersions.put(30, 71);
|
||||||
|
Rollback origRb = mRollbackStore.createNonStagedRollback(
|
||||||
|
ID, USER, INSTALLER, null, extensionVersions);
|
||||||
|
|
||||||
origRb.setRestoreUserDataInProgress(true);
|
origRb.setRestoreUserDataInProgress(true);
|
||||||
origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2));
|
origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2));
|
||||||
@@ -195,9 +232,63 @@ public class RollbackStoreTest {
|
|||||||
assertRollbacksAreEquivalent(loadedRb, origRb);
|
assertRollbacksAreEquivalent(loadedRb, origRb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loadFromJsonNoExtensionVersions() throws Exception {
|
||||||
|
Rollback expectedRb = mRollbackStore.createNonStagedRollback(
|
||||||
|
ID, USER, INSTALLER, null, new SparseIntArray(0));
|
||||||
|
|
||||||
|
expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
|
||||||
|
expectedRb.setRestoreUserDataInProgress(true);
|
||||||
|
expectedRb.info.getCausePackages().add(new VersionedPackage("hello", 23));
|
||||||
|
expectedRb.info.getCausePackages().add(new VersionedPackage("something", 999));
|
||||||
|
expectedRb.info.setCommittedSessionId(45654465);
|
||||||
|
|
||||||
|
PackageRollbackInfo pkgInfo1 = new PackageRollbackInfo(new VersionedPackage("blah", 55),
|
||||||
|
new VersionedPackage("blah1", 50), new IntArray(), new ArrayList<>(),
|
||||||
|
false, false, new IntArray(), new SparseLongArray());
|
||||||
|
pkgInfo1.getPendingBackups().add(59);
|
||||||
|
pkgInfo1.getPendingBackups().add(1245);
|
||||||
|
pkgInfo1.getPendingBackups().add(124544);
|
||||||
|
pkgInfo1.getCeSnapshotInodes().put(546546, 345689375);
|
||||||
|
pkgInfo1.getCeSnapshotInodes().put(2222, 81641654445L);
|
||||||
|
pkgInfo1.getCeSnapshotInodes().put(1, -6);
|
||||||
|
|
||||||
|
pkgInfo1.getPendingRestores().add(
|
||||||
|
new PackageRollbackInfo.RestoreInfo(498, 32322, "wombles"));
|
||||||
|
pkgInfo1.getPendingRestores().add(
|
||||||
|
new PackageRollbackInfo.RestoreInfo(-895, 1, "pingu"));
|
||||||
|
|
||||||
|
pkgInfo1.getSnapshottedUsers().add(498468432);
|
||||||
|
pkgInfo1.getSnapshottedUsers().add(1111);
|
||||||
|
pkgInfo1.getSnapshottedUsers().add(98464);
|
||||||
|
|
||||||
|
PackageRollbackInfo pkgInfo2 = new PackageRollbackInfo(new VersionedPackage("chips", 28),
|
||||||
|
new VersionedPackage("com.chips.test", 48), new IntArray(), new ArrayList<>(),
|
||||||
|
false, false, new IntArray(), new SparseLongArray());
|
||||||
|
pkgInfo2.getPendingBackups().add(5);
|
||||||
|
|
||||||
|
pkgInfo2.getPendingRestores().add(
|
||||||
|
new PackageRollbackInfo.RestoreInfo(18, -12, ""));
|
||||||
|
|
||||||
|
pkgInfo2.getSnapshottedUsers().add(55);
|
||||||
|
pkgInfo2.getSnapshottedUsers().add(79);
|
||||||
|
|
||||||
|
expectedRb.info.getPackages().add(pkgInfo1);
|
||||||
|
expectedRb.info.getPackages().add(pkgInfo2);
|
||||||
|
|
||||||
|
Rollback parsedRb = RollbackStore.rollbackFromJson(
|
||||||
|
new JSONObject(JSON_ROLLBACK_NO_EXT), expectedRb.getBackupDir());
|
||||||
|
|
||||||
|
assertRollbacksAreEquivalent(parsedRb, expectedRb);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loadFromJson() throws Exception {
|
public void loadFromJson() throws Exception {
|
||||||
Rollback expectedRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
|
SparseIntArray extensionVersions = new SparseIntArray();
|
||||||
|
extensionVersions.put(5, 25);
|
||||||
|
extensionVersions.put(30, 71);
|
||||||
|
Rollback expectedRb = mRollbackStore.createNonStagedRollback(
|
||||||
|
ID, USER, INSTALLER, null, extensionVersions);
|
||||||
|
|
||||||
expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
|
expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
|
||||||
expectedRb.setRestoreUserDataInProgress(true);
|
expectedRb.setRestoreUserDataInProgress(true);
|
||||||
@@ -246,7 +337,8 @@ public class RollbackStoreTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void saveAndDelete() {
|
public void saveAndDelete() {
|
||||||
Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
|
Rollback rollback = mRollbackStore.createNonStagedRollback(
|
||||||
|
ID, USER, INSTALLER, null, new SparseIntArray(0));
|
||||||
|
|
||||||
RollbackStore.saveRollback(rollback);
|
RollbackStore.saveRollback(rollback);
|
||||||
|
|
||||||
@@ -294,6 +386,13 @@ public class RollbackStoreTest {
|
|||||||
|
|
||||||
assertThat(a.getUserId()).isEqualTo(b.getUserId());
|
assertThat(a.getUserId()).isEqualTo(b.getUserId());
|
||||||
assertThat(a.getInstallerPackageName()).isEqualTo(b.getInstallerPackageName());
|
assertThat(a.getInstallerPackageName()).isEqualTo(b.getInstallerPackageName());
|
||||||
|
|
||||||
|
if (a.getExtensionVersions() == null) {
|
||||||
|
assertThat(b.getExtensionVersions()).isNull();
|
||||||
|
} else {
|
||||||
|
assertThat(b.getExtensionVersions().toString())
|
||||||
|
.isEqualTo(a.getExtensionVersions().toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertPackageRollbacksAreEquivalent(PackageRollbackInfo b, PackageRollbackInfo a) {
|
private void assertPackageRollbacksAreEquivalent(PackageRollbackInfo b, PackageRollbackInfo a) {
|
||||||
|
|||||||
@@ -24,12 +24,18 @@ import static org.mockito.ArgumentMatchers.argThat;
|
|||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.content.pm.PackageManagerInternal;
|
||||||
import android.content.pm.VersionedPackage;
|
import android.content.pm.VersionedPackage;
|
||||||
import android.content.rollback.PackageRollbackInfo;
|
import android.content.rollback.PackageRollbackInfo;
|
||||||
import android.util.IntArray;
|
import android.util.IntArray;
|
||||||
|
import android.util.SparseIntArray;
|
||||||
import android.util.SparseLongArray;
|
import android.util.SparseLongArray;
|
||||||
|
|
||||||
|
import com.android.server.pm.PackageList;
|
||||||
|
import com.android.server.pm.parsing.pkg.PackageImpl;
|
||||||
|
|
||||||
import com.google.common.collect.Range;
|
import com.google.common.collect.Range;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -44,6 +50,7 @@ import java.io.File;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class RollbackUnitTest {
|
public class RollbackUnitTest {
|
||||||
@@ -56,10 +63,17 @@ public class RollbackUnitTest {
|
|||||||
private static final String INSTALLER = "some.installer";
|
private static final String INSTALLER = "some.installer";
|
||||||
|
|
||||||
@Mock private AppDataRollbackHelper mMockDataHelper;
|
@Mock private AppDataRollbackHelper mMockDataHelper;
|
||||||
|
@Mock private PackageManagerInternal mMockPmi;
|
||||||
|
|
||||||
|
private List<String> mPackages;
|
||||||
|
private PackageList mPackageList;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mPackages = new ArrayList<>();
|
||||||
|
mPackageList = new PackageList(mPackages, null);
|
||||||
|
when(mMockPmi.getPackageList()).thenReturn(mPackageList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -317,7 +331,7 @@ public class RollbackUnitTest {
|
|||||||
public void notifySessionWithSuccess() {
|
public void notifySessionWithSuccess() {
|
||||||
int[] sessionIds = new int[]{ 7777, 8888 };
|
int[] sessionIds = new int[]{ 7777, 8888 };
|
||||||
Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
|
Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
|
||||||
sessionIds);
|
sessionIds, null);
|
||||||
// The 1st invocation returns false because not all child sessions are notified.
|
// The 1st invocation returns false because not all child sessions are notified.
|
||||||
assertThat(rollback.notifySessionWithSuccess()).isFalse();
|
assertThat(rollback.notifySessionWithSuccess()).isFalse();
|
||||||
// The 2nd invocation returns true because now all child sessions are notified.
|
// The 2nd invocation returns true because now all child sessions are notified.
|
||||||
@@ -328,7 +342,7 @@ public class RollbackUnitTest {
|
|||||||
public void allPackagesEnabled() {
|
public void allPackagesEnabled() {
|
||||||
int[] sessionIds = new int[]{ 7777, 8888 };
|
int[] sessionIds = new int[]{ 7777, 8888 };
|
||||||
Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
|
Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
|
||||||
sessionIds);
|
sessionIds, null);
|
||||||
// #allPackagesEnabled returns false when 1 out of 2 packages is enabled.
|
// #allPackagesEnabled returns false when 1 out of 2 packages is enabled.
|
||||||
rollback.info.getPackages().add(newPkgInfoFor(PKG_1, 12, 10, false));
|
rollback.info.getPackages().add(newPkgInfoFor(PKG_1, 12, 10, false));
|
||||||
assertThat(rollback.allPackagesEnabled()).isFalse();
|
assertThat(rollback.allPackagesEnabled()).isFalse();
|
||||||
@@ -340,6 +354,85 @@ public class RollbackUnitTest {
|
|||||||
assertThat(rollback.allPackagesEnabled()).isTrue();
|
assertThat(rollback.allPackagesEnabled()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void minExtVerConstraintNotViolated() {
|
||||||
|
addPkgWithMinExtVersions("pkg0", new int[][] {{30, 4}});
|
||||||
|
addPkgWithMinExtVersions("pkg1", new int[][] {});
|
||||||
|
addPkgWithMinExtVersions("pkg2", new int[][] {{30, 5}, {31, 1}});
|
||||||
|
addPkgWithMinExtVersions("pkg3", new int[][] {{31, 7}, {32, 15}});
|
||||||
|
|
||||||
|
assertThat(Rollback.extensionVersionReductionWouldViolateConstraint(
|
||||||
|
sparseArrayFrom(new int[][] {{30, 5}}), mMockPmi)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void minExtVerConstraintExists() {
|
||||||
|
addPkgWithMinExtVersions("pkg0", null);
|
||||||
|
addPkgWithMinExtVersions("pkg1", new int[][] {{30, 5}, {31, 1}});
|
||||||
|
|
||||||
|
assertThat(Rollback.extensionVersionReductionWouldViolateConstraint(
|
||||||
|
sparseArrayFrom(new int[][] {{30, 4}}), mMockPmi)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void minExtVerConstraintExistsOnOnePackage() {
|
||||||
|
addPkgWithMinExtVersions("pkg0", new int[][] {{30, 4}});
|
||||||
|
addPkgWithMinExtVersions("pkg1", new int[][] {});
|
||||||
|
addPkgWithMinExtVersions("pkg2", new int[][] {{30, 5}, {31, 1}});
|
||||||
|
addPkgWithMinExtVersions("pkg3", new int[][] {{31, 7}, {32, 15}});
|
||||||
|
|
||||||
|
assertThat(Rollback.extensionVersionReductionWouldViolateConstraint(
|
||||||
|
sparseArrayFrom(new int[][] {{30, 4}}), mMockPmi)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void minExtVerConstraintDifferentSdk() {
|
||||||
|
addPkgWithMinExtVersions("pkg0", null);
|
||||||
|
addPkgWithMinExtVersions("pkg1", new int[][] {{30, 5}, {31, 1}});
|
||||||
|
|
||||||
|
assertThat(Rollback.extensionVersionReductionWouldViolateConstraint(
|
||||||
|
sparseArrayFrom(new int[][] {{32, 4}}), mMockPmi)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void minExtVerConstraintNoneRecordedOnRollback() {
|
||||||
|
addPkgWithMinExtVersions("pkg0", new int[][] {{30, 4}});
|
||||||
|
addPkgWithMinExtVersions("pkg1", new int[][] {});
|
||||||
|
addPkgWithMinExtVersions("pkg2", new int[][] {{30, 5}, {31, 1}});
|
||||||
|
addPkgWithMinExtVersions("pkg3", new int[][] {{31, 7}, {32, 15}});
|
||||||
|
|
||||||
|
assertThat(Rollback.extensionVersionReductionWouldViolateConstraint(
|
||||||
|
new SparseIntArray(0), mMockPmi)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void minExtVerConstraintNoMinsRecorded() {
|
||||||
|
addPkgWithMinExtVersions("pkg0", null);
|
||||||
|
addPkgWithMinExtVersions("pkg1", null);
|
||||||
|
|
||||||
|
assertThat(Rollback.extensionVersionReductionWouldViolateConstraint(
|
||||||
|
sparseArrayFrom(new int[][] {{32, 4}}), mMockPmi)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPkgWithMinExtVersions(String pkg, int[][] minExtVersions) {
|
||||||
|
mPackages.add(pkg);
|
||||||
|
PackageImpl pkgImpl = new PackageImpl(pkg, "baseCodePath", "codePath", null, false);
|
||||||
|
pkgImpl.setMinExtensionVersions(sparseArrayFrom(minExtVersions));
|
||||||
|
|
||||||
|
when(mMockPmi.getPackage(pkg)).thenReturn(pkgImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SparseIntArray sparseArrayFrom(int[][] arr) {
|
||||||
|
if (arr == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
SparseIntArray result = new SparseIntArray(arr.length);
|
||||||
|
for (int[] pair : arr) {
|
||||||
|
result.put(pair[0], pair[1]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private static PackageRollbackInfo newPkgInfoFor(
|
private static PackageRollbackInfo newPkgInfoFor(
|
||||||
String packageName, long fromVersion, long toVersion, boolean isApex) {
|
String packageName, long fromVersion, long toVersion, boolean isApex) {
|
||||||
return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion),
|
return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion),
|
||||||
|
|||||||
Reference in New Issue
Block a user