summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt37
-rw-r--r--core/java/android/app/AppOpsManager.java25
-rw-r--r--core/java/android/app/ForegroundServiceTypePolicy.java1033
-rw-r--r--core/java/android/app/Service.java137
-rw-r--r--core/java/android/content/pm/PackageManager.java15
-rw-r--r--core/java/android/content/pm/ServiceInfo.java265
-rw-r--r--core/res/AndroidManifest.xml110
-rw-r--r--core/res/res/values/attrs_manifest.xml110
-rw-r--r--core/res/res/values/strings.xml60
-rw-r--r--data/etc/privapp-permissions-platform.xml4
-rw-r--r--packages/Shell/AndroidManifest.xml51
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java212
-rw-r--r--services/core/java/com/android/server/am/AppFGSTracker.java4
-rw-r--r--services/core/java/com/android/server/am/AppRestrictionController.java108
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";
/**
+ * &lt;service&gt; 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:
+ * &lt;service&gt;
+ * &lt;property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
+ * android:value="foo"/&gt;
+ * &lt;/service&gt;
+ */
+ 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 &lt; {@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 &lt;property&gt;} in
+ * {@code AndroidManifest.xml} as a hint of what the exact use case here is.
+ * Here is an example:
+ * <pre>
+ * &lt;uses-permission
+ * android:name="android.permissions.FOREGROUND_SERVICE_SPECIAL_USE"
+ * /&gt;
+ * &lt;service
+ * android:name=".MySpecialForegroundService"
+ * android:foregroundServiceType="specialUse"&gt;
+ * &lt;property
+ * android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
+ * android:value="foo"
+ * /&gt;
+ * &lt;/service&gt;
+ * </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 &lt;uses-permission&gt; 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>
+ * &lt;uses-permission
+ * android:name="android.permissions.FOREGROUND_SERVICE_SPECIAL_USE"
+ * android:maxSdkVersion="last_sdk_version_without_type_foo"
+ * /&gt;
+ * &lt;service
+ * android:name=".MySpecialForegroundService"
+ * android:foregroundServiceType="specialUse|foo"&gt;
+ * &lt;property
+ * android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE""
+ * android:value="foo"
+ * /&gt;
+ * &lt;/service&gt;
+ * </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;
}