Merge "Fix RRO loading from inside APEXes." am: de3baffb29 am: 23cd20c926
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1830600 Change-Id: I2d2fdda8b4f0fc6f4c9b32ca3e2e1c810b3d5384
This commit is contained in:
@@ -294,6 +294,7 @@ java_defaults {
|
|||||||
srcs: [
|
srcs: [
|
||||||
":framework-non-updatable-sources",
|
":framework-non-updatable-sources",
|
||||||
"core/java/**/*.logtags",
|
"core/java/**/*.logtags",
|
||||||
|
":apex-info-list",
|
||||||
],
|
],
|
||||||
aidl: {
|
aidl: {
|
||||||
generate_get_transaction_name: true,
|
generate_get_transaction_name: true,
|
||||||
|
|||||||
@@ -25,17 +25,22 @@ import android.os.Trace;
|
|||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.apex.ApexInfo;
|
||||||
|
import com.android.apex.XmlParser;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.content.om.OverlayConfigParser.OverlayPartition;
|
import com.android.internal.content.om.OverlayConfigParser.OverlayPartition;
|
||||||
import com.android.internal.content.om.OverlayConfigParser.ParsedConfiguration;
|
import com.android.internal.content.om.OverlayConfigParser.ParsedConfiguration;
|
||||||
import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
|
import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
|
||||||
import com.android.internal.util.Preconditions;
|
import com.android.internal.util.Preconditions;
|
||||||
|
import com.android.internal.util.function.TriConsumer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.List;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,7 +78,7 @@ public class OverlayConfig {
|
|||||||
public interface PackageProvider {
|
public interface PackageProvider {
|
||||||
|
|
||||||
/** Performs the given action for each package. */
|
/** Performs the given action for each package. */
|
||||||
void forEachPackage(BiConsumer<ParsingPackageRead, Boolean> p);
|
void forEachPackage(TriConsumer<ParsingPackageRead, Boolean, File> p);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Comparator<ParsedConfiguration> sStaticOverlayComparator = (c1, c2) -> {
|
private static final Comparator<ParsedConfiguration> sStaticOverlayComparator = (c1, c2) -> {
|
||||||
@@ -115,6 +120,8 @@ public class OverlayConfig {
|
|||||||
p)));
|
p)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayMap<Integer, List<String>> activeApexesPerPartition = getActiveApexes(partitions);
|
||||||
|
|
||||||
boolean foundConfigFile = false;
|
boolean foundConfigFile = false;
|
||||||
ArrayList<ParsedOverlayInfo> packageManagerOverlayInfos = null;
|
ArrayList<ParsedOverlayInfo> packageManagerOverlayInfos = null;
|
||||||
|
|
||||||
@@ -123,7 +130,9 @@ public class OverlayConfig {
|
|||||||
final OverlayPartition partition = partitions.get(i);
|
final OverlayPartition partition = partitions.get(i);
|
||||||
final OverlayScanner scanner = (scannerFactory == null) ? null : scannerFactory.get();
|
final OverlayScanner scanner = (scannerFactory == null) ? null : scannerFactory.get();
|
||||||
final ArrayList<ParsedConfiguration> partitionOverlays =
|
final ArrayList<ParsedConfiguration> partitionOverlays =
|
||||||
OverlayConfigParser.getConfigurations(partition, scanner);
|
OverlayConfigParser.getConfigurations(partition, scanner,
|
||||||
|
activeApexesPerPartition.getOrDefault(partition.type,
|
||||||
|
Collections.emptyList()));
|
||||||
if (partitionOverlays != null) {
|
if (partitionOverlays != null) {
|
||||||
foundConfigFile = true;
|
foundConfigFile = true;
|
||||||
overlays.addAll(partitionOverlays);
|
overlays.addAll(partitionOverlays);
|
||||||
@@ -145,7 +154,8 @@ public class OverlayConfig {
|
|||||||
// Filter out overlays not present in the partition.
|
// Filter out overlays not present in the partition.
|
||||||
partitionOverlayInfos = new ArrayList<>(packageManagerOverlayInfos);
|
partitionOverlayInfos = new ArrayList<>(packageManagerOverlayInfos);
|
||||||
for (int j = partitionOverlayInfos.size() - 1; j >= 0; j--) {
|
for (int j = partitionOverlayInfos.size() - 1; j >= 0; j--) {
|
||||||
if (!partition.containsFile(partitionOverlayInfos.get(j).path)) {
|
if (!partition.containsFile(partitionOverlayInfos.get(j)
|
||||||
|
.getOriginalPartitionPath())) {
|
||||||
partitionOverlayInfos.remove(j);
|
partitionOverlayInfos.remove(j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,16 +302,49 @@ public class OverlayConfig {
|
|||||||
private static ArrayList<ParsedOverlayInfo> getOverlayPackageInfos(
|
private static ArrayList<ParsedOverlayInfo> getOverlayPackageInfos(
|
||||||
@NonNull PackageProvider packageManager) {
|
@NonNull PackageProvider packageManager) {
|
||||||
final ArrayList<ParsedOverlayInfo> overlays = new ArrayList<>();
|
final ArrayList<ParsedOverlayInfo> overlays = new ArrayList<>();
|
||||||
packageManager.forEachPackage((ParsingPackageRead p, Boolean isSystem) -> {
|
packageManager.forEachPackage((ParsingPackageRead p, Boolean isSystem,
|
||||||
|
@Nullable File preInstalledApexPath) -> {
|
||||||
if (p.getOverlayTarget() != null && isSystem) {
|
if (p.getOverlayTarget() != null && isSystem) {
|
||||||
overlays.add(new ParsedOverlayInfo(p.getPackageName(), p.getOverlayTarget(),
|
overlays.add(new ParsedOverlayInfo(p.getPackageName(), p.getOverlayTarget(),
|
||||||
p.getTargetSdkVersion(), p.isOverlayIsStatic(), p.getOverlayPriority(),
|
p.getTargetSdkVersion(), p.isOverlayIsStatic(), p.getOverlayPriority(),
|
||||||
new File(p.getBaseApkPath())));
|
new File(p.getBaseApkPath()), preInstalledApexPath));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return overlays;
|
return overlays;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns a map of PartitionType to List of active APEX module names. */
|
||||||
|
@NonNull
|
||||||
|
private static ArrayMap<Integer, List<String>> getActiveApexes(
|
||||||
|
@NonNull List<OverlayPartition> partitions) {
|
||||||
|
// An Overlay in an APEX, which is an update of an APEX in a given partition,
|
||||||
|
// is considered as belonging to that partition.
|
||||||
|
ArrayMap<Integer, List<String>> result = new ArrayMap<>();
|
||||||
|
for (OverlayPartition partition : partitions) {
|
||||||
|
result.put(partition.type, new ArrayList<String>());
|
||||||
|
}
|
||||||
|
// Read from apex-info-list because ApexManager is not accessible to zygote.
|
||||||
|
File apexInfoList = new File("/apex/apex-info-list.xml");
|
||||||
|
if (apexInfoList.exists() && apexInfoList.canRead()) {
|
||||||
|
try (FileInputStream stream = new FileInputStream(apexInfoList)) {
|
||||||
|
List<ApexInfo> apexInfos = XmlParser.readApexInfoList(stream).getApexInfo();
|
||||||
|
for (ApexInfo info : apexInfos) {
|
||||||
|
if (info.getIsActive()) {
|
||||||
|
for (OverlayPartition partition : partitions) {
|
||||||
|
if (partition.containsPath(info.getPreinstalledModulePath())) {
|
||||||
|
result.get(partition.type).add(info.getModuleName());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Error reading apex-info-list: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/** Represents a single call to idmap create-multiple. */
|
/** Represents a single call to idmap create-multiple. */
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static class IdmapInvocation {
|
public static class IdmapInvocation {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import java.io.FileNotFoundException;
|
|||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsible for parsing configurations of Runtime Resource Overlays that control mutability,
|
* Responsible for parsing configurations of Runtime Resource Overlays that control mutability,
|
||||||
@@ -192,13 +193,19 @@ final class OverlayConfigParser {
|
|||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
static ArrayList<ParsedConfiguration> getConfigurations(
|
static ArrayList<ParsedConfiguration> getConfigurations(
|
||||||
@NonNull OverlayPartition partition, @Nullable OverlayScanner scanner) {
|
@NonNull OverlayPartition partition, @Nullable OverlayScanner scanner,
|
||||||
if (partition.getOverlayFolder() == null) {
|
@NonNull List<String> activeApexes) {
|
||||||
return null;
|
if (scanner != null) {
|
||||||
|
if (partition.getOverlayFolder() != null) {
|
||||||
|
scanner.scanDir(partition.getOverlayFolder());
|
||||||
|
}
|
||||||
|
for (String apex : activeApexes) {
|
||||||
|
scanner.scanDir(new File("/apex/" + apex + "/overlay/"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scanner != null) {
|
if (partition.getOverlayFolder() == null) {
|
||||||
scanner.scanDir(partition.getOverlayFolder());
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final File configFile = new File(partition.getOverlayFolder(), CONFIG_DEFAULT_FILENAME);
|
final File configFile = new File(partition.getOverlayFolder(), CONFIG_DEFAULT_FILENAME);
|
||||||
|
|||||||
@@ -47,23 +47,38 @@ public class OverlayScanner {
|
|||||||
public final boolean isStatic;
|
public final boolean isStatic;
|
||||||
public final int priority;
|
public final int priority;
|
||||||
public final File path;
|
public final File path;
|
||||||
|
@Nullable public final File preInstalledApexPath;
|
||||||
|
|
||||||
public ParsedOverlayInfo(String packageName, String targetPackageName,
|
public ParsedOverlayInfo(String packageName, String targetPackageName,
|
||||||
int targetSdkVersion, boolean isStatic, int priority, File path) {
|
int targetSdkVersion, boolean isStatic, int priority, File path,
|
||||||
|
@Nullable File preInstalledApexPath) {
|
||||||
this.packageName = packageName;
|
this.packageName = packageName;
|
||||||
this.targetPackageName = targetPackageName;
|
this.targetPackageName = targetPackageName;
|
||||||
this.targetSdkVersion = targetSdkVersion;
|
this.targetSdkVersion = targetSdkVersion;
|
||||||
this.isStatic = isStatic;
|
this.isStatic = isStatic;
|
||||||
this.priority = priority;
|
this.priority = priority;
|
||||||
this.path = path;
|
this.path = path;
|
||||||
|
this.preInstalledApexPath = preInstalledApexPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getClass().getSimpleName() + String.format("{packageName=%s"
|
return getClass().getSimpleName() + String.format("{packageName=%s"
|
||||||
+ ", targetPackageName=%s, targetSdkVersion=%s, isStatic=%s"
|
+ ", targetPackageName=%s, targetSdkVersion=%s, isStatic=%s"
|
||||||
+ ", priority=%s, path=%s}",
|
+ ", priority=%s, path=%s, preInstalledApexPath=%s}",
|
||||||
packageName, targetPackageName, targetSdkVersion, isStatic, priority, path);
|
packageName, targetPackageName, targetSdkVersion, isStatic,
|
||||||
|
priority, path, preInstalledApexPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the path of the overlay in its original installation partition.
|
||||||
|
*
|
||||||
|
* An Overlay in an APEX, which is an update of an APEX in a given partition,
|
||||||
|
* is considered as belonging to that partition.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public File getOriginalPartitionPath() {
|
||||||
|
return preInstalledApexPath != null ? preInstalledApexPath : path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,6 +153,6 @@ public class OverlayScanner {
|
|||||||
return apkLite.getTargetPackageName() == null ? null :
|
return apkLite.getTargetPackageName() == null ? null :
|
||||||
new ParsedOverlayInfo(apkLite.getPackageName(), apkLite.getTargetPackageName(),
|
new ParsedOverlayInfo(apkLite.getPackageName(), apkLite.getTargetPackageName(),
|
||||||
apkLite.getTargetSdkVersion(), apkLite.isOverlayIsStatic(),
|
apkLite.getTargetSdkVersion(), apkLite.isOverlayIsStatic(),
|
||||||
apkLite.getOverlayPriority(), new File(apkLite.getPath()));
|
apkLite.getOverlayPriority(), new File(apkLite.getPath()), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,6 +138,14 @@ bool FileDescriptorAllowlist::IsAllowed(const std::string& path) const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow Runtime Resource Overlays inside APEXes.
|
||||||
|
static const char* kOverlayPathSuffix = "/overlay";
|
||||||
|
if (android::base::StartsWith(path, kApexPrefix) &&
|
||||||
|
android::base::EndsWith(android::base::Dirname(path), kOverlayPathSuffix) &&
|
||||||
|
android::base::EndsWith(path, kApkSuffix) && path.find("/../") == std::string::npos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static const char* kOverlayIdmapPrefix = "/data/resource-cache/";
|
static const char* kOverlayIdmapPrefix = "/data/resource-cache/";
|
||||||
static const char* kOverlayIdmapSuffix = ".apk@idmap";
|
static const char* kOverlayIdmapSuffix = ".apk@idmap";
|
||||||
if (android::base::StartsWith(path, kOverlayIdmapPrefix) &&
|
if (android::base::StartsWith(path, kOverlayIdmapPrefix) &&
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import android.util.ArrayMap;
|
|||||||
import com.android.internal.content.om.OverlayConfig.PackageProvider;
|
import com.android.internal.content.om.OverlayConfig.PackageProvider;
|
||||||
import com.android.internal.content.om.OverlayScanner;
|
import com.android.internal.content.om.OverlayScanner;
|
||||||
import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
|
import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
|
||||||
|
import com.android.internal.util.function.TriConsumer;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.rules.TestRule;
|
import org.junit.rules.TestRule;
|
||||||
@@ -39,7 +40,6 @@ import org.mockito.invocation.InvocationOnMock;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,7 +73,7 @@ public class OverlayConfigIterationRule implements TestRule {
|
|||||||
final File canonicalPath = new File(path.getCanonicalPath());
|
final File canonicalPath = new File(path.getCanonicalPath());
|
||||||
mOverlayStubResults.put(canonicalPath, new ParsedOverlayInfo(
|
mOverlayStubResults.put(canonicalPath, new ParsedOverlayInfo(
|
||||||
packageName, targetPackage, targetSdkVersion, isStatic, priority,
|
packageName, targetPackage, targetSdkVersion, isStatic, priority,
|
||||||
canonicalPath));
|
canonicalPath, null));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Assert.fail("Failed to add overlay " + e);
|
Assert.fail("Failed to add overlay " + e);
|
||||||
}
|
}
|
||||||
@@ -135,8 +135,8 @@ public class OverlayConfigIterationRule implements TestRule {
|
|||||||
mIteration = Iteration.SYSTEM_SERVER;
|
mIteration = Iteration.SYSTEM_SERVER;
|
||||||
doAnswer((InvocationOnMock invocation) -> {
|
doAnswer((InvocationOnMock invocation) -> {
|
||||||
final Object[] args = invocation.getArguments();
|
final Object[] args = invocation.getArguments();
|
||||||
final BiConsumer<ParsingPackageRead, Boolean> f =
|
final TriConsumer<ParsingPackageRead, Boolean, File> f =
|
||||||
(BiConsumer<ParsingPackageRead, Boolean>) args[0];
|
(TriConsumer<ParsingPackageRead, Boolean, File>) args[0];
|
||||||
for (Map.Entry<File, ParsedOverlayInfo> overlay :
|
for (Map.Entry<File, ParsedOverlayInfo> overlay :
|
||||||
mOverlayStubResults.entrySet()) {
|
mOverlayStubResults.entrySet()) {
|
||||||
final ParsingPackageRead a = Mockito.mock(ParsingPackageRead.class);
|
final ParsingPackageRead a = Mockito.mock(ParsingPackageRead.class);
|
||||||
@@ -147,7 +147,8 @@ public class OverlayConfigIterationRule implements TestRule {
|
|||||||
when(a.isOverlayIsStatic()).thenReturn(info.isStatic);
|
when(a.isOverlayIsStatic()).thenReturn(info.isStatic);
|
||||||
when(a.getOverlayPriority()).thenReturn(info.priority);
|
when(a.getOverlayPriority()).thenReturn(info.priority);
|
||||||
when(a.getBaseApkPath()).thenReturn(info.path.getPath());
|
when(a.getBaseApkPath()).thenReturn(info.path.getPath());
|
||||||
f.accept(a, !info.path.getPath().contains("data/overlay"));
|
f.accept(a, !info.path.getPath().contains("data/overlay"),
|
||||||
|
/*preInstalledApexPath=*/null);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}).when(mPkgProvider).forEachPackage(any());
|
}).when(mPkgProvider).forEachPackage(any());
|
||||||
|
|||||||
@@ -7337,9 +7337,16 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
|
|
||||||
// Parse overlay configuration files to set default enable state, mutability, and
|
// Parse overlay configuration files to set default enable state, mutability, and
|
||||||
// priority of system overlays.
|
// priority of system overlays.
|
||||||
|
final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
|
||||||
|
for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
|
||||||
|
for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
|
||||||
|
apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
mOverlayConfig = OverlayConfig.initializeSystemInstance(
|
mOverlayConfig = OverlayConfig.initializeSystemInstance(
|
||||||
consumer -> mPmInternal.forEachPackage(
|
consumer -> mPmInternal.forEachPackage(
|
||||||
pkg -> consumer.accept(pkg, pkg.isSystem())));
|
pkg -> consumer.accept(pkg, pkg.isSystem(),
|
||||||
|
apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
|
||||||
|
|
||||||
// Prune any system packages that no longer exist.
|
// Prune any system packages that no longer exist.
|
||||||
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
|
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
|
||||||
|
|||||||
Reference in New Issue
Block a user