Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 236abad7 authored by Makoto Onuki's avatar Makoto Onuki
Browse files

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
parent 808fd192
Loading
Loading
Loading
Loading
+65 −2
Original line number Diff line number Diff line
@@ -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,9 +1550,15 @@ 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) {
        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;
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -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(
+40 −0
Original line number Diff line number Diff line
@@ -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;
+10 −0
Original line number Diff line number Diff line
@@ -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();
}
+58 −2
Original line number Diff line number Diff line
@@ -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() {}

Loading