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:
Treehugger Robot
2021-10-26 22:46:40 +00:00
committed by Automerger Merge Worker
7 changed files with 103 additions and 21 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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