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:
Ryan Mitchell 2019-10-16 08:32:55 -07:00
parent fe50d739f7
commit ee4a564d4f
33 changed files with 1029 additions and 42 deletions

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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)) {

View File

@ -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;
}

View File

@ -0,0 +1,7 @@
{
"presubmit": [
{
"name" : "OverlayRemountedTest"
}
]
}

View 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",
],
}

View 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>

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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"],
}

View File

@ -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>

View File

@ -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>

View 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>
<public type="bool" name="shared_library_overlaid" id="0x00050001"/>
</resources>

View 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>
<bool name="shared_library_overlaid">false</bool>
</resources>

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_SharedLibraryOverlay",
sdk_version: "current",
}

View File

@ -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>

View 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>
<bool name="shared_library_overlaid">true</bool>
</resources>

View 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"],
}

View 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>

View 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>

View File

@ -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());
}
}

View File

@ -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});
}
}
}

View File

@ -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.

View File

@ -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.
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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. */

View File

@ -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: ");

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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;