diff options
14 files changed, 704 insertions, 61 deletions
diff --git a/core/java/android/content/pm/ProcessInfo.java b/core/java/android/content/pm/ProcessInfo.java new file mode 100644 index 000000000000..c77a267958f5 --- /dev/null +++ b/core/java/android/content/pm/ProcessInfo.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.ArraySet; + +/** + * Information about a process an app may run. This corresponds to information collected from the + * AndroidManifest.xml's <permission-group> tags. + * @hide + */ +public class ProcessInfo implements Parcelable { + /** + * The name of the process, fully-qualified based on the app's package name. + */ + public String name; + + /** + * If non-null, these are permissions that are not allowed in this process. + */ + @Nullable + public ArraySet<String> deniedPermissions; + + public ProcessInfo(String name, ArraySet<String> deniedPermissions) { + this.name = name; + this.deniedPermissions = deniedPermissions; + } + + @Deprecated + public ProcessInfo(@NonNull ProcessInfo orig) { + this.name = orig.name; + this.deniedPermissions = orig.deniedPermissions; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int parcelableFlags) { + dest.writeString(this.name); + final int numDenied = this.deniedPermissions != null + ? this.deniedPermissions.size() : 0; + dest.writeInt(numDenied); + for (int i = 0; i < numDenied; i++) { + dest.writeString(this.deniedPermissions.valueAt(i)); + } + } + + public static final @NonNull Creator<ProcessInfo> CREATOR = + new Creator<ProcessInfo>() { + public ProcessInfo createFromParcel(Parcel source) { + return new ProcessInfo(source); + } + public ProcessInfo[] newArray(int size) { + return new ProcessInfo[size]; + } + }; + + private ProcessInfo(Parcel source) { + this.name = source.readString(); + final int numDenied = source.readInt(); + if (numDenied > 0) { + this.deniedPermissions = new ArraySet<>(numDenied); + for (int i = numDenied - 1; i >= 0; i--) { + this.deniedPermissions.add(TextUtils.safeIntern(source.readString())); + } + } + } +} diff --git a/core/java/android/content/pm/parsing/AndroidPackage.java b/core/java/android/content/pm/parsing/AndroidPackage.java index 990c8359e698..fbe5a48ad61e 100644 --- a/core/java/android/content/pm/parsing/AndroidPackage.java +++ b/core/java/android/content/pm/parsing/AndroidPackage.java @@ -36,6 +36,7 @@ import android.content.pm.parsing.ComponentParseUtils.ParsedService; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseArray; @@ -379,6 +380,9 @@ public interface AndroidPackage extends Parcelable { @Nullable long[] getUsesStaticLibrariesVersions(); + @Nullable + ArrayMap<String, ComponentParseUtils.ParsedProcess> getProcesses(); + int getVersionCode(); int getVersionCodeMajor(); diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java index a001ada8df4a..02030a6b5982 100644 --- a/core/java/android/content/pm/parsing/ApkParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkParseUtils.java @@ -2364,6 +2364,21 @@ public class ApkParseUtils { XmlUtils.skipCurrentTag(parser); break; + case "processes": + ArrayMap<String, ComponentParseUtils.ParsedProcess> processes = + ComponentParseUtils.parseProcesses(separateProcesses, + parsingPackage, + res, parser, flags, + outError); + if (processes == null) { + return parseInput.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + outError[0] + ); + } + + parsingPackage.setProcesses(processes); + break; case "uses-package": // Dependencies for app installers; we don't currently try to // enforce this. diff --git a/core/java/android/content/pm/parsing/ComponentParseUtils.java b/core/java/android/content/pm/parsing/ComponentParseUtils.java index f04a30ce4239..75226c9bbb2d 100644 --- a/core/java/android/content/pm/parsing/ComponentParseUtils.java +++ b/core/java/android/content/pm/parsing/ComponentParseUtils.java @@ -50,6 +50,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.PatternMatcher; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; @@ -1357,6 +1358,72 @@ public class ComponentParseUtils { }; } + public static class ParsedProcess implements Parcelable { + + public String name; + @Nullable + public ArraySet<String> deniedPermissions; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.name); + final int numDenied = this.deniedPermissions != null + ? this.deniedPermissions.size() : 0; + dest.writeInt(numDenied); + for (int i = 0; i < numDenied; i++) { + dest.writeString(this.deniedPermissions.valueAt(i)); + } + } + + public ParsedProcess() { + } + + public ParsedProcess(@NonNull ParsedProcess other) { + name = other.name; + if (other.deniedPermissions != null) { + deniedPermissions = new ArraySet<>(other.deniedPermissions); + } + } + + public void addStateFrom(@NonNull ParsedProcess other) { + if (other.deniedPermissions != null) { + for (int i = other.deniedPermissions.size() - 1; i >= 0; i--) { + if (deniedPermissions == null) { + deniedPermissions = new ArraySet<>(other.deniedPermissions.size()); + } + deniedPermissions.add(other.deniedPermissions.valueAt(i)); + } + } + } + + protected ParsedProcess(Parcel in) { + this.name = TextUtils.safeIntern(in.readString()); + final int numDenied = in.readInt(); + if (numDenied > 0) { + this.deniedPermissions = new ArraySet<>(numDenied); + this.deniedPermissions.add(TextUtils.safeIntern(in.readString())); + } + } + + public static final Creator<ParsedProcess> CREATOR = + new Creator<ParsedProcess>() { + @Override + public ParsedProcess createFromParcel(Parcel source) { + return new ParsedProcess(source); + } + + @Override + public ParsedProcess[] newArray(int size) { + return new ParsedProcess[size]; + } + }; + } + public static ParsedActivity parseActivity( String[] separateProcesses, ParsingPackage parsingPackage, @@ -3253,6 +3320,189 @@ public class ComponentParseUtils { return result; } + private static @Nullable ArraySet<String> parseDenyPermission( + ArraySet<String> perms, + Resources res, + XmlResourceParser parser, + String[] outError + ) throws IOException, XmlPullParserException { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestDenyPermission); + if (sa == null) { + outError[0] = "<deny-permission> could not be parsed"; + return null; + } + + try { + String perm = sa.getNonConfigurationString( + R.styleable.AndroidManifestDenyPermission_name,0); + if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) { + if (perms == null) { + perms = new ArraySet<>(); + } + perms.add(perm); + } + } finally { + sa.recycle(); + } + XmlUtils.skipCurrentTag(parser); + return perms; + } + + private static ArraySet<String> parseAllowPermission( + ArraySet<String> perms, + Resources res, + XmlResourceParser parser, + String[] outError + ) throws IOException, XmlPullParserException { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAllowPermission); + if (sa == null) { + outError[0] = "<allow-permission> could not be parsed"; + return null; + } + + try { + String perm = sa.getNonConfigurationString( + R.styleable.AndroidManifestAllowPermission_name,0); + if (perm != null && perm.equals(android.Manifest.permission.INTERNET) + && perms != null) { + perms.remove(perm); + if (perms.size() <= 0) { + perms = null; + } + } + } finally { + sa.recycle(); + } + XmlUtils.skipCurrentTag(parser); + return perms; + } + + public static ParsedProcess parseProcess( + ArraySet<String> perms, + String[] separateProcesses, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, + int flags, + String[] outError + ) throws IOException, XmlPullParserException { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProcess); + if (sa == null) { + outError[0] = "<process> could not be parsed"; + return null; + } + + ParsedProcess proc = new ParsedProcess(); + if (perms != null) { + proc.deniedPermissions = new ArraySet(perms); + } + + try { + proc.name = sa.getNonConfigurationString( + R.styleable.AndroidManifestProcess_process,0); + proc.name = PackageParser.buildProcessName(parsingPackage.getPackageName(), + null, proc.name, flags, separateProcesses, outError); + + if (proc.name == null || proc.name.length() <= 0) { + outError[0] = "<process> does not specify android:process"; + return null; + } + proc.name = PackageParser.buildProcessName(parsingPackage.getPackageName(), + parsingPackage.getPackageName(), proc.name, + flags, separateProcesses, outError); + if (outError[0] != null) { + return null; + } + } finally { + sa.recycle(); + } + + int type; + final int innerDepth = parser.getDepth(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("deny-permission")) { + proc.deniedPermissions = parseDenyPermission(proc.deniedPermissions, res, parser, + outError); + if (outError[0] != null) { + return null; + } + } else if (tagName.equals("allow-permission")) { + proc.deniedPermissions = parseAllowPermission(proc.deniedPermissions, res, parser, + outError); + if (outError[0] != null) { + return null; + } + } else { + Slog.w(TAG, "Unknown element under <process>: " + tagName + + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } + } + + return proc; + } + + public static ArrayMap<String, ParsedProcess> parseProcesses( + String[] separateProcesses, + ParsingPackage parsingPackage, + Resources res, + XmlResourceParser parser, + int flags, + String[] outError + ) throws IOException, XmlPullParserException { + ArraySet<String> deniedPerms = null; + ArrayMap<String, ParsedProcess> processes = new ArrayMap<>(); + + int type; + final int innerDepth = parser.getDepth(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("deny-permission")) { + deniedPerms = parseDenyPermission(deniedPerms, res, parser, outError); + if (outError[0] != null) { + return null; + } + } else if (tagName.equals("allow-permission")) { + deniedPerms = parseAllowPermission(deniedPerms, res, parser, outError); + if (outError[0] != null) { + return null; + } + } else if (tagName.equals("process")) { + ParsedProcess proc = parseProcess(deniedPerms, separateProcesses, parsingPackage, + res, parser, flags, outError); + if (outError[0] != null) { + return null; + } + if (processes.get(proc.name) != null) { + outError[0] = "<process> specified existing name '" + proc.name + "'"; + return null; + } + processes.put(proc.name, proc); + } else { + Slog.w(TAG, "Unknown element under <processes>: " + tagName + + " at " + parsingPackage.getBaseCodePath() + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } + } + + return processes; + } + public static ActivityInfo.WindowLayout parseLayout(Resources res, AttributeSet attrs) { TypedArray sw = res.obtainAttributes(attrs, R.styleable.AndroidManifestLayout); diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java index 8677fced18fa..9baf3258a230 100644 --- a/core/java/android/content/pm/parsing/PackageImpl.java +++ b/core/java/android/content/pm/parsing/PackageImpl.java @@ -215,6 +215,9 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android @Nullable private ArrayList<String> queriesPackages; + @Nullable + private ArrayMap<String, ComponentParseUtils.ParsedProcess> processes; + private String[] splitClassLoaderNames; private String[] splitCodePaths; private SparseArray<int[]> splitDependencies; @@ -527,6 +530,12 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android return usesStaticLibraries; } + @Nullable + @Override + public ArrayMap<String, ComponentParseUtils.ParsedProcess> getProcesses() { + return processes; + } + @Override public boolean isBaseHardwareAccelerated() { return baseHardwareAccelerated; @@ -948,6 +957,12 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android } @Override + public PackageImpl setProcesses(ArrayMap<String, ComponentParseUtils.ParsedProcess> processes) { + this.processes = processes; + return this; + } + + @Override public PackageImpl setSupportsSmallScreens(int supportsSmallScreens) { if (supportsSmallScreens == 1) { return this; @@ -3010,6 +3025,11 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android dest.writeStringList(this.usesOptionalLibraries); dest.writeStringList(this.usesStaticLibraries); dest.writeLongArray(this.usesStaticLibrariesVersions); + final int numProcesses = this.processes != null ? this.processes.size() : 0; + dest.writeInt(numProcesses); + for (int i = 0; i < numProcesses; i++) { + this.processes.valueAt(i).writeToParcel(dest, 0); + } if (this.usesStaticLibrariesCertDigests == null) { dest.writeInt(-1); @@ -3161,6 +3181,16 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android this.usesStaticLibraries = in.createStringArrayList(); internStringArrayList(usesStaticLibraries); this.usesStaticLibrariesVersions = in.createLongArray(); + final int numProcesses = in.readInt(); + if (numProcesses > 0) { + this.processes = new ArrayMap<>(numProcesses); + for (int i = 0; i < numProcesses; i++) { + ComponentParseUtils.ParsedProcess proc = new ComponentParseUtils.ParsedProcess(in); + this.processes.put(proc.name, proc); + } + } else { + this.processes = null; + } int digestsSize = in.readInt(); if (digestsSize >= 0) { diff --git a/core/java/android/content/pm/parsing/PackageInfoUtils.java b/core/java/android/content/pm/parsing/PackageInfoUtils.java index 73a8d2a7dc0f..41de4ffc7e2f 100644 --- a/core/java/android/content/pm/parsing/PackageInfoUtils.java +++ b/core/java/android/content/pm/parsing/PackageInfoUtils.java @@ -32,6 +32,7 @@ import android.content.pm.PackageParser; import android.content.pm.PackageUserState; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; +import android.content.pm.ProcessInfo; import android.content.pm.ProviderInfo; import android.content.pm.SELinuxUtil; import android.content.pm.ServiceInfo; @@ -41,11 +42,11 @@ import android.content.pm.parsing.ComponentParseUtils.ParsedActivity; import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation; import android.content.pm.parsing.ComponentParseUtils.ParsedPermission; import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup; +import android.util.ArrayMap; import android.util.ArraySet; import com.android.internal.util.ArrayUtils; -import java.util.LinkedHashSet; import java.util.Set; /** @hide */ @@ -458,6 +459,24 @@ public class PackageInfoUtils { return ii; } + public static ArrayMap<String, ProcessInfo> generateProcessInfo( + ArrayMap<String, ComponentParseUtils.ParsedProcess> procs, + @PackageManager.ComponentInfoFlags int flags) { + if (procs == null) { + return null; + } + + final int numProcs = procs.size(); + ArrayMap<String, ProcessInfo> retProcs = new ArrayMap(numProcs); + for (int i = 0; i < numProcs; i++) { + ComponentParseUtils.ParsedProcess proc = procs.valueAt(i); + retProcs.put(proc.name, new ProcessInfo(proc.name, + proc.deniedPermissions != null + ? new ArraySet<>(proc.deniedPermissions) : null)); + } + return retProcs; + } + public static PermissionInfo generatePermissionInfo(ParsedPermission p, @PackageManager.ComponentInfoFlags int flags) { if (p == null) return null; diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index 411c74991594..9ddcc0995fd4 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -31,6 +31,7 @@ import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup; import android.content.pm.parsing.ComponentParseUtils.ParsedProvider; import android.content.pm.parsing.ComponentParseUtils.ParsedService; import android.os.Bundle; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseArray; @@ -99,6 +100,8 @@ public interface ParsingPackage extends AndroidPackage { ParsingPackage addQueriesPackage(String packageName); + ParsingPackage setProcesses(ArrayMap<String, ComponentParseUtils.ParsedProcess> processes); + ParsingPackage asSplit( String[] splitNames, String[] splitCodePaths, diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 6435cddebd1e..17403bc50de0 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2235,6 +2235,55 @@ <attr name="name" /> </declare-styleable> + <!-- The <code>processes</code> tag specifies the processes the application will run code in + and optionally characteristics of those processes. This tag is optional; if not + specified, components will simply run in the processes they specify. If supplied, + they can only specify processes that are enumerated here, and if they don't this + will be treated as a corrupt apk and result in an install failure. + + <p>This appears as a child tag of the + {@link #AndroidManifestApplication application} tag. --> + <declare-styleable name="AndroidManifestProcesses" parent="AndroidManifestApplication"> + </declare-styleable> + + <!-- The <code>process</code> tag enumerates one of the available processes under its + containing <code>processes</code> tag. + + <p>This appears as a child tag of the + {@link #AndroidManifestProcesses processes} tag. --> + <declare-styleable name="AndroidManifestProcess" parent="AndroidManifestProcesses"> + <!-- Required name of the process that is allowed --> + <attr name="process" /> + </declare-styleable> + + <!-- The <code>deny-permission</code> tag specifies that a permission is to be denied + for a particular process (if specified under the + {@link #AndroidManifestProcess process} tag) or by default for all + processes {if specified under the + @link #AndroidManifestProcesses processes} tag). + + <p>This appears as a child tag of the + {@link #AndroidManifestProcesses processes} and + {@link #AndroidManifestProcess process} tags. --> + <declare-styleable name="AndroidManifestDenyPermission" + parent="AndroidManifestProcesses"> + <!-- Required name of the permission that is to be denied --> + <attr name="name" /> + </declare-styleable> + + <!-- The <code>allow-permission</code> tag specifies that a permission is to be allowed + for a particular process, when it was previously denied for all processes through + {@link #AndroidManifestDenyPermission deny-permission} + + <p>This appears as a child tag of the + {@link #AndroidManifestProcesses processes} and + {@link #AndroidManifestProcess process} tags. --> + <declare-styleable name="AndroidManifestAllowPermission" + parent="AndroidManifestProcesses"> + <!-- Required name of the permission that is to be allowed. --> + <attr name="name" /> + </declare-styleable> + <!-- The <code>provider</code> tag declares a {@link android.content.ContentProvider} class that is available as part of the package's application components, supplying structured diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 368416b0d4a1..c3cc5dee0e14 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -33,6 +33,7 @@ import android.content.pm.parsing.AndroidPackage; import android.content.pm.parsing.ComponentParseUtils; import android.os.Bundle; import android.os.PersistableBundle; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseArray; @@ -689,6 +690,27 @@ public abstract class PackageManagerInternal { int userId); /** + * Return the processes that have been declared for a uid. + * + * @param uid The uid to query. + * + * @return Returns null if there are no declared processes for the uid; otherwise, + * returns the set of processes it declared. + */ + public abstract ArrayMap<String, ProcessInfo> getProcessesForUid(int uid); + + /** + * Return the gids associated with a particular permission. + * + * @param permissionName The name of the permission to query. + * @param userId The user id the gids will be associated with. + * + * @return Returns null if there are no gids associated with the permission, otherwise an + * array if the gid ints. + */ + public abstract int[] getPermissionGids(String permissionName, int userId); + + /** * Return if device is currently in a "core" boot environment, typically * used to support full-disk encryption. Only apps marked with * {@code coreApp} attribute are available. diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index c21adb08270e..d0e25327a2e0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -213,6 +213,7 @@ import android.content.pm.PackageParser; import android.content.pm.ParceledListSlice; import android.content.pm.PathPermission; import android.content.pm.PermissionInfo; +import android.content.pm.ProcessInfo; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.SELinuxUtil; @@ -749,67 +750,23 @@ public class ActivityManagerService extends IActivityManager.Stub } /** + * These are the currently running processes for which we have a ProcessInfo. + * Note: needs to be static since the permission checking call chain is static. This + * all probably should be refactored into a separate permission checking object. + */ + @GuardedBy("sActiveProcessInfoSelfLocked") + static final SparseArray<ProcessInfo> sActiveProcessInfoSelfLocked = new SparseArray<>(); + + /** * All of the processes we currently have running organized by pid. * The keys are the pid running the application. * * <p>NOTE: This object is protected by its own lock, NOT the global activity manager lock! */ final PidMap mPidsSelfLocked = new PidMap(); - final class PidMap { + static final class PidMap { private final SparseArray<ProcessRecord> mPidMap = new SparseArray<>(); - /** - * Puts the process record in the map. - * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this - * method. - */ - void put(ProcessRecord app) { - synchronized (this) { - mPidMap.put(app.pid, app); - } - mAtmInternal.onProcessMapped(app.pid, app.getWindowProcessController()); - } - - /** - * Removes the process record from the map. - * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this - * method. - */ - void remove(ProcessRecord app) { - boolean removed = false; - synchronized (this) { - final ProcessRecord existingApp = mPidMap.get(app.pid); - if (existingApp != null && existingApp.startSeq == app.startSeq) { - mPidMap.remove(app.pid); - removed = true; - } - } - if (removed) { - mAtmInternal.onProcessUnMapped(app.pid); - } - } - - /** - * Removes the process record from the map if it has a thread. - * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this - * method. - */ - boolean removeIfNoThread(ProcessRecord app) { - boolean removed = false; - synchronized (this) { - final ProcessRecord existingApp = get(app.pid); - if (existingApp != null && existingApp.startSeq == app.startSeq - && app.thread == null) { - mPidMap.remove(app.pid); - removed = true; - } - } - if (removed) { - mAtmInternal.onProcessUnMapped(app.pid); - } - return removed; - } - ProcessRecord get(int pid) { return mPidMap.get(pid); } @@ -829,6 +786,82 @@ public class ActivityManagerService extends IActivityManager.Stub int indexOfKey(int key) { return mPidMap.indexOfKey(key); } + + void doAddInternal(ProcessRecord app) { + mPidMap.put(app.pid, app); + } + + boolean doRemoveInternal(ProcessRecord app) { + final ProcessRecord existingApp = mPidMap.get(app.pid); + if (existingApp != null && existingApp.startSeq == app.startSeq) { + mPidMap.remove(app.pid); + return true; + } + return false; + } + + boolean doRemoveIfNoThreadInternal(ProcessRecord app) { + if (app == null || app.thread != null) { + return false; + } + return doRemoveInternal(app); + } + } + + /** + * Puts the process record in the map. + * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this + * method. + */ + void addPidLocked(ProcessRecord app) { + synchronized (mPidsSelfLocked) { + mPidsSelfLocked.doAddInternal(app); + } + synchronized (sActiveProcessInfoSelfLocked) { + if (app.processInfo != null) { + sActiveProcessInfoSelfLocked.put(app.pid, app.processInfo); + } else { + sActiveProcessInfoSelfLocked.remove(app.pid); + } + } + mAtmInternal.onProcessMapped(app.pid, app.getWindowProcessController()); + } + + /** + * Removes the process record from the map. + * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this + * method. + */ + void removePidLocked(ProcessRecord app) { + final boolean removed; + synchronized (mPidsSelfLocked) { + removed = mPidsSelfLocked.doRemoveInternal(app); + } + if (removed) { + synchronized (sActiveProcessInfoSelfLocked) { + sActiveProcessInfoSelfLocked.remove(app.pid); + } + mAtmInternal.onProcessUnMapped(app.pid); + } + } + + /** + * Removes the process record from the map if it doesn't have a thread. + * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this + * method. + */ + boolean removePidIfNoThread(ProcessRecord app) { + final boolean removed; + synchronized (mPidsSelfLocked) { + removed = mPidsSelfLocked.doRemoveIfNoThreadInternal(app); + } + if (removed) { + synchronized (sActiveProcessInfoSelfLocked) { + sActiveProcessInfoSelfLocked.remove(app.pid); + } + mAtmInternal.onProcessUnMapped(app.pid); + } + return removed; } /** @@ -2058,7 +2091,7 @@ public class ActivityManagerService extends IActivityManager.Stub app.getWindowProcessController().setPid(MY_PID); app.maxAdj = ProcessList.SYSTEM_ADJ; app.makeActive(mSystemThread.getApplicationThread(), mProcessStats); - mPidsSelfLocked.put(app); + addPidLocked(app); mProcessList.updateLruProcessLocked(app, false, null); updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); } @@ -4715,7 +4748,7 @@ public class ActivityManagerService extends IActivityManager.Stub @GuardedBy("this") private final void processStartTimedOutLocked(ProcessRecord app) { final int pid = app.pid; - boolean gone = mPidsSelfLocked.removeIfNoThread(app); + boolean gone = removePidIfNoThread(app); if (gone) { Slog.w(TAG, "Process " + app + " failed to attach"); @@ -4788,7 +4821,7 @@ public class ActivityManagerService extends IActivityManager.Stub // If there is already an app occupying that pid that hasn't been cleaned up cleanUpApplicationRecordLocked(app, false, false, -1, true /*replacingPid*/); - mPidsSelfLocked.remove(app); + removePidLocked(app); app = null; } } else { @@ -5886,6 +5919,21 @@ public class ActivityManagerService extends IActivityManager.Stub if (pid == MY_PID) { return PackageManager.PERMISSION_GRANTED; } + // If there is an explicit permission being checked, and this is coming from a process + // that has been denied access to that permission, then just deny. Ultimately this may + // not be quite right -- it means that even if the caller would have access for another + // reason (such as being the owner of the component it is trying to access), it would still + // fail. This also means the system and root uids would be able to deny themselves + // access to permissions, which... well okay. ¯\_(ツ)_/¯ + if (permission != null) { + synchronized (sActiveProcessInfoSelfLocked) { + ProcessInfo procInfo = sActiveProcessInfoSelfLocked.get(pid); + if (procInfo != null && procInfo.deniedPermissions != null + && procInfo.deniedPermissions.contains(permission)) { + return PackageManager.PERMISSION_DENIED; + } + } + } return ActivityManager.checkComponentPermission(permission, uid, owningUid, exported); } @@ -14255,7 +14303,7 @@ public class ActivityManagerService extends IActivityManager.Stub return true; } else if (app.pid > 0 && app.pid != MY_PID) { // Goodbye! - mPidsSelfLocked.remove(app); + removePidLocked(app); mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid); if (app.isolated) { diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index e11008c246dd..e4a61a69e7e8 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1516,7 +1516,7 @@ public final class ProcessList { long startTime = SystemClock.uptimeMillis(); if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) { checkSlow(startTime, "startProcess: removing from pids map"); - mService.mPidsSelfLocked.remove(app); + mService.removePidLocked(app); mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); checkSlow(startTime, "startProcess: done removing from pids map"); app.setPid(0); @@ -1561,10 +1561,27 @@ public final class ProcessList { } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } + + // Remove any gids needed if the process has been denied permissions. + // NOTE: eventually we should probably have the package manager pre-compute + // this for us? + if (app.processInfo != null && app.processInfo.deniedPermissions != null) { + for (int i = app.processInfo.deniedPermissions.size() - 1; i >= 0; i--) { + int[] denyGids = mService.mPackageManagerInt.getPermissionGids( + app.processInfo.deniedPermissions.valueAt(i), app.userId); + if (denyGids != null) { + for (int gid : denyGids) { + permGids = ArrayUtils.removeInt(permGids, gid); + } + } + } + } + int numGids = 3; if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) { numGids++; } + /* * Add shared application and profile GIDs so applications can share some * resources like shared libraries and access user-wide resources @@ -2291,7 +2308,7 @@ public final class ProcessList { mService.cleanUpApplicationRecordLocked(oldApp, false, false, -1, true /*replacingPid*/); } - mService.mPidsSelfLocked.put(app); + mService.addPidLocked(app); synchronized (mService.mPidsSelfLocked) { if (!procAttached) { Message msg = mService.mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); @@ -2464,7 +2481,7 @@ public final class ProcessList { .pendingStart)) { int pid = app.pid; if (pid > 0) { - mService.mPidsSelfLocked.remove(app); + mService.removePidLocked(app); mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid); if (app.isolated) { diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 1fef3538165a..eff7667d3dd6 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -32,6 +32,7 @@ import android.app.IApplicationThread; import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.ProcessInfo; import android.content.pm.ServiceInfo; import android.content.pm.VersionedPackage; import android.content.res.CompatibilityInfo; @@ -83,6 +84,7 @@ class ProcessRecord implements WindowProcessListener { private final ActivityManagerService mService; // where we came from final ApplicationInfo info; // all about the first app in the process + final ProcessInfo processInfo; // if non-null, process-specific manifest info final boolean isolated; // true if this is a special isolated process final boolean appZygote; // true if this is forked from the app zygote final int uid; // uid of process; may be different from 'info' if isolated @@ -602,6 +604,13 @@ class ProcessRecord implements WindowProcessListener { int _uid) { mService = _service; info = _info; + if (_service.mPackageManagerInt != null) { + ArrayMap<String, ProcessInfo> processes = + _service.mPackageManagerInt.getProcessesForUid(_uid); + processInfo = processes != null ? processes.get(_processName) : null; + } else { + processInfo = null; + } isolated = _info.uid != _uid; appZygote = (UserHandle.getAppId(_uid) >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID && UserHandle.getAppId(_uid) <= Process.LAST_APP_ZYGOTE_ISOLATED_UID); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 159b4e49bfea..e61737c3a0b8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -184,6 +184,7 @@ import android.content.pm.PackageUserState; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; +import android.content.pm.ProcessInfo; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.SELinuxUtil; @@ -3155,6 +3156,7 @@ public class PackageManagerService extends IPackageManager.Stub // Adjust seInfo to ensure apps which share a sharedUserId are placed in the same // SELinux domain. setting.fixSeInfoLocked(); + setting.updateProcesses(); } // Now that we know all the packages we are keeping, @@ -23443,6 +23445,20 @@ public class PackageManagerService extends IPackageManager.Stub } @Override + public ArrayMap<String, ProcessInfo> getProcessesForUid(int uid) { + synchronized (mLock) { + return getProcessesForUidLocked(uid); + } + } + + @Override + public int[] getPermissionGids(String permissionName, int userId) { + synchronized (mLock) { + return getPermissionGidsLocked(permissionName, userId); + } + } + + @Override public boolean isOnlyCoreApps() { return PackageManagerService.this.isOnlyCoreApps(); } @@ -23697,6 +23713,30 @@ public class PackageManagerService extends IPackageManager.Stub return res != null ? res : EmptyArray.STRING; } + @GuardedBy("mLock") + public ArrayMap<String, ProcessInfo> getProcessesForUidLocked(int uid) { + final int appId = UserHandle.getAppId(uid); + final SettingBase obj = mSettings.getSettingLPr(appId); + if (obj instanceof SharedUserSetting) { + final SharedUserSetting sus = (SharedUserSetting) obj; + return PackageInfoUtils.generateProcessInfo(sus.processes, 0); + } else if (obj instanceof PackageSetting) { + final PackageSetting ps = (PackageSetting) obj; + return PackageInfoUtils.generateProcessInfo(ps.pkg.getProcesses(), 0); + } + return null; + } + + @GuardedBy("mLock") + public int[] getPermissionGidsLocked(String permissionName, int userId) { + BasePermission perm + = mPermissionManager.getPermissionSettings().getPermission(permissionName); + if (perm != null) { + return perm.computeGids(userId); + } + return null; + } + @Override public int getRuntimePermissionsVersion(@UserIdInt int userId) { Preconditions.checkArgumentNonnegative(userId); diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java index 0a42ccf1c5ba..b9bb9e0ad0d8 100644 --- a/services/core/java/com/android/server/pm/SharedUserSetting.java +++ b/services/core/java/com/android/server/pm/SharedUserSetting.java @@ -19,7 +19,9 @@ package com.android.server.pm; import android.annotation.Nullable; import android.content.pm.ApplicationInfo; import android.content.pm.parsing.AndroidPackage; +import android.content.pm.parsing.ComponentParseUtils; import android.service.pm.PackageServiceDumpProto; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.proto.ProtoOutputStream; @@ -51,6 +53,8 @@ public final class SharedUserSetting extends SettingBase { final PackageSignatures signatures = new PackageSignatures(); Boolean signaturesChanged; + ArrayMap<String, ComponentParseUtils.ParsedProcess> processes; + SharedUserSetting(String _name, int _pkgFlags, int _pkgPrivateFlags) { super(_pkgFlags, _pkgPrivateFlags); uidFlags = _pkgFlags; @@ -72,6 +76,25 @@ public final class SharedUserSetting extends SettingBase { proto.end(token); } + void addProcesses(ArrayMap<String, ComponentParseUtils.ParsedProcess> newProcs) { + if (newProcs != null) { + final int numProcs = newProcs.size(); + if (processes == null) { + processes = new ArrayMap<>(numProcs); + } + for (int i = 0; i < numProcs; i++) { + ComponentParseUtils.ParsedProcess newProc = newProcs.valueAt(i); + ComponentParseUtils.ParsedProcess proc = processes.get(newProc.name); + if (proc == null) { + proc = new ComponentParseUtils.ParsedProcess(newProc); + processes.put(newProc.name, proc); + } else { + proc.addStateFrom(newProc); + } + } + } + } + boolean removePackage(PackageSetting packageSetting) { if (!packages.remove(packageSetting)) { return false; @@ -91,6 +114,8 @@ public final class SharedUserSetting extends SettingBase { } setPrivateFlags(aggregatedPrivateFlags); } + // recalculate processes. + updateProcesses(); return true; } @@ -104,6 +129,9 @@ public final class SharedUserSetting extends SettingBase { setFlags(this.pkgFlags | packageSetting.pkgFlags); setPrivateFlags(this.pkgPrivateFlags | packageSetting.pkgPrivateFlags); } + if (packageSetting.pkg != null) { + addProcesses(packageSetting.pkg.getProcesses()); + } } public @Nullable List<AndroidPackage> getPackages() { @@ -148,6 +176,16 @@ public final class SharedUserSetting extends SettingBase { } } + /** + * Update tracked data about processes based on all known packages in the shared user ID. + */ + public void updateProcesses() { + processes = null; + for (int i = packages.size() - 1; i >= 0; i--) { + addProcesses(packages.valueAt(i).pkg.getProcesses()); + } + } + /** Returns userIds which doesn't have any packages with this sharedUserId */ public int[] getNotInstalledUserIds() { int[] excludedUserIds = null; @@ -176,6 +214,17 @@ public final class SharedUserSetting extends SettingBase { this.packages.clear(); this.packages.addAll(sharedUser.packages); this.signaturesChanged = sharedUser.signaturesChanged; + if (sharedUser.processes != null) { + final int numProcs = sharedUser.processes.size(); + this.processes = new ArrayMap<>(numProcs); + for (int i = 0; i < numProcs; i++) { + ComponentParseUtils.ParsedProcess proc = + new ComponentParseUtils.ParsedProcess(sharedUser.processes.valueAt(i)); + this.processes.put(proc.name, proc); + } + } else { + this.processes = null; + } return this; } } |