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;
|
public boolean isMultiPackage;
|
||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
public boolean isStaged;
|
public boolean isStaged;
|
||||||
|
/** {@hide} */
|
||||||
|
public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct parameters for a new package install session.
|
* Construct parameters for a new package install session.
|
||||||
@@ -1350,6 +1352,7 @@ public class PackageInstaller {
|
|||||||
installerPackageName = source.readString();
|
installerPackageName = source.readString();
|
||||||
isMultiPackage = source.readBoolean();
|
isMultiPackage = source.readBoolean();
|
||||||
isStaged = source.readBoolean();
|
isStaged = source.readBoolean();
|
||||||
|
requiredInstalledVersionCode = source.readLong();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
@@ -1372,6 +1375,7 @@ public class PackageInstaller {
|
|||||||
ret.installerPackageName = installerPackageName;
|
ret.installerPackageName = installerPackageName;
|
||||||
ret.isMultiPackage = isMultiPackage;
|
ret.isMultiPackage = isMultiPackage;
|
||||||
ret.isStaged = isStaged;
|
ret.isStaged = isStaged;
|
||||||
|
ret.requiredInstalledVersionCode = requiredInstalledVersionCode;
|
||||||
return ret;
|
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} */
|
/** {@hide} */
|
||||||
public void setInstallFlagsForcePermissionPrompt() {
|
public void setInstallFlagsForcePermissionPrompt() {
|
||||||
installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
|
installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
|
||||||
@@ -1703,6 +1720,7 @@ public class PackageInstaller {
|
|||||||
pw.printPair("installerPackageName", installerPackageName);
|
pw.printPair("installerPackageName", installerPackageName);
|
||||||
pw.printPair("isMultiPackage", isMultiPackage);
|
pw.printPair("isMultiPackage", isMultiPackage);
|
||||||
pw.printPair("isStaged", isStaged);
|
pw.printPair("isStaged", isStaged);
|
||||||
|
pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode);
|
||||||
pw.println();
|
pw.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1731,6 +1749,7 @@ public class PackageInstaller {
|
|||||||
dest.writeString(installerPackageName);
|
dest.writeString(installerPackageName);
|
||||||
dest.writeBoolean(isMultiPackage);
|
dest.writeBoolean(isMultiPackage);
|
||||||
dest.writeBoolean(isStaged);
|
dest.writeBoolean(isStaged);
|
||||||
|
dest.writeLong(requiredInstalledVersionCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Parcelable.Creator<SessionParams>
|
public static final Parcelable.Creator<SessionParams>
|
||||||
|
|||||||
@@ -1423,6 +1423,14 @@ public abstract class PackageManager {
|
|||||||
*/
|
*/
|
||||||
public static final int INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY = -120;
|
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 */
|
/** @hide */
|
||||||
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
|
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
|
||||||
DELETE_KEEP_DATA,
|
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_BAD_DEX_METADATA: return "INSTALL_FAILED_BAD_DEX_METADATA";
|
||||||
case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT";
|
case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT";
|
||||||
case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE";
|
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);
|
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_MEDIA_UNAVAILABLE = -5;
|
||||||
public static final int RECOMMEND_FAILED_INVALID_URI = -6;
|
public static final int RECOMMEND_FAILED_INVALID_URI = -6;
|
||||||
public static final int RECOMMEND_FAILED_VERSION_DOWNGRADE = -7;
|
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";
|
private static final String TAG = "PackageHelper";
|
||||||
// App installation location settings values
|
// App installation location settings values
|
||||||
|
|||||||
@@ -14996,12 +14996,14 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
final int installReason;
|
final int installReason;
|
||||||
@Nullable
|
@Nullable
|
||||||
MultiPackageInstallParams mParentInstallParams;
|
MultiPackageInstallParams mParentInstallParams;
|
||||||
|
final long requiredInstalledVersionCode;
|
||||||
|
|
||||||
InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
|
InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
|
||||||
int installFlags, String installerPackageName, String volumeUuid,
|
int installFlags, String installerPackageName, String volumeUuid,
|
||||||
VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
|
VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
|
||||||
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
|
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
|
||||||
PackageParser.SigningDetails signingDetails, int installReason) {
|
PackageParser.SigningDetails signingDetails, int installReason,
|
||||||
|
long requiredInstalledVersionCode) {
|
||||||
super(user);
|
super(user);
|
||||||
this.origin = origin;
|
this.origin = origin;
|
||||||
this.move = move;
|
this.move = move;
|
||||||
@@ -15015,6 +15017,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
|
this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
|
||||||
this.signingDetails = signingDetails;
|
this.signingDetails = signingDetails;
|
||||||
this.installReason = installReason;
|
this.installReason = installReason;
|
||||||
|
this.requiredInstalledVersionCode = requiredInstalledVersionCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstallParams(ActiveInstallSession activeInstallSession) {
|
InstallParams(ActiveInstallSession activeInstallSession) {
|
||||||
@@ -15045,6 +15048,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
whitelistedRestrictedPermissions = activeInstallSession.getSessionParams()
|
whitelistedRestrictedPermissions = activeInstallSession.getSessionParams()
|
||||||
.whitelistedRestrictedPermissions;
|
.whitelistedRestrictedPermissions;
|
||||||
signingDetails = activeInstallSession.getSigningDetails();
|
signingDetails = activeInstallSession.getSigningDetails();
|
||||||
|
requiredInstalledVersionCode = activeInstallSession.getSessionParams()
|
||||||
|
.requiredInstalledVersionCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 (dataOwnerPkg != null) {
|
||||||
if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
|
if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
|
||||||
dataOwnerPkg.applicationInfo.flags)) {
|
dataOwnerPkg.applicationInfo.flags)) {
|
||||||
@@ -15199,6 +15221,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
loc = installLocationPolicy(pkgLite);
|
loc = installLocationPolicy(pkgLite);
|
||||||
if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
|
if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
|
||||||
ret = PackageManager.INSTALL_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) {
|
} else if (!onInt) {
|
||||||
// Override install location with flags
|
// Override install location with flags
|
||||||
if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
|
if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
|
||||||
@@ -23311,7 +23335,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
installerPackageName, volumeUuid, null /*verificationInfo*/, user,
|
installerPackageName, volumeUuid, null /*verificationInfo*/, user,
|
||||||
packageAbiOverride, null /*grantedPermissions*/,
|
packageAbiOverride, null /*grantedPermissions*/,
|
||||||
null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN,
|
null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN,
|
||||||
PackageManager.INSTALL_REASON_UNKNOWN);
|
PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST);
|
||||||
params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
|
params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
|
||||||
msg.obj = params;
|
msg.obj = params;
|
||||||
|
|
||||||
|
|||||||
@@ -163,6 +163,22 @@ public class StagingManager {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
long activeVersion = activePackage.applicationInfo.longVersionCode;
|
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(
|
boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
|
||||||
session.params.installFlags, activePackage.applicationInfo.flags);
|
session.params.installFlags, activePackage.applicationInfo.flags);
|
||||||
if (activeVersion > newPackage.versionCode && !allowsDowngrade) {
|
if (activeVersion > newPackage.versionCode && !allowsDowngrade) {
|
||||||
|
|||||||
@@ -357,31 +357,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
|||||||
return;
|
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
|
// Get a context for the caller to use to install the downgraded
|
||||||
// version of the package.
|
// version of the package.
|
||||||
Context context = null;
|
Context context = null;
|
||||||
@@ -420,6 +395,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
params.setRequestDowngrade(true);
|
params.setRequestDowngrade(true);
|
||||||
|
params.setRequiredInstalledVersionCode(
|
||||||
|
info.getVersionRolledBackFrom().getLongVersionCode());
|
||||||
if (data.isStaged()) {
|
if (data.isStaged()) {
|
||||||
params.setStaged();
|
params.setStaged();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,14 @@ android_test_helper_app {
|
|||||||
resource_dirs: ["TestApp/res_v2"],
|
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 {
|
android_test_helper_app {
|
||||||
name: "RollbackTestAppACrashingV2",
|
name: "RollbackTestAppACrashingV2",
|
||||||
manifest: "TestApp/ACrashingV2.xml",
|
manifest: "TestApp/ACrashingV2.xml",
|
||||||
@@ -118,6 +126,7 @@ android_test {
|
|||||||
java_resources: [
|
java_resources: [
|
||||||
":RollbackTestAppAv1",
|
":RollbackTestAppAv1",
|
||||||
":RollbackTestAppAv2",
|
":RollbackTestAppAv2",
|
||||||
|
":RollbackTestAppAv3",
|
||||||
":RollbackTestAppACrashingV2",
|
":RollbackTestAppACrashingV2",
|
||||||
":RollbackTestAppBv1",
|
":RollbackTestAppBv1",
|
||||||
":RollbackTestAppBv2",
|
":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
|
// Helper function to test that the given rollback info is a rollback for
|
||||||
// the atomic set {A2, B2} -> {A1, B1}.
|
// the atomic set {A2, B2} -> {A1, B1}.
|
||||||
private void assertRollbackInfoForAandB(RollbackInfo rollback) {
|
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