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

Change-Id: I665cf5d9d0b5f5a8424e7a36cbad7048a02f3786
This commit is contained in:
Oli Lan
2020-04-29 09:53:16 +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.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,6 +37,7 @@ 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;
@@ -43,8 +45,11 @@ 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;
@@ -175,10 +180,9 @@ class Rollback {
private int mNumPackageSessionsWithSuccess; private int mNumPackageSessionsWithSuccess;
/** /**
* The extension versions supported at the time of rollback creation. May be null if not set * The extension versions supported at the time of rollback creation.
* at creation time.
*/ */
@Nullable private final SparseIntArray mExtensionVersions; private final SparseIntArray mExtensionVersions;
/** /**
* Constructs a new, empty Rollback instance. * Constructs a new, empty Rollback instance.
@@ -211,7 +215,8 @@ class Rollback {
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, 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, * Returns the extension versions that were supported at the time that the rollback was created,
* as a mapping from SdkVersion to ExtensionVersion. * as a mapping from SdkVersion to ExtensionVersion.
*/ */
@Nullable SparseIntArray getExtensionVersions() { SparseIntArray getExtensionVersions() {
return mExtensionVersions; return mExtensionVersions;
} }
@@ -470,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 {
@@ -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) { void dump(IndentingPrintWriter ipw) {
synchronized (mLock) { synchronized (mLock) {
ipw.println(info.getRollbackId() + ":"); ipw.println(info.getRollbackId() + ":");
@@ -871,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();
} }
} }

View File

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

View File

@@ -234,8 +234,8 @@ public class RollbackStoreTest {
@Test @Test
public void loadFromJsonNoExtensionVersions() throws Exception { public void loadFromJsonNoExtensionVersions() throws Exception {
Rollback expectedRb = Rollback expectedRb = mRollbackStore.createNonStagedRollback(
mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null, null); ID, USER, INSTALLER, null, new SparseIntArray(0));
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);
@@ -337,7 +337,8 @@ public class RollbackStoreTest {
@Test @Test
public void saveAndDelete() { 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); 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.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
@@ -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),