From 4826749a105a396bcf557710ae403f6bdac2f396 Mon Sep 17 00:00:00 2001 From: Jing Ji Date: Sun, 6 Nov 2022 18:43:01 -0800 Subject: [PATCH] Implement the foreground service type enforcement Currently it's warning only. To test with it: adb shell am compat enable FGS_TYPE_PERMISSION_CHANGE_ID \ com.my.package.name adb shell am compat enable FGS_TYPE_DATA_SYNC_DISABLED_CHANGE_ID \ com.my.package.name Bug: 246792057 Bug: 254662046 Test: atest CtsAppFgsTestCases Change-Id: I87a112dc9f254e5ea3231bddaf7f542a19cb7604 --- .../app/ForegroundServiceTypePolicy.java | 1033 +++++++++++++++++ data/etc/privapp-permissions-platform.xml | 4 + packages/Shell/AndroidManifest.xml | 51 + .../com/android/server/am/ActiveServices.java | 212 +++- .../server/am/AppRestrictionController.java | 108 +- 5 files changed, 1360 insertions(+), 48 deletions(-) create mode 100644 core/java/android/app/ForegroundServiceTypePolicy.java diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java new file mode 100644 index 0000000000000..eccc5631d86a4 --- /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}. + * + *

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.

+ * + * @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}. + * + *

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.

+ * + * @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}. + * + *

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.

+ * + * @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}. + * + *

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.

+ * + * @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. + * + *

A 0 indicates it's not deprecated.

+ */ + final long mDeprecationChangeId; + + /** + * The change id to tell if this FGS type is disabled. + * + *

A 0 indicates it's not disabled.

+ */ + 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 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 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 enabled = new ArraySet<>(); + final ArraySet 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 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 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/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index decfb9fc59dfb..3e2b71f18b755 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 + + + + diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 90fab08ed43ee..2e4a245df6a62 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -720,6 +720,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 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/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java index ba1c3b34d7a2e..6abf6d8e9eea8 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; }