Loading Android.bp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -297,6 +297,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, Loading core/java/com/android/internal/content/om/OverlayConfig.java +50 −6 Original line number Original line Diff line number Diff line Loading @@ -26,20 +26,25 @@ import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.IndentingPrintWriter; 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.io.PrintWriter; import java.io.PrintWriter; 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.HashMap; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Supplier; import java.util.function.Supplier; /** /** Loading Loading @@ -77,7 +82,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) -> { Loading Loading @@ -119,6 +124,8 @@ public class OverlayConfig { p))); p))); } } ArrayMap<Integer, List<String>> activeApexesPerPartition = getActiveApexes(partitions); boolean foundConfigFile = false; boolean foundConfigFile = false; final Map<String, ParsedOverlayInfo> packageManagerOverlayInfos = final Map<String, ParsedOverlayInfo> packageManagerOverlayInfos = packageProvider == null ? null : getOverlayPackageInfos(packageProvider); packageProvider == null ? null : getOverlayPackageInfos(packageProvider); Loading @@ -129,7 +136,9 @@ public class OverlayConfig { 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, packageManagerOverlayInfos); packageManagerOverlayInfos, activeApexesPerPartition.getOrDefault(partition.type, Collections.emptyList())); if (partitionOverlays != null) { if (partitionOverlays != null) { foundConfigFile = true; foundConfigFile = true; overlays.addAll(partitionOverlays); overlays.addAll(partitionOverlays); Loading @@ -147,7 +156,8 @@ public class OverlayConfig { // Filter out overlays not present in the partition. // Filter out overlays not present in the partition. partitionOverlayInfos = new ArrayList<>(packageManagerOverlayInfos.values()); partitionOverlayInfos = new ArrayList<>(packageManagerOverlayInfos.values()); 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); } } } } Loading Loading @@ -294,16 +304,50 @@ public class OverlayConfig { private static Map<String, ParsedOverlayInfo> getOverlayPackageInfos( private static Map<String, ParsedOverlayInfo> getOverlayPackageInfos( @NonNull PackageProvider packageManager) { @NonNull PackageProvider packageManager) { final HashMap<String, ParsedOverlayInfo> overlays = new HashMap<>(); final HashMap<String, ParsedOverlayInfo> overlays = new HashMap<>(); 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.put(p.getPackageName(), new ParsedOverlayInfo(p.getPackageName(), overlays.put(p.getPackageName(), new ParsedOverlayInfo(p.getPackageName(), p.getOverlayTarget(), p.getTargetSdkVersion(), p.isOverlayIsStatic(), p.getOverlayTarget(), p.getTargetSdkVersion(), p.isOverlayIsStatic(), p.getOverlayPriority(), new File(p.getBaseApkPath()))); p.getOverlayPriority(), 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 { Loading core/java/com/android/internal/content/om/OverlayConfigParser.java +12 −5 Original line number Original line Diff line number Diff line Loading @@ -41,6 +41,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; import java.util.Map; import java.util.Map; /** /** Loading Loading @@ -195,14 +196,20 @@ 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, @Nullable Map<String, ParsedOverlayInfo> packageManagerOverlayInfos) { @Nullable Map<String, ParsedOverlayInfo> packageManagerOverlayInfos, if (partition.getOverlayFolder() == null) { @NonNull List<String> activeApexes) { return null; } if (scanner != null) { if (scanner != null) { if (partition.getOverlayFolder() != null) { scanner.scanDir(partition.getOverlayFolder()); scanner.scanDir(partition.getOverlayFolder()); } } for (String apex : activeApexes) { scanner.scanDir(new File("/apex/" + apex + "/overlay/")); } } if (partition.getOverlayFolder() == null) { return null; } final File configFile = new File(partition.getOverlayFolder(), CONFIG_DEFAULT_FILENAME); final File configFile = new File(partition.getOverlayFolder(), CONFIG_DEFAULT_FILENAME); if (!configFile.exists()) { if (!configFile.exists()) { Loading core/java/com/android/internal/content/om/OverlayScanner.java +19 −4 Original line number Original line Diff line number Diff line Loading @@ -54,23 +54,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; } } } } Loading Loading @@ -189,6 +204,6 @@ public class OverlayScanner { } } return new ParsedOverlayInfo(apkLite.getPackageName(), apkLite.getTargetPackageName(), return 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); } } } } core/jni/fd_utils.cpp +8 −0 Original line number Original line Diff line number Diff line Loading @@ -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) && Loading Loading
Android.bp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -297,6 +297,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, Loading
core/java/com/android/internal/content/om/OverlayConfig.java +50 −6 Original line number Original line Diff line number Diff line Loading @@ -26,20 +26,25 @@ import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.IndentingPrintWriter; 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.io.PrintWriter; import java.io.PrintWriter; 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.HashMap; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Supplier; import java.util.function.Supplier; /** /** Loading Loading @@ -77,7 +82,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) -> { Loading Loading @@ -119,6 +124,8 @@ public class OverlayConfig { p))); p))); } } ArrayMap<Integer, List<String>> activeApexesPerPartition = getActiveApexes(partitions); boolean foundConfigFile = false; boolean foundConfigFile = false; final Map<String, ParsedOverlayInfo> packageManagerOverlayInfos = final Map<String, ParsedOverlayInfo> packageManagerOverlayInfos = packageProvider == null ? null : getOverlayPackageInfos(packageProvider); packageProvider == null ? null : getOverlayPackageInfos(packageProvider); Loading @@ -129,7 +136,9 @@ public class OverlayConfig { 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, packageManagerOverlayInfos); packageManagerOverlayInfos, activeApexesPerPartition.getOrDefault(partition.type, Collections.emptyList())); if (partitionOverlays != null) { if (partitionOverlays != null) { foundConfigFile = true; foundConfigFile = true; overlays.addAll(partitionOverlays); overlays.addAll(partitionOverlays); Loading @@ -147,7 +156,8 @@ public class OverlayConfig { // Filter out overlays not present in the partition. // Filter out overlays not present in the partition. partitionOverlayInfos = new ArrayList<>(packageManagerOverlayInfos.values()); partitionOverlayInfos = new ArrayList<>(packageManagerOverlayInfos.values()); 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); } } } } Loading Loading @@ -294,16 +304,50 @@ public class OverlayConfig { private static Map<String, ParsedOverlayInfo> getOverlayPackageInfos( private static Map<String, ParsedOverlayInfo> getOverlayPackageInfos( @NonNull PackageProvider packageManager) { @NonNull PackageProvider packageManager) { final HashMap<String, ParsedOverlayInfo> overlays = new HashMap<>(); final HashMap<String, ParsedOverlayInfo> overlays = new HashMap<>(); 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.put(p.getPackageName(), new ParsedOverlayInfo(p.getPackageName(), overlays.put(p.getPackageName(), new ParsedOverlayInfo(p.getPackageName(), p.getOverlayTarget(), p.getTargetSdkVersion(), p.isOverlayIsStatic(), p.getOverlayTarget(), p.getTargetSdkVersion(), p.isOverlayIsStatic(), p.getOverlayPriority(), new File(p.getBaseApkPath()))); p.getOverlayPriority(), 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 { Loading
core/java/com/android/internal/content/om/OverlayConfigParser.java +12 −5 Original line number Original line Diff line number Diff line Loading @@ -41,6 +41,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; import java.util.Map; import java.util.Map; /** /** Loading Loading @@ -195,14 +196,20 @@ 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, @Nullable Map<String, ParsedOverlayInfo> packageManagerOverlayInfos) { @Nullable Map<String, ParsedOverlayInfo> packageManagerOverlayInfos, if (partition.getOverlayFolder() == null) { @NonNull List<String> activeApexes) { return null; } if (scanner != null) { if (scanner != null) { if (partition.getOverlayFolder() != null) { scanner.scanDir(partition.getOverlayFolder()); scanner.scanDir(partition.getOverlayFolder()); } } for (String apex : activeApexes) { scanner.scanDir(new File("/apex/" + apex + "/overlay/")); } } if (partition.getOverlayFolder() == null) { return null; } final File configFile = new File(partition.getOverlayFolder(), CONFIG_DEFAULT_FILENAME); final File configFile = new File(partition.getOverlayFolder(), CONFIG_DEFAULT_FILENAME); if (!configFile.exists()) { if (!configFile.exists()) { Loading
core/java/com/android/internal/content/om/OverlayScanner.java +19 −4 Original line number Original line Diff line number Diff line Loading @@ -54,23 +54,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; } } } } Loading Loading @@ -189,6 +204,6 @@ public class OverlayScanner { } } return new ParsedOverlayInfo(apkLite.getPackageName(), apkLite.getTargetPackageName(), return 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); } } } }
core/jni/fd_utils.cpp +8 −0 Original line number Original line Diff line number Diff line Loading @@ -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) && Loading