Allow for overlaying dynamic shared libraries
Overlays targeting shared libraries should be loaded into the resources of every target that depends on the shared library. Static shared libraries are currently not supported because overlays should override all versions of static shared libraries and there is not currently support for an overlay targeting multiple APKs. Also created a test instrumentation and host test suite for testing overlays and packages on the system image. Bug: 140790224 Test: atest OverlayRemountedTest Change-Id: I20a217b6368d6cf92b2b9f46908fd58012933f72
This commit is contained in:
parent
fe50d739f7
commit
ee4a564d4f
@ -33,6 +33,8 @@ namespace android::idmap2 {
|
||||
|
||||
namespace {
|
||||
|
||||
#define REWRITE_PACKAGE(resid, package_id) \
|
||||
(((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U))
|
||||
#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
|
||||
|
||||
std::string ConcatPolicies(const std::vector<std::string>& policies) {
|
||||
@ -154,6 +156,7 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManage
|
||||
return Error("root element is not <overlay> tag");
|
||||
}
|
||||
|
||||
const uint8_t target_package_id = target_package->GetPackageId();
|
||||
const uint8_t overlay_package_id = overlay_package->GetPackageId();
|
||||
auto overlay_it_end = root_it.end();
|
||||
for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
|
||||
@ -187,6 +190,9 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManage
|
||||
continue;
|
||||
}
|
||||
|
||||
// Retrieve the compile-time resource id of the target resource.
|
||||
target_id = REWRITE_PACKAGE(target_id, target_package_id);
|
||||
|
||||
if (overlay_resource->dataType == Res_value::TYPE_STRING) {
|
||||
overlay_resource->data += string_pool_offset;
|
||||
}
|
||||
@ -214,6 +220,7 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy(
|
||||
const AssetManager2* target_am, const AssetManager2* overlay_am,
|
||||
const LoadedPackage* target_package, const LoadedPackage* overlay_package) {
|
||||
ResourceMapping resource_mapping;
|
||||
const uint8_t target_package_id = target_package->GetPackageId();
|
||||
const auto end = overlay_package->end();
|
||||
for (auto iter = overlay_package->begin(); iter != end; ++iter) {
|
||||
const ResourceId overlay_resid = *iter;
|
||||
@ -225,11 +232,14 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy(
|
||||
// Find the resource with the same type and entry name within the target package.
|
||||
const std::string full_name =
|
||||
base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str());
|
||||
const ResourceId target_resource = target_am->GetResourceId(full_name);
|
||||
ResourceId target_resource = target_am->GetResourceId(full_name);
|
||||
if (target_resource == 0U) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Retrieve the compile-time resource id of the target resource.
|
||||
target_resource = REWRITE_PACKAGE(target_resource, target_package_id);
|
||||
|
||||
resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid,
|
||||
/* rewrite_overlay_reference */ false);
|
||||
}
|
||||
|
@ -482,19 +482,6 @@ public class ResourcesManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (key.mOverlayDirs != null) {
|
||||
for (final String idmapPath : key.mOverlayDirs) {
|
||||
try {
|
||||
builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/,
|
||||
true /*overlay*/));
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "failed to add overlay path " + idmapPath);
|
||||
|
||||
// continue.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (key.mLibDirs != null) {
|
||||
for (final String libDir : key.mLibDirs) {
|
||||
if (libDir.endsWith(".apk")) {
|
||||
@ -513,6 +500,19 @@ public class ResourcesManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (key.mOverlayDirs != null) {
|
||||
for (final String idmapPath : key.mOverlayDirs) {
|
||||
try {
|
||||
builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/,
|
||||
true /*overlay*/));
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "failed to add overlay path " + idmapPath);
|
||||
|
||||
// continue.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
@ -7803,7 +7803,7 @@ public class PackageParser {
|
||||
ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
|
||||
}
|
||||
ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
|
||||
ai.resourceDirs = state.overlayPaths;
|
||||
ai.resourceDirs = state.getAllOverlayPaths();
|
||||
ai.icon = (sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes;
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,8 @@ import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@ -77,7 +79,9 @@ public class PackageUserState {
|
||||
public ArraySet<String> disabledComponents;
|
||||
public ArraySet<String> enabledComponents;
|
||||
|
||||
public String[] overlayPaths;
|
||||
private String[] overlayPaths;
|
||||
private ArrayMap<String, String[]> sharedLibraryOverlayPaths; // Lib name to overlay paths
|
||||
private String[] cachedOverlayPaths;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
public PackageUserState() {
|
||||
@ -112,9 +116,33 @@ public class PackageUserState {
|
||||
enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
|
||||
overlayPaths =
|
||||
o.overlayPaths == null ? null : Arrays.copyOf(o.overlayPaths, o.overlayPaths.length);
|
||||
if (o.sharedLibraryOverlayPaths != null) {
|
||||
sharedLibraryOverlayPaths = new ArrayMap<>(o.sharedLibraryOverlayPaths);
|
||||
}
|
||||
harmfulAppWarning = o.harmfulAppWarning;
|
||||
}
|
||||
|
||||
public String[] getOverlayPaths() {
|
||||
return overlayPaths;
|
||||
}
|
||||
|
||||
public void setOverlayPaths(String[] paths) {
|
||||
overlayPaths = paths;
|
||||
cachedOverlayPaths = null;
|
||||
}
|
||||
|
||||
public Map<String, String[]> getSharedLibraryOverlayPaths() {
|
||||
return sharedLibraryOverlayPaths;
|
||||
}
|
||||
|
||||
public void setSharedLibraryOverlayPaths(String library, String[] paths) {
|
||||
if (sharedLibraryOverlayPaths == null) {
|
||||
sharedLibraryOverlayPaths = new ArrayMap<>();
|
||||
}
|
||||
sharedLibraryOverlayPaths.put(library, paths);
|
||||
cachedOverlayPaths = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if this package is installed.
|
||||
*/
|
||||
@ -235,6 +263,38 @@ public class PackageUserState {
|
||||
return isComponentEnabled;
|
||||
}
|
||||
|
||||
public String[] getAllOverlayPaths() {
|
||||
if (overlayPaths == null && sharedLibraryOverlayPaths == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (cachedOverlayPaths != null) {
|
||||
return cachedOverlayPaths;
|
||||
}
|
||||
|
||||
final LinkedHashSet<String> paths = new LinkedHashSet<>();
|
||||
if (overlayPaths != null) {
|
||||
final int N = overlayPaths.length;
|
||||
for (int i = 0; i < N; i++) {
|
||||
paths.add(overlayPaths[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (sharedLibraryOverlayPaths != null) {
|
||||
for (String[] libOverlayPaths : sharedLibraryOverlayPaths.values()) {
|
||||
if (libOverlayPaths != null) {
|
||||
final int N = libOverlayPaths.length;
|
||||
for (int i = 0; i < N; i++) {
|
||||
paths.add(libOverlayPaths[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cachedOverlayPaths = paths.toArray(new String[0]);
|
||||
return cachedOverlayPaths;
|
||||
}
|
||||
|
||||
@Override
|
||||
final public boolean equals(Object obj) {
|
||||
if (!(obj instanceof PackageUserState)) {
|
||||
|
@ -41,9 +41,11 @@ import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
|
||||
import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
|
||||
import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
|
||||
import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/** @hide */
|
||||
@ -545,7 +547,7 @@ public class PackageInfoUtils {
|
||||
ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
|
||||
}
|
||||
ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
|
||||
ai.resourceDirs = state.overlayPaths;
|
||||
ai.resourceDirs = state.getAllOverlayPaths();
|
||||
ai.icon = (PackageParser.sUseRoundIcon && ai.roundIconRes != 0)
|
||||
? ai.roundIconRes : ai.iconRes;
|
||||
}
|
||||
|
7
core/tests/overlaytests/remount/TEST_MAPPING
Normal file
7
core/tests/overlaytests/remount/TEST_MAPPING
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"presubmit": [
|
||||
{
|
||||
"name" : "OverlayRemountedTest"
|
||||
}
|
||||
]
|
||||
}
|
28
core/tests/overlaytests/remount/host/Android.bp
Normal file
28
core/tests/overlaytests/remount/host/Android.bp
Normal file
@ -0,0 +1,28 @@
|
||||
// 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.
|
||||
|
||||
java_test_host {
|
||||
name: "OverlayRemountedTest",
|
||||
srcs: ["src/**/*.java"],
|
||||
libs: [
|
||||
"tradefed",
|
||||
"junit",
|
||||
],
|
||||
test_suites: ["general-tests"],
|
||||
java_resources: [
|
||||
":OverlayRemountedTest_SharedLibrary",
|
||||
":OverlayRemountedTest_SharedLibraryOverlay",
|
||||
":OverlayRemountedTest_Target",
|
||||
],
|
||||
}
|
29
core/tests/overlaytests/remount/host/AndroidTest.xml
Normal file
29
core/tests/overlaytests/remount/host/AndroidTest.xml
Normal file
@ -0,0 +1,29 @@
|
||||
<?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.
|
||||
-->
|
||||
|
||||
<configuration description="Test module config for OverlayRemountedTest">
|
||||
<option name="test-tag" value="OverlayRemountedTest" />
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
|
||||
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
|
||||
<option name="run-command" value="remount" />
|
||||
</target_preparer>
|
||||
|
||||
<test class="com.android.tradefed.testtype.HostTest">
|
||||
<option name="jar" value="OverlayRemountedTest.jar" />
|
||||
</test>
|
||||
</configuration>
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.overlaytest.remounted;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import com.android.tradefed.device.DeviceNotAvailableException;
|
||||
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.rules.RuleChain;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class OverlayHostTest extends BaseHostJUnit4Test {
|
||||
private static final long TIME_OUT_MS = 30000;
|
||||
private static final String RES_INSTRUMENTATION_ARG = "res";
|
||||
private static final String OVERLAY_INSTRUMENTATION_ARG = "overlays";
|
||||
private static final String RESOURCES_TYPE_SUFFIX = "_type";
|
||||
private static final String RESOURCES_DATA_SUFFIX = "_data";
|
||||
|
||||
public final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
|
||||
public final SystemPreparer mPreparer = new SystemPreparer(mTemporaryFolder, this::getDevice);
|
||||
|
||||
@Rule
|
||||
public final RuleChain ruleChain = RuleChain.outerRule(mTemporaryFolder).around(mPreparer);
|
||||
private Map<String, String> mLastResults;
|
||||
|
||||
/**
|
||||
* Retrieves the values of the resources in the test package. The test package must use the
|
||||
* {@link com.android.overlaytest.remounted.target.ResourceRetrievalRunner} instrumentation.
|
||||
**/
|
||||
void retrieveResource(String testPackageName, List<String> requiredOverlayPaths,
|
||||
String... resourceNames) throws DeviceNotAvailableException {
|
||||
final HashMap<String, String> args = new HashMap<>();
|
||||
if (!requiredOverlayPaths.isEmpty()) {
|
||||
// Enclose the require overlay paths in quotes so the arguments will be string arguments
|
||||
// rather than file arguments.
|
||||
args.put(OVERLAY_INSTRUMENTATION_ARG,
|
||||
String.format("\"%s\"", String.join(" ", requiredOverlayPaths)));
|
||||
}
|
||||
|
||||
if (resourceNames.length == 0) {
|
||||
throw new IllegalArgumentException("Must specify at least one resource to retrieve.");
|
||||
}
|
||||
|
||||
// Pass the names of the resources to retrieve into the test as one string.
|
||||
args.put(RES_INSTRUMENTATION_ARG,
|
||||
String.format("\"%s\"", String.join(" ", resourceNames)));
|
||||
|
||||
runDeviceTests(getDevice(), null, testPackageName, null, null, null, TIME_OUT_MS,
|
||||
TIME_OUT_MS, TIME_OUT_MS, false, false, args);
|
||||
|
||||
// Retrieve the results of the most recently run test.
|
||||
mLastResults = (getLastDeviceRunResults().getRunMetrics() == mLastResults) ? null :
|
||||
getLastDeviceRunResults().getRunMetrics();
|
||||
}
|
||||
|
||||
/** Returns the base resource directories of the specified packages. */
|
||||
List<String> getPackagePaths(String... packageNames)
|
||||
throws DeviceNotAvailableException {
|
||||
final ArrayList<String> paths = new ArrayList<>();
|
||||
for (String packageName : packageNames) {
|
||||
// Use the package manager shell command to find the path of the package.
|
||||
final String result = getDevice().executeShellCommand(
|
||||
String.format("pm dump %s | grep \"resourcePath=\"", packageName));
|
||||
assertNotNull("Failed to find path for package " + packageName, result);
|
||||
int splitIndex = result.indexOf('=');
|
||||
assertTrue(splitIndex >= 0);
|
||||
paths.add(result.substring(splitIndex + 1).trim());
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the type and data of a a previously retrieved is the same as expected.
|
||||
* @param resourceName the full name of the resource in the form package:type/entry
|
||||
* @param type the expected {@link android.util.TypedValue} type of the resource
|
||||
* @param data the expected value of the resource when coerced to a string using
|
||||
* {@link android.util.TypedValue#coerceToString()}
|
||||
**/
|
||||
void assertResource(String resourceName, int type, String data) {
|
||||
assertNotNull("Failed to get test results", mLastResults);
|
||||
assertNotEquals("No resource values were retrieved", mLastResults.size(), 0);
|
||||
assertEquals("" + type, mLastResults.get(resourceName + RESOURCES_TYPE_SUFFIX));
|
||||
assertEquals("" + data, mLastResults.get(resourceName + RESOURCES_DATA_SUFFIX));
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.overlaytest.remounted;
|
||||
|
||||
import com.android.tradefed.device.DeviceNotAvailableException;
|
||||
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(DeviceJUnit4ClassRunner.class)
|
||||
public class OverlaySharedLibraryTest extends OverlayHostTest {
|
||||
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";
|
||||
|
||||
@Test
|
||||
public void testSharedLibrary() throws Exception {
|
||||
final String targetResource = resourceName(TARGET_PACKAGE, "bool",
|
||||
"uses_shared_library_overlaid");
|
||||
final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool",
|
||||
"shared_library_overlaid");
|
||||
|
||||
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)
|
||||
.installResourceApk(TARGET_APK, TARGET_PACKAGE);
|
||||
|
||||
// The shared library resource is not currently overlaid.
|
||||
retrieveResource(Collections.emptyList(), targetResource, libraryResource);
|
||||
assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "false");
|
||||
assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "false");
|
||||
|
||||
// Overlay the shared library resource.
|
||||
mPreparer.setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true);
|
||||
retrieveResource(getPackagePaths(SHARED_LIBRARY_OVERLAY_PACKAGE), targetResource,
|
||||
libraryResource);
|
||||
assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
|
||||
assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSharedLibraryPreEnabled() throws Exception {
|
||||
final String targetResource = resourceName(TARGET_PACKAGE, "bool",
|
||||
"uses_shared_library_overlaid");
|
||||
final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool",
|
||||
"shared_library_overlaid");
|
||||
|
||||
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()
|
||||
.installResourceApk(TARGET_APK, TARGET_PACKAGE);
|
||||
|
||||
retrieveResource(getPackagePaths(SHARED_LIBRARY_OVERLAY_PACKAGE), targetResource,
|
||||
libraryResource);
|
||||
assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
|
||||
assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
|
||||
}
|
||||
|
||||
private void retrieveResource(List<String> requiredOverlayPaths, String... resourceNames)
|
||||
throws DeviceNotAvailableException {
|
||||
retrieveResource(TARGET_PACKAGE, requiredOverlayPaths, resourceNames);
|
||||
}
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.rules.ExternalResource;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.io.File;
|
||||
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 REBOOT_SLEEP_MS = 30000;
|
||||
private static final long OVERLAY_ENABLE_TIMEOUT_MS = 20000;
|
||||
|
||||
// The paths of the files pushed onto the device through this rule.
|
||||
private ArrayList<String> mPushedFiles = new ArrayList<>();
|
||||
|
||||
// The package names of packages installed through this rule.
|
||||
private ArrayList<String> mInstalledPackages = new ArrayList<>();
|
||||
|
||||
private final TemporaryFolder mHostTempFolder;
|
||||
private final DeviceProvider mDeviceProvider;
|
||||
|
||||
SystemPreparer(TemporaryFolder hostTempFolder, DeviceProvider deviceProvider) {
|
||||
mHostTempFolder = hostTempFolder;
|
||||
mDeviceProvider = deviceProvider;
|
||||
}
|
||||
|
||||
/** Copies a file within the host test jar to a path on device. */
|
||||
SystemPreparer pushResourceFile(String resourcePath,
|
||||
String outputPath) throws DeviceNotAvailableException, IOException {
|
||||
final ITestDevice device = mDeviceProvider.getDevice();
|
||||
assertTrue(device.pushFile(copyResourceToTemp(resourcePath), outputPath));
|
||||
mPushedFiles.add(outputPath);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Installs an APK within the host test jar onto the device. */
|
||||
SystemPreparer installResourceApk(String resourcePath, String packageName)
|
||||
throws DeviceNotAvailableException, IOException {
|
||||
final ITestDevice device = mDeviceProvider.getDevice();
|
||||
final File tmpFile = copyResourceToTemp(resourcePath);
|
||||
final String result = device.installPackage(tmpFile, true);
|
||||
Assert.assertNull(result);
|
||||
mInstalledPackages.add(packageName);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Sets the enable state of an overlay pacakage. */
|
||||
SystemPreparer setOverlayEnabled(String packageName, boolean enabled)
|
||||
throws ExecutionException, TimeoutException {
|
||||
final ITestDevice device = mDeviceProvider.getDevice();
|
||||
|
||||
// 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 pattern = (enabled ? "[x]" : "[ ]") + " " + packageName;
|
||||
if (device.executeShellCommand("cmd overlay list").contains(pattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
final Executor executor = (cmd) -> new Thread(cmd).start();
|
||||
executor.execute(enabledListener);
|
||||
try {
|
||||
enabledListener.get(OVERLAY_ENABLE_TIMEOUT_MS, MILLISECONDS);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Restarts the device and waits until after boot is completed. */
|
||||
SystemPreparer reboot() throws DeviceNotAvailableException {
|
||||
final ITestDevice device = mDeviceProvider.getDevice();
|
||||
device.executeShellCommand("stop");
|
||||
device.executeShellCommand("start");
|
||||
try {
|
||||
// Sleep until the device is ready for test execution.
|
||||
Thread.sleep(REBOOT_SLEEP_MS);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
|
||||
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);
|
||||
final ClassLoader classLoader = getClass().getClassLoader();
|
||||
try (InputStream assetIs = classLoader.getResource(resourcePath).openStream();
|
||||
FileOutputStream assetOs = new FileOutputStream(tempFile)) {
|
||||
if (assetIs == null) {
|
||||
throw new IllegalStateException("Failed to find resource " + resourcePath);
|
||||
}
|
||||
|
||||
int b;
|
||||
while ((b = assetIs.read()) >= 0) {
|
||||
assetOs.write(b);
|
||||
}
|
||||
}
|
||||
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
/** Removes installed packages and files that were pushed to the device. */
|
||||
@Override
|
||||
protected void after() {
|
||||
final ITestDevice device = mDeviceProvider.getDevice();
|
||||
try {
|
||||
for (final String file : mPushedFiles) {
|
||||
device.deleteFile(file);
|
||||
}
|
||||
for (final String packageName : mInstalledPackages) {
|
||||
device.uninstallPackage(packageName);
|
||||
}
|
||||
} catch (DeviceNotAvailableException e) {
|
||||
Assert.fail(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
interface DeviceProvider {
|
||||
ITestDevice getDevice();
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
// 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_SharedLibrary",
|
||||
sdk_version: "current",
|
||||
aaptflags: ["--shared-lib"],
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
<?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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.overlaytest.remounted.shared_library">
|
||||
<application>
|
||||
<library android:name="com.android.overlaytest.remounted.shared_library" />
|
||||
</application>
|
||||
</manifest>
|
@ -0,0 +1,24 @@
|
||||
<?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>
|
||||
<overlayable name="TestResources">
|
||||
<policy type="public">
|
||||
<item type="bool" name="shared_library_overlaid" />
|
||||
</policy>
|
||||
</overlayable>
|
||||
</resources>
|
@ -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>
|
||||
<public type="bool" name="shared_library_overlaid" id="0x00050001"/>
|
||||
</resources>
|
@ -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>
|
||||
<bool name="shared_library_overlaid">false</bool>
|
||||
</resources>
|
@ -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_SharedLibraryOverlay",
|
||||
sdk_version: "current",
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
<?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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.overlaytest.remounted.shared_library.overlay">
|
||||
<application android:hasCode="false" />
|
||||
<overlay android:targetPackage="com.android.overlaytest.remounted.shared_library"
|
||||
android:targetName="TestResources" />
|
||||
</manifest>
|
@ -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>
|
||||
<bool name="shared_library_overlaid">true</bool>
|
||||
</resources>
|
20
core/tests/overlaytests/remount/target/Android.bp
Normal file
20
core/tests/overlaytests/remount/target/Android.bp
Normal file
@ -0,0 +1,20 @@
|
||||
// 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.
|
||||
|
||||
android_test_helper_app {
|
||||
name: "OverlayRemountedTest_Target",
|
||||
srcs: ["src/**/*.java"],
|
||||
sdk_version: "test_current",
|
||||
libs: ["OverlayRemountedTest_SharedLibrary"],
|
||||
}
|
30
core/tests/overlaytests/remount/target/AndroidManifest.xml
Normal file
30
core/tests/overlaytests/remount/target/AndroidManifest.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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" />
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="com.android.overlaytest.remounted.target.ResourceRetrievalRunner"
|
||||
android:targetPackage="com.android.overlaytest.remounted.target"
|
||||
android:label="Remounted system RRO tests" />
|
||||
</manifest>
|
20
core/tests/overlaytests/remount/target/res/values/values.xml
Normal file
20
core/tests/overlaytests/remount/target/res/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 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>
|
||||
</resources>
|
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.overlaytest.remounted.target;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Instrumentation;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* An {@link Instrumentation} that retrieves the value of specified resources within the
|
||||
* application.
|
||||
**/
|
||||
public class ResourceRetrievalRunner extends Instrumentation {
|
||||
private static final String TAG = ResourceRetrievalRunner.class.getSimpleName();
|
||||
|
||||
// A list of whitespace separated resource names of which to retrieve the resource values.
|
||||
private static final String RESOURCE_LIST_TAG = "res";
|
||||
|
||||
// A list of whitespace separated overlay package paths that must be present before retrieving
|
||||
// resource values.
|
||||
private static final String REQUIRED_OVERLAYS_LIST_TAG = "overlays";
|
||||
|
||||
// The suffixes of the keys returned from the instrumentation. To retrieve the type of a
|
||||
// resource looked up with the instrumentation, append the {@link #RESOURCES_TYPE_SUFFIX} suffix
|
||||
// to the end of the name of the resource. For the value of a resource, use
|
||||
// {@link #RESOURCES_DATA_SUFFIX} instead.
|
||||
private static final String RESOURCES_TYPE_SUFFIX = "_type";
|
||||
private static final String RESOURCES_DATA_SUFFIX = "_data";
|
||||
|
||||
// The amount of time in seconds to wait for the overlays to be present in the AssetManager.
|
||||
private static final int OVERLAY_PATH_TIMEOUT = 60;
|
||||
|
||||
private final ArrayList<String> mResourceNames = new ArrayList<>();
|
||||
private final ArrayList<String> mOverlayPaths = new ArrayList<>();
|
||||
private final Bundle mResult = new Bundle();
|
||||
|
||||
/**
|
||||
* Receives the instrumentation arguments and runs the resource retrieval.
|
||||
* The entry with key {@link #RESOURCE_LIST_TAG} in the {@link Bundle} arguments is a
|
||||
* whitespace separated string of resource names of which to retrieve the resource values.
|
||||
* The entry with key {@link #REQUIRED_OVERLAYS_LIST_TAG} in the {@link Bundle} arguments is a
|
||||
* whitespace separated string of overlay package paths prefixes that must be present before
|
||||
* retrieving the resource values.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle arguments) {
|
||||
super.onCreate(arguments);
|
||||
mResourceNames.addAll(Arrays.asList(arguments.getString(RESOURCE_LIST_TAG).split(" ")));
|
||||
if (arguments.containsKey(REQUIRED_OVERLAYS_LIST_TAG)) {
|
||||
mOverlayPaths.addAll(Arrays.asList(
|
||||
arguments.getString(REQUIRED_OVERLAYS_LIST_TAG).split(" ")));
|
||||
}
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
final Resources res = getContext().getResources();
|
||||
res.getAssets().setResourceResolutionLoggingEnabled(true);
|
||||
|
||||
if (!mOverlayPaths.isEmpty()) {
|
||||
Log.d(TAG, String.format("Waiting for overlay paths [%s]",
|
||||
String.join(",", mOverlayPaths)));
|
||||
|
||||
// Wait for all required overlays to be present in the AssetManager.
|
||||
final FutureTask<Boolean> overlayListener = new FutureTask<>(() -> {
|
||||
while (!mOverlayPaths.isEmpty()) {
|
||||
final String[] apkPaths = res.getAssets().getApkPaths();
|
||||
for (String path : apkPaths) {
|
||||
for (String overlayPath : mOverlayPaths) {
|
||||
if (path.startsWith(overlayPath)) {
|
||||
mOverlayPaths.remove(overlayPath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
try {
|
||||
final Executor executor = (t) -> new Thread(t).start();
|
||||
executor.execute(overlayListener);
|
||||
overlayListener.get(OVERLAY_PATH_TIMEOUT, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, String.format("Failed to wait for required overlays [%s]",
|
||||
String.join(",", mOverlayPaths)), e);
|
||||
finish(Activity.RESULT_CANCELED, mResult);
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the values for each resource passed in.
|
||||
final TypedValue typedValue = new TypedValue();
|
||||
for (final String resourceName : mResourceNames) {
|
||||
try {
|
||||
final int resId = res.getIdentifier(resourceName, null, null);
|
||||
res.getValue(resId, typedValue, true);
|
||||
Log.d(TAG, String.format("Resolution for 0x%s: %s", Integer.toHexString(resId),
|
||||
res.getAssets().getLastResourceResolution()));
|
||||
} catch (Resources.NotFoundException e) {
|
||||
Log.e(TAG, "Failed to retrieve value for resource " + resourceName, e);
|
||||
finish(Activity.RESULT_CANCELED, mResult);
|
||||
}
|
||||
|
||||
putValue(resourceName, typedValue);
|
||||
}
|
||||
|
||||
finish(Activity.RESULT_OK, mResult);
|
||||
}
|
||||
|
||||
private void putValue(String resourceName, TypedValue value) {
|
||||
mResult.putInt(resourceName + RESOURCES_TYPE_SUFFIX, value.type);
|
||||
final CharSequence textValue = value.coerceToString();
|
||||
mResult.putString(resourceName + RESOURCES_DATA_SUFFIX,
|
||||
textValue == null ? "null" : textValue.toString());
|
||||
}
|
||||
}
|
@ -141,7 +141,10 @@ void AssetManager2::BuildDynamicRefTable() {
|
||||
// to take effect.
|
||||
const auto& loaded_idmap = apk_assets->GetLoadedIdmap();
|
||||
auto target_package_iter = apk_assets_package_ids.find(loaded_idmap->TargetApkPath());
|
||||
if (target_package_iter != apk_assets_package_ids.end()) {
|
||||
if (target_package_iter == apk_assets_package_ids.end()) {
|
||||
LOG(INFO) << "failed to find target package for overlay "
|
||||
<< loaded_idmap->OverlayApkPath();
|
||||
} else {
|
||||
const uint8_t target_package_id = target_package_iter->second;
|
||||
const uint8_t target_idx = package_ids_[target_package_id];
|
||||
CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not"
|
||||
@ -591,7 +594,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
if (resource_resolution_logging_enabled_) {
|
||||
last_resolution_.steps.push_back(
|
||||
Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(),
|
||||
&package_group.packages_[0].loaded_package_->GetPackageName()});
|
||||
overlay_result.package_name});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ import com.android.server.pm.PackageList;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@ -478,10 +479,12 @@ public abstract class PackageManagerInternal {
|
||||
* will be disabled. Pass in null or an empty list to disable
|
||||
* all overlays. The order of the items is significant if several
|
||||
* overlays modify the same resource.
|
||||
* @param outUpdatedPackageNames An output list that contains the package names of packages
|
||||
* affected by the update of enabled overlays.
|
||||
* @return true if all packages names were known by the package manager, false otherwise
|
||||
*/
|
||||
public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName,
|
||||
List<String> overlayPackageNames);
|
||||
List<String> overlayPackageNames, Collection<String> outUpdatedPackageNames);
|
||||
|
||||
/**
|
||||
* Resolves an activity intent, allowing instant apps to be resolved.
|
||||
|
@ -83,6 +83,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@ -925,7 +926,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
/**
|
||||
* Updates the target packages' set of enabled overlays in PackageManager.
|
||||
*/
|
||||
private void updateOverlayPaths(int userId, List<String> targetPackageNames) {
|
||||
private ArrayList<String> updateOverlayPaths(int userId, List<String> targetPackageNames) {
|
||||
try {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames);
|
||||
if (DEBUG) {
|
||||
@ -955,6 +956,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
final HashSet<String> updatedPackages = new HashSet<>();
|
||||
final int n = targetPackageNames.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
final String targetPackageName = targetPackageNames.get(i);
|
||||
@ -965,11 +967,13 @@ public final class OverlayManagerService extends SystemService {
|
||||
}
|
||||
|
||||
if (!pm.setEnabledOverlayPackages(
|
||||
userId, targetPackageName, pendingChanges.get(targetPackageName))) {
|
||||
userId, targetPackageName, pendingChanges.get(targetPackageName),
|
||||
updatedPackages)) {
|
||||
Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
|
||||
targetPackageName, userId));
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(updatedPackages);
|
||||
} finally {
|
||||
traceEnd(TRACE_TAG_RRO);
|
||||
}
|
||||
@ -980,10 +984,10 @@ public final class OverlayManagerService extends SystemService {
|
||||
}
|
||||
|
||||
private void updateAssets(final int userId, List<String> targetPackageNames) {
|
||||
updateOverlayPaths(userId, targetPackageNames);
|
||||
final IActivityManager am = ActivityManager.getService();
|
||||
try {
|
||||
am.scheduleApplicationInfoChanged(targetPackageNames, userId);
|
||||
final ArrayList<String> updatedPaths = updateOverlayPaths(userId, targetPackageNames);
|
||||
am.scheduleApplicationInfoChanged(updatedPaths, userId);
|
||||
} catch (RemoteException e) {
|
||||
// Intentionally left empty.
|
||||
}
|
||||
|
@ -685,7 +685,7 @@ final class OverlayManagerServiceImpl {
|
||||
// Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
|
||||
if (targetPackage != null && overlayPackage != null
|
||||
&& !("android".equals(targetPackageName)
|
||||
&& overlayPackage.isStaticOverlayPackage())) {
|
||||
&& overlayPackage.isStaticOverlayPackage())) {
|
||||
mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
|
||||
}
|
||||
|
||||
@ -703,9 +703,9 @@ final class OverlayManagerServiceImpl {
|
||||
if (currentState != newState) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, String.format("%s:%d: %s -> %s",
|
||||
overlayPackageName, userId,
|
||||
OverlayInfo.stateToString(currentState),
|
||||
OverlayInfo.stateToString(newState)));
|
||||
overlayPackageName, userId,
|
||||
OverlayInfo.stateToString(currentState),
|
||||
OverlayInfo.stateToString(newState)));
|
||||
}
|
||||
modified |= mSettings.setState(overlayPackageName, userId, newState);
|
||||
}
|
||||
|
@ -11595,6 +11595,23 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
return false;
|
||||
}
|
||||
SharedLibraryInfo libraryInfo = versionedLib.valueAt(libIdx);
|
||||
|
||||
// Remove the shared library overlays from its dependent packages.
|
||||
for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
|
||||
final List<VersionedPackage> dependents = getPackagesUsingSharedLibraryLPr(
|
||||
libraryInfo, 0, currentUserId);
|
||||
if (dependents == null) {
|
||||
continue;
|
||||
}
|
||||
for (VersionedPackage dependentPackage : dependents) {
|
||||
final PackageSetting ps = mSettings.mPackages.get(
|
||||
dependentPackage.getPackageName());
|
||||
if (ps != null) {
|
||||
ps.setOverlayPathsForLibrary(libraryInfo.getName(), null, currentUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
versionedLib.remove(version);
|
||||
if (versionedLib.size() <= 0) {
|
||||
mSharedLibraries.remove(name);
|
||||
@ -15200,6 +15217,29 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
// upcoming call to mSettings.writeLPr().
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the overlays for shared libraries of the package.
|
||||
if (pkg.getUsesLibraryInfos() != null) {
|
||||
for (SharedLibraryInfo sharedLib : pkg.getUsesLibraryInfos()) {
|
||||
for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
|
||||
if (!sharedLib.isDynamic()) {
|
||||
// TODO(146804378): Support overlaying static shared libraries
|
||||
continue;
|
||||
}
|
||||
final PackageSetting libPs = mSettings.mPackages.get(
|
||||
sharedLib.getPackageName());
|
||||
if (libPs == null) {
|
||||
continue;
|
||||
}
|
||||
final String[] overlayPaths = libPs.getOverlayPaths(currentUserId);
|
||||
if (overlayPaths != null) {
|
||||
ps.setOverlayPathsForLibrary(sharedLib.getName(),
|
||||
Arrays.asList(overlayPaths), currentUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// It's implied that when a user requests installation, they want the app to be
|
||||
// installed and enabled.
|
||||
if (userId != UserHandle.USER_ALL) {
|
||||
@ -23226,9 +23266,11 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|
||||
@Override
|
||||
public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName,
|
||||
@Nullable List<String> overlayPackageNames) {
|
||||
@Nullable List<String> overlayPackageNames,
|
||||
@NonNull Collection<String> outUpdatedPackageNames) {
|
||||
synchronized (mLock) {
|
||||
if (targetPackageName == null || mPackages.get(targetPackageName) == null) {
|
||||
final AndroidPackage targetPkg = mPackages.get(targetPackageName);
|
||||
if (targetPackageName == null || targetPkg == null) {
|
||||
Slog.e(TAG, "failed to find package " + targetPackageName);
|
||||
return false;
|
||||
}
|
||||
@ -23247,8 +23289,41 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
ArraySet<String> updatedPackageNames = null;
|
||||
if (targetPkg.getLibraryNames() != null) {
|
||||
// Set the overlay paths for dependencies of the shared library.
|
||||
updatedPackageNames = new ArraySet<>();
|
||||
for (String libName : targetPkg.getLibraryNames()) {
|
||||
final SharedLibraryInfo info = getSharedLibraryInfoLPr(libName,
|
||||
SharedLibraryInfo.VERSION_UNDEFINED);
|
||||
if (info == null) {
|
||||
continue;
|
||||
}
|
||||
final List<VersionedPackage> dependents = getPackagesUsingSharedLibraryLPr(
|
||||
info, 0, userId);
|
||||
if (dependents == null) {
|
||||
continue;
|
||||
}
|
||||
for (VersionedPackage dependent : dependents) {
|
||||
final PackageSetting ps = mSettings.mPackages.get(
|
||||
dependent.getPackageName());
|
||||
if (ps == null) {
|
||||
continue;
|
||||
}
|
||||
ps.setOverlayPathsForLibrary(libName, overlayPaths, userId);
|
||||
updatedPackageNames.add(dependent.getPackageName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final PackageSetting ps = mSettings.mPackages.get(targetPackageName);
|
||||
ps.setOverlayPaths(overlayPaths, userId);
|
||||
|
||||
outUpdatedPackageNames.add(targetPackageName);
|
||||
if (updatedPackageNames != null) {
|
||||
outUpdatedPackageNames.addAll(updatedPackageNames);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ import com.android.internal.util.Preconditions;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
@ -312,12 +313,21 @@ public abstract class PackageSettingBase extends SettingBase {
|
||||
}
|
||||
|
||||
void setOverlayPaths(List<String> overlayPaths, int userId) {
|
||||
modifyUserState(userId).overlayPaths = overlayPaths == null ? null :
|
||||
overlayPaths.toArray(new String[overlayPaths.size()]);
|
||||
modifyUserState(userId).setOverlayPaths(overlayPaths == null ? null :
|
||||
overlayPaths.toArray(new String[overlayPaths.size()]));
|
||||
}
|
||||
|
||||
String[] getOverlayPaths(int userId) {
|
||||
return readUserState(userId).overlayPaths;
|
||||
return readUserState(userId).getOverlayPaths();
|
||||
}
|
||||
|
||||
void setOverlayPathsForLibrary(String libName, List<String> overlayPaths, int userId) {
|
||||
modifyUserState(userId).setSharedLibraryOverlayPaths(libName,
|
||||
overlayPaths == null ? null : overlayPaths.toArray(new String[0]));
|
||||
}
|
||||
|
||||
Map<String, String[]> getOverlayPathsForLibrary(int userId) {
|
||||
return readUserState(userId).getSharedLibraryOverlayPaths();
|
||||
}
|
||||
|
||||
/** Only use for testing. Do NOT use in production code. */
|
||||
|
@ -4725,6 +4725,22 @@ public final class Settings {
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String[]> sharedLibraryOverlayPaths =
|
||||
ps.getOverlayPathsForLibrary(user.id);
|
||||
if (sharedLibraryOverlayPaths != null) {
|
||||
for (Map.Entry<String, String[]> libOverlayPaths :
|
||||
sharedLibraryOverlayPaths.entrySet()) {
|
||||
if (libOverlayPaths.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
pw.print(prefix); pw.print(" ");
|
||||
pw.print(libOverlayPaths.getKey()); pw.println(" overlay paths:");
|
||||
for (String path : libOverlayPaths.getValue()) {
|
||||
pw.print(prefix); pw.print(" "); pw.println(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
|
||||
if (lastDisabledAppCaller != null) {
|
||||
pw.print(prefix); pw.print(" lastDisabledCaller: ");
|
||||
|
@ -895,7 +895,7 @@ class Linker {
|
||||
// android:versionCode from the framework AndroidManifest.xml.
|
||||
ExtractCompileSdkVersions(asset_source->GetAssetManager());
|
||||
}
|
||||
} else if (asset_source->IsPackageDynamic(entry.first)) {
|
||||
} else if (asset_source->IsPackageDynamic(entry.first, entry.second)) {
|
||||
final_table_.included_packages_[entry.first] = entry.second;
|
||||
}
|
||||
}
|
||||
|
@ -245,7 +245,8 @@ std::map<size_t, std::string> AssetManagerSymbolSource::GetAssignedPackageIds()
|
||||
return package_map;
|
||||
}
|
||||
|
||||
bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const {
|
||||
bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId,
|
||||
const std::string& package_name) const {
|
||||
if (packageId == 0) {
|
||||
return true;
|
||||
}
|
||||
@ -253,7 +254,7 @@ bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const {
|
||||
for (const std::unique_ptr<const ApkAssets>& assets : apk_assets_) {
|
||||
for (const std::unique_ptr<const android::LoadedPackage>& loaded_package
|
||||
: assets->GetLoadedArsc()->GetPackages()) {
|
||||
if (packageId == loaded_package->GetPackageId() && loaded_package->IsDynamic()) {
|
||||
if (package_name == loaded_package->GetPackageName() && loaded_package->IsDynamic()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -328,12 +329,12 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName(
|
||||
bool found = false;
|
||||
ResourceId res_id = 0;
|
||||
uint32_t type_spec_flags;
|
||||
ResourceName real_name;
|
||||
|
||||
// There can be mangled resources embedded within other packages. Here we will
|
||||
// look into each package and look-up the mangled name until we find the resource.
|
||||
asset_manager_.ForEachPackage([&](const std::string& package_name, uint8_t id) -> bool {
|
||||
ResourceName real_name(name.package, name.type, name.entry);
|
||||
|
||||
real_name = ResourceName(name.package, name.type, name.entry);
|
||||
if (package_name != name.package) {
|
||||
real_name.entry = mangled_entry;
|
||||
real_name.package = package_name;
|
||||
@ -353,12 +354,12 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName(
|
||||
}
|
||||
|
||||
std::unique_ptr<SymbolTable::Symbol> s;
|
||||
if (name.type == ResourceType::kAttr) {
|
||||
if (real_name.type == ResourceType::kAttr) {
|
||||
s = LookupAttributeInTable(asset_manager_, res_id);
|
||||
} else {
|
||||
s = util::make_unique<SymbolTable::Symbol>();
|
||||
s->id = res_id;
|
||||
s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id());
|
||||
s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package);
|
||||
}
|
||||
|
||||
if (s) {
|
||||
@ -406,7 +407,7 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
|
||||
} else {
|
||||
s = util::make_unique<SymbolTable::Symbol>();
|
||||
s->id = id;
|
||||
s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id());
|
||||
s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package);
|
||||
}
|
||||
|
||||
if (s) {
|
||||
|
@ -194,7 +194,7 @@ class AssetManagerSymbolSource : public ISymbolSource {
|
||||
|
||||
bool AddAssetPath(const android::StringPiece& path);
|
||||
std::map<size_t, std::string> GetAssignedPackageIds() const;
|
||||
bool IsPackageDynamic(uint32_t packageId) const;
|
||||
bool IsPackageDynamic(uint32_t packageId, const std::string& package_name) const;
|
||||
|
||||
std::unique_ptr<SymbolTable::Symbol> FindByName(
|
||||
const ResourceName& name) override;
|
||||
|
Loading…
x
Reference in New Issue
Block a user