From 0162f2a1cf739b6f38ccebde4bbfec1c5c86d17d Mon Sep 17 00:00:00 2001 From: Makoto Onuki <omakoto@google.com> Date: Tue, 31 Oct 2023 09:13:53 -0700 Subject: [PATCH] New Importance API for Rb Bug: 292533010 Test: atest CtsGetBindingUidImportanceTest ... with and without android.app.get_binding_uid_importance set. Change-Id: Iee6f0e08ba499f2f51d8173e45168c69933cd451 --- core/api/system-current.txt | 2 + core/java/android/app/ActivityManager.java | 28 +++++- core/java/android/app/IActivityManager.aidl | 1 + .../java/android/app/activity_manager.aconfig | 9 +- core/res/AndroidManifest.xml | 9 ++ data/etc/privapp-permissions-platform.xml | 1 + packages/Shell/AndroidManifest.xml | 1 + .../server/am/ActivityManagerService.java | 93 ++++++++++++++++++- 8 files changed, 139 insertions(+), 5 deletions(-) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 0ad73af28a2c..0cfd10f63f38 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -133,6 +133,7 @@ package android { field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; field public static final String GET_APP_METADATA = "android.permission.GET_APP_METADATA"; field public static final String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS"; + field @FlaggedApi("android.app.get_binding_uid_importance") public static final String GET_BINDING_UID_IMPORTANCE = "android.permission.GET_BINDING_UID_IMPORTANCE"; field public static final String GET_HISTORICAL_APP_OPS_STATS = "android.permission.GET_HISTORICAL_APP_OPS_STATS"; field public static final String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE"; field public static final String GET_RUNTIME_PERMISSIONS = "android.permission.GET_RUNTIME_PERMISSIONS"; @@ -542,6 +543,7 @@ package android.app { public class ActivityManager { method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int); method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String); + method @FlaggedApi("android.app.get_binding_uid_importance") @RequiresPermission(android.Manifest.permission.GET_BINDING_UID_IMPORTANCE) public int getBindingUidImportance(int); method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public static int getCurrentUser(); method @FlaggedApi("android.app.app_start_info") @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List<android.app.ApplicationStartInfo> getExternalHistoricalProcessStartReasons(@NonNull String, @IntRange(from=0) int); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index f68681b54e48..8b4ebaee04c5 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -73,7 +73,6 @@ import android.os.PowerExemptionManager.ReasonCode; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; @@ -4268,6 +4267,33 @@ public class ActivityManager { } } + /** + * Same as {@link #getUidImportance(int)}, but it only works on UIDs that currently + * have a service binding, or provider reference, to the calling UID, even if the target UID + * belong to another android user or profile. + * + * <p>This will return {@link RunningAppProcessInfo#IMPORTANCE_GONE} on all other UIDs, + * regardless of if they're valid or not. + * + * <p>Privileged system apps may prefer this API to {@link #getUidImportance(int)} to + * avoid requesting the permission {@link Manifest.permission#PACKAGE_USAGE_STATS}, which + * would allow access to APIs that return more senstive information. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_GET_BINDING_UID_IMPORTANCE) + @SystemApi + @RequiresPermission(Manifest.permission.GET_BINDING_UID_IMPORTANCE) + public @RunningAppProcessInfo.Importance int getBindingUidImportance(int uid) { + try { + int procState = getService().getBindingUidProcessState(uid, + mContext.getOpPackageName()); + return RunningAppProcessInfo.procStateToImportanceForClient(procState, mContext); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Callback to get reports about changes to the importance of a uid. Use with * {@link #addOnUidImportanceListener}. diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 520bf7dc890c..260e9859c72d 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -949,4 +949,5 @@ interface IActivityManager { * @param err The binder transaction error */ oneway void frozenBinderTransactionDetected(int debugPid, int code, int flags, int err); + int getBindingUidProcessState(int uid, in String callingPackage); } diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig index 2076e85828a6..b303ea64406c 100644 --- a/core/java/android/app/activity_manager.aconfig +++ b/core/java/android/app/activity_manager.aconfig @@ -5,4 +5,11 @@ flag { name: "app_start_info" description: "Control collecting of ApplicationStartInfo records and APIs." bug: "247814855" -} \ No newline at end of file +} + +flag { + namespace: "backstage_power" + name: "get_binding_uid_importance" + description: "API to get importance of UID that's binding to the caller" + bug: "292533010" +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 8c91be8b21c0..25958eed3f0b 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -7765,6 +7765,15 @@ <permission android:name="android.permission.WRITE_FLAGS" android:protectionLevel="signature" /> + <!-- @hide @SystemApi + @FlaggedApi("android.app.get_binding_uid_importance") + Allows to get the importance of an UID that has a service + binding to the app. + <p>Protection level: signature|privileged + --> + <permission android:name="android.permission.GET_BINDING_UID_IMPORTANCE" + android:protectionLevel="signature|privileged" /> + <!-- @hide Allows internal applications to manage displays. <p>This means intercept internal signals about displays being (dis-)connected and being able to enable or disable the external displays. diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 69aa40156e78..ab18a500fc03 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -529,6 +529,7 @@ applications that come with the platform <permission name="android.permission.LAUNCH_CREDENTIAL_SELECTOR"/> <!-- Permission required for CTS test IntentRedirectionTest --> <permission name="android.permission.QUERY_CLONED_APPS"/> + <permission name="android.permission.GET_BINDING_UID_IMPORTANCE"/> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 10d04d3ff6b3..5bd37c397d0e 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -863,6 +863,7 @@ <!-- Permissions required for CTS test - CtsVoiceInteractionTestCases --> <uses-permission android:name="android.permission.RESET_HOTWORD_TRAINING_DATA_EGRESS_COUNT" /> <uses-permission android:name="android.permission.RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA" /> + <uses-permission android:name="android.permission.GET_BINDING_UID_IMPORTANCE" /> <application android:label="@string/app_label" diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ae79f19f70f1..82f672453aba 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -79,6 +79,7 @@ import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_ import static android.os.PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION; import static android.os.PowerExemptionManager.REASON_BOOT_COMPLETED; import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER; +import static android.os.PowerExemptionManager.REASON_DENIED; import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION; import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED; import static android.os.PowerExemptionManager.REASON_PROC_STATE_BTOP; @@ -7698,13 +7699,99 @@ public class ActivityManagerService extends IActivityManager.Stub "getUidProcessState", callingPackage); // Ignore return value synchronized (mProcLock) { - if (mPendingStartActivityUids.isPendingTopUid(uid)) { - return PROCESS_STATE_TOP; + return getUidProcessStateInnerLOSP(uid); + } + } + + @Override + public int getBindingUidProcessState(int targetUid, String callingPackage) { + if (!hasUsageStatsPermission(callingPackage)) { + enforceCallingPermission(android.Manifest.permission.GET_BINDING_UID_IMPORTANCE, + "getBindingUidProcessState"); + } + // We don't need to do a cross-user check here (unlike getUidProcessState), + // because we only allow to see UIDs that are actively communicating with the caller. + + final int callingUid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (this) { + final boolean allowed = (callingUid == targetUid) + || hasServiceBindingOrProviderUseLocked(callingUid, targetUid); + if (!allowed) { + return PROCESS_STATE_NONEXISTENT; + } + return getUidProcessStateInnerLOSP(targetUid); } - return mProcessList.getUidProcStateLOSP(uid); + } finally { + Binder.restoreCallingIdentity(token); } } + @GuardedBy(anyOf = {"this", "mProcLock"}) + private int getUidProcessStateInnerLOSP(int uid) { + if (mPendingStartActivityUids.isPendingTopUid(uid)) { + return PROCESS_STATE_TOP; + } + return mProcessList.getUidProcStateLOSP(uid); + } + + /** + * Ensure that {@code clientUid} has a bound service client to {@code callingUid} + */ + @GuardedBy("this") + private boolean hasServiceBindingOrProviderUseLocked(int callingUid, int clientUid) { + // See if there's a service binding + final Boolean hasBinding = mProcessList.searchEachLruProcessesLOSP( + false, pr -> { + if (pr.uid == callingUid) { + final ProcessServiceRecord psr = pr.mServices; + final int serviceCount = psr.mServices.size(); + for (int svc = 0; svc < serviceCount; svc++) { + final ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns = + psr.mServices.valueAt(svc).getConnections(); + final int size = conns.size(); + for (int conni = 0; conni < size; conni++) { + final ArrayList<ConnectionRecord> crs = conns.valueAt(conni); + for (int con = 0; con < crs.size(); con++) { + final ConnectionRecord cr = crs.get(con); + final ProcessRecord clientPr = cr.binding.client; + + if (clientPr.uid == clientUid) { + return Boolean.TRUE; + } + } + } + } + } + return null; + }); + if (Boolean.TRUE.equals(hasBinding)) { + return true; + } + + final Boolean hasProviderClient = mProcessList.searchEachLruProcessesLOSP( + false, pr -> { + if (pr.uid == callingUid) { + final ProcessProviderRecord ppr = pr.mProviders; + for (int provi = ppr.numberOfProviders() - 1; provi >= 0; provi--) { + ContentProviderRecord cpr = ppr.getProviderAt(provi); + + for (int i = cpr.connections.size() - 1; i >= 0; i--) { + ContentProviderConnection conn = cpr.connections.get(i); + ProcessRecord client = conn.client; + if (client.uid == clientUid) { + return Boolean.TRUE; + } + } + } + } + return null; + }); + + return Boolean.TRUE.equals(hasProviderClient); + } + @Override public @ProcessCapability int getUidProcessCapabilities(int uid, String callingPackage) { if (!hasUsageStatsPermission(callingPackage)) { -- GitLab