Merge changes from topic "per-process-app-class-2"
* changes:
Support per-process Application class (AM and client side)
Support per-process Application class (package parser)
Support per-process Application class in <process> tag
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 5750484..77af474 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1352,7 +1352,9 @@
Application app = null;
- String appClass = mApplicationInfo.className;
+ final String myProcessName = Process.myProcessName();
+ String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess(
+ myProcessName);
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 2998f76..76b4e5c 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -33,6 +33,7 @@
import android.os.Parcelable;
import android.os.UserHandle;
import android.os.storage.StorageManager;
+import android.util.ArrayMap;
import android.util.Printer;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
@@ -1533,6 +1534,15 @@
private int mHiddenApiPolicy = HIDDEN_API_ENFORCEMENT_DEFAULT;
+ /**
+ * A map from a process name to an (custom) application class name in this package, derived
+ * from the <processes> tag in the app's manifest. This map may not contain all the process
+ * names. Processses not in this map will use the default app class name,
+ * which is {@link #className}, or the default class {@link android.app.Application}.
+ */
+ @Nullable
+ private ArrayMap<String, String> mAppClassNamesByProcess;
+
public void dump(Printer pw, String prefix) {
dump(pw, prefix, DUMP_FLAG_ALL);
}
@@ -1540,8 +1550,14 @@
/** @hide */
public void dump(Printer pw, String prefix, int dumpFlags) {
super.dumpFront(pw, prefix);
- if ((dumpFlags & DUMP_FLAG_DETAILS) != 0 && className != null) {
- pw.println(prefix + "className=" + className);
+ if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
+ if (className != null) {
+ pw.println(prefix + "className=" + className);
+ }
+ for (int i = 0; i < ArrayUtils.size(mAppClassNamesByProcess); i++) {
+ pw.println(prefix + " process=" + mAppClassNamesByProcess.keyAt(i)
+ + " className=" + mAppClassNamesByProcess.valueAt(i));
+ }
}
if (permission != null) {
pw.println(prefix + "permission=" + permission);
@@ -1967,6 +1983,16 @@
dest.writeInt(nativeHeapZeroInitialized);
sForBoolean.parcel(requestRawExternalStorageAccess, dest, parcelableFlags);
dest.writeLong(createTimestamp);
+ if (mAppClassNamesByProcess == null) {
+ dest.writeInt(0);
+ } else {
+ final int size = mAppClassNamesByProcess.size();
+ dest.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ dest.writeString(mAppClassNamesByProcess.keyAt(i));
+ dest.writeString(mAppClassNamesByProcess.valueAt(i));
+ }
+ }
}
public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
@@ -2055,6 +2081,13 @@
nativeHeapZeroInitialized = source.readInt();
requestRawExternalStorageAccess = sForBoolean.unparcel(source);
createTimestamp = source.readLong();
+ final int allClassesSize = source.readInt();
+ if (allClassesSize > 0) {
+ mAppClassNamesByProcess = new ArrayMap<>(allClassesSize);
+ for (int i = 0; i < allClassesSize; i++) {
+ mAppClassNamesByProcess.put(source.readString(), source.readString());
+ }
+ }
}
/**
@@ -2538,6 +2571,19 @@
requestRawExternalStorageAccess = value;
}
+ /**
+ * Replaces {@link #mAppClassNamesByProcess}. This takes over the ownership of the passed map.
+ * Do not modify the argument at the callsite.
+ * {@hide}
+ */
+ public void setAppClassNamesByProcess(@Nullable ArrayMap<String, String> value) {
+ if (ArrayUtils.size(value) == 0) {
+ mAppClassNamesByProcess = null;
+ } else {
+ mAppClassNamesByProcess = value;
+ }
+ }
+
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String getCodePath() { return scanSourceDir; }
@@ -2568,4 +2614,21 @@
public int getNativeHeapZeroInitialized() {
return nativeHeapZeroInitialized;
}
+
+ /**
+ * Return the application class name defined in the manifest. The class name set in the
+ * <processes> tag for this process, then return it. Otherwise it'll return the class
+ * name set in the <application> tag. If neither is set, it'll return null.
+ * @hide
+ */
+ @Nullable
+ public String getCustomApplicationClassNameForProcess(String processName) {
+ if (mAppClassNamesByProcess != null) {
+ String byProcess = mAppClassNamesByProcess.get(processName);
+ if (byProcess != null) {
+ return byProcess;
+ }
+ }
+ return className;
+ }
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 2fa5df7..fc9f1a5 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -118,6 +118,7 @@
ParsingPackage addQueriesProvider(String authority);
+ /** Sets a process name -> {@link ParsedProcess} map coming from the <processes> tag. */
ParsingPackage setProcesses(@NonNull Map<String, ParsedProcess> processes);
ParsingPackage asSplit(
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index f03ab6a..424f477 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -62,6 +62,7 @@
import android.os.Parcelable;
import android.os.storage.StorageManager;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
import android.util.SparseArray;
@@ -297,6 +298,9 @@
// @DataClass.ParcelWith(ParsingUtils.StringPairListParceler.class)
private List<Pair<String, ParsedIntentInfo>> preferredActivityFilters = emptyList();
+ /**
+ * Map from a process name to a {@link ParsedProcess}.
+ */
@NonNull
private Map<String, ParsedProcess> processes = emptyMap();
@@ -1131,10 +1135,46 @@
appInfo.setSplitCodePaths(splitCodePaths);
appInfo.setSplitResourcePaths(splitCodePaths);
appInfo.setVersionCode(mLongVersionCode);
+ appInfo.setAppClassNamesByProcess(buildAppClassNamesByProcess());
return appInfo;
}
+ /**
+ * Create a map from a process name to the custom application class for this process,
+ * which comes from <processes><process android:name="xxx">.
+ *
+ * The original information is stored in {@link #processes}, but it's stored in
+ * a form of: [process name] -[1:N]-> [package name] -[1:N]-> [class name].
+ * We scan it and collect the process names and their app class names, only for this package.
+ *
+ * The resulting map only contains processes with a custom application class set.
+ */
+ @Nullable
+ private ArrayMap<String, String> buildAppClassNamesByProcess() {
+ if (processes == null) {
+ return null;
+ }
+ final ArrayMap<String, String> ret = new ArrayMap<>(4);
+ for (String processName : processes.keySet()) {
+ final ParsedProcess process = processes.get(processName);
+ final ArrayMap<String, String> appClassesByPackage =
+ process.getAppClassNamesByPackage();
+
+ for (int i = 0; i < appClassesByPackage.size(); i++) {
+ final String packageName = appClassesByPackage.keyAt(i);
+
+ if (this.packageName.equals(packageName)) {
+ final String appClassName = appClassesByPackage.valueAt(i);
+ if (!TextUtils.isEmpty(appClassName)) {
+ ret.put(processName, appClassName);
+ }
+ }
+ }
+ }
+ return ret;
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java
index c2d5163..27a540d 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcess.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcess.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.content.pm.ApplicationInfo;
import android.os.Parcelable;
+import android.util.ArrayMap;
import java.util.Set;
@@ -37,6 +38,15 @@
@NonNull
String getName();
+ /**
+ * The app class names in this (potentially shared) process, from a package name to
+ * the application class name.
+ * It's a map, because in shared processes, different packages can have different application
+ * classes.
+ */
+ @NonNull
+ ArrayMap<String, String> getAppClassNamesByPackage();
+
@ApplicationInfo.NativeHeapZeroInitialized
int getNativeHeapZeroInitialized();
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java b/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java
index 3fd60eb..d404ecfd 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java
@@ -22,6 +22,7 @@
import android.content.pm.ApplicationInfo;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
@@ -39,6 +40,11 @@
@NonNull
private String name;
+
+ /** @see ParsedProcess#getAppClassNamesByPackage() */
+ @NonNull
+ private ArrayMap<String, String> appClassNamesByPackage = ArrayMap.EMPTY;
+
@NonNull
@DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
private Set<String> deniedPermissions = emptySet();
@@ -55,6 +61,8 @@
public ParsedProcessImpl(@NonNull ParsedProcess other) {
name = other.getName();
+ appClassNamesByPackage = (other.getAppClassNamesByPackage().size() == 0)
+ ? ArrayMap.EMPTY : new ArrayMap<>(other.getAppClassNamesByPackage());
deniedPermissions = new ArraySet<>(other.getDeniedPermissions());
gwpAsanMode = other.getGwpAsanMode();
memtagMode = other.getMemtagMode();
@@ -66,6 +74,21 @@
gwpAsanMode = other.getGwpAsanMode();
memtagMode = other.getMemtagMode();
nativeHeapZeroInitialized = other.getNativeHeapZeroInitialized();
+
+ final ArrayMap<String, String> oacn = other.getAppClassNamesByPackage();
+ for (int i = 0; i < oacn.size(); i++) {
+ appClassNamesByPackage.put(oacn.keyAt(i), oacn.valueAt(i));
+ }
+ }
+
+ /**
+ * Sets a custom application name used in this process for a given package.
+ */
+ public void putAppClassNameForPackage(String packageName, String className) {
+ if (appClassNamesByPackage.size() == 0) {
+ appClassNamesByPackage = new ArrayMap<>(4);
+ }
+ appClassNamesByPackage.put(packageName, className);
}
@@ -83,9 +106,14 @@
//@formatter:off
+ /**
+ * Creates a new ParsedProcessImpl.
+ *
+ */
@DataClass.Generated.Member
public ParsedProcessImpl(
@NonNull String name,
+ @NonNull ArrayMap<String,String> appClassNamesByPackage,
@NonNull Set<String> deniedPermissions,
@ApplicationInfo.GwpAsanMode int gwpAsanMode,
@ApplicationInfo.MemtagMode int memtagMode,
@@ -93,6 +121,9 @@
this.name = name;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, name);
+ this.appClassNamesByPackage = appClassNamesByPackage;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, appClassNamesByPackage);
this.deniedPermissions = deniedPermissions;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, deniedPermissions);
@@ -114,6 +145,14 @@
return name;
}
+ /**
+ * @see ParsedProcess#getAppClassNamesByPackage()
+ */
+ @DataClass.Generated.Member
+ public @NonNull ArrayMap<String,String> getAppClassNamesByPackage() {
+ return appClassNamesByPackage;
+ }
+
@DataClass.Generated.Member
public @NonNull Set<String> getDeniedPermissions() {
return deniedPermissions;
@@ -142,6 +181,17 @@
return this;
}
+ /**
+ * @see ParsedProcess#getAppClassNamesByPackage()
+ */
+ @DataClass.Generated.Member
+ public @NonNull ParsedProcessImpl setAppClassNamesByPackage(@NonNull ArrayMap<String,String> value) {
+ appClassNamesByPackage = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, appClassNamesByPackage);
+ return this;
+ }
+
@DataClass.Generated.Member
public @NonNull ParsedProcessImpl setDeniedPermissions(@NonNull Set<String> value) {
deniedPermissions = value;
@@ -192,6 +242,7 @@
// void parcelFieldName(Parcel dest, int flags) { ... }
dest.writeString(name);
+ dest.writeMap(appClassNamesByPackage);
sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
dest.writeInt(gwpAsanMode);
dest.writeInt(memtagMode);
@@ -210,6 +261,8 @@
// static FieldType unparcelFieldName(Parcel in) { ... }
String _name = in.readString();
+ ArrayMap<String,String> _appClassNamesByPackage = new ArrayMap();
+ in.readMap(_appClassNamesByPackage, String.class.getClassLoader());
Set<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
int _gwpAsanMode = in.readInt();
int _memtagMode = in.readInt();
@@ -218,6 +271,9 @@
this.name = _name;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, name);
+ this.appClassNamesByPackage = _appClassNamesByPackage;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, appClassNamesByPackage);
this.deniedPermissions = _deniedPermissions;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, deniedPermissions);
@@ -249,10 +305,10 @@
};
@DataClass.Generated(
- time = 1627605368434L,
+ time = 1639076603310L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java",
- inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcessImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedProcess]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
+ inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.String> appClassNamesByPackage\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\npublic void putAppClassNameForPackage(java.lang.String,java.lang.String)\nclass ParsedProcessImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedProcess]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
index cf83586..5e4cf66 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -97,7 +97,12 @@
return input.error(processNameResult);
}
+ String packageName = pkg.getPackageName();
+ String className = ParsingUtils.buildClassName(packageName,
+ sa.getNonConfigurationString(R.styleable.AndroidManifestProcess_name, 0));
+
proc.setName(processNameResult.getResult());
+ proc.putAppClassNameForPackage(packageName, className);
proc.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1));
proc.setMemtagMode(sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1));
if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized)) {
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 0a6ef7c..06f347f 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2584,6 +2584,9 @@
<declare-styleable name="AndroidManifestProcess" parent="AndroidManifestProcesses">
<!-- Required name of the process that is allowed -->
<attr name="process" />
+ <!-- custom Application class name. We use call it "name", not "className", to be
+ consistent with the Application tag. -->
+ <attr name="name" />
<attr name="gwpAsanMode" />
<attr name="memtagMode" />
<attr name="nativeHeapZeroInitialized" />
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
index e633850..005d3e8 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
@@ -18,6 +18,7 @@
import android.content.pm.parsing.component.ParsedProcess
import android.content.pm.parsing.component.ParsedProcessImpl
+import android.util.ArrayMap
import kotlin.contracts.ExperimentalContracts
@ExperimentalContracts
@@ -29,6 +30,8 @@
override val excludedMethods = listOf(
// Copying method
"addStateFrom",
+ // Utility method
+ "putAppClassNameForPackage",
)
override val baseParams = listOf(
@@ -39,6 +42,9 @@
)
override fun extraParams() = listOf(
- getter(ParsedProcess::getDeniedPermissions, setOf("testDeniedPermission"))
+ getter(ParsedProcess::getDeniedPermissions, setOf("testDeniedPermission")),
+ getter(ParsedProcess::getAppClassNamesByPackage, ArrayMap<String, String>().apply {
+ put("package1", "classname1");
+ }),
)
}
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index d204190..d432341 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -588,10 +588,21 @@
child_el->name == "provider" || child_el->name == "receiver" ||
child_el->name == "service") {
FullyQualifyClassName(original_package, xml::kSchemaAndroid, "name", child_el);
+ continue;
}
if (child_el->name == "activity-alias") {
FullyQualifyClassName(original_package, xml::kSchemaAndroid, "targetActivity", child_el);
+ continue;
+ }
+
+ if (child_el->name == "processes") {
+ for (xml::Element* grand_child_el : child_el->GetChildElements()) {
+ if (grand_child_el->name == "process") {
+ FullyQualifyClassName(original_package, xml::kSchemaAndroid, "name", grand_child_el);
+ }
+ }
+ continue;
}
}
}