diff options
| -rw-r--r-- | core/api/current.txt | 37 | ||||
| -rw-r--r-- | core/java/android/app/AppOpsManager.java | 25 | ||||
| -rw-r--r-- | core/java/android/app/ForegroundServiceTypePolicy.java | 1033 | ||||
| -rw-r--r-- | core/java/android/app/Service.java | 137 | ||||
| -rw-r--r-- | core/java/android/content/pm/PackageManager.java | 15 | ||||
| -rw-r--r-- | core/java/android/content/pm/ServiceInfo.java | 265 | ||||
| -rw-r--r-- | core/res/AndroidManifest.xml | 110 | ||||
| -rw-r--r-- | core/res/res/values/attrs_manifest.xml | 110 | ||||
| -rw-r--r-- | core/res/res/values/strings.xml | 60 | ||||
| -rw-r--r-- | data/etc/privapp-permissions-platform.xml | 4 | ||||
| -rw-r--r-- | packages/Shell/AndroidManifest.xml | 51 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActiveServices.java | 212 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/AppFGSTracker.java | 4 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/AppRestrictionController.java | 108 |
14 files changed, 2054 insertions, 117 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index b89c3f7ffd95..b01afa4aa8d1 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -91,6 +91,18 @@ package android { field public static final String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR"; field public static final String FACTORY_TEST = "android.permission.FACTORY_TEST"; field public static final String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE"; + field public static final String FOREGROUND_SERVICE_CAMERA = "android.permission.FOREGROUND_SERVICE_CAMERA"; + field public static final String FOREGROUND_SERVICE_CONNECTED_DEVICE = "android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE"; + field public static final String FOREGROUND_SERVICE_DATA_SYNC = "android.permission.FOREGROUND_SERVICE_DATA_SYNC"; + field public static final String FOREGROUND_SERVICE_HEALTH = "android.permission.FOREGROUND_SERVICE_HEALTH"; + field public static final String FOREGROUND_SERVICE_LOCATION = "android.permission.FOREGROUND_SERVICE_LOCATION"; + field public static final String FOREGROUND_SERVICE_MEDIA_PLAYBACK = "android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"; + field public static final String FOREGROUND_SERVICE_MEDIA_PROJECTION = "android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION"; + field public static final String FOREGROUND_SERVICE_MICROPHONE = "android.permission.FOREGROUND_SERVICE_MICROPHONE"; + field public static final String FOREGROUND_SERVICE_PHONE_CALL = "android.permission.FOREGROUND_SERVICE_PHONE_CALL"; + field public static final String FOREGROUND_SERVICE_REMOTE_MESSAGING = "android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING"; + field public static final String FOREGROUND_SERVICE_SPECIAL_USE = "android.permission.FOREGROUND_SERVICE_SPECIAL_USE"; + field public static final String FOREGROUND_SERVICE_SYSTEM_EXEMPTED = "android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED"; field public static final String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; field public static final String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED"; field public static final String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE"; @@ -6946,7 +6958,7 @@ package android.app { method public void onTrimMemory(int); method public boolean onUnbind(android.content.Intent); method public final void startForeground(int, android.app.Notification); - method public final void startForeground(int, @NonNull android.app.Notification, int); + method public final void startForeground(int, @NonNull android.app.Notification, @RequiresPermission int); method @Deprecated public final void stopForeground(boolean); method public final void stopForeground(int); method public final void stopSelf(); @@ -12194,6 +12206,7 @@ package android.content.pm { field public static final int PERMISSION_DENIED = -1; // 0xffffffff field public static final int PERMISSION_GRANTED = 0; // 0x0 field public static final String PROPERTY_MEDIA_CAPABILITIES = "android.media.PROPERTY_MEDIA_CAPABILITIES"; + field public static final String PROPERTY_SPECIAL_USE_FGS_SUBTYPE = "android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"; field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff field public static final int SIGNATURE_MATCH = 0; // 0x0 field public static final int SIGNATURE_NEITHER_SIGNED = 1; // 0x1 @@ -12407,16 +12420,20 @@ package android.content.pm { field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000 field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1 field public static final int FLAG_USE_APP_ZYGOTE = 8; // 0x8 - field public static final int FOREGROUND_SERVICE_TYPE_CAMERA = 64; // 0x40 - field public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 16; // 0x10 - field public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1; // 0x1 - field public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 8; // 0x8 + field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CAMERA}, anyOf={android.Manifest.permission.CAMERA}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CAMERA = 64; // 0x40 + field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE}, anyOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.CHANGE_NETWORK_STATE, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, android.Manifest.permission.NFC, android.Manifest.permission.TRANSMIT_IR}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 16; // 0x10 + field @Deprecated @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1; // 0x1 + field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_HEALTH}, anyOf={android.Manifest.permission.ACTIVITY_RECOGNITION, android.Manifest.permission.BODY_SENSORS, android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 256; // 0x100 + field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_LOCATION}, anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 8; // 0x8 field public static final int FOREGROUND_SERVICE_TYPE_MANIFEST = -1; // 0xffffffff - field public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 2; // 0x2 - field public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION = 32; // 0x20 - field public static final int FOREGROUND_SERVICE_TYPE_MICROPHONE = 128; // 0x80 - field public static final int FOREGROUND_SERVICE_TYPE_NONE = 0; // 0x0 - field public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 4; // 0x4 + field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 2; // 0x2 + field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION = 32; // 0x20 + field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_MICROPHONE}, anyOf={android.Manifest.permission.CAPTURE_AUDIO_OUTPUT, android.Manifest.permission.RECORD_AUDIO}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_MICROPHONE = 128; // 0x80 + field @Deprecated public static final int FOREGROUND_SERVICE_TYPE_NONE = 0; // 0x0 + field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_PHONE_CALL}, anyOf={android.Manifest.permission.MANAGE_OWN_CALLS}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 4; // 0x4 + field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING = 512; // 0x200 + field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_SPECIAL_USE, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_SPECIAL_USE = 1073741824; // 0x40000000 + field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED = 1024; // 0x400 field public int flags; field public String permission; } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index da30c1ba6fd7..1b3282e752f4 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1398,9 +1398,18 @@ public class AppOpsManager { */ public static final int OP_READ_WRITE_HEALTH_DATA = AppProtoEnums.APP_OP_READ_WRITE_HEALTH_DATA; + /** + * Use foreground service with the type + * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE}. + * + * @hide + */ + public static final int OP_FOREGROUND_SERVICE_SPECIAL_USE = + AppProtoEnums.APP_OP_FOREGROUND_SERVICE_SPECIAL_USE; + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int _NUM_OP = 127; + public static final int _NUM_OP = 128; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -1930,6 +1939,14 @@ public class AppOpsManager { public static final String OPSTR_SYSTEM_EXEMPT_FROM_FORCED_APP_STANDBY = "android:system_exempt_from_forced_app_standby"; + /** + * Start a foreground service with the type "specialUse". + * + * @hide + */ + public static final String OPSTR_FOREGROUND_SERVICE_SPECIAL_USE = + "android:foreground_service_special_use"; + /** {@link #sAppOpsToNote} not initialized yet for this op */ private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0; /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */ @@ -2026,6 +2043,7 @@ public class AppOpsManager { OP_TURN_SCREEN_ON, OP_RUN_LONG_JOBS, OP_READ_MEDIA_VISUAL_USER_SELECTED, + OP_FOREGROUND_SERVICE_SPECIAL_USE, }; static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{ @@ -2419,7 +2437,10 @@ public class AppOpsManager { OPSTR_SYSTEM_EXEMPT_FROM_FORCED_APP_STANDBY, "SYSTEM_EXEMPT_FROM_FORCED_APP_STANDBY").build(), new AppOpInfo.Builder(OP_READ_WRITE_HEALTH_DATA, OPSTR_READ_WRITE_HEALTH_DATA, - "READ_WRITE_HEALTH_DATA").setDefaultMode(AppOpsManager.MODE_ALLOWED).build() + "READ_WRITE_HEALTH_DATA").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), + new AppOpInfo.Builder(OP_FOREGROUND_SERVICE_SPECIAL_USE, + OPSTR_FOREGROUND_SERVICE_SPECIAL_USE, "FOREGROUND_SERVICE_SPECIAL_USE") + .setPermission(Manifest.permission.FOREGROUND_SERVICE_SPECIAL_USE).build(), }; // The number of longs needed to form a full bitmask of app ops diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java new file mode 100644 index 000000000000..eccc5631d86a --- /dev/null +++ b/core/java/android/app/ForegroundServiceTypePolicy.java @@ -0,0 +1,1033 @@ +/* + * Copyright (C) 2022 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.app; + +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.MODE_FOREGROUND; +import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_HEALTH; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED; + +import android.Manifest; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.app.compat.CompatChanges; +import android.compat.Compatibility; +import android.compat.annotation.ChangeId; +import android.compat.annotation.Disabled; +import android.compat.annotation.EnabledAfter; +import android.compat.annotation.Overridable; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.content.pm.ServiceInfo.ForegroundServiceType; +import android.hardware.usb.UsbAccessory; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.ArraySet; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.compat.CompatibilityChangeConfig; +import com.android.internal.compat.IPlatformCompat; +import com.android.internal.util.ArrayUtils; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.HashMap; +import java.util.Optional; + +/** + * This class enforces the policies around the foreground service types. + * + * @hide + */ +public abstract class ForegroundServiceTypePolicy { + static final String TAG = "ForegroundServiceTypePolicy"; + static final boolean DEBUG_FOREGROUND_SERVICE_TYPE_POLICY = false; + + /** + * The FGS type enforcement: + * deprecating the {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_NONE}. + * + * <p>Starting a FGS with this type (equivalent of no type) from apps with + * targetSdkVersion {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or later will + * result in a warning in the log.</p> + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.TIRAMISU) + @Overridable + public static final long FGS_TYPE_NONE_DEPRECATION_CHANGE_ID = 255042465L; + + /** + * The FGS type enforcement: + * disabling the {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_NONE}. + * + * <p>Starting a FGS with this type (equivalent of no type) from apps with + * targetSdkVersion {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or later will + * result in an exception.</p> + * + * @hide + */ + // TODO (b/254661666): Change to @EnabledAfter(T) + @ChangeId + @Disabled + @Overridable + public static final long FGS_TYPE_NONE_DISABLED_CHANGE_ID = 255038118L; + + /** + * The FGS type enforcement: + * deprecating the {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_DATA_SYNC}. + * + * <p>Starting a FGS with this type from apps with targetSdkVersion + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or later will + * result in a warning in the log.</p> + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.TIRAMISU) + @Overridable + public static final long FGS_TYPE_DATA_SYNC_DEPRECATION_CHANGE_ID = 255039210L; + + /** + * The FGS type enforcement: + * disabling the {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_DATA_SYNC}. + * + * <p>Starting a FGS with this type from apps with targetSdkVersion + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or later will + * result in an exception.</p> + * + * @hide + */ + // TODO (b/254661666): Change to @EnabledSince(U) in next OS release + @ChangeId + @Disabled + @Overridable + public static final long FGS_TYPE_DATA_SYNC_DISABLED_CHANGE_ID = 255659651L; + + /** + * The FGS type enforcement: Starting a FGS from apps with targetSdkVersion + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or later but without the required + * permissions associated with the FGS type will result in a SecurityException. + * + * @hide + */ + // TODO (b/254661666): Change to @EnabledAfter(T) + @ChangeId + @Disabled + @Overridable + public static final long FGS_TYPE_PERMISSION_CHANGE_ID = 254662522L; + + /** + * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_NONE}. + * + * @hide + */ + public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_NONE = + new ForegroundServiceTypePolicyInfo( + FOREGROUND_SERVICE_TYPE_NONE, + FGS_TYPE_NONE_DEPRECATION_CHANGE_ID, + FGS_TYPE_NONE_DISABLED_CHANGE_ID, + null, + null + ); + + /** + * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_DATA_SYNC}. + * + * @hide + */ + public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_DATA_SYNC = + new ForegroundServiceTypePolicyInfo( + FOREGROUND_SERVICE_TYPE_DATA_SYNC, + FGS_TYPE_DATA_SYNC_DEPRECATION_CHANGE_ID, + FGS_TYPE_DATA_SYNC_DISABLED_CHANGE_ID, + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC) + }, true), + null + ); + + /** + * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK}. + * + * @hide + */ + public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_MEDIA_PLAYBACK = + new ForegroundServiceTypePolicyInfo( + FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK) + }, true), + null + ); + + /** + * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_PHONE_CALL}. + * + * @hide + */ + public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_PHONE_CALL = + new ForegroundServiceTypePolicyInfo( + FOREGROUND_SERVICE_TYPE_PHONE_CALL, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_PHONE_CALL) + }, true), + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.MANAGE_OWN_CALLS) + }, false) + ); + + /** + * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_LOCATION}. + * + * @hide + */ + public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_LOCATION = + new ForegroundServiceTypePolicyInfo( + FOREGROUND_SERVICE_TYPE_LOCATION, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_LOCATION) + }, true), + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.ACCESS_COARSE_LOCATION), + new RegularPermission(Manifest.permission.ACCESS_FINE_LOCATION), + }, false) + ); + + /** + * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE}. + * + * @hide + */ + public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_CONNECTED_DEVICE = + new ForegroundServiceTypePolicyInfo( + FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE) + }, true), + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.BLUETOOTH_CONNECT), + new RegularPermission(Manifest.permission.CHANGE_NETWORK_STATE), + new RegularPermission(Manifest.permission.CHANGE_WIFI_STATE), + new RegularPermission(Manifest.permission.CHANGE_WIFI_MULTICAST_STATE), + new RegularPermission(Manifest.permission.NFC), + new RegularPermission(Manifest.permission.TRANSMIT_IR), + new UsbDevicePermission(), + new UsbAccessoryPermission(), + }, false) + ); + + /** + * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION}. + * + * @hide + */ + public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_MEDIA_PROJECTION = + new ForegroundServiceTypePolicyInfo( + FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION) + }, true), + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.CAPTURE_VIDEO_OUTPUT), + new AppOpPermission(AppOpsManager.OP_PROJECT_MEDIA) + }, false) + ); + + /** + * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CAMERA}. + * + * @hide + */ + public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_CAMERA = + new ForegroundServiceTypePolicyInfo( + FOREGROUND_SERVICE_TYPE_CAMERA, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_CAMERA) + }, true), + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.CAMERA), + new RegularPermission(Manifest.permission.SYSTEM_CAMERA), + }, false) + ); + + /** + * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE}. + * + * @hide + */ + public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_MICROPHONE = + new ForegroundServiceTypePolicyInfo( + FOREGROUND_SERVICE_TYPE_MICROPHONE, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_MICROPHONE) + }, true), + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD), + new RegularPermission(Manifest.permission.CAPTURE_AUDIO_OUTPUT), + new RegularPermission(Manifest.permission.CAPTURE_MEDIA_OUTPUT), + new RegularPermission(Manifest.permission.CAPTURE_TUNER_AUDIO_INPUT), + new RegularPermission(Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT), + new RegularPermission(Manifest.permission.RECORD_AUDIO), + }, false) + ); + + /** + * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_HEALTH}. + * + * @hide + */ + public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_HEALTH = + new ForegroundServiceTypePolicyInfo( + FOREGROUND_SERVICE_TYPE_HEALTH, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_HEALTH) + }, true), + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.ACTIVITY_RECOGNITION), + new RegularPermission(Manifest.permission.BODY_SENSORS), + new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS), + }, false) + ); + + /** + * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING}. + * + * @hide + */ + public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_REMOTE_MESSAGING = + new ForegroundServiceTypePolicyInfo( + FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING) + }, true), + null + ); + + /** + * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED}. + * + * @hide + */ + public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_SYSTEM_EXEMPTED = + new ForegroundServiceTypePolicyInfo( + FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED) + }, true), + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.SCHEDULE_EXACT_ALARM), + new RegularPermission(Manifest.permission.USE_EXACT_ALARM), + new AppOpPermission(AppOpsManager.OP_ACTIVATE_VPN), + new AppOpPermission(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN), + }, false) + ); + + /** + * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE}. + * + * @hide + */ + public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_SPECIAL_USE = + new ForegroundServiceTypePolicyInfo( + FOREGROUND_SERVICE_TYPE_SPECIAL_USE, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_SPECIAL_USE) + }, true), + null + ); + + /** + * Foreground service policy check result code: this one is not actually being used. + * + * @hide + */ + public static final int FGS_TYPE_POLICY_CHECK_UNKNOWN = + AppProtoEnums.FGS_TYPE_POLICY_CHECK_UNKNOWN; + + /** + * Foreground service policy check result code: okay to go. + * + * @hide + */ + public static final int FGS_TYPE_POLICY_CHECK_OK = + AppProtoEnums.FGS_TYPE_POLICY_CHECK_OK; + + /** + * Foreground service policy check result code: this foreground service type is deprecated. + * + * @hide + */ + public static final int FGS_TYPE_POLICY_CHECK_DEPRECATED = + AppProtoEnums.FGS_TYPE_POLICY_CHECK_DEPRECATED; + + /** + * Foreground service policy check result code: this foreground service type is disabled. + * + * @hide + */ + public static final int FGS_TYPE_POLICY_CHECK_DISABLED = + AppProtoEnums.FGS_TYPE_POLICY_CHECK_DISABLED; + + /** + * Foreground service policy check result code: the caller doesn't have permission to start + * foreground service with this type, but the policy is permissive. + * + * @hide + */ + public static final int FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_PERMISSIVE = + AppProtoEnums.FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_PERMISSIVE; + + /** + * Foreground service policy check result code: the caller doesn't have permission to start + * foreground service with this type, and the policy is enforced. + * + * @hide + */ + public static final int FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_ENFORCED = + AppProtoEnums.FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_ENFORCED; + + /** + * @hide + */ + @IntDef(flag = true, prefix = { "FGS_TYPE_POLICY_CHECK_" }, value = { + FGS_TYPE_POLICY_CHECK_UNKNOWN, + FGS_TYPE_POLICY_CHECK_OK, + FGS_TYPE_POLICY_CHECK_DEPRECATED, + FGS_TYPE_POLICY_CHECK_DISABLED, + FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_PERMISSIVE, + FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_ENFORCED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ForegroundServicePolicyCheckCode{} + + /** + * @return The policy info for the given type. + */ + @NonNull + public abstract ForegroundServiceTypePolicyInfo getForegroundServiceTypePolicyInfo( + @ForegroundServiceType int type, @ForegroundServiceType int defaultToType); + + /** + * Run check on the foreground service type policy for the given uid/pid + * + * @hide + */ + @ForegroundServicePolicyCheckCode + public abstract int checkForegroundServiceTypePolicy(@NonNull Context context, + @NonNull String packageName, int callerUid, int callerPid, boolean allowWhileInUse, + @NonNull ForegroundServiceTypePolicyInfo policy); + + @GuardedBy("sLock") + private static ForegroundServiceTypePolicy sDefaultForegroundServiceTypePolicy = null; + + private static final Object sLock = new Object(); + + /** + * Return the default policy for FGS type. + */ + public static @NonNull ForegroundServiceTypePolicy getDefaultPolicy() { + synchronized (sLock) { + if (sDefaultForegroundServiceTypePolicy == null) { + sDefaultForegroundServiceTypePolicy = new DefaultForegroundServiceTypePolicy(); + } + return sDefaultForegroundServiceTypePolicy; + } + } + + /** + * Constructor. + * + * @hide + */ + public ForegroundServiceTypePolicy() { + } + + /** + * This class represents the policy for a specific FGS service type. + * + * @hide + */ + public static final class ForegroundServiceTypePolicyInfo { + /** + * The foreground service type. + */ + final @ForegroundServiceType int mType; + + /** + * The change id to tell if this FGS type is deprecated. + * + * <p>A 0 indicates it's not deprecated.</p> + */ + final long mDeprecationChangeId; + + /** + * The change id to tell if this FGS type is disabled. + * + * <p>A 0 indicates it's not disabled.</p> + */ + final long mDisabledChangeId; + + /** + * The required permissions to start a foreground with this type, all of them + * MUST have been granted. + */ + final @Nullable ForegroundServiceTypePermissions mAllOfPermissions; + + /** + * The required permissions to start a foreground with this type, any one of them + * being granted is sufficient. + */ + final @Nullable ForegroundServiceTypePermissions mAnyOfPermissions; + + /** + * A customized check for the permissions. + */ + @Nullable ForegroundServiceTypePermission mCustomPermission; + + /** + * Not a real change id, but a place holder. + */ + private static final long INVALID_CHANGE_ID = 0L; + + /** + * @return {@code true} if the given change id is valid. + */ + private static boolean isValidChangeId(long changeId) { + return changeId != INVALID_CHANGE_ID; + } + + /** + * Construct a new instance. + * + * @hide + */ + public ForegroundServiceTypePolicyInfo(@ForegroundServiceType int type, + long deprecationChangeId, long disabledChangeId, + @Nullable ForegroundServiceTypePermissions allOfPermissions, + @Nullable ForegroundServiceTypePermissions anyOfPermissions) { + mType = type; + mDeprecationChangeId = deprecationChangeId; + mDisabledChangeId = disabledChangeId; + mAllOfPermissions = allOfPermissions; + mAnyOfPermissions = anyOfPermissions; + } + + /** + * @return The foreground service type. + */ + @ForegroundServiceType + public int getForegroundServiceType() { + return mType; + } + + @Override + public String toString() { + final StringBuilder sb = toPermissionString(new StringBuilder()); + sb.append("type=0x"); + sb.append(Integer.toHexString(mType)); + sb.append(" deprecationChangeId="); + sb.append(mDeprecationChangeId); + sb.append(" disabledChangeId="); + sb.append(mDisabledChangeId); + sb.append(" customPermission="); + sb.append(mCustomPermission); + return sb.toString(); + } + + /** + * @return The required permissions. + */ + public String toPermissionString() { + return toPermissionString(new StringBuilder()).toString(); + } + + private StringBuilder toPermissionString(StringBuilder sb) { + if (mAllOfPermissions != null) { + sb.append("all of the permissions "); + sb.append(mAllOfPermissions.toString()); + sb.append(' '); + } + if (mAnyOfPermissions != null) { + sb.append("any of the permissions "); + sb.append(mAnyOfPermissions.toString()); + sb.append(' '); + } + return sb; + } + + /** + * @hide + */ + public void setCustomPermission( + @Nullable ForegroundServiceTypePermission customPermission) { + mCustomPermission = customPermission; + } + + /** + * @return The name of the permissions which are all required. + * It may contain app op names. + * + * For test only. + */ + public @NonNull Optional<String[]> getRequiredAllOfPermissionsForTest() { + if (mAllOfPermissions == null) { + return Optional.empty(); + } + return Optional.of(mAllOfPermissions.toStringArray()); + } + + /** + * @return The name of the permissions where any of the is granted is sufficient. + * It may contain app op names. + * + * For test only. + */ + public @NonNull Optional<String[]> getRequiredAnyOfPermissionsForTest() { + if (mAnyOfPermissions == null) { + return Optional.empty(); + } + return Optional.of(mAnyOfPermissions.toStringArray()); + } + + /** + * Whether or not this type is disabled. + */ + @SuppressLint("AndroidFrameworkRequiresPermission") + public boolean isTypeDisabled(int callerUid) { + return isValidChangeId(mDisabledChangeId) + && CompatChanges.isChangeEnabled(mDisabledChangeId, callerUid); + } + + /** + * Override the type disabling change Id. + * + * For test only. + */ + public void setTypeDisabledForTest(boolean disabled, @NonNull String packageName) + throws RemoteException { + overrideChangeIdForTest(mDisabledChangeId, disabled, packageName); + } + + /** + * clear the type disabling change Id. + * + * For test only. + */ + public void clearTypeDisabledForTest(@NonNull String packageName) throws RemoteException { + clearOverrideForTest(mDisabledChangeId, packageName); + } + + @SuppressLint("AndroidFrameworkRequiresPermission") + boolean isTypeDeprecated(int callerUid) { + return isValidChangeId(mDeprecationChangeId) + && CompatChanges.isChangeEnabled(mDeprecationChangeId, callerUid); + } + + private void overrideChangeIdForTest(long changeId, boolean enable, String packageName) + throws RemoteException { + if (!isValidChangeId(changeId)) { + return; + } + final ArraySet<Long> enabled = new ArraySet<>(); + final ArraySet<Long> disabled = new ArraySet<>(); + if (enable) { + enabled.add(changeId); + } else { + disabled.add(changeId); + } + final CompatibilityChangeConfig overrides = new CompatibilityChangeConfig( + new Compatibility.ChangeConfig(enabled, disabled)); + IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface( + ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + platformCompat.setOverridesForTest(overrides, packageName); + } + + private void clearOverrideForTest(long changeId, @NonNull String packageName) + throws RemoteException { + IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface( + ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + platformCompat.clearOverrideForTest(changeId, packageName); + } + } + + /** + * This represents the set of permissions that's going to be required + * for a specific service type. + * + * @hide + */ + public static class ForegroundServiceTypePermissions { + /** + * The set of the permissions to be required. + */ + final @NonNull ForegroundServiceTypePermission[] mPermissions; + + /** + * Are we requiring all of the permissions to be granted or any of them. + */ + final boolean mAllOf; + + /** + * Constructor. + */ + public ForegroundServiceTypePermissions( + @NonNull ForegroundServiceTypePermission[] permissions, boolean allOf) { + mPermissions = permissions; + mAllOf = allOf; + } + + /** + * Check the permissions. + */ + @PackageManager.PermissionResult + public int checkPermissions(@NonNull Context context, int callerUid, int callerPid, + @NonNull String packageName, boolean allowWhileInUse) { + if (mAllOf) { + for (ForegroundServiceTypePermission perm : mPermissions) { + final int result = perm.checkPermission(context, callerUid, callerPid, + packageName, allowWhileInUse); + if (result != PERMISSION_GRANTED) { + return PERMISSION_DENIED; + } + } + return PERMISSION_GRANTED; + } else { + boolean anyOfGranted = false; + for (ForegroundServiceTypePermission perm : mPermissions) { + final int result = perm.checkPermission(context, callerUid, callerPid, + packageName, allowWhileInUse); + if (result == PERMISSION_GRANTED) { + anyOfGranted = true; + break; + } + } + return anyOfGranted ? PERMISSION_GRANTED : PERMISSION_DENIED; + } + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("allOf="); + sb.append(mAllOf); + sb.append(' '); + sb.append('['); + for (int i = 0; i < mPermissions.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(mPermissions[i].toString()); + } + sb.append(']'); + return sb.toString(); + } + + @NonNull String[] toStringArray() { + final String[] names = new String[mPermissions.length]; + for (int i = 0; i < mPermissions.length; i++) { + names[i] = mPermissions[i].mName; + } + return names; + } + } + + /** + * This represents a permission that's going to be required for a specific service type. + * + * @hide + */ + public abstract static class ForegroundServiceTypePermission { + /** + * The name of this permission. + */ + final @NonNull String mName; + + /** + * Constructor. + */ + public ForegroundServiceTypePermission(@NonNull String name) { + mName = name; + } + + /** + * Check if the given uid/pid/package has the access to the permission. + */ + @PackageManager.PermissionResult + public abstract int checkPermission(@NonNull Context context, int callerUid, int callerPid, + @NonNull String packageName, boolean allowWhileInUse); + + @Override + public String toString() { + return mName; + } + } + + /** + * This represents a regular Android permission to be required for a specific service type. + */ + static class RegularPermission extends ForegroundServiceTypePermission { + RegularPermission(@NonNull String name) { + super(name); + } + + @Override + @SuppressLint("AndroidFrameworkRequiresPermission") + @PackageManager.PermissionResult + public int checkPermission(Context context, int callerUid, int callerPid, + String packageName, boolean allowWhileInUse) { + // Simple case, check if it's already granted. + if (context.checkPermission(mName, callerPid, callerUid) == PERMISSION_GRANTED) { + return PERMISSION_GRANTED; + } + if (allowWhileInUse) { + // Check its appops + final int opCode = AppOpsManager.permissionToOpCode(mName); + final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); + if (opCode != AppOpsManager.OP_NONE) { + final int currentMode = appOpsManager.unsafeCheckOpRawNoThrow(opCode, callerUid, + packageName); + if (currentMode == MODE_FOREGROUND) { + // It's in foreground only mode and we're allowing while-in-use. + return PERMISSION_GRANTED; + } + } + } + return PERMISSION_DENIED; + } + } + + /** + * This represents an app op permission to be required for a specific service type. + */ + static class AppOpPermission extends ForegroundServiceTypePermission { + final int mOpCode; + + AppOpPermission(int opCode) { + super(AppOpsManager.opToPublicName(opCode)); + mOpCode = opCode; + } + + @Override + @PackageManager.PermissionResult + public int checkPermission(Context context, int callerUid, int callerPid, + String packageName, boolean allowWhileInUse) { + final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); + final int mode = appOpsManager.unsafeCheckOpRawNoThrow(mOpCode, callerUid, packageName); + return (mode == MODE_ALLOWED || (allowWhileInUse && mode == MODE_FOREGROUND)) + ? PERMISSION_GRANTED : PERMISSION_DENIED; + } + } + + /** + * This represents a special Android permission to be required for accessing usb devices. + */ + static class UsbDevicePermission extends ForegroundServiceTypePermission { + UsbDevicePermission() { + super("USB Device"); + } + + @Override + @SuppressLint("AndroidFrameworkRequiresPermission") + @PackageManager.PermissionResult + public int checkPermission(Context context, int callerUid, int callerPid, + String packageName, boolean allowWhileInUse) { + final UsbManager usbManager = context.getSystemService(UsbManager.class); + final HashMap<String, UsbDevice> devices = usbManager.getDeviceList(); + if (!ArrayUtils.isEmpty(devices)) { + for (UsbDevice device : devices.values()) { + if (usbManager.hasPermission(device, packageName, callerPid, callerUid)) { + return PERMISSION_GRANTED; + } + } + } + return PERMISSION_DENIED; + } + } + + /** + * This represents a special Android permission to be required for accessing usb accessories. + */ + static class UsbAccessoryPermission extends ForegroundServiceTypePermission { + UsbAccessoryPermission() { + super("USB Accessory"); + } + + @Override + @SuppressLint("AndroidFrameworkRequiresPermission") + @PackageManager.PermissionResult + public int checkPermission(Context context, int callerUid, int callerPid, + String packageName, boolean allowWhileInUse) { + final UsbManager usbManager = context.getSystemService(UsbManager.class); + final UsbAccessory[] accessories = usbManager.getAccessoryList(); + if (!ArrayUtils.isEmpty(accessories)) { + for (UsbAccessory accessory: accessories) { + if (usbManager.hasPermission(accessory, callerPid, callerUid)) { + return PERMISSION_GRANTED; + } + } + } + return PERMISSION_DENIED; + } + } + + /** + * The default policy for the foreground service types. + * + * @hide + */ + public static class DefaultForegroundServiceTypePolicy extends ForegroundServiceTypePolicy { + private final SparseArray<ForegroundServiceTypePolicyInfo> mForegroundServiceTypePolicies = + new SparseArray<>(); + + /** + * Constructor + */ + public DefaultForegroundServiceTypePolicy() { + mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_NONE, + FGS_TYPE_POLICY_NONE); + mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_DATA_SYNC, + FGS_TYPE_POLICY_DATA_SYNC); + mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, + FGS_TYPE_POLICY_MEDIA_PLAYBACK); + mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_PHONE_CALL, + FGS_TYPE_POLICY_PHONE_CALL); + mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_LOCATION, + FGS_TYPE_POLICY_LOCATION); + mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE, + FGS_TYPE_POLICY_CONNECTED_DEVICE); + mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION, + FGS_TYPE_POLICY_MEDIA_PROJECTION); + mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_CAMERA, + FGS_TYPE_POLICY_CAMERA); + mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_MICROPHONE, + FGS_TYPE_POLICY_MICROPHONE); + mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_HEALTH, + FGS_TYPE_POLICY_HEALTH); + mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING, + FGS_TYPE_POLICY_REMOTE_MESSAGING); + mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED, + FGS_TYPE_POLICY_SYSTEM_EXEMPTED); + mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_SPECIAL_USE, + FGS_TYPE_POLICY_SPECIAL_USE); + } + + @Override + public ForegroundServiceTypePolicyInfo getForegroundServiceTypePolicyInfo( + @ForegroundServiceType int type, @ForegroundServiceType int defaultToType) { + ForegroundServiceTypePolicyInfo info = mForegroundServiceTypePolicies.get(type); + if (info == null) { + // Unknown type, fallback to the defaultToType + info = mForegroundServiceTypePolicies.get(defaultToType); + if (info == null) { + // It shouldn't happen. + throw new IllegalArgumentException("Invalid default fgs type " + defaultToType); + } + } + return info; + } + + @Override + @SuppressLint("AndroidFrameworkRequiresPermission") + @ForegroundServicePolicyCheckCode + public int checkForegroundServiceTypePolicy(Context context, String packageName, + int callerUid, int callerPid, boolean allowWhileInUse, + @NonNull ForegroundServiceTypePolicyInfo policy) { + // Has this FGS type been disabled and not allowed to use anymore? + if (policy.isTypeDisabled(callerUid)) { + return FGS_TYPE_POLICY_CHECK_DISABLED; + } + int permissionResult = PERMISSION_DENIED; + // Do we have the permission to start FGS with this type. + if (policy.mAllOfPermissions != null) { + permissionResult = policy.mAllOfPermissions.checkPermissions(context, + callerUid, callerPid, packageName, allowWhileInUse); + } + // If it has the "all of" permissions granted, check the "any of" ones. + if (permissionResult == PERMISSION_GRANTED) { + boolean checkCustomPermission = true; + // Check the "any of" permissions. + if (policy.mAnyOfPermissions != null) { + permissionResult = policy.mAnyOfPermissions.checkPermissions(context, + callerUid, callerPid, packageName, allowWhileInUse); + if (permissionResult == PERMISSION_GRANTED) { + // We have one of them granted, no need to check custom permissions. + checkCustomPermission = false; + } + } + // If we have a customized permission checker, also call it now. + if (checkCustomPermission && policy.mCustomPermission != null) { + permissionResult = policy.mCustomPermission.checkPermission(context, + callerUid, callerPid, packageName, allowWhileInUse); + } + } + if (permissionResult != PERMISSION_GRANTED) { + return (CompatChanges.isChangeEnabled( + FGS_TYPE_PERMISSION_CHANGE_ID, callerUid)) + ? FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_ENFORCED + : FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_PERMISSIVE; + } + // Has this FGS type been deprecated? + if (policy.isTypeDeprecated(callerUid)) { + return FGS_TYPE_POLICY_CHECK_DEPRECATED; + } + return FGS_TYPE_POLICY_CHECK_OK; + } + } +} diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index 7635138f6cdd..01e4b150fd7c 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -21,6 +21,7 @@ import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentCallbacks2; import android.content.ComponentName; @@ -726,10 +727,32 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * for more details. * </div> * + * <div class="caution"> + * <p><strong>Note:</strong> + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} + * or higher are not allowed to start foreground services without specifying a valid + * foreground service type in the manifest attribute + * {@link android.R.attr#foregroundServiceType}. + * See + * <a href="{@docRoot}/about/versions/14/behavior-changes-14"> + * Behavior changes: Apps targeting Android 14 + * </a> + * for more details. + * </div> + * * @throws ForegroundServiceStartNotAllowedException * If the app targeting API is * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from * becoming foreground service due to background restriction. + * @throws ForegroundServiceTypeNotAllowedException + * If the app targeting API is + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or later, and the manifest attribute + * {@link android.R.attr#foregroundServiceType} is not set. + * @throws SecurityException If the app targeting API is + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or later and doesn't have the + * permission to start the foreground service with the specified type in the manifest attribute + * {@link android.R.attr#foregroundServiceType}. * * @param id The identifier for this notification as per * {@link NotificationManager#notify(int, Notification) @@ -748,51 +771,77 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac } } - /** - * An overloaded version of {@link #startForeground(int, Notification)} with additional - * foregroundServiceType parameter. - * - * <p>Apps built with SDK version {@link android.os.Build.VERSION_CODES#Q} or later can specify - * the foreground service types using attribute {@link android.R.attr#foregroundServiceType} in - * service element of manifest file. The value of attribute - * {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p> - * - * <p>The foregroundServiceType parameter must be a subset flags of what is specified in manifest - * attribute {@link android.R.attr#foregroundServiceType}, if not, an IllegalArgumentException is - * thrown. Specify foregroundServiceType parameter as - * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST} to use all flags that - * is specified in manifest attribute foregroundServiceType.</p> - * - * <div class="caution"> - * <p><strong>Note:</strong> - * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S}, - * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S} - * or higher are not allowed to start foreground services from the background. - * See - * <a href="{@docRoot}/about/versions/12/behavior-changes-12"> - * Behavior changes: Apps targeting Android 12 - * </a> - * for more details. - * </div> - * - * @param id The identifier for this notification as per - * {@link NotificationManager#notify(int, Notification) - * NotificationManager.notify(int, Notification)}; must not be 0. - * @param notification The Notification to be displayed. - * @param foregroundServiceType must be a subset flags of manifest attribute - * {@link android.R.attr#foregroundServiceType} flags. - * - * @throws IllegalArgumentException if param foregroundServiceType is not subset of manifest - * attribute {@link android.R.attr#foregroundServiceType}. - * @throws ForegroundServiceStartNotAllowedException - * If the app targeting API is - * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from - * becoming foreground service due to background restriction. - * - * @see android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST - */ + /** + * An overloaded version of {@link #startForeground(int, Notification)} with additional + * foregroundServiceType parameter. + * + * <p>Apps built with SDK version {@link android.os.Build.VERSION_CODES#Q} or later can specify + * the foreground service types using attribute {@link android.R.attr#foregroundServiceType} in + * service element of manifest file. The value of attribute + * {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p> + * + * <p>The foregroundServiceType parameter must be a subset flags of what is specified in + * manifest attribute {@link android.R.attr#foregroundServiceType}, if not, an + * IllegalArgumentException is thrown. Specify foregroundServiceType parameter as + * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST} to use all flags that + * is specified in manifest attribute foregroundServiceType.</p> + * + * <div class="caution"> + * <p><strong>Note:</strong> + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S} + * or higher are not allowed to start foreground services from the background. + * See + * <a href="{@docRoot}/about/versions/12/behavior-changes-12"> + * Behavior changes: Apps targeting Android 12 + * </a> + * for more details. + * </div> + * + * <div class="caution"> + * <p><strong>Note:</strong> + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} + * or higher are not allowed to start foreground services without specifying a valid + * foreground service type in the manifest attribute + * {@link android.R.attr#foregroundServiceType}, and the parameter {@code foregroundServiceType} + * here must not be the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_NONE}. + * See + * <a href="{@docRoot}/about/versions/14/behavior-changes-14"> + * Behavior changes: Apps targeting Android 14 + * </a> + * for more details. + * </div> + * + * @param id The identifier for this notification as per + * {@link NotificationManager#notify(int, Notification) + * NotificationManager.notify(int, Notification)}; must not be 0. + * @param notification The Notification to be displayed. + * @param foregroundServiceType must be a subset flags of manifest attribute + * {@link android.R.attr#foregroundServiceType} flags; must not be + * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_NONE}. + * + * @throws IllegalArgumentException if param foregroundServiceType is not subset of manifest + * attribute {@link android.R.attr#foregroundServiceType}. + * @throws ForegroundServiceStartNotAllowedException + * If the app targeting API is + * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from + * becoming foreground service due to background restriction. + * @throws ForegroundServiceTypeNotAllowedException + * If the app targeting API is + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or later, and the manifest attribute + * {@link android.R.attr#foregroundServiceType} is not set, or the param + * {@code foregroundServiceType} is {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_NONE}. + * @throws SecurityException If the app targeting API is + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or later and doesn't have the + * permission to start the foreground service with the specified type in + * {@code foregroundServiceType}. + * {@link android.R.attr#foregroundServiceType}. + * + * @see android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST + */ public final void startForeground(int id, @NonNull Notification notification, - @ForegroundServiceType int foregroundServiceType) { + @RequiresPermission @ForegroundServiceType int foregroundServiceType) { try { mActivityManager.setServiceForeground( new ComponentName(this, mClassName), mToken, id, diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index aa86af92df87..103b3f18a33e 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -165,6 +165,21 @@ public abstract class PackageManager { "android.internal.PROPERTY_NO_APP_DATA_STORAGE"; /** + * <service> level {@link android.content.pm.PackageManager.Property} tag specifying + * the actual use case of the service if it's foreground service with the type + * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE}. + * + * <p> + * For example: + * <service> + * <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" + * android:value="foo"/> + * </service> + */ + public static final String PROPERTY_SPECIAL_USE_FGS_SUBTYPE = + "android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"; + + /** * A property value set within the manifest. * <p> * The value of a property will only have a single type, as defined by diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java index 88d700432e8a..ab20b6f7f9cc 100644 --- a/core/java/android/content/pm/ServiceInfo.java +++ b/core/java/android/content/pm/ServiceInfo.java @@ -16,7 +16,9 @@ package android.content.pm; +import android.Manifest; import android.annotation.IntDef; +import android.annotation.RequiresPermission; import android.os.Parcel; import android.os.Parcelable; import android.util.Printer; @@ -100,7 +102,15 @@ public class ServiceInfo extends ComponentInfo /** * The default foreground service type if not been set in manifest file. + * + * <p>Apps targeting API level {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and + * later should NOT use this type, + * calling {@link android.app.Service#startForeground(int, android.app.Notification, int)} with + * this type will get a {@link android.app.ForegroundServiceTypeNotAllowedException}.</p> + * + * @deprecated Do not use. */ + @Deprecated public static final int FOREGROUND_SERVICE_TYPE_NONE = 0; /** @@ -108,14 +118,36 @@ public class ServiceInfo extends ComponentInfo * the {@link android.R.attr#foregroundServiceType} attribute. * Data(photo, file, account) upload/download, backup/restore, import/export, fetch, * transfer over network between device and cloud. + * + * <p>Apps targeting API level {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and + * later should NOT use this type: + * calling {@link android.app.Service#startForeground(int, android.app.Notification, int)} with + * this type on devices running {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} is still + * allowed, but calling it with this type on devices running future platform releases may get a + * {@link android.app.ForegroundServiceTypeNotAllowedException}.</p> + * + * @deprecated Use {@link android.app.job.JobInfo.Builder} data transfer APIs instead. */ + @RequiresPermission( + value = Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC, + conditional = true + ) + @Deprecated public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1 << 0; /** * Constant corresponding to <code>mediaPlayback</code> in * the {@link android.R.attr#foregroundServiceType} attribute. * Music, video, news or other media playback. + * + * <p>Starting foreground service with this type from apps targeting API level + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and later, will require permission + * {@link android.Manifest.permission#FOREGROUND_SERVICE_MEDIA_PLAYBACK}. */ + @RequiresPermission( + value = Manifest.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK, + conditional = true + ) public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 1 << 1; /** @@ -123,28 +155,94 @@ public class ServiceInfo extends ComponentInfo * the {@link android.R.attr#foregroundServiceType} attribute. * Ongoing operations related to phone calls, video conferencing, * or similar interactive communication. + * + * <p>Starting foreground service with this type from apps targeting API level + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and later, will require permission + * {@link android.Manifest.permission#FOREGROUND_SERVICE_PHONE_CALL} and + * {@link android.Manifest.permission#MANAGE_OWN_CALLS}. */ + @RequiresPermission( + allOf = { + Manifest.permission.FOREGROUND_SERVICE_PHONE_CALL, + }, + anyOf = { + Manifest.permission.MANAGE_OWN_CALLS, + }, + conditional = true + ) public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 1 << 2; /** * Constant corresponding to <code>location</code> in * the {@link android.R.attr#foregroundServiceType} attribute. * GPS, map, navigation location update. + * + * <p>Starting foreground service with this type from apps targeting API level + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and later, will require permission + * {@link android.Manifest.permission#FOREGROUND_SERVICE_LOCATION} and one of the + * following permissions: + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. */ + @RequiresPermission( + allOf = { + Manifest.permission.FOREGROUND_SERVICE_LOCATION, + }, + anyOf = { + Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION, + }, + conditional = true + ) public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 1 << 3; /** * Constant corresponding to <code>connectedDevice</code> in * the {@link android.R.attr#foregroundServiceType} attribute. * Auto, bluetooth, TV or other devices connection, monitoring and interaction. + * + * <p>Starting foreground service with this type from apps targeting API level + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and later, will require permission + * {@link android.Manifest.permission#FOREGROUND_SERVICE_CONNECTED_DEVICE} and one of the + * following permissions: + * {@link android.Manifest.permission#BLUETOOTH_CONNECT}, + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}, + * {@link android.Manifest.permission#CHANGE_WIFI_STATE}, + * {@link android.Manifest.permission#CHANGE_WIFI_MULTICAST_STATE}, + * {@link android.Manifest.permission#NFC}, + * {@link android.Manifest.permission#TRANSMIT_IR}, + * or has been granted the access to one of the attached USB devices/accessories. */ + @RequiresPermission( + allOf = { + Manifest.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE, + }, + anyOf = { + Manifest.permission.BLUETOOTH_CONNECT, + Manifest.permission.CHANGE_NETWORK_STATE, + Manifest.permission.CHANGE_WIFI_STATE, + Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, + Manifest.permission.NFC, + Manifest.permission.TRANSMIT_IR, + }, + conditional = true + ) public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 1 << 4; /** * Constant corresponding to {@code mediaProjection} in * the {@link android.R.attr#foregroundServiceType} attribute. * Managing a media projection session, e.g for screen recording or taking screenshots. + * + * <p>Starting foreground service with this type from apps targeting API level + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and later, will require permission + * {@link android.Manifest.permission#FOREGROUND_SERVICE_MEDIA_PROJECTION}, and the user must + * have allowed the screen capture request from this app. */ + @RequiresPermission( + value = Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION, + conditional = true + ) public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION = 1 << 5; /** @@ -155,7 +253,21 @@ public class ServiceInfo extends ComponentInfo * above, a foreground service will not be able to access the camera if this type is not * specified in the manifest and in * {@link android.app.Service#startForeground(int, android.app.Notification, int)}. + * + * <p>Starting foreground service with this type from apps targeting API level + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and later, will require permission + * {@link android.Manifest.permission#FOREGROUND_SERVICE_CAMERA} and + * {@link android.Manifest.permission#CAMERA}. */ + @RequiresPermission( + allOf = { + Manifest.permission.FOREGROUND_SERVICE_CAMERA, + }, + anyOf = { + Manifest.permission.CAMERA, + }, + conditional = true + ) public static final int FOREGROUND_SERVICE_TYPE_CAMERA = 1 << 6; /** @@ -166,17 +278,148 @@ public class ServiceInfo extends ComponentInfo * above, a foreground service will not be able to access the microphone if this type is not * specified in the manifest and in * {@link android.app.Service#startForeground(int, android.app.Notification, int)}. + * + * <p>Starting foreground service with this type from apps targeting API level + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and later, will require permission + * {@link android.Manifest.permission#FOREGROUND_SERVICE_MICROPHONE} and one of the following + * permissions: + * {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT}, + * {@link android.Manifest.permission#RECORD_AUDIO}. */ + @RequiresPermission( + allOf = { + Manifest.permission.FOREGROUND_SERVICE_MICROPHONE, + }, + anyOf = { + Manifest.permission.CAPTURE_AUDIO_OUTPUT, + Manifest.permission.RECORD_AUDIO, + }, + conditional = true + ) public static final int FOREGROUND_SERVICE_TYPE_MICROPHONE = 1 << 7; /** - * The number of foreground service types, this doesn't include - * the {@link #FOREGROUND_SERVICE_TYPE_MANIFEST} and {@link #FOREGROUND_SERVICE_TYPE_NONE} - * as they're not real service types. + * Constant corresponding to {@code health} in + * the {@link android.R.attr#foregroundServiceType} attribute. + * Health, wellness and fitness. + * + * <p>The caller app is required to have the permissions + * {@link android.Manifest.permission#FOREGROUND_SERVICE_HEALTH} and one of the following + * permissions: + * {@link android.Manifest.permission#ACTIVITY_RECOGNITION}, + * {@link android.Manifest.permission#BODY_SENSORS}, + * {@link android.Manifest.permission#HIGH_SAMPLING_RATE_SENSORS}. + */ + @RequiresPermission( + allOf = { + Manifest.permission.FOREGROUND_SERVICE_HEALTH, + }, + anyOf = { + Manifest.permission.ACTIVITY_RECOGNITION, + Manifest.permission.BODY_SENSORS, + Manifest.permission.HIGH_SAMPLING_RATE_SENSORS, + }, + conditional = true + ) + public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 1 << 8; + + /** + * Constant corresponding to {@code remoteMessaging} in + * the {@link android.R.attr#foregroundServiceType} attribute. + * Messaging use cases which host local server to relay messages across devices. + */ + @RequiresPermission( + value = Manifest.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING, + conditional = true + ) + public static final int FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING = 1 << 9; + + /** + * Constant corresponding to {@code systemExempted} in + * the {@link android.R.attr#foregroundServiceType} attribute. + * The system exmpted foreground service use cases. + * + * <p class="note">Note, apps are allowed to use this type only in the following cases: + * <ul> + * <li>App has a UID < {@link android.os.Process#FIRST_APPLICATION_UID}</li> + * <li>App is on Doze allowlist</li> + * <li>Device is running in <a href="https://android.googlesource.com/platform/frameworks/base/+/master/packages/SystemUI/docs/demo_mode.md">Demo Mode</a></li> + * <li><a href="https://source.android.com/devices/tech/admin/provision">Device owner app</a><li> + * <li><a href="https://source.android.com/devices/tech/admin/managed-profiles">Profile owner apps</a><li> + * <li>Persistent apps</li> + * <li><a href="https://source.android.com/docs/core/connect/carrier">Carrier privileged apps</a></li> + * <li>Apps that have the {@code android.app.role.RoleManager#ROLE_EMERGENCY} role</li> + * <li>Headless system apps</li> + * <li><a href="{@docRoot}guide/topics/admin/device-admin">Device admin apps</a></li> + * <li>Active VPN apps</li> + * <li>Apps holding {@link Manifest.permission#SCHEDULE_EXACT_ALARM} or + * {@link Manifest.permission#USE_EXACT_ALARM} permission.</li> + * </ul> + * </p> + */ + @RequiresPermission( + value = Manifest.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED, + conditional = true + ) + public static final int FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED = 1 << 10; + + /** + * Constant corresponding to {@code specialUse} in + * the {@link android.R.attr#foregroundServiceType} attribute. + * Use cases that can't be categorized into any other foreground service types, but also + * can't use {@link android.app.job.JobInfo.Builder} APIs. + * + * <p>The use of this foreground service type may be restricted. Additionally, apps must declare + * a service-level {@link PackageManager#PROPERTY_SPECIAL_USE_FGS_SUBTYPE <property>} in + * {@code AndroidManifest.xml} as a hint of what the exact use case here is. + * Here is an example: + * <pre> + * <uses-permission + * android:name="android.permissions.FOREGROUND_SERVICE_SPECIAL_USE" + * /> + * <service + * android:name=".MySpecialForegroundService" + * android:foregroundServiceType="specialUse"> + * <property + * android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" + * android:value="foo" + * /> + * </service> + * </pre> + * + * In a future release of Android, if the above foreground service type {@code foo} is supported + * by the platform, to offer the backward compatibility, the app could specify + * the {@code android:maxSdkVersion} attribute in the <uses-permission> section, + * and also add the foreground service type {@code foo} into + * the {@code android:foregroundServiceType}, therefore the same app could be installed + * in both platforms. + * <pre> + * <uses-permission + * android:name="android.permissions.FOREGROUND_SERVICE_SPECIAL_USE" + * android:maxSdkVersion="last_sdk_version_without_type_foo" + * /> + * <service + * android:name=".MySpecialForegroundService" + * android:foregroundServiceType="specialUse|foo"> + * <property + * android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"" + * android:value="foo" + * /> + * </service> + * </pre> + */ + @RequiresPermission( + value = Manifest.permission.FOREGROUND_SERVICE_SPECIAL_USE, + conditional = true + ) + public static final int FOREGROUND_SERVICE_TYPE_SPECIAL_USE = 1 << 30; + + /** + * The max index being used in the definition of foreground service types. * * @hide */ - public static final int NUM_OF_FOREGROUND_SERVICE_TYPES = 8; + public static final int FOREGROUND_SERVICE_TYPES_MAX_INDEX = 30; /** * A special value indicates to use all types set in manifest file. @@ -199,7 +442,11 @@ public class ServiceInfo extends ComponentInfo FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE, FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION, FOREGROUND_SERVICE_TYPE_CAMERA, - FOREGROUND_SERVICE_TYPE_MICROPHONE + FOREGROUND_SERVICE_TYPE_MICROPHONE, + FOREGROUND_SERVICE_TYPE_HEALTH, + FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING, + FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED, + FOREGROUND_SERVICE_TYPE_SPECIAL_USE }) @Retention(RetentionPolicy.SOURCE) public @interface ForegroundServiceType {} @@ -275,6 +522,14 @@ public class ServiceInfo extends ComponentInfo return "camera"; case FOREGROUND_SERVICE_TYPE_MICROPHONE: return "microphone"; + case FOREGROUND_SERVICE_TYPE_HEALTH: + return "health"; + case FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING: + return "remoteMessaging"; + case FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED: + return "systemExempted"; + case FOREGROUND_SERVICE_TYPE_SPECIAL_USE: + return "specialUse"; default: return "unknown"; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7ada5483b92b..196ea59fe0f4 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -6184,6 +6184,116 @@ android:label="@string/permlab_foregroundService" android:protectionLevel="normal|instant" /> + <!-- Allows a regular application to use {@link android.app.Service#startForeground + Service.startForeground} with the type "camera". + <p>Protection level: normal|instant + --> + <permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" + android:description="@string/permdesc_foregroundServiceCamera" + android:label="@string/permlab_foregroundServiceCamera" + android:protectionLevel="normal|instant" /> + + <!-- Allows a regular application to use {@link android.app.Service#startForeground + Service.startForeground} with the type "connectedDevice". + <p>Protection level: normal|instant + --> + <permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" + android:description="@string/permdesc_foregroundServiceConnectedDevice" + android:label="@string/permlab_foregroundServiceConnectedDevice" + android:protectionLevel="normal|instant" /> + + <!-- Allows a regular application to use {@link android.app.Service#startForeground + Service.startForeground} with the type "dataSync". + <p>Protection level: normal|instant + --> + <permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" + android:description="@string/permdesc_foregroundServiceDataSync" + android:label="@string/permlab_foregroundServiceDataSync" + android:protectionLevel="normal|instant" /> + + <!-- Allows a regular application to use {@link android.app.Service#startForeground + Service.startForeground} with the type "location". + <p>Protection level: normal|instant + --> + <permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" + android:description="@string/permdesc_foregroundServiceLocation" + android:label="@string/permlab_foregroundServiceLocation" + android:protectionLevel="normal|instant" /> + + <!-- Allows a regular application to use {@link android.app.Service#startForeground + Service.startForeground} with the type "mediaPlayback". + <p>Protection level: normal|instant + --> + <permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" + android:description="@string/permdesc_foregroundServiceMediaPlayback" + android:label="@string/permlab_foregroundServiceMediaPlayback" + android:protectionLevel="normal|instant" /> + + <!-- Allows a regular application to use {@link android.app.Service#startForeground + Service.startForeground} with the type "mediaProjection". + <p>Protection level: normal|instant + --> + <permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" + android:description="@string/permdesc_foregroundServiceMediaProjection" + android:label="@string/permlab_foregroundServiceMediaProjection" + android:protectionLevel="normal|instant" /> + + <!-- Allows a regular application to use {@link android.app.Service#startForeground + Service.startForeground} with the type "microphone". + <p>Protection level: normal|instant + --> + <permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" + android:description="@string/permdesc_foregroundServiceMicrophone" + android:label="@string/permlab_foregroundServiceMicrophone" + android:protectionLevel="normal|instant" /> + + <!-- Allows a regular application to use {@link android.app.Service#startForeground + Service.startForeground} with the type "phoneCall". + <p>Protection level: normal|instant + --> + <permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" + android:description="@string/permdesc_foregroundServicePhoneCall" + android:label="@string/permlab_foregroundServicePhoneCall" + android:protectionLevel="normal|instant" /> + + <!-- Allows a regular application to use {@link android.app.Service#startForeground + Service.startForeground} with the type "health". + <p>Protection level: normal|instant + --> + <permission android:name="android.permission.FOREGROUND_SERVICE_HEALTH" + android:description="@string/permdesc_foregroundServiceHealth" + android:label="@string/permlab_foregroundServiceHealth" + android:protectionLevel="normal|instant" /> + + <!-- Allows a regular application to use {@link android.app.Service#startForeground + Service.startForeground} with the type "remoteMessaging". + <p>Protection level: normal|instant + --> + <permission android:name="android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING" + android:description="@string/permdesc_foregroundServiceRemoteMessaging" + android:label="@string/permlab_foregroundServiceRemoteMessaging" + android:protectionLevel="normal|instant" /> + + <!-- Allows a regular application to use {@link android.app.Service#startForeground + Service.startForeground} with the type "systemExempted". + Apps are allowed to use this type only in the use cases listed in + {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED}. + <p>Protection level: normal|instant + --> + <permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED" + android:description="@string/permdesc_foregroundServiceSystemExempted" + android:label="@string/permlab_foregroundServiceSystemExempted" + android:protectionLevel="normal|instant" /> + + <!-- Allows a regular application to use {@link android.app.Service#startForeground + Service.startForeground} with the type "specialUse". + <p>Protection level: signature|appop|instant + --> + <permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" + android:description="@string/permdesc_foregroundServiceSpecialUse" + android:label="@string/permlab_foregroundServiceSpecialUse" + android:protectionLevel="signature|appop|instant" /> + <!-- @SystemApi Allows to access all app shortcuts. @hide --> <permission android:name="android.permission.ACCESS_SHORTCUTS" diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index eac2b9443631..607467abe24f 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1586,19 +1586,71 @@ together. --> <attr name="foregroundServiceType"> <!-- Data (photo, file, account) upload/download, backup/restore, import/export, fetch, - transfer over network between device and cloud. --> + transfer over network between device and cloud. + + <p>For apps with <code>targetSdkVersion</code> + {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, this type should NOT + be used: calling + {@link android.app.Service#startForeground(int, android.app.Notification, int)} with + this type on devices running {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} + is still allowed, but calling it with this type on devices running future platform + releases may get a {@link android.app.ForegroundServiceTypeNotAllowedException}. + --> <flag name="dataSync" value="0x01" /> - <!-- Music, video, news or other media play. --> + <!-- Music, video, news or other media play. + + <p>For apps with <code>targetSdkVersion</code> + {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a foreground + service with this type will require permission + {@link android.Manifest.permission#FOREGROUND_SERVICE_MEDIA_PLAYBACK}. + --> <flag name="mediaPlayback" value="0x02" /> <!-- Ongoing operations related to phone calls, video conferencing, - or similar interactive communication. --> + or similar interactive communication. + + <p>For apps with <code>targetSdkVersion</code> + {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a foreground + service with this type will require permission + {@link android.Manifest.permission#FOREGROUND_SERVICE_PHONE_CALL} and + {@link android.Manifest.permission#MANAGE_OWN_CALLS}. + --> <flag name="phoneCall" value="0x04" /> - <!-- GPS, map, navigation location update. --> + <!-- GPS, map, navigation location update. + + <p>For apps with <code>targetSdkVersion</code> + {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a foreground + service with this type will require permission + {@link android.Manifest.permission#FOREGROUND_SERVICE_LOCATION} and one of the + following permissions: + {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, + {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. + --> <flag name="location" value="0x08" /> - <!-- Auto, bluetooth, TV or other devices connection, monitoring and interaction. --> + <!-- Auto, bluetooth, TV or other devices connection, monitoring and interaction. + + <p>For apps with <code>targetSdkVersion</code> + {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a foreground + service with this type will require permission + {@link android.Manifest.permission#FOREGROUND_SERVICE_CONNECTED_DEVICE} and one of the + following permissions: + {@link android.Manifest.permission#BLUETOOTH_CONNECT}, + {@link android.Manifest.permission#CHANGE_NETWORK_STATE}, + {@link android.Manifest.permission#CHANGE_WIFI_STATE}, + {@link android.Manifest.permission#CHANGE_WIFI_MULTICAST_STATE}, + {@link android.Manifest.permission#NFC}, + {@link android.Manifest.permission#TRANSMIT_IR}, + or has been granted the access to one of the attached USB devices/accessories. + --> <flag name="connectedDevice" value="0x10" /> <!-- Managing a media projection session, e.g, for screen recording or taking - screenshots.--> + screenshots. + + <p>For apps with <code>targetSdkVersion</code> + {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a foreground + service with this type will require permission + {@link android.Manifest.permission#FOREGROUND_SERVICE_MEDIA_PROJECTION}, and the user + must have allowed the screen capture request from this app. + --> <flag name="mediaProjection" value="0x20" /> <!-- Use the camera device or record video. @@ -1606,6 +1658,12 @@ and above, a foreground service will not be able to access the camera if this type is not specified in the manifest and in {@link android.app.Service#startForeground(int, android.app.Notification, int)}. + + <p>For apps with <code>targetSdkVersion</code> + {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a foreground + service with this type will require permission + {@link android.Manifest.permission#FOREGROUND_SERVICE_CAMERA} and + {@link android.Manifest.permission#CAMERA}. --> <flag name="camera" value="0x40" /> <!--Use the microphone device or record audio. @@ -1614,8 +1672,48 @@ and above, a foreground service will not be able to access the microphone if this type is not specified in the manifest and in {@link android.app.Service#startForeground(int, android.app.Notification, int)}. + + <p>For apps with <code>targetSdkVersion</code> + {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a foreground + service with this type will require permission + {@link android.Manifest.permission#FOREGROUND_SERVICE_MICROPHONE} and one of the + following permissions: + {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT}, + {@link android.Manifest.permission#RECORD_AUDIO}. --> <flag name="microphone" value="0x80" /> + <!--Health, wellness and fitness. + <p>Requires the app to hold the permission + {@link android.Manifest.permission#FOREGROUND_SERVICE_HEALTH} and one of the following + permissions + {@link android.Manifest.permission#ACTIVITY_RECOGNITION}, + {@link android.Manifest.permission#BODY_SENSORS}, + {@link android.Manifest.permission#HIGH_SAMPLING_RATE_SENSORS}. + --> + <flag name="health" value="0x100" /> + <!-- Messaging use cases which host local server to relay messages across devices. + <p>Requires the app to hold the permission + {@link android.Manifest.permission#FOREGROUND_SERVICE_REMOTE_MESSAGING} in order to use + this type. + --> + <flag name="remoteMessaging" value="0x200" /> + <!-- The system exmpted foreground service use cases. + <p>Requires the app to hold the permission + {@link android.Manifest.permission#FOREGROUND_SERVICE_SYSTEM_EXEMPTED} in order to use + this type. Apps are allowed to use this type only in the use cases listed in + {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED}. + --> + <flag name="systemExempted" value="0x400" /> + <!-- Use cases that can't be categorized into any other foreground service types, but also + can't use @link android.app.job.JobInfo.Builder} APIs. + See {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE} for the + best practice of the use of this type. + + <p>Requires the app to hold the permission + {@link android.Manifest.permission#FOREGROUND_SERVICE_SPECIAL_USE} in order to use + this type. + --> + <flag name="specialUse" value="0x40000000" /> </attr> <!-- Enable sampled memory bug detection in this process. diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 216975d2d2e6..7714082dcfc4 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1143,6 +1143,66 @@ <string name="permdesc_foregroundService">Allows the app to make use of foreground services.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_foregroundServiceCamera">run foreground service with the type \"camera\"</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_foregroundServiceCamera">Allows the app to make use of foreground services with the type \"camera\"</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_foregroundServiceConnectedDevice">run foreground service with the type \"connectedDevice\"</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_foregroundServiceConnectedDevice">Allows the app to make use of foreground services with the type \"connectedDevice\"</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_foregroundServiceDataSync">run foreground service with the type \"dataSync\"</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_foregroundServiceDataSync">Allows the app to make use of foreground services with the type \"dataSync\"</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_foregroundServiceLocation">run foreground service with the type \"location\"</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_foregroundServiceLocation">Allows the app to make use of foreground services with the type \"location\"</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_foregroundServiceMediaPlayback">run foreground service with the type \"mediaPlayback\"</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_foregroundServiceMediaPlayback">Allows the app to make use of foreground services with the type \"mediaPlayback\"</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_foregroundServiceMediaProjection">run foreground service with the type \"mediaProjection\"</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_foregroundServiceMediaProjection">Allows the app to make use of foreground services with the type \"mediaProjection\"</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_foregroundServiceMicrophone">run foreground service with the type \"microphone\"</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_foregroundServiceMicrophone">Allows the app to make use of foreground services with the type \"microphone\"</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_foregroundServicePhoneCall">run foreground service with the type \"phoneCall\"</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_foregroundServicePhoneCall">Allows the app to make use of foreground services with the type \"phoneCall\"</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_foregroundServiceHealth">run foreground service with the type \"health\"</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_foregroundServiceHealth">Allows the app to make use of foreground services with the type \"health\"</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_foregroundServiceRemoteMessaging">run foreground service with the type \"remoteMessaging\"</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_foregroundServiceRemoteMessaging">Allows the app to make use of foreground services with the type \"remoteMessaging\"</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_foregroundServiceSystemExempted">run foreground service with the type \"systemExempted\"</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_foregroundServiceSystemExempted">Allows the app to make use of foreground services with the type \"systemExempted\"</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_foregroundServiceSpecialUse">run foreground service with the type \"specialUse\"</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_foregroundServiceSpecialUse">Allows the app to make use of foreground services with the type \"specialUse\"</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_getPackageSize">measure app storage space</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_getPackageSize">Allows the app to retrieve its code, data, and cache sizes</string> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index decfb9fc59df..3e2b71f18b75 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -495,6 +495,10 @@ applications that come with the platform <permission name="android.permission.READ_SAFETY_CENTER_STATUS" /> <!-- Permission required for CTS test - CtsTelephonyTestCases --> <permission name="android.permission.BIND_TELECOM_CONNECTION_SERVICE" /> + <!-- Permission required for CTS test - CtsAppTestCases --> + <permission name="android.permission.CAPTURE_MEDIA_OUTPUT" /> + <permission name="android.permission.CAPTURE_TUNER_AUDIO_INPUT" /> + <permission name="android.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 90fab08ed43e..2e4a245df6a6 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -720,6 +720,57 @@ <!-- Permission required for CTS test - CtsDeviceLockTestCases --> <uses-permission android:name="android.permission.MANAGE_DEVICE_LOCK_STATE" /> + <!-- Permission required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" /> + + <!-- Permission required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" /> + + <!-- Permission required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" /> + + <!-- Permission required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" /> + + <!-- Permission required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" /> + + <!-- Permission required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" /> + + <!-- Permission required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" /> + + <!-- Permission required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" /> + + <!-- Permission required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE_HEALTH" /> + + <!-- Permission required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING" /> + + <!-- Permission required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED" /> + + <!-- Permission required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" /> + + <!-- Permissions required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.CAPTURE_MEDIA_OUTPUT" /> + + <!-- Permissions required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.CAPTURE_TUNER_AUDIO_INPUT" /> + + <!-- Permissions required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT" /> + + <!-- Permissions required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> + + <!-- Permissions required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.USE_EXACT_ALARM" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 7fb376522cc4..86bb699f07d2 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -23,18 +23,29 @@ import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGRO import static android.app.ActivityManager.PROCESS_STATE_HEAVY_WEIGHT; import static android.app.ActivityManager.PROCESS_STATE_RECEIVER; import static android.app.ActivityManager.PROCESS_STATE_TOP; +import static android.app.ForegroundServiceTypePolicy.FGS_TYPE_POLICY_CHECK_DEPRECATED; +import static android.app.ForegroundServiceTypePolicy.FGS_TYPE_POLICY_CHECK_DISABLED; +import static android.app.ForegroundServiceTypePolicy.FGS_TYPE_POLICY_CHECK_OK; +import static android.app.ForegroundServiceTypePolicy.FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_ENFORCED; +import static android.app.ForegroundServiceTypePolicy.FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_PERMISSIVE; +import static android.app.ForegroundServiceTypePolicy.FGS_TYPE_POLICY_CHECK_UNKNOWN; +import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST; +import static android.os.PowerExemptionManager.REASON_ACTIVE_DEVICE_ADMIN; import static android.os.PowerExemptionManager.REASON_ACTIVITY_STARTER; import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD; import static android.os.PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE; import static android.os.PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION; import static android.os.PowerExemptionManager.REASON_BACKGROUND_FGS_PERMISSION; +import static android.os.PowerExemptionManager.REASON_CARRIER_PRIVILEGED_APP; import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER; import static android.os.PowerExemptionManager.REASON_CURRENT_INPUT_METHOD; import static android.os.PowerExemptionManager.REASON_DENIED; import static android.os.PowerExemptionManager.REASON_DEVICE_DEMO_MODE; import static android.os.PowerExemptionManager.REASON_DEVICE_OWNER; +import static android.os.PowerExemptionManager.REASON_DISALLOW_APPS_CONTROL; +import static android.os.PowerExemptionManager.REASON_DPO_PROTECTED_APP; import static android.os.PowerExemptionManager.REASON_FGS_BINDING; import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION; import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_FGS_PERMISSION; @@ -45,10 +56,12 @@ import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT; import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI; import static android.os.PowerExemptionManager.REASON_PROC_STATE_TOP; import static android.os.PowerExemptionManager.REASON_PROFILE_OWNER; +import static android.os.PowerExemptionManager.REASON_ROLE_EMERGENCY; import static android.os.PowerExemptionManager.REASON_SERVICE_LAUNCH; import static android.os.PowerExemptionManager.REASON_START_ACTIVITY_FLAG; import static android.os.PowerExemptionManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION; import static android.os.PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED; +import static android.os.PowerExemptionManager.REASON_SYSTEM_MODULE; import static android.os.PowerExemptionManager.REASON_SYSTEM_UID; import static android.os.PowerExemptionManager.REASON_TEMP_ALLOWED_WHILE_IN_USE; import static android.os.PowerExemptionManager.REASON_UID_VISIBLE; @@ -63,6 +76,9 @@ import static android.os.Process.SYSTEM_UID; import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY; import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICE_BG_LAUNCH; +import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED; +import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER; +import static com.android.internal.util.FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT; import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED; import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD; import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_HOT; @@ -94,6 +110,11 @@ import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.ForegroundServiceStartNotAllowedException; +import android.app.ForegroundServiceTypeNotAllowedException; +import android.app.ForegroundServiceTypePolicy; +import android.app.ForegroundServiceTypePolicy.ForegroundServicePolicyCheckCode; +import android.app.ForegroundServiceTypePolicy.ForegroundServiceTypePermission; +import android.app.ForegroundServiceTypePolicy.ForegroundServiceTypePolicyInfo; import android.app.IApplicationThread; import android.app.IForegroundServiceObserver; import android.app.IServiceConnection; @@ -122,6 +143,7 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.pm.ServiceInfo.ForegroundServiceType; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -576,6 +598,7 @@ public final class ActiveServices { getAppStateTracker().addBackgroundRestrictedAppListener(new BackgroundRestrictedListener()); mAppWidgetManagerInternal = LocalServices.getService(AppWidgetManagerInternal.class); setAllowListWhileInUsePermissionInFgs(); + initSystemExemptedFgsTypePermission(); } private AppStateTracker getAppStateTracker() { @@ -757,8 +780,8 @@ public final class ActiveServices { Slog.w(TAG, msg); showFgsBgRestrictedNotificationLocked(r); logFGSStateChangeLocked(r, - FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED, - 0, FGS_STOP_REASON_UNKNOWN); + FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED, + 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN); if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, callingUid)) { throw new ForegroundServiceStartNotAllowedException(msg); } @@ -1911,6 +1934,7 @@ public final class ActiveServices { ignoreForeground = true; } + int fgsTypeCheckCode = FGS_TYPE_POLICY_CHECK_UNKNOWN; if (!ignoreForeground) { if (r.mStartForegroundCount == 0) { /* @@ -1969,13 +1993,49 @@ public final class ActiveServices { updateServiceForegroundLocked(psr, true); ignoreForeground = true; logFGSStateChangeLocked(r, - FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED, - 0, FGS_STOP_REASON_UNKNOWN); + FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED, + 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN); if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, r.appInfo.uid)) { throw new ForegroundServiceStartNotAllowedException(msg); } } + + if (!ignoreForeground) { + Pair<Integer, RuntimeException> fgsTypeResult = null; + if (foregroundServiceType == ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE) { + fgsTypeResult = validateForegroundServiceType(r, + foregroundServiceType, + ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE); + } else { + int fgsTypes = foregroundServiceType; + // If the service has declared some unknown types which might be coming + // from future releases, and if it also comes with the "specialUse", + // then it'll be deemed as the "specialUse" and we ignore this + // unknown type. Otherwise, it'll be treated as an invalid type. + int defaultFgsTypes = (foregroundServiceType + & ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE) != 0 + ? ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE + : ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE; + for (int serviceType = Integer.highestOneBit(fgsTypes); + serviceType != 0; + serviceType = Integer.highestOneBit(fgsTypes)) { + fgsTypeResult = validateForegroundServiceType(r, + serviceType, defaultFgsTypes); + fgsTypes &= ~serviceType; + if (fgsTypeResult.first != FGS_TYPE_POLICY_CHECK_OK) { + break; + } + } + } + fgsTypeCheckCode = fgsTypeResult.first; + if (fgsTypeResult.second != null) { + logFGSStateChangeLocked(r, + FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED, + 0, FGS_STOP_REASON_UNKNOWN, fgsTypeResult.first); + throw fgsTypeResult.second; + } + } } // Apps under strict background restrictions simply don't get to have foreground @@ -2044,8 +2104,8 @@ public final class ActiveServices { registerAppOpCallbackLocked(r); mAm.updateForegroundServiceUsageStats(r.name, r.userId, true); logFGSStateChangeLocked(r, - FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER, - 0, FGS_STOP_REASON_UNKNOWN); + FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER, + 0, FGS_STOP_REASON_UNKNOWN, fgsTypeCheckCode); updateNumForegroundServicesLocked(); } // Even if the service is already a FGS, we need to update the notification, @@ -2126,10 +2186,11 @@ public final class ActiveServices { AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null); unregisterAppOpCallbackLocked(r); logFGSStateChangeLocked(r, - FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT, + FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT, r.mFgsExitTime > r.mFgsEnterTime ? (int) (r.mFgsExitTime - r.mFgsEnterTime) : 0, - FGS_STOP_REASON_STOP_FOREGROUND); + FGS_STOP_REASON_STOP_FOREGROUND, + FGS_TYPE_POLICY_CHECK_UNKNOWN); r.mFgsNotificationWasDeferred = false; signalForegroundServiceObserversLocked(r); resetFgsRestrictionLocked(r); @@ -2165,6 +2226,118 @@ public final class ActiveServices { return now < eligible; } + /** + * Validate if the given service can start a foreground service with given type. + * + * @return A pair, where the first parameter is the result code and second is the exception + * object if it fails to start a foreground service with given type. + */ + @NonNull + private Pair<Integer, RuntimeException> validateForegroundServiceType(ServiceRecord r, + @ForegroundServiceType int type, + @ForegroundServiceType int defaultToType) { + final ForegroundServiceTypePolicy policy = ForegroundServiceTypePolicy.getDefaultPolicy(); + final ForegroundServiceTypePolicyInfo policyInfo = + policy.getForegroundServiceTypePolicyInfo(type, defaultToType); + final @ForegroundServicePolicyCheckCode int code = policy.checkForegroundServiceTypePolicy( + mAm.mContext, r.packageName, r.app.uid, r.app.getPid(), + r.mAllowWhileInUsePermissionInFgs, policyInfo); + RuntimeException exception = null; + switch (code) { + case FGS_TYPE_POLICY_CHECK_DEPRECATED: { + final String msg = "Starting FGS with type " + + ServiceInfo.foregroundServiceTypeToLabel(type) + + " code=" + code + + " callerApp=" + r.app + + " targetSDK=" + r.app.info.targetSdkVersion; + Slog.wtfQuiet(TAG, msg); + Slog.w(TAG, msg); + } break; + case FGS_TYPE_POLICY_CHECK_DISABLED: { + exception = new ForegroundServiceTypeNotAllowedException( + "Starting FGS with type " + + ServiceInfo.foregroundServiceTypeToLabel(type) + + " callerApp=" + r.app + + " targetSDK=" + r.app.info.targetSdkVersion + + " has been prohibited"); + } break; + case FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_PERMISSIVE: { + final String msg = "Starting FGS with type " + + ServiceInfo.foregroundServiceTypeToLabel(type) + + " code=" + code + + " callerApp=" + r.app + + " targetSDK=" + r.app.info.targetSdkVersion + + " requiredPermissions=" + policyInfo.toPermissionString(); + Slog.wtfQuiet(TAG, msg); + Slog.w(TAG, msg); + } break; + case FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_ENFORCED: { + exception = new SecurityException("Starting FGS with type " + + ServiceInfo.foregroundServiceTypeToLabel(type) + + " callerApp=" + r.app + + " targetSDK=" + r.app.info.targetSdkVersion + + " requires permissions: " + + policyInfo.toPermissionString()); + } break; + case FGS_TYPE_POLICY_CHECK_OK: + default: + break; + } + return Pair.create(code, exception); + } + + private class SystemExemptedFgsTypePermission extends ForegroundServiceTypePermission { + SystemExemptedFgsTypePermission() { + super("System exempted"); + } + + @Override + public int checkPermission(@NonNull Context context, int callerUid, int callerPid, + @NonNull String packageName, boolean allowWhileInUse) { + final AppRestrictionController appRestrictionController = mAm.mAppRestrictionController; + @ReasonCode int reason = appRestrictionController + .getPotentialSystemExemptionReason(callerUid); + if (reason == REASON_DENIED) { + reason = appRestrictionController + .getPotentialSystemExemptionReason(callerUid, packageName); + if (reason == REASON_DENIED) { + reason = appRestrictionController + .getPotentialUserAllowedExemptionReason(callerUid, packageName); + } + } + switch (reason) { + case REASON_SYSTEM_UID: + case REASON_SYSTEM_ALLOW_LISTED: + case REASON_DEVICE_DEMO_MODE: + case REASON_DISALLOW_APPS_CONTROL: + case REASON_DEVICE_OWNER: + case REASON_PROFILE_OWNER: + case REASON_PROC_STATE_PERSISTENT: + case REASON_PROC_STATE_PERSISTENT_UI: + case REASON_SYSTEM_MODULE: + case REASON_CARRIER_PRIVILEGED_APP: + case REASON_DPO_PROTECTED_APP: + case REASON_ACTIVE_DEVICE_ADMIN: + case REASON_ROLE_EMERGENCY: + case REASON_ALLOWLISTED_PACKAGE: + return PERMISSION_GRANTED; + default: + return PERMISSION_DENIED; + } + } + } + + private void initSystemExemptedFgsTypePermission() { + final ForegroundServiceTypePolicy policy = ForegroundServiceTypePolicy.getDefaultPolicy(); + final ForegroundServiceTypePolicyInfo policyInfo = + policy.getForegroundServiceTypePolicyInfo( + ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED, + ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE); + if (policyInfo != null) { + policyInfo.setCustomPermission(new SystemExemptedFgsTypePermission()); + } + } + 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 @@ -4777,10 +4950,11 @@ public final class ActiveServices { unregisterAppOpCallbackLocked(r); r.mFgsExitTime = SystemClock.uptimeMillis(); logFGSStateChangeLocked(r, - FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT, + FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT, r.mFgsExitTime > r.mFgsEnterTime ? (int) (r.mFgsExitTime - r.mFgsEnterTime) : 0, - FGS_STOP_REASON_STOP_SERVICE); + FGS_STOP_REASON_STOP_SERVICE, + FGS_TYPE_POLICY_CHECK_UNKNOWN); mAm.updateForegroundServiceUsageStats(r.name, r.userId, false); } @@ -7020,17 +7194,20 @@ public final class ActiveServices { * @param r ServiceRecord * @param state one of ENTER/EXIT/DENIED event. * @param durationMs Only meaningful for EXIT event, the duration from ENTER and EXIT state. + * @param fgsStopReason why was this FGS stopped. + * @param fgsTypeCheckCode The FGS type policy check result. */ private void logFGSStateChangeLocked(ServiceRecord r, int state, int durationMs, - @FgsStopReason int fgsStopReason) { + @FgsStopReason int fgsStopReason, + @ForegroundServicePolicyCheckCode int fgsTypeCheckCode) { if (!ActivityManagerUtils.shouldSamplePackageForAtom( r.packageName, mAm.mConstants.mFgsAtomSampleRate)) { return; } boolean allowWhileInUsePermissionInFgs; @PowerExemptionManager.ReasonCode int fgsStartReasonCode; - if (state == FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER - || state == FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT) { + if (state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER + || state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT) { allowWhileInUsePermissionInFgs = r.mAllowWhileInUsePermissionInFgsAtEntering; fgsStartReasonCode = r.mAllowStartForegroundAtEntering; } else { @@ -7056,14 +7233,15 @@ public final class ActiveServices { r.mStartForegroundCount, ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName), r.mFgsHasNotificationPermission, - r.foregroundServiceType); + r.foregroundServiceType, + fgsTypeCheckCode); int event = 0; - if (state == FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER) { + if (state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER) { event = EventLogTags.AM_FOREGROUND_SERVICE_START; - } else if (state == FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT) { + } else if (state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT) { event = EventLogTags.AM_FOREGROUND_SERVICE_STOP; - } else if (state == FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED) { + } else if (state == FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED) { event = EventLogTags.AM_FOREGROUND_SERVICE_DENIED; } else { // Unknown event. diff --git a/services/core/java/com/android/server/am/AppFGSTracker.java b/services/core/java/com/android/server/am/AppFGSTracker.java index 50515cd923d1..1f98aba5bbd7 100644 --- a/services/core/java/com/android/server/am/AppFGSTracker.java +++ b/services/core/java/com/android/server/am/AppFGSTracker.java @@ -16,10 +16,10 @@ package com.android.server.am; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPES_MAX_INDEX; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE; -import static android.content.pm.ServiceInfo.NUM_OF_FOREGROUND_SERVICE_TYPES; import static android.content.pm.ServiceInfo.foregroundServiceTypeToLabel; import static android.os.PowerExemptionManager.REASON_DENIED; @@ -645,7 +645,7 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac PackageDurations(int uid, String packageName, MaxTrackingDurationConfig maxTrackingDurationConfig, AppFGSTracker tracker) { - super(uid, packageName, NUM_OF_FOREGROUND_SERVICE_TYPES + 1, TAG, + super(uid, packageName, FOREGROUND_SERVICE_TYPES_MAX_INDEX + 1, TAG, maxTrackingDurationConfig); mEvents[DEFAULT_INDEX] = new LinkedList<>(); mTracker = tracker; diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java index ba1c3b34d7a2..6abf6d8e9eea 100644 --- a/services/core/java/com/android/server/am/AppRestrictionController.java +++ b/services/core/java/com/android/server/am/AppRestrictionController.java @@ -2784,6 +2784,37 @@ public final class AppRestrictionController { */ @ReasonCode int getBackgroundRestrictionExemptionReason(int uid) { + @ReasonCode int reason = getPotentialSystemExemptionReason(uid); + if (reason != REASON_DENIED) { + return reason; + } + final String[] packages = mInjector.getPackageManager().getPackagesForUid(uid); + if (packages != null) { + // Check each packages to see if any of them is in the "fixed" exemption cases. + for (String pkg : packages) { + reason = getPotentialSystemExemptionReason(uid, pkg); + if (reason != REASON_DENIED) { + return reason; + } + } + // Loop the packages again, and check the user-configurable exemptions. + for (String pkg : packages) { + reason = getPotentialUserAllowedExemptionReason(uid, pkg); + if (reason != REASON_DENIED) { + return reason; + } + } + } + return REASON_DENIED; + } + + /** + * @param uid The uid to check. + * @return The potential exemption reason of the given uid. The caller must decide + * whether or not it should be exempted. + */ + @ReasonCode + int getPotentialSystemExemptionReason(int uid) { if (UserHandle.isCore(uid)) { return REASON_SYSTEM_UID; } @@ -2811,37 +2842,51 @@ public final class AppRestrictionController { } else if (uidProcState <= PROCESS_STATE_PERSISTENT_UI) { return REASON_PROC_STATE_PERSISTENT_UI; } - final String[] packages = mInjector.getPackageManager().getPackagesForUid(uid); - if (packages != null) { - final AppOpsManager appOpsManager = mInjector.getAppOpsManager(); - final PackageManagerInternal pm = mInjector.getPackageManagerInternal(); - final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal(); - // Check each packages to see if any of them is in the "fixed" exemption cases. - for (String pkg : packages) { - if (isSystemModule(pkg)) { - return REASON_SYSTEM_MODULE; - } else if (isCarrierApp(pkg)) { - return REASON_CARRIER_PRIVILEGED_APP; - } else if (isExemptedFromSysConfig(pkg)) { - return REASON_SYSTEM_ALLOW_LISTED; - } else if (mConstantsObserver.mBgRestrictionExemptedPackages.contains(pkg)) { - return REASON_SYSTEM_ALLOW_LISTED; - } else if (pm.isPackageStateProtected(pkg, userId)) { - return REASON_DPO_PROTECTED_APP; - } else if (appStandbyInternal.isActiveDeviceAdmin(pkg, userId)) { - return REASON_ACTIVE_DEVICE_ADMIN; - } - } - // Loop the packages again, and check the user-configurable exemptions. - for (String pkg : packages) { - if (appOpsManager.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, - uid, pkg) == AppOpsManager.MODE_ALLOWED) { - return REASON_OP_ACTIVATE_VPN; - } else if (appOpsManager.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, - uid, pkg) == AppOpsManager.MODE_ALLOWED) { - return REASON_OP_ACTIVATE_PLATFORM_VPN; - } - } + return REASON_DENIED; + } + + /** + * @param uid The uid to check. + * @param pkgName The package name to check. + * @return The potential system-fixed exemption reason of the given uid/package. The caller + * must decide whether or not it should be exempted. + */ + @ReasonCode + int getPotentialSystemExemptionReason(int uid, String pkg) { + final PackageManagerInternal pm = mInjector.getPackageManagerInternal(); + final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal(); + final int userId = UserHandle.getUserId(uid); + if (isSystemModule(pkg)) { + return REASON_SYSTEM_MODULE; + } else if (isCarrierApp(pkg)) { + return REASON_CARRIER_PRIVILEGED_APP; + } else if (isExemptedFromSysConfig(pkg)) { + return REASON_SYSTEM_ALLOW_LISTED; + } else if (mConstantsObserver.mBgRestrictionExemptedPackages.contains(pkg)) { + return REASON_SYSTEM_ALLOW_LISTED; + } else if (pm.isPackageStateProtected(pkg, userId)) { + return REASON_DPO_PROTECTED_APP; + } else if (appStandbyInternal.isActiveDeviceAdmin(pkg, userId)) { + return REASON_ACTIVE_DEVICE_ADMIN; + } + return REASON_DENIED; + } + + /** + * @param uid The uid to check. + * @param pkgName The package name to check. + * @return The potential user-allowed exemption reason of the given uid/package. The caller + * must decide whether or not it should be exempted. + */ + @ReasonCode + int getPotentialUserAllowedExemptionReason(int uid, String pkg) { + final AppOpsManager appOpsManager = mInjector.getAppOpsManager(); + if (appOpsManager.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, + uid, pkg) == AppOpsManager.MODE_ALLOWED) { + return REASON_OP_ACTIVATE_VPN; + } else if (appOpsManager.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, + uid, pkg) == AppOpsManager.MODE_ALLOWED) { + return REASON_OP_ACTIVATE_PLATFORM_VPN; } if (isRoleHeldByUid(RoleManager.ROLE_DIALER, uid)) { return REASON_ROLE_DIALER; @@ -2852,6 +2897,7 @@ public final class AppRestrictionController { if (isOnDeviceIdleAllowlist(uid)) { return REASON_ALLOWLISTED_PACKAGE; } + final ActivityManagerInternal am = mInjector.getActivityManagerInternal(); if (am.isAssociatedCompanionApp(UserHandle.getUserId(uid), uid)) { return REASON_COMPANION_DEVICE_MANAGER; } |