diff options
| author | 2023-03-09 17:04:08 +0000 | |
|---|---|---|
| committer | 2023-03-09 17:04:08 +0000 | |
| commit | c3a0a78db0f2e9202a2c2ddb3a5c04e23f24c5ad (patch) | |
| tree | 6dee4a329d0e2bac14354e4185621c57d3c79546 | |
| parent | 26ac560f852d5adaf084fad8cc145f846f7e946e (diff) | |
| parent | 5ce5acd11733168872fb7fb32171780a4e214965 (diff) | |
Merge "Allow apps to create FGS with type mediaProjection if the user granted" into udc-dev
6 files changed, 391 insertions, 23 deletions
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index d810f055e96e..90427cb51c26 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -18,6 +18,7 @@ package android.app; import static android.app.ActivityManager.StopUserOnSwitch; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.PermissionMethod; @@ -47,6 +48,8 @@ import android.util.Pair; import com.android.internal.os.TimeoutRecord; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -991,4 +994,48 @@ public abstract class ActivityManagerInternal { */ public abstract void logFgsApiEnd(int apiType, int uid, int pid); + /** + * The list of the events about the {@link android.media.projection.IMediaProjection} itself. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + MEDIA_PROJECTION_TOKEN_EVENT_CREATED, + MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED, + }) + public @interface MediaProjectionTokenEvent{}; + + /** + * An instance of {@link android.media.projection.IMediaProjection} has been created + * by the system. + * + * @hide + */ + public static final @MediaProjectionTokenEvent int MEDIA_PROJECTION_TOKEN_EVENT_CREATED = 0; + + /** + * An instance of {@link android.media.projection.IMediaProjection} has been destroyed + * by the system. + * + * @hide + */ + public static final @MediaProjectionTokenEvent int MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED = 1; + + /** + * Called after the system created/destroyed a media projection for an app, if the user + * has granted the permission to start a media projection from this app. + * + * <p>This API is specifically for the use case of enforcing the FGS type + * {@code android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION}, + * where the app who is starting this type of FGS must have been granted with the permission + * to start the projection via the {@link android.media.projection.MediaProjection} APIs. + * + * @param uid The uid of the app which the system created/destroyed a media projection for. + * @param projectionToken The {@link android.media.projection.IMediaProjection} token that + * the system created/destroyed. + * @param event The actual event happening to the given {@code projectionToken}. + */ + public abstract void notifyMediaProjectionEvent(int uid, @NonNull IBinder projectionToken, + @MediaProjectionTokenEvent int event); } diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java index a7824a850e59..be012cf8e202 100644 --- a/core/java/android/app/ForegroundServiceTypePolicy.java +++ b/core/java/android/app/ForegroundServiceTypePolicy.java @@ -59,6 +59,9 @@ import android.hardware.usb.UsbManager; import android.os.RemoteException; import android.os.ServiceManager; import android.permission.PermissionCheckerManager; +import android.provider.DeviceConfig; +import android.text.TextUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseArray; @@ -147,6 +150,108 @@ public abstract class ForegroundServiceTypePolicy { public static final long FGS_TYPE_PERMISSION_CHANGE_ID = 254662522L; /** + * The prefix for the feature flags of the permission enforcement. + */ + private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX = + "fgs_type_perm_enforcement_flag_"; + + /** + * The feature flag of the permission enforcement for + * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_DATA_SYNC}, + * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}. + */ + private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_DATA_SYNC = + FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "data_sync"; + + /** + * The feature flag of the permission enforcement for + * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK}, + * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}. + */ + private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PLAYBACK = + FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "media_playback"; + + /** + * The feature flag of the permission enforcement for + * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_PHONE_CALL}, + * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}. + */ + private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_PHONE_CALL = + FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "phone_call"; + + /** + * The feature flag of the permission enforcement for + * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_LOCATION}, + * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}. + */ + private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_LOCATION = + FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "location"; + + /** + * The feature flag of the permission enforcement for + * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE}, + * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}. + */ + private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_CONNECTED_DEVICE = + FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "connected_device"; + + /** + * The feature flag of the permission enforcement for + * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION}, + * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}. + */ + private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PROJECTION = + FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "media_projection"; + + /** + * The feature flag of the permission enforcement for + * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CAMERA}, + * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}. + */ + private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_CAMERA = + FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "camera"; + + /** + * The feature flag of the permission enforcement for + * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE}, + * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}. + */ + private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_MICROPHONE = + FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "microphone"; + + /** + * The feature flag of the permission enforcement for + * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_HEALTH}, + * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}. + */ + private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH = + FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "health"; + + /** + * The feature flag of the permission enforcement for + * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING}, + * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}. + */ + private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_REMOTE_MESSAGING = + FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "remote_messaging"; + + /** + * The feature flag of the permission enforcement for + * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED}, + * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}. + */ + private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_SYSTEM_EXEMPTED = + FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "system_exempted"; + + /** + * The feature flag of the permission enforcement for + * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE}, + * in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}. + */ + private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_SPECIAL_USE = + FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "special_use"; + + /** * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST}. * * @hide @@ -156,8 +261,10 @@ public abstract class ForegroundServiceTypePolicy { FOREGROUND_SERVICE_TYPE_MANIFEST, FGS_TYPE_NONE_DEPRECATION_CHANGE_ID, FGS_TYPE_NONE_DISABLED_CHANGE_ID, - null, - null + null /* allOfPermissions */, + null /* anyOfPermissions */, + null /* permissionEnforcementFlag */, + false /* permissionEnforcementFlagDefaultValue */ ); /** @@ -170,8 +277,10 @@ public abstract class ForegroundServiceTypePolicy { FOREGROUND_SERVICE_TYPE_NONE, FGS_TYPE_NONE_DEPRECATION_CHANGE_ID, FGS_TYPE_NONE_DISABLED_CHANGE_ID, - null, - null + null /* allOfPermissions */, + null /* anyOfPermissions */, + null /* permissionEnforcementFlag */, + false /* permissionEnforcementFlagDefaultValue */ ); /** @@ -187,7 +296,9 @@ public abstract class ForegroundServiceTypePolicy { new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC) }, true), - null + null /* anyOfPermissions */, + FGS_TYPE_PERM_ENFORCEMENT_FLAG_DATA_SYNC /* permissionEnforcementFlag */, + true /* permissionEnforcementFlagDefaultValue */ ); /** @@ -203,7 +314,9 @@ public abstract class ForegroundServiceTypePolicy { new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK) }, true), - null + null /* anyOfPermissions */, + FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PLAYBACK /* permissionEnforcementFlag */, + true /* permissionEnforcementFlagDefaultValue */ ); /** @@ -221,7 +334,9 @@ public abstract class ForegroundServiceTypePolicy { }, true), new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { new RegularPermission(Manifest.permission.MANAGE_OWN_CALLS) - }, false) + }, false), + FGS_TYPE_PERM_ENFORCEMENT_FLAG_PHONE_CALL /* permissionEnforcementFlag */, + true /* permissionEnforcementFlagDefaultValue */ ); /** @@ -240,7 +355,9 @@ public abstract class ForegroundServiceTypePolicy { new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { new RegularPermission(Manifest.permission.ACCESS_COARSE_LOCATION), new RegularPermission(Manifest.permission.ACCESS_FINE_LOCATION), - }, false) + }, false), + FGS_TYPE_PERM_ENFORCEMENT_FLAG_LOCATION /* permissionEnforcementFlag */, + true /* permissionEnforcementFlagDefaultValue */ ); /** @@ -268,7 +385,9 @@ public abstract class ForegroundServiceTypePolicy { new RegularPermission(Manifest.permission.UWB_RANGING), new UsbDevicePermission(), new UsbAccessoryPermission(), - }, false) + }, false), + FGS_TYPE_PERM_ENFORCEMENT_FLAG_CONNECTED_DEVICE /* permissionEnforcementFlag */, + true /* permissionEnforcementFlagDefaultValue */ ); /** @@ -284,7 +403,12 @@ public abstract class ForegroundServiceTypePolicy { new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION) }, true), - null + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.CAPTURE_VIDEO_OUTPUT), + new AppOpPermission(AppOpsManager.OP_PROJECT_MEDIA) + }, false), + FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PROJECTION /* permissionEnforcementFlag */, + true /* permissionEnforcementFlagDefaultValue */ ); /** @@ -303,7 +427,9 @@ public abstract class ForegroundServiceTypePolicy { new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { new RegularPermission(Manifest.permission.CAMERA), new RegularPermission(Manifest.permission.SYSTEM_CAMERA), - }, false) + }, false), + FGS_TYPE_PERM_ENFORCEMENT_FLAG_CAMERA /* permissionEnforcementFlag */, + true /* permissionEnforcementFlagDefaultValue */ ); /** @@ -326,7 +452,9 @@ public abstract class ForegroundServiceTypePolicy { new RegularPermission(Manifest.permission.CAPTURE_TUNER_AUDIO_INPUT), new RegularPermission(Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT), new RegularPermission(Manifest.permission.RECORD_AUDIO), - }, false) + }, false), + FGS_TYPE_PERM_ENFORCEMENT_FLAG_MICROPHONE /* permissionEnforcementFlag */, + true /* permissionEnforcementFlagDefaultValue */ ); /** @@ -347,7 +475,9 @@ public abstract class ForegroundServiceTypePolicy { new RegularPermission(Manifest.permission.BODY_SENSORS), new RegularPermission(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE), new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS), - }, false) + }, false), + FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH /* permissionEnforcementFlag */, + true /* permissionEnforcementFlagDefaultValue */ ); /** @@ -363,7 +493,9 @@ public abstract class ForegroundServiceTypePolicy { new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING) }, true), - null + null /* anyOfPermissions */, + FGS_TYPE_PERM_ENFORCEMENT_FLAG_REMOTE_MESSAGING /* permissionEnforcementFlag */, + true /* permissionEnforcementFlagDefaultValue */ ); /** @@ -383,7 +515,9 @@ public abstract class ForegroundServiceTypePolicy { new RegularPermission(Manifest.permission.SCHEDULE_EXACT_ALARM), new RegularPermission(Manifest.permission.USE_EXACT_ALARM), new AppOpPermission(AppOpsManager.OP_ACTIVATE_VPN), - }, false) + }, false), + FGS_TYPE_PERM_ENFORCEMENT_FLAG_SYSTEM_EXEMPTED /* permissionEnforcementFlag */, + true /* permissionEnforcementFlagDefaultValue */ ); /** @@ -396,7 +530,10 @@ public abstract class ForegroundServiceTypePolicy { FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, - null /* no type specific permissions */, null /* no type specific permissions */ + null /* allOfPermissions */, + null /* anyOfPermissions */, + null /* permissionEnforcementFlag */, + false /* permissionEnforcementFlagDefaultValue */ ); /** @@ -412,7 +549,9 @@ public abstract class ForegroundServiceTypePolicy { new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_FILE_MANAGEMENT) }, true), - null + null /* anyOfPermissions */, + null /* permissionEnforcementFlag */, + false /* permissionEnforcementFlagDefaultValue */ ); /** @@ -428,7 +567,9 @@ public abstract class ForegroundServiceTypePolicy { new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_SPECIAL_USE) }, true), - null + null /* anyOfPermissions */, + FGS_TYPE_PERM_ENFORCEMENT_FLAG_SPECIAL_USE /* permissionEnforcementFlag */, + true /* permissionEnforcementFlagDefaultValue */ ); /** @@ -512,6 +653,14 @@ public abstract class ForegroundServiceTypePolicy { @NonNull String packageName, int callerUid, int callerPid, boolean allowWhileInUse, @NonNull ForegroundServiceTypePolicyInfo policy); + /** + * Run the given {@code policyFunctor} on the matching policy, if the flag is known + * to the policy. + * + * @hide + */ + public abstract void updatePermissionEnforcementFlagIfNecessary(@NonNull String flag); + @GuardedBy("sLock") private static ForegroundServiceTypePolicy sDefaultForegroundServiceTypePolicy = null; @@ -575,11 +724,31 @@ public abstract class ForegroundServiceTypePolicy { final @Nullable ForegroundServiceTypePermissions mAnyOfPermissions; /** + * A permission enforcement flag, unlike the {@link #FGS_TYPE_PERMISSION_CHANGE_ID}, + * here it applies to all apps using this FGS type. + */ + final @Nullable String mPermissionEnforcementFlag; + + /** + * The default value to {@link #mPermissionEnforcementFlag}. + */ + final boolean mPermissionEnforcementFlagDefaultValue; + + /** * A customized check for the permissions. */ @Nullable ForegroundServiceTypePermission mCustomPermission; /** + * The value of the permission enforcement flag, will be updated by the system. + * If the value is {@code false}, the FGS permission check will be ignored. + * + * <p>This value could be updated via the DeviceConfig flag specified + * in the {@link #mPermissionEnforcementFlag}.</p> + */ + volatile boolean mPermissionEnforcementFlagValue; + + /** * Not a real change id, but a place holder. */ private static final long INVALID_CHANGE_ID = 0L; @@ -599,12 +768,17 @@ public abstract class ForegroundServiceTypePolicy { public ForegroundServiceTypePolicyInfo(@ForegroundServiceType int type, long deprecationChangeId, long disabledChangeId, @Nullable ForegroundServiceTypePermissions allOfPermissions, - @Nullable ForegroundServiceTypePermissions anyOfPermissions) { + @Nullable ForegroundServiceTypePermissions anyOfPermissions, + @Nullable String permissionEnforcementFlag, + boolean permissionEnforcementFlagDefaultValue) { mType = type; mDeprecationChangeId = deprecationChangeId; mDisabledChangeId = disabledChangeId; mAllOfPermissions = allOfPermissions; mAnyOfPermissions = anyOfPermissions; + mPermissionEnforcementFlag = permissionEnforcementFlag; + mPermissionEnforcementFlagDefaultValue = permissionEnforcementFlagDefaultValue; + mPermissionEnforcementFlagValue = permissionEnforcementFlagDefaultValue; } /** @@ -650,6 +824,17 @@ public abstract class ForegroundServiceTypePolicy { return sb; } + private void updatePermissionEnforcementFlagIfNecessary(@NonNull String flagName) { + if (mPermissionEnforcementFlag == null + || !TextUtils.equals(flagName, mPermissionEnforcementFlag)) { + return; + } + mPermissionEnforcementFlagValue = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + mPermissionEnforcementFlag, + mPermissionEnforcementFlagDefaultValue); + } + /** * @hide */ @@ -745,6 +930,15 @@ public abstract class ForegroundServiceTypePolicy { ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); platformCompat.clearOverrideForTest(changeId, packageName); } + + /** + * For test only. + * + * @return The permission enforcement flag. + */ + public @Nullable String getPermissionEnforcementFlagForTest() { + return mPermissionEnforcementFlag; + } } /** @@ -991,6 +1185,12 @@ public abstract class ForegroundServiceTypePolicy { new SparseArray<>(); /** + * The map between permission enforcement flag to its permission policy info. + */ + private final ArrayMap<String, ForegroundServiceTypePolicyInfo> + mPermissionEnforcementToPolicyInfoMap = new ArrayMap<>(); + + /** * Constructor */ public DefaultForegroundServiceTypePolicy() { @@ -1022,10 +1222,15 @@ public abstract class ForegroundServiceTypePolicy { FGS_TYPE_POLICY_SYSTEM_EXEMPTED); mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, FGS_TYPE_POLICY_SHORT_SERVICE); - mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_FILE_MANAGEMENT, - FGS_TYPE_POLICY_FILE_MANAGEMENT); + // TODO (b/271950506): revisit it in the next release. + // Hide the file management type for now. If anyone uses it, will default to "none". mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_SPECIAL_USE, FGS_TYPE_POLICY_SPECIAL_USE); + for (int i = 0, size = mForegroundServiceTypePolicies.size(); i < size; i++) { + final ForegroundServiceTypePolicyInfo info = + mForegroundServiceTypePolicies.valueAt(i); + mPermissionEnforcementToPolicyInfoMap.put(info.mPermissionEnforcementFlag, info); + } } @Override @@ -1078,8 +1283,8 @@ public abstract class ForegroundServiceTypePolicy { } } if (permissionResult != PERMISSION_GRANTED) { - return (CompatChanges.isChangeEnabled( - FGS_TYPE_PERMISSION_CHANGE_ID, callerUid)) + return policy.mPermissionEnforcementFlagValue + && (CompatChanges.isChangeEnabled(FGS_TYPE_PERMISSION_CHANGE_ID, callerUid)) ? FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_ENFORCED : FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_PERMISSIVE; } @@ -1089,5 +1294,14 @@ public abstract class ForegroundServiceTypePolicy { } return FGS_TYPE_POLICY_CHECK_OK; } + + @Override + public void updatePermissionEnforcementFlagIfNecessary(@NonNull String flagName) { + final ForegroundServiceTypePolicyInfo info = + mPermissionEnforcementToPolicyInfoMap.get(flagName); + if (info != null) { + info.updatePermissionEnforcementFlagIfNecessary(flagName); + } + } } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 24c9e0f55ab9..0b22f36958d1 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -640,6 +640,7 @@ public final class ActiveServices { mAppWidgetManagerInternal = LocalServices.getService(AppWidgetManagerInternal.class); setAllowListWhileInUsePermissionInFgs(); initSystemExemptedFgsTypePermission(); + initMediaProjectFgsTypeCustomPermission(); } private AppStateTracker getAppStateTracker() { @@ -2580,6 +2581,39 @@ public final class ActiveServices { } } + /** + * A custom permission checker for the "mediaProjection" FGS type: + * if the app has been granted the permission to start a media projection via + * the {@link android.media.project.MediaProjectionManager#createScreenCaptureIntent()}, + * it'll get the permission to start a foreground service with type "mediaProjection". + */ + private class MediaProjectionFgsTypeCustomPermission extends ForegroundServiceTypePermission { + MediaProjectionFgsTypeCustomPermission() { + super("Media projection screen capture permission"); + } + + @Override + public int checkPermission(@NonNull Context context, int callerUid, int callerPid, + @NonNull String packageName, boolean allowWhileInUse) { + return mAm.isAllowedMediaProjectionNoOpCheck(callerUid) + ? PERMISSION_GRANTED : PERMISSION_DENIED; + } + } + + /** + * Set a custom permission checker for the "mediaProjection" FGS type. + */ + private void initMediaProjectFgsTypeCustomPermission() { + final ForegroundServiceTypePolicy policy = ForegroundServiceTypePolicy.getDefaultPolicy(); + final ForegroundServiceTypePolicyInfo policyInfo = + policy.getForegroundServiceTypePolicyInfo( + ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION, + ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE); + if (policyInfo != null) { + policyInfo.setCustomPermission(new MediaProjectionFgsTypeCustomPermission()); + } + } + ServiceNotificationPolicy applyForegroundServiceNotificationLocked(Notification notification, final String tag, final int id, final String pkg, final int userId) { // By nature of the FGS API, all FGS notifications have a null tag diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 9e95e5fb0a40..569600482873 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -25,6 +25,7 @@ import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROA import android.annotation.NonNull; import android.app.ActivityThread; +import android.app.ForegroundServiceTypePolicy; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -1196,6 +1197,7 @@ final class ActivityManagerConstants extends ContentObserver { updateUseTieredCachedAdj(); break; default: + updateFGSPermissionEnforcementFlagsIfNecessary(name); break; } } @@ -1951,6 +1953,11 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME); } + private void updateFGSPermissionEnforcementFlagsIfNecessary(@NonNull String name) { + ForegroundServiceTypePolicy.getDefaultPolicy() + .updatePermissionEnforcementFlagIfNecessary(name); + } + @NeverCompile // Avoid size overhead of debugging code. void dump(PrintWriter pw) { pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) " diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 10450f032ffb..fdd55a8406ce 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -38,6 +38,8 @@ import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.app.ActivityManager.StopUserOnSwitch; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; +import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_CREATED; +import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.OP_NONE; import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT; @@ -178,6 +180,7 @@ import android.app.ActivityManagerInternal; import android.app.ActivityManagerInternal.BindServiceEventListener; import android.app.ActivityManagerInternal.BroadcastEventListener; import android.app.ActivityManagerInternal.ForegroundServiceStateListener; +import android.app.ActivityManagerInternal.MediaProjectionTokenEvent; import android.app.ActivityTaskManager.RootTaskInfo; import android.app.ActivityThread; import android.app.AnrController; @@ -1508,6 +1511,17 @@ public class ActivityManagerService extends IActivityManager.Stub final AppRestrictionController mAppRestrictionController; + /** + * The collection of the MediaProjection tokens per UID, for the apps that are allowed to + * start FGS with the type "mediaProjection"; this permission is granted via the request over + * the call to {@link android.media.project.MediaProjectionManager#createScreenCaptureIntent()}. + * + * <p>Note, the "token" here is actually an instance of + * {@link android.media.projection.IMediaProjection}.</p> + */ + @GuardedBy("mMediaProjectionTokenMap") + private final SparseArray<ArraySet<IBinder>> mMediaProjectionTokenMap = new SparseArray(); + private final class AppDeathRecipient implements IBinder.DeathRecipient { final ProcessRecord mApp; final int mPid; @@ -18737,6 +18751,12 @@ public class ActivityManagerService extends IActivityManager.Stub int uid, int pid) { ActivityManagerService.this.logFgsApiEnd(apiType, uid, pid); } + + @Override + public void notifyMediaProjectionEvent(int uid, @NonNull IBinder projectionToken, + @MediaProjectionTokenEvent int event) { + ActivityManagerService.this.notifyMediaProjectionEvent(uid, projectionToken, event); + } } long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) { @@ -19970,4 +19990,41 @@ public class ActivityManagerService extends IActivityManager.Stub return invalidValue; } } + + private void notifyMediaProjectionEvent(int uid, @NonNull IBinder projectionToken, + @MediaProjectionTokenEvent int event) { + synchronized (mMediaProjectionTokenMap) { + final int index = mMediaProjectionTokenMap.indexOfKey(uid); + ArraySet<IBinder> tokens; + if (event == MEDIA_PROJECTION_TOKEN_EVENT_CREATED) { + if (index < 0) { + tokens = new ArraySet(); + mMediaProjectionTokenMap.put(uid, tokens); + } else { + tokens = mMediaProjectionTokenMap.valueAt(index); + } + tokens.add(projectionToken); + } else if (event == MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED && index >= 0) { + tokens = mMediaProjectionTokenMap.valueAt(index); + tokens.remove(projectionToken); + if (tokens.isEmpty()) { + mMediaProjectionTokenMap.removeAt(index); + } + } + } + } + + /** + * @return {@code true} if the MediaProjectionManagerService has created a media projection + * for the given {@code uid} because the user has granted the permission; + * it doesn't necessarily mean it has started the projection. + * + * <p>It doesn't check the {@link AppOpsManager#OP_PROJECT_MEDIA}.</p> + */ + boolean isAllowedMediaProjectionNoOpCheck(int uid) { + synchronized (mMediaProjectionTokenMap) { + final int index = mMediaProjectionTokenMap.indexOfKey(uid); + return index >= 0 && !mMediaProjectionTokenMap.valueAt(index).isEmpty(); + } + } } diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index 77b9abed0d41..48326188e215 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -16,6 +16,9 @@ package com.android.server.media.projection; +import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_CREATED; +import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -484,6 +487,9 @@ public final class MediaProjectionManagerService extends SystemService userHandle = new UserHandle(UserHandle.getUserId(uid)); mTargetSdkVersion = targetSdkVersion; mIsPrivileged = isPrivileged; + // TODO(b/267740338): Add unit test. + mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(), + MEDIA_PROJECTION_TOKEN_EVENT_CREATED); } @Override // Binder call @@ -630,6 +636,9 @@ public final class MediaProjectionManagerService extends SystemService mToken = null; unregisterCallback(mCallback); mCallback = null; + // TODO(b/267740338): Add unit test. + mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(), + MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED); } } |