Support per-process Application class (package parser)

Now the <process> tag supports `android:name`, which takes a custom
Application class name. If omitted, the system defaults to the
class set in the <application> tag, or the default class which is
`android.app.Application`.

Bug: 197264681
Test: atest CtsProcessTest
Test: atest PackageManagerServiceUnitTests
Change-Id: Iaf336cc34fe950c4b3d8887de557857f8c23dc83
This commit is contained in:
Makoto Onuki
2021-12-10 09:43:41 -08:00
parent 808fd192fa
commit 236abad797
7 changed files with 186 additions and 5 deletions

View File

@@ -33,6 +33,7 @@ import android.os.Parcel;
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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
/** @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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
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 class ApplicationInfo extends PackageItemInfo implements Parcelable {
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;
}
}

View File

@@ -118,6 +118,7 @@ public interface ParsingPackage extends ParsingPackageRead {
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(

View File

@@ -62,6 +62,7 @@ import android.os.Parcel;
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 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
// @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 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
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;

View File

@@ -19,6 +19,7 @@ package android.content.pm.parsing.component;
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 @@ public interface ParsedProcess extends Parcelable {
@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();
}

View File

@@ -22,6 +22,7 @@ import android.annotation.NonNull;
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 @@ public class ParsedProcessImpl implements ParsedProcess {
@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 class ParsedProcessImpl implements ParsedProcess {
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 @@ public class ParsedProcessImpl implements ParsedProcess {
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 @@ public class ParsedProcessImpl implements ParsedProcess {
//@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 @@ public class ParsedProcessImpl implements ParsedProcess {
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 @@ public class ParsedProcessImpl implements ParsedProcess {
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 @@ public class ParsedProcessImpl implements ParsedProcess {
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 @@ public class ParsedProcessImpl implements ParsedProcess {
// 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 @@ public class ParsedProcessImpl implements ParsedProcess {
// 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 @@ public class ParsedProcessImpl implements ParsedProcess {
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 @@ public class ParsedProcessImpl implements ParsedProcess {
};
@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() {}

View File

@@ -97,7 +97,12 @@ public class ParsedProcessUtils {
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)) {

View File

@@ -18,6 +18,7 @@ package com.android.server.pm.test.parsing.parcelling
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 @@ class ParsedProcessTest : ParcelableComponentTest(ParsedProcess::class, ParsedPr
override val excludedMethods = listOf(
// Copying method
"addStateFrom",
// Utility method
"putAppClassNameForPackage",
)
override val baseParams = listOf(
@@ -39,6 +42,9 @@ class ParsedProcessTest : ParcelableComponentTest(ParsedProcess::class, ParsedPr
)
override fun extraParams() = listOf(
getter(ParsedProcess::getDeniedPermissions, setOf("testDeniedPermission"))
getter(ParsedProcess::getDeniedPermissions, setOf("testDeniedPermission")),
getter(ParsedProcess::getAppClassNamesByPackage, ArrayMap<String, String>().apply {
put("package1", "classname1");
}),
)
}