Test that upgrading target apk invalidates idmap

This change adds tests that run in presubmit to verify that updating
the target APK invalidates the idmap for overlays targeting the
target.

Bug: 147794117
Bug: 150877400
Test: atest PackagedUpgradedTest
Change-Id: I0268ea99dc7beb4bbc7009a67d9929e2d82862cb
This commit is contained in:
Ryan Mitchell
2020-03-25 14:41:54 -07:00
parent a90930528d
commit 09943af999
25 changed files with 333 additions and 81 deletions

View File

@@ -3,5 +3,10 @@
{
"name" : "idmap2_tests"
}
],
"imports": [
{
"path": "frameworks/base/services/core/java/com/android/server/om"
}
]
}

View File

@@ -24,5 +24,7 @@ java_test_host {
":OverlayRemountedTest_SharedLibrary",
":OverlayRemountedTest_SharedLibraryOverlay",
":OverlayRemountedTest_Target",
":OverlayRemountedTest_TargetUpgrade",
":OverlayRemountedTest_Overlay",
],
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2020 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.
*/
package com.android.overlaytest.remounted;
import static org.junit.Assert.fail;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.RuleChain;
import org.junit.rules.TemporaryFolder;
public class OverlayRemountedTestBase extends BaseHostJUnit4Test {
static final long ASSERT_RESOURCE_TIMEOUT_MS = 30000;
static final String TARGET_APK = "OverlayRemountedTest_Target.apk";
static final String TARGET_PACKAGE = "com.android.overlaytest.remounted.target";
static final String TARGET_UPGRADE_APK = "OverlayRemountedTest_TargetUpgrade.apk";
static final String OVERLAY_APK = "OverlayRemountedTest_Overlay.apk";
static final String OVERLAY_PACKAGE = "com.android.overlaytest.remounted.target.overlay";
static final String SHARED_LIBRARY_APK =
"OverlayRemountedTest_SharedLibrary.apk";
static final String SHARED_LIBRARY_PACKAGE =
"com.android.overlaytest.remounted.shared_library";
static final String SHARED_LIBRARY_OVERLAY_APK =
"OverlayRemountedTest_SharedLibraryOverlay.apk";
static final String SHARED_LIBRARY_OVERLAY_PACKAGE =
"com.android.overlaytest.remounted.shared_library.overlay";
private final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
protected final SystemPreparer mPreparer = new SystemPreparer(mTemporaryFolder,
this::getDevice);
@Rule
public final RuleChain ruleChain = RuleChain.outerRule(mTemporaryFolder).around(mPreparer);
@Before
public void startBefore() throws DeviceNotAvailableException {
getDevice().waitForDeviceAvailable();
}
/** Builds the full name of a resource in the form package:type/entry. */
String resourceName(String pkg, String type, String entry) {
return String.format("%s:%s/%s", pkg, type, entry);
}
void assertResource(String resourceName, String expectedValue)
throws DeviceNotAvailableException {
String result = null;
final long endMillis = System.currentTimeMillis() + ASSERT_RESOURCE_TIMEOUT_MS;
while (System.currentTimeMillis() <= endMillis) {
result = getDevice().executeShellCommand(
String.format("cmd overlay lookup %s %s", TARGET_PACKAGE, resourceName));
if (result.equals(expectedValue + "\n") ||
result.endsWith("-> " + expectedValue + "\n")) {
return;
}
try {
Thread.sleep(200);
} catch (InterruptedException ignore) {
}
}
fail(String.format("expected: <[%s]> in: <[%s]>", expectedValue, result));
}
}

View File

@@ -16,42 +16,13 @@
package com.android.overlaytest.remounted;
import static org.junit.Assert.assertTrue;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
@RunWith(DeviceJUnit4ClassRunner.class)
public class OverlaySharedLibraryTest extends BaseHostJUnit4Test {
private static final String TARGET_APK = "OverlayRemountedTest_Target.apk";
private static final String TARGET_PACKAGE = "com.android.overlaytest.remounted.target";
private static final String SHARED_LIBRARY_APK =
"OverlayRemountedTest_SharedLibrary.apk";
private static final String SHARED_LIBRARY_PACKAGE =
"com.android.overlaytest.remounted.shared_library";
private static final String SHARED_LIBRARY_OVERLAY_APK =
"OverlayRemountedTest_SharedLibraryOverlay.apk";
private static final String SHARED_LIBRARY_OVERLAY_PACKAGE =
"com.android.overlaytest.remounted.shared_library.overlay";
public final TemporaryFolder temporaryFolder = new TemporaryFolder();
public final SystemPreparer preparer = new SystemPreparer(temporaryFolder, this::getDevice);
@Rule
public final RuleChain ruleChain = RuleChain.outerRule(temporaryFolder).around(preparer);
@Before
public void startBefore() throws DeviceNotAvailableException {
getDevice().waitForDeviceAvailable();
}
public class OverlaySharedLibraryTest extends OverlayRemountedTestBase {
@Test
public void testSharedLibrary() throws Exception {
@@ -60,7 +31,7 @@ public class OverlaySharedLibraryTest extends BaseHostJUnit4Test {
final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool",
"shared_library_overlaid");
preparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
mPreparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
.installResourceApk(SHARED_LIBRARY_OVERLAY_APK, SHARED_LIBRARY_OVERLAY_PACKAGE)
.reboot()
.setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, false)
@@ -71,7 +42,7 @@ public class OverlaySharedLibraryTest extends BaseHostJUnit4Test {
assertResource(libraryResource, "false");
// Overlay the shared library resource.
preparer.setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true);
mPreparer.setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true);
assertResource(targetResource, "true");
assertResource(libraryResource, "true");
}
@@ -83,7 +54,7 @@ public class OverlaySharedLibraryTest extends BaseHostJUnit4Test {
final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool",
"shared_library_overlaid");
preparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
mPreparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
.installResourceApk(SHARED_LIBRARY_OVERLAY_APK, SHARED_LIBRARY_OVERLAY_PACKAGE)
.setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true)
.reboot()
@@ -92,18 +63,4 @@ public class OverlaySharedLibraryTest extends BaseHostJUnit4Test {
assertResource(targetResource, "true");
assertResource(libraryResource, "true");
}
/** Builds the full name of a resource in the form package:type/entry. */
String resourceName(String pkg, String type, String entry) {
return String.format("%s:%s/%s", pkg, type, entry);
}
void assertResource(String resourceName, String expectedValue)
throws DeviceNotAvailableException {
final String result = getDevice().executeShellCommand(
String.format("cmd overlay lookup %s %s", TARGET_PACKAGE, resourceName));
assertTrue(String.format("expected: <[%s]> in: <[%s]>", expectedValue, result),
result.equals(expectedValue + "\n") ||
result.endsWith("-> " + expectedValue + "\n"));
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2020 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.
*/
package com.android.overlaytest.remounted;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(DeviceJUnit4ClassRunner.class)
public class PackagedUpgradedTest extends OverlayRemountedTestBase {
@Test
public void testTargetUpgrade() throws Exception {
final String targetOverlaid = resourceName(TARGET_PACKAGE, "bool", "target_overlaid");
final String targetReference = resourceName(TARGET_PACKAGE, "bool", "target_reference");
mPreparer.pushResourceFile(TARGET_APK, "/product/app/OverlayTarget.apk")
.reboot()
.installResourceApk(OVERLAY_APK, OVERLAY_PACKAGE)
.setOverlayEnabled(OVERLAY_PACKAGE, true);
assertResource(targetReference, "@" + 0x7f010000 + " -> true");
assertResource(targetOverlaid, "true");
mPreparer.installResourceApk(TARGET_UPGRADE_APK, TARGET_PACKAGE);
assertResource(targetReference, "@" + 0x7f0100ff + " -> true");
assertResource(targetOverlaid, "true");
}
@Test
public void testTargetRelocated() throws Exception {
final String targetOverlaid = resourceName(TARGET_PACKAGE, "bool", "target_overlaid");
final String originalPath = "/product/app/OverlayTarget.apk";
mPreparer.pushResourceFile(TARGET_APK, originalPath)
.reboot()
.installResourceApk(OVERLAY_APK, OVERLAY_PACKAGE)
.setOverlayEnabled(OVERLAY_PACKAGE, true);
assertResource(targetOverlaid, "true");
mPreparer.remount();
getDevice().deleteFile(originalPath);
mPreparer.pushResourceFile(TARGET_UPGRADE_APK, "/product/app/OverlayTarget2.apk")
.reboot();
assertResource(targetOverlaid, "true");
}
}

View File

@@ -18,8 +18,6 @@ package com.android.overlaytest.remounted;
import static org.junit.Assert.assertTrue;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
@@ -32,10 +30,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeoutException;
class SystemPreparer extends ExternalResource {
private static final long OVERLAY_ENABLE_TIMEOUT_MS = 30000;
@@ -58,7 +52,7 @@ class SystemPreparer extends ExternalResource {
SystemPreparer pushResourceFile(String resourcePath,
String outputPath) throws DeviceNotAvailableException, IOException {
final ITestDevice device = mDeviceProvider.getDevice();
device.executeAdbCommand("remount");
remount();
assertTrue(device.pushFile(copyResourceToTemp(resourcePath), outputPath));
mPushedFiles.add(outputPath);
return this;
@@ -69,7 +63,7 @@ class SystemPreparer extends ExternalResource {
throws DeviceNotAvailableException, IOException {
final ITestDevice device = mDeviceProvider.getDevice();
final File tmpFile = copyResourceToTemp(resourcePath);
final String result = device.installPackage(tmpFile, true);
final String result = device.installPackage(tmpFile, true /* reinstall */);
Assert.assertNull(result);
mInstalledPackages.add(packageName);
return this;
@@ -77,34 +71,29 @@ class SystemPreparer extends ExternalResource {
/** Sets the enable state of an overlay pacakage. */
SystemPreparer setOverlayEnabled(String packageName, boolean enabled)
throws ExecutionException, DeviceNotAvailableException {
throws DeviceNotAvailableException {
final ITestDevice device = mDeviceProvider.getDevice();
final String enable = enabled ? "enable" : "disable";
// Wait for the overlay to change its enabled state.
final FutureTask<Boolean> enabledListener = new FutureTask<>(() -> {
while (true) {
device.executeShellCommand(String.format("cmd overlay %s %s",
enabled ? "enable" : "disable", packageName));
final String result = device.executeShellCommand("cmd overlay dump " + packageName);
final int startIndex = result.indexOf("mIsEnabled");
final int endIndex = result.indexOf('\n', startIndex);
if (result.substring(startIndex, endIndex).contains((enabled) ? "true" : "false")) {
return true;
}
final long endMillis = System.currentTimeMillis() + OVERLAY_ENABLE_TIMEOUT_MS;
String result;
while (System.currentTimeMillis() <= endMillis) {
device.executeShellCommand(String.format("cmd overlay %s %s", enable, packageName));
result = device.executeShellCommand("cmd overlay dump isenabled "
+ packageName);
if (((enabled) ? "true\n" : "false\n").equals(result)) {
return this;
}
});
final Executor executor = (cmd) -> new Thread(cmd).start();
executor.execute(enabledListener);
try {
enabledListener.get(OVERLAY_ENABLE_TIMEOUT_MS, MILLISECONDS);
} catch (InterruptedException ignored) {
} catch (TimeoutException e) {
throw new IllegalStateException(device.executeShellCommand("cmd overlay list"));
try {
Thread.sleep(200);
} catch (InterruptedException ignore) {
}
}
return this;
throw new IllegalStateException(String.format("Failed to %s overlay %s:\n%s", enable,
packageName, device.executeShellCommand("cmd overlay list")));
}
/** Restarts the device and waits until after boot is completed. */
@@ -114,6 +103,11 @@ class SystemPreparer extends ExternalResource {
return this;
}
SystemPreparer remount() throws DeviceNotAvailableException {
mDeviceProvider.getDevice().executeAdbCommand("remount");
return this;
}
/** Copies a file within the host test jar to a temporary file on the host machine. */
private File copyResourceToTemp(String resourcePath) throws IOException {
final File tempFile = mHostTempFolder.newFile(resourcePath);
@@ -138,7 +132,7 @@ class SystemPreparer extends ExternalResource {
protected void after() {
final ITestDevice device = mDeviceProvider.getDevice();
try {
device.executeAdbCommand("remount");
remount();
for (final String file : mPushedFiles) {
device.deleteFile(file);
}

View File

@@ -0,0 +1,18 @@
// 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.
android_test_helper_app {
name: "OverlayRemountedTest_Overlay",
sdk_version: "current",
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2020 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.overlaytest.remounted.target.overlay">
<application android:hasCode="false" />
<overlay android:targetPackage="com.android.overlaytest.remounted.target"
android:targetName="TestResources" />
</manifest>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2020 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>
<bool name="target_overlaid">true</bool>
</resources>

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2018 The Android Open Source Project
// 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.
@@ -14,7 +14,12 @@
android_test_helper_app {
name: "OverlayRemountedTest_Target",
srcs: ["src/**/*.java"],
sdk_version: "test_current",
libs: ["OverlayRemountedTest_SharedLibrary"],
}
android_test_helper_app {
name: "OverlayRemountedTest_TargetUpgrade",
resource_dirs: ["res_upgrade"],
sdk_version: "test_current",
}

View File

@@ -19,8 +19,7 @@
package="com.android.overlaytest.remounted.target">
<application>
<uses-library android:name="android.test.runner" />
<uses-library android:name="com.android.overlaytest.remounted.shared_library"
android:required="true" />
android:required="false" />
</application>
</manifest>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2020 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>
<overlayable name="TestResources">
<policy type="public">
<item type="bool" name="target_overlaid" />
</policy>
</overlayable>
</resources>

View File

@@ -17,4 +17,10 @@
<resources xmlns:sharedlib="http://schemas.android.com/apk/res/com.android.overlaytest.remounted.shared_library">
<bool name="uses_shared_library_overlaid">@sharedlib:bool/shared_library_overlaid</bool>
<!-- This resource has a different id in the updated version of this target app to test that the
idmap is regenerated when the target is updated. -->
<bool name="target_overlaid">false</bool>
<public type="bool" name="target_overlaid" id="0x7f010000" />
<bool name="target_reference">@bool/target_overlaid</bool>
</resources>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2020 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>
<overlayable name="TestResources">
<policy type="public">
<item type="bool" name="target_overlaid" />
</policy>
</overlayable>
</resources>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2020 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>
<!-- This resource has a different id in the updated target app than the base target app to test
that the idmap is regenerated when the target is updated. -->
<bool name="target_overlaid">false</bool>
<public type="bool" name="target_overlaid" id="0x7f0100ff" />
<bool name="target_reference">@bool/target_overlaid</bool>
</resources>

View File

@@ -14,6 +14,9 @@
{
"name": "OverlayHostTests"
},
{
"name": "OverlayRemountedTest"
},
{
"name": "CtsAppSecurityHostTestCases",
"options": [