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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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");
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user