Merge "Merge "Ensure race between rollback and roll forward is properly handled" into qt-dev am: 07a9e9f9f9" into qt-dev-plus-aosp
This commit is contained in:
committed by
Android (Google) Code Review
commit
2019673855
@@ -1318,6 +1318,8 @@ public class PackageInstaller {
|
||||
public boolean isMultiPackage;
|
||||
/** {@hide} */
|
||||
public boolean isStaged;
|
||||
/** {@hide} */
|
||||
public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
|
||||
|
||||
/**
|
||||
* Construct parameters for a new package install session.
|
||||
@@ -1350,6 +1352,7 @@ public class PackageInstaller {
|
||||
installerPackageName = source.readString();
|
||||
isMultiPackage = source.readBoolean();
|
||||
isStaged = source.readBoolean();
|
||||
requiredInstalledVersionCode = source.readLong();
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
@@ -1372,6 +1375,7 @@ public class PackageInstaller {
|
||||
ret.installerPackageName = installerPackageName;
|
||||
ret.isMultiPackage = isMultiPackage;
|
||||
ret.isStaged = isStaged;
|
||||
ret.requiredInstalledVersionCode = requiredInstalledVersionCode;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1562,6 +1566,19 @@ public class PackageInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Require the given version of the package be installed.
|
||||
* The install will only be allowed if the existing version code of
|
||||
* the package installed on the device matches the given version code.
|
||||
* Use {@link * PackageManager#VERSION_CODE_HIGHEST} to allow
|
||||
* installation regardless of the currently installed package version.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setRequiredInstalledVersionCode(long versionCode) {
|
||||
requiredInstalledVersionCode = versionCode;
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
public void setInstallFlagsForcePermissionPrompt() {
|
||||
installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
|
||||
@@ -1703,6 +1720,7 @@ public class PackageInstaller {
|
||||
pw.printPair("installerPackageName", installerPackageName);
|
||||
pw.printPair("isMultiPackage", isMultiPackage);
|
||||
pw.printPair("isStaged", isStaged);
|
||||
pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode);
|
||||
pw.println();
|
||||
}
|
||||
|
||||
@@ -1731,6 +1749,7 @@ public class PackageInstaller {
|
||||
dest.writeString(installerPackageName);
|
||||
dest.writeBoolean(isMultiPackage);
|
||||
dest.writeBoolean(isStaged);
|
||||
dest.writeLong(requiredInstalledVersionCode);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SessionParams>
|
||||
|
||||
@@ -1423,6 +1423,14 @@ public abstract class PackageManager {
|
||||
*/
|
||||
public static final int INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY = -120;
|
||||
|
||||
/**
|
||||
* Installation failed return code: the required installed version code
|
||||
* does not match the currently installed package version code.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int INSTALL_FAILED_WRONG_INSTALLED_VERSION = -121;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
|
||||
DELETE_KEEP_DATA,
|
||||
@@ -6918,6 +6926,7 @@ public abstract class PackageManager {
|
||||
case INSTALL_FAILED_BAD_DEX_METADATA: return "INSTALL_FAILED_BAD_DEX_METADATA";
|
||||
case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT";
|
||||
case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE";
|
||||
case INSTALL_FAILED_WRONG_INSTALLED_VERSION: return "INSTALL_FAILED_WRONG_INSTALLED_VERSION";
|
||||
default: return Integer.toString(status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,8 @@ public class PackageHelper {
|
||||
public static final int RECOMMEND_MEDIA_UNAVAILABLE = -5;
|
||||
public static final int RECOMMEND_FAILED_INVALID_URI = -6;
|
||||
public static final int RECOMMEND_FAILED_VERSION_DOWNGRADE = -7;
|
||||
/** {@hide} */
|
||||
public static final int RECOMMEND_FAILED_WRONG_INSTALLED_VERSION = -8;
|
||||
|
||||
private static final String TAG = "PackageHelper";
|
||||
// App installation location settings values
|
||||
|
||||
@@ -14996,12 +14996,14 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
final int installReason;
|
||||
@Nullable
|
||||
MultiPackageInstallParams mParentInstallParams;
|
||||
final long requiredInstalledVersionCode;
|
||||
|
||||
InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
|
||||
int installFlags, String installerPackageName, String volumeUuid,
|
||||
VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
|
||||
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
|
||||
PackageParser.SigningDetails signingDetails, int installReason) {
|
||||
PackageParser.SigningDetails signingDetails, int installReason,
|
||||
long requiredInstalledVersionCode) {
|
||||
super(user);
|
||||
this.origin = origin;
|
||||
this.move = move;
|
||||
@@ -15015,6 +15017,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
|
||||
this.signingDetails = signingDetails;
|
||||
this.installReason = installReason;
|
||||
this.requiredInstalledVersionCode = requiredInstalledVersionCode;
|
||||
}
|
||||
|
||||
InstallParams(ActiveInstallSession activeInstallSession) {
|
||||
@@ -15045,6 +15048,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
whitelistedRestrictedPermissions = activeInstallSession.getSessionParams()
|
||||
.whitelistedRestrictedPermissions;
|
||||
signingDetails = activeInstallSession.getSigningDetails();
|
||||
requiredInstalledVersionCode = activeInstallSession.getSessionParams()
|
||||
.requiredInstalledVersionCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -15073,6 +15078,23 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
|
||||
if (dataOwnerPkg == null) {
|
||||
Slog.w(TAG, "Required installed version code was "
|
||||
+ requiredInstalledVersionCode
|
||||
+ " but package is not installed");
|
||||
return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION;
|
||||
}
|
||||
|
||||
if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
|
||||
Slog.w(TAG, "Required installed version code was "
|
||||
+ requiredInstalledVersionCode
|
||||
+ " but actual installed version is "
|
||||
+ dataOwnerPkg.getLongVersionCode());
|
||||
return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
if (dataOwnerPkg != null) {
|
||||
if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
|
||||
dataOwnerPkg.applicationInfo.flags)) {
|
||||
@@ -15199,6 +15221,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
loc = installLocationPolicy(pkgLite);
|
||||
if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
|
||||
ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
|
||||
} else if (loc == PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION) {
|
||||
ret = PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
|
||||
} else if (!onInt) {
|
||||
// Override install location with flags
|
||||
if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
|
||||
@@ -23311,7 +23335,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
installerPackageName, volumeUuid, null /*verificationInfo*/, user,
|
||||
packageAbiOverride, null /*grantedPermissions*/,
|
||||
null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN,
|
||||
PackageManager.INSTALL_REASON_UNKNOWN);
|
||||
PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST);
|
||||
params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
|
||||
msg.obj = params;
|
||||
|
||||
|
||||
@@ -163,6 +163,22 @@ public class StagingManager {
|
||||
continue;
|
||||
}
|
||||
long activeVersion = activePackage.applicationInfo.longVersionCode;
|
||||
if (session.params.requiredInstalledVersionCode
|
||||
!= PackageManager.VERSION_CODE_HIGHEST) {
|
||||
if (activeVersion != session.params.requiredInstalledVersionCode) {
|
||||
session.setStagedSessionFailed(
|
||||
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
|
||||
"Installed version of APEX package " + newPackage.packageName
|
||||
+ " does not match required. Active version: " + activeVersion
|
||||
+ " required: " + session.params.requiredInstalledVersionCode);
|
||||
|
||||
if (!mApexManager.abortActiveSession()) {
|
||||
Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
|
||||
session.params.installFlags, activePackage.applicationInfo.flags);
|
||||
if (activeVersion > newPackage.versionCode && !allowsDowngrade) {
|
||||
|
||||
@@ -357,31 +357,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify the RollbackData is up to date with what's installed on
|
||||
// device.
|
||||
// TODO: We assume that between now and the time we commit the
|
||||
// downgrade install, the currently installed package version does not
|
||||
// change. This is not safe to assume, particularly in the case of a
|
||||
// rollback racing with a roll-forward fix of a buggy package.
|
||||
// Figure out how to ensure we don't commit the rollback if
|
||||
// roll forward happens at the same time.
|
||||
for (PackageRollbackInfo info : data.info.getPackages()) {
|
||||
VersionedPackage installedVersion = getInstalledPackageVersion(info.getPackageName());
|
||||
if (installedVersion == null) {
|
||||
// TODO: Test this case
|
||||
sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
|
||||
"Package to roll back is not installed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!packageVersionsEqual(info.getVersionRolledBackFrom(), installedVersion)) {
|
||||
// TODO: Test this case
|
||||
sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
|
||||
"Package version to roll back not installed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get a context for the caller to use to install the downgraded
|
||||
// version of the package.
|
||||
Context context = null;
|
||||
@@ -420,6 +395,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
||||
}
|
||||
}
|
||||
params.setRequestDowngrade(true);
|
||||
params.setRequiredInstalledVersionCode(
|
||||
info.getVersionRolledBackFrom().getLongVersionCode());
|
||||
if (data.isStaged()) {
|
||||
params.setStaged();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,14 @@ android_test_helper_app {
|
||||
resource_dirs: ["TestApp/res_v2"],
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "RollbackTestAppAv3",
|
||||
manifest: "TestApp/Av3.xml",
|
||||
sdk_version: "current",
|
||||
srcs: ["TestApp/src/**/*.java"],
|
||||
resource_dirs: ["TestApp/res_v3"],
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "RollbackTestAppACrashingV2",
|
||||
manifest: "TestApp/ACrashingV2.xml",
|
||||
@@ -118,6 +126,7 @@ android_test {
|
||||
java_resources: [
|
||||
":RollbackTestAppAv1",
|
||||
":RollbackTestAppAv2",
|
||||
":RollbackTestAppAv3",
|
||||
":RollbackTestAppACrashingV2",
|
||||
":RollbackTestAppBv1",
|
||||
":RollbackTestAppBv2",
|
||||
|
||||
@@ -871,6 +871,51 @@ public class RollbackTest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test race between roll back and roll forward.
|
||||
*/
|
||||
@Test
|
||||
public void testRollForwardRace() throws Exception {
|
||||
try {
|
||||
RollbackTestUtils.adoptShellPermissionIdentity(
|
||||
Manifest.permission.INSTALL_PACKAGES,
|
||||
Manifest.permission.DELETE_PACKAGES,
|
||||
Manifest.permission.TEST_MANAGE_ROLLBACKS,
|
||||
Manifest.permission.MANAGE_ROLLBACKS);
|
||||
|
||||
RollbackManager rm = RollbackTestUtils.getRollbackManager();
|
||||
|
||||
RollbackTestUtils.uninstall(TEST_APP_A);
|
||||
RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
|
||||
RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
|
||||
assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
|
||||
|
||||
RollbackInfo rollback = getUniqueRollbackInfoForPackage(
|
||||
rm.getAvailableRollbacks(), TEST_APP_A);
|
||||
assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
|
||||
|
||||
// Install a new version of package A, then immediately rollback
|
||||
// the previous version. We expect the rollback to fail, because
|
||||
// it is no longer available.
|
||||
// There are a couple different ways this could fail depending on
|
||||
// thread interleaving, so don't ignore flaky failures.
|
||||
RollbackTestUtils.install("RollbackTestAppAv3.apk", false);
|
||||
try {
|
||||
RollbackTestUtils.rollback(rollback.getRollbackId());
|
||||
// Note: Don't ignore flaky failures here.
|
||||
fail("Expected rollback to fail, but it did not.");
|
||||
} catch (AssertionError e) {
|
||||
Log.i(TAG, "Note expected failure: ", e);
|
||||
// Expected
|
||||
}
|
||||
|
||||
// Note: Don't ignore flaky failures here.
|
||||
assertEquals(3, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
|
||||
} finally {
|
||||
RollbackTestUtils.dropShellPermissionIdentity();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to test that the given rollback info is a rollback for
|
||||
// the atomic set {A2, B2} -> {A1, B1}.
|
||||
private void assertRollbackInfoForAandB(RollbackInfo rollback) {
|
||||
|
||||
35
tests/RollbackTest/TestApp/Av3.xml
Normal file
35
tests/RollbackTest/TestApp/Av3.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2018 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.tests.rollback.testapp.A"
|
||||
android:versionCode="3"
|
||||
android:versionName="3.0" >
|
||||
|
||||
|
||||
<uses-sdk android:minSdkVersion="19" />
|
||||
|
||||
<application android:label="Rollback Test App A v3">
|
||||
<receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
|
||||
android:exported="true" />
|
||||
<activity android:name="com.android.tests.rollback.testapp.MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
19
tests/RollbackTest/TestApp/res_v3/values-anydpi/values.xml
Normal file
19
tests/RollbackTest/TestApp/res_v3/values-anydpi/values.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<integer name="split_version">3</integer>
|
||||
</resources>
|
||||
20
tests/RollbackTest/TestApp/res_v3/values/values.xml
Normal file
20
tests/RollbackTest/TestApp/res_v3/values/values.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<integer name="app_version">3</integer>
|
||||
<integer name="split_version">0</integer>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user