Merge changes I7fd7d98a,If770a615 into rvc-dev am: 266fba1670 am: 87193786f0

Change-Id: I7ead6adf31880ff1d29f42806290ba637c1fe5b3
This commit is contained in:
Oli Lan
2020-04-29 09:42:45 +00:00
committed by Automerger Merge Worker
4 changed files with 175 additions and 14 deletions

View File

@@ -27,6 +27,7 @@ import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
@@ -36,6 +37,7 @@ import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.ext.SdkExtensions;
import android.text.TextUtils;
import android.util.IntArray;
import android.util.Slog;
@@ -43,8 +45,11 @@ import android.util.SparseIntArray;
import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
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.IOException;
@@ -175,10 +180,9 @@ class Rollback {
private int mNumPackageSessionsWithSuccess;
/**
* The extension versions supported at the time of rollback creation. May be null if not set
* at creation time.
* The extension versions supported at the time of rollback creation.
*/
@Nullable private final SparseIntArray mExtensionVersions;
private final SparseIntArray mExtensionVersions;
/**
* Constructs a new, empty Rollback instance.
@@ -211,7 +215,8 @@ class Rollback {
Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
String installerPackageName) {
this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null, null);
this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null,
new SparseIntArray(0));
}
/**
@@ -297,7 +302,7 @@ class Rollback {
* Returns the extension versions that were supported at the time that the rollback was created,
* as a mapping from SdkVersion to ExtensionVersion.
*/
@Nullable SparseIntArray getExtensionVersions() {
SparseIntArray getExtensionVersions() {
return mExtensionVersions;
}
@@ -470,6 +475,15 @@ class Rollback {
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.
Context pkgContext;
try {
@@ -845,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) {
synchronized (mLock) {
ipw.println(info.getRollbackId() + ":");
@@ -871,6 +935,12 @@ class Rollback {
ipw.decreaseIndent();
ipw.println("-committedSessionId: " + info.getCommittedSessionId());
}
if (mExtensionVersions.size() > 0) {
ipw.println("-extensionVersions:");
ipw.increaseIndent();
ipw.println(mExtensionVersions.toString());
ipw.decreaseIndent();
}
ipw.decreaseIndent();
}
}

View File

@@ -179,10 +179,7 @@ class RollbackStore {
}
private static @Nullable JSONArray extensionVersionsToJson(
@Nullable SparseIntArray extensionVersions) throws JSONException {
if (extensionVersions == null) {
return null;
}
SparseIntArray extensionVersions) throws JSONException {
JSONArray array = new JSONArray();
for (int i = 0; i < extensionVersions.size(); i++) {
JSONObject entryJson = new JSONObject();
@@ -193,10 +190,10 @@ class RollbackStore {
return array;
}
private static @Nullable SparseIntArray extensionVersionsFromJson(@Nullable JSONArray json)
private static @Nullable SparseIntArray extensionVersionsFromJson(JSONArray json)
throws JSONException {
if (json == null) {
return null;
return new SparseIntArray(0);
}
SparseIntArray extensionVersions = new SparseIntArray(json.length());
for (int i = 0; i < json.length(); i++) {

View File

@@ -234,8 +234,8 @@ public class RollbackStoreTest {
@Test
public void loadFromJsonNoExtensionVersions() throws Exception {
Rollback expectedRb =
mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null, null);
Rollback expectedRb = mRollbackStore.createNonStagedRollback(
ID, USER, INSTALLER, null, new SparseIntArray(0));
expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
expectedRb.setRestoreUserDataInProgress(true);
@@ -337,7 +337,8 @@ public class RollbackStoreTest {
@Test
public void saveAndDelete() {
Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null, null);
Rollback rollback = mRollbackStore.createNonStagedRollback(
ID, USER, INSTALLER, null, new SparseIntArray(0));
RollbackStore.saveRollback(rollback);

View File

@@ -24,12 +24,18 @@ import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
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.rollback.PackageRollbackInfo;
import android.util.IntArray;
import android.util.SparseIntArray;
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 org.junit.Before;
@@ -44,6 +50,7 @@ import java.io.File;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@RunWith(JUnit4.class)
public class RollbackUnitTest {
@@ -56,10 +63,17 @@ public class RollbackUnitTest {
private static final String INSTALLER = "some.installer";
@Mock private AppDataRollbackHelper mMockDataHelper;
@Mock private PackageManagerInternal mMockPmi;
private List<String> mPackages;
private PackageList mPackageList;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mPackages = new ArrayList<>();
mPackageList = new PackageList(mPackages, null);
when(mMockPmi.getPackageList()).thenReturn(mPackageList);
}
@Test
@@ -340,6 +354,85 @@ public class RollbackUnitTest {
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(
String packageName, long fromVersion, long toVersion, boolean isApex) {
return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion),