blob: ac9c497f2a365da4c5aa88d16f7f765759443913 [file] [log] [blame]
/*
* 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_DEFAULT;
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
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_FILE_MANAGEMENT;
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_MANIFEST;
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_SHORT_SERVICE;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED;
import static android.permission.PermissionCheckerManager.PERMISSION_HARD_DENIED;
import static android.permission.PermissionCheckerManager.PERMISSION_SOFT_DENIED;
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.app.role.RoleManager;
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.PermissionChecker;
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.os.UserHandle;
import android.permission.PermissionCheckerManager;
import android.provider.DeviceConfig;
import android.text.TextUtils;
import android.util.ArrayMap;
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.ArrayList;
import java.util.HashMap;
import java.util.List;
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
*/
@ChangeId
@EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.TIRAMISU)
@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}.
*
* @hide
*/
@ChangeId
@Disabled
@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}.
*
* @hide
*/
@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
*/
@ChangeId
@EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.TIRAMISU)
@Overridable
public static final long FGS_TYPE_PERMISSION_CHANGE_ID = 254662522L;
/**
* The prefix for the feature flags of the permission enforcement.
*/
private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX =
"fgs_type_perm_enforcement_flag_";
/**
* The feature flag of the permission enforcement for
* {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_DATA_SYNC},
* in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
*/
private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_DATA_SYNC =
FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "data_sync";
/**
* The feature flag of the permission enforcement for
* {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK},
* in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
*/
private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PLAYBACK =
FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "media_playback";
/**
* The feature flag of the permission enforcement for
* {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_PHONE_CALL},
* in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
*/
private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_PHONE_CALL =
FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "phone_call";
/**
* The feature flag of the permission enforcement for
* {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_LOCATION},
* in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
*/
private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_LOCATION =
FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "location";
/**
* The feature flag of the permission enforcement for
* {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE},
* in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
*/
private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_CONNECTED_DEVICE =
FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "connected_device";
/**
* The feature flag of the permission enforcement for
* {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION},
* in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
*/
private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PROJECTION =
FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "media_projection";
/**
* The feature flag of the permission enforcement for
* {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CAMERA},
* in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
*/
private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_CAMERA =
FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "camera";
/**
* The feature flag of the permission enforcement for
* {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE},
* in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
*/
private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_MICROPHONE =
FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "microphone";
/**
* The feature flag of the permission enforcement for
* {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_HEALTH},
* in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
*/
private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH =
FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "health";
/**
* The feature flag of the permission enforcement for
* {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING},
* in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
*/
private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_REMOTE_MESSAGING =
FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "remote_messaging";
/**
* The feature flag of the permission enforcement for
* {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED},
* in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
*/
private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_SYSTEM_EXEMPTED =
FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "system_exempted";
/**
* The feature flag of the permission enforcement for
* {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE},
* in the namespace of {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER}.
*/
private static final String FGS_TYPE_PERM_ENFORCEMENT_FLAG_SPECIAL_USE =
FGS_TYPE_PERM_ENFORCEMENT_FLAG_PREFIX + "special_use";
/**
* The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST}.
*
* @hide
*/
public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_MANIFEST =
new ForegroundServiceTypePolicyInfo(
FOREGROUND_SERVICE_TYPE_MANIFEST,
FGS_TYPE_NONE_DEPRECATION_CHANGE_ID,
FGS_TYPE_NONE_DISABLED_CHANGE_ID,
null /* allOfPermissions */,
null /* anyOfPermissions */,
null /* permissionEnforcementFlag */,
false /* permissionEnforcementFlagDefaultValue */,
false /* foregroundOnlyPermission */
);
/**
* 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 /* allOfPermissions */,
null /* anyOfPermissions */,
null /* permissionEnforcementFlag */,
false /* permissionEnforcementFlagDefaultValue */,
false /* foregroundOnlyPermission */
);
/**
* 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 /* anyOfPermissions */,
FGS_TYPE_PERM_ENFORCEMENT_FLAG_DATA_SYNC /* permissionEnforcementFlag */,
true /* permissionEnforcementFlagDefaultValue */,
false /* foregroundOnlyPermission */
);
/**
* 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 /* anyOfPermissions */,
FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PLAYBACK /* permissionEnforcementFlag */,
true /* permissionEnforcementFlagDefaultValue */,
false /* foregroundOnlyPermission */
);
/**
* 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),
new RolePermission(RoleManager.ROLE_DIALER)
}, false),
FGS_TYPE_PERM_ENFORCEMENT_FLAG_PHONE_CALL /* permissionEnforcementFlag */,
true /* permissionEnforcementFlagDefaultValue */,
false /* foregroundOnlyPermission */
);
/**
* 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),
FGS_TYPE_PERM_ENFORCEMENT_FLAG_LOCATION /* permissionEnforcementFlag */,
true /* permissionEnforcementFlagDefaultValue */,
true /* foregroundOnlyPermission */
);
/**
* 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_ADVERTISE),
new RegularPermission(Manifest.permission.BLUETOOTH_CONNECT),
new RegularPermission(Manifest.permission.BLUETOOTH_SCAN),
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 RegularPermission(Manifest.permission.UWB_RANGING),
new UsbDevicePermission(),
new UsbAccessoryPermission(),
}, false),
FGS_TYPE_PERM_ENFORCEMENT_FLAG_CONNECTED_DEVICE /* permissionEnforcementFlag */,
true /* permissionEnforcementFlagDefaultValue */,
false /* foregroundOnlyPermission */
);
/**
* 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),
FGS_TYPE_PERM_ENFORCEMENT_FLAG_MEDIA_PROJECTION /* permissionEnforcementFlag */,
true /* permissionEnforcementFlagDefaultValue */,
false /* foregroundOnlyPermission */
);
/**
* 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),
FGS_TYPE_PERM_ENFORCEMENT_FLAG_CAMERA /* permissionEnforcementFlag */,
true /* permissionEnforcementFlagDefaultValue */,
true /* foregroundOnlyPermission */
);
/**
* 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),
FGS_TYPE_PERM_ENFORCEMENT_FLAG_MICROPHONE /* permissionEnforcementFlag */,
true /* permissionEnforcementFlagDefaultValue */,
true /* foregroundOnlyPermission */
);
/**
* 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),
FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH /* permissionEnforcementFlag */,
true /* permissionEnforcementFlagDefaultValue */,
false /* foregroundOnlyPermission */
);
/**
* 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 /* anyOfPermissions */,
FGS_TYPE_PERM_ENFORCEMENT_FLAG_REMOTE_MESSAGING /* permissionEnforcementFlag */,
true /* permissionEnforcementFlagDefaultValue */,
false /* foregroundOnlyPermission */
);
/**
* 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),
}, false),
FGS_TYPE_PERM_ENFORCEMENT_FLAG_SYSTEM_EXEMPTED /* permissionEnforcementFlag */,
true /* permissionEnforcementFlagDefaultValue */,
false /* foregroundOnlyPermission */
);
/**
* The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SHORT_SERVICE}.
*
* @hide
*/
public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_SHORT_SERVICE =
new ForegroundServiceTypePolicyInfo(
FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
null /* allOfPermissions */,
null /* anyOfPermissions */,
null /* permissionEnforcementFlag */,
false /* permissionEnforcementFlagDefaultValue */,
false /* foregroundOnlyPermission */
);
/**
* The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_FILE_MANAGEMENT}.
*
* @hide
*/
public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_FILE_MANAGEMENT =
new ForegroundServiceTypePolicyInfo(
FOREGROUND_SERVICE_TYPE_FILE_MANAGEMENT,
ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID,
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_FILE_MANAGEMENT)
}, true),
null /* anyOfPermissions */,
null /* permissionEnforcementFlag */,
false /* permissionEnforcementFlagDefaultValue */,
false /* foregroundOnlyPermission */
);
/**
* 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 /* anyOfPermissions */,
FGS_TYPE_PERM_ENFORCEMENT_FLAG_SPECIAL_USE /* permissionEnforcementFlag */,
true /* permissionEnforcementFlagDefaultValue */,
false /* foregroundOnlyPermission */
);
/**
* 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{}
/**
* Whether or not to require that app to have actual access to certain foreground only
* permissions before starting the foreground service.
*
* <p>
* Examples here are microphone, camera and fg location related permissions.
* When the user grants the permission, its permission state is set to "granted",
* but the actual capability to access these sensors, is to be evaluated according to
* its process state. The Android {@link android.os.Build.VERSION_CODES#R} introduced
* the while-in-use permission, basically the background-started FGS will not have access
* to these sensors. In this context, there is no legitimate reasons to start a FGS from
* the background with these types. This flag controls the behavior of the enforcement,
* when it's enabled, in the aforementioned case, the FGS start will result in
* a SecurityException. </p>
*/
private static final String FGS_TYPE_FG_PERM_ENFORCEMENT_FLAG =
"fgs_type_fg_perm_enforcement_flag";
/**
* The default value to the {@link #FGS_TYPE_FG_PERM_ENFORCEMENT_FLAG}.
*/
private static final boolean DEFAULT_FGS_TYPE_FG_PERM_ENFORCEMENT_FLAG_VALUE = true;
/**
* @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);
/**
* Run the given {@code policyFunctor} on the matching policy, if the flag is known
* to the policy.
*
* @hide
*/
public abstract void updatePermissionEnforcementFlagIfNecessary(@NonNull String flag);
@GuardedBy("sLock")
private static ForegroundServiceTypePolicy sDefaultForegroundServiceTypePolicy = null;
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;
}
}
private static boolean isFgsTypeFgPermissionEnforcementEnabled() {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
FGS_TYPE_FG_PERM_ENFORCEMENT_FLAG, DEFAULT_FGS_TYPE_FG_PERM_ENFORCEMENT_FLAG_VALUE);
}
/**
* 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 permission enforcement flag, unlike the {@link #FGS_TYPE_PERMISSION_CHANGE_ID},
* here it applies to all apps using this FGS type.
*/
final @Nullable String mPermissionEnforcementFlag;
/**
* The default value to {@link #mPermissionEnforcementFlag}.
*/
final boolean mPermissionEnforcementFlagDefaultValue;
/**
* Whether or not the permissions here are limited to foreground only.
* Typical examples are microphone/camera/location.
*/
final boolean mForegroundOnlyPermission;
/**
* A customized check for the permissions.
*/
@Nullable ForegroundServiceTypePermission mCustomPermission;
/**
* The value of the permission enforcement flag, will be updated by the system.
* If the value is {@code false}, the FGS permission check will be ignored.
*
* <p>This value could be updated via the DeviceConfig flag specified
* in the {@link #mPermissionEnforcementFlag}.</p>
*/
volatile boolean mPermissionEnforcementFlagValue;
/**
* Not a real change id, but a place holder.
*/
private static final long INVALID_CHANGE_ID = 0L;
/**
* @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,
@Nullable String permissionEnforcementFlag,
boolean permissionEnforcementFlagDefaultValue,
boolean foregroundOnlyPermission) {
mType = type;
mDeprecationChangeId = deprecationChangeId;
mDisabledChangeId = disabledChangeId;
mAllOfPermissions = allOfPermissions;
mAnyOfPermissions = anyOfPermissions;
mPermissionEnforcementFlag = permissionEnforcementFlag;
mPermissionEnforcementFlagDefaultValue = permissionEnforcementFlagDefaultValue;
mPermissionEnforcementFlagValue = permissionEnforcementFlagDefaultValue;
mForegroundOnlyPermission = foregroundOnlyPermission;
}
/**
* @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;
}
private void updatePermissionEnforcementFlagIfNecessary(@NonNull String flagName) {
if (mPermissionEnforcementFlag == null
|| !TextUtils.equals(flagName, mPermissionEnforcementFlag)) {
return;
}
mPermissionEnforcementFlagValue = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
mPermissionEnforcementFlag,
mPermissionEnforcementFlagDefaultValue);
}
/**
* @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(
@NonNull Context context) {
if (mAllOfPermissions == null) {
return Optional.empty();
}
return Optional.of(mAllOfPermissions.toStringArray(context));
}
/**
* @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(
@NonNull Context context) {
if (mAnyOfPermissions == null) {
return Optional.empty();
}
return Optional.of(mAnyOfPermissions.toStringArray(context));
}
/**
* Whether or not this type is disabled.
*/
@SuppressLint("AndroidFrameworkRequiresPermission")
public boolean isTypeDisabled(int callerUid) {
return isValidChangeId(mDisabledChangeId)
&& CompatChanges.isChangeEnabled(mDisabledChangeId, callerUid);
}
/**
* Whether or not the permissions here are limited to foreground only.
* Typical examples are microphone/camera/location.
*/
public boolean hasForegroundOnlyPermission() {
return mForegroundOnlyPermission;
}
/**
* 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);
}
/**
* For test only.
*
* @return The permission enforcement flag.
*/
public @Nullable String getPermissionEnforcementFlagForTest() {
return mPermissionEnforcementFlag;
}
}
/**
* 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(Context context) {
final ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < mPermissions.length; i++) {
mPermissions[i].addToList(context, list);
}
return list.toArray(new String[list.size()]);
}
}
/**
* 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.
*/
protected 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;
}
void addToList(@NonNull Context context, @NonNull ArrayList<String> list) {
list.add(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(@NonNull Context context, int callerUid, int callerPid,
String packageName, boolean allowWhileInUse) {
return checkPermission(context, mName, callerUid, callerPid, packageName,
allowWhileInUse);
}
@SuppressLint("AndroidFrameworkRequiresPermission")
@PackageManager.PermissionResult
int checkPermission(@NonNull Context context, @NonNull String name, int callerUid,
int callerPid, String packageName, boolean allowWhileInUse) {
@PermissionCheckerManager.PermissionResult final int result =
PermissionChecker.checkPermissionForPreflight(context, name,
callerPid, callerUid, packageName);
if (result == PERMISSION_HARD_DENIED) {
// If the user didn't grant this permission at all.
return PERMISSION_DENIED;
}
final int opCode = AppOpsManager.permissionToOpCode(name);
if (opCode == AppOpsManager.OP_NONE) {
// Simple case, check if it's already granted.
return result == PermissionCheckerManager.PERMISSION_GRANTED
? PERMISSION_GRANTED : PERMISSION_DENIED;
}
final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
final int mode = appOpsManager.unsafeCheckOpRawNoThrow(opCode, callerUid, packageName);
switch (mode) {
case MODE_ALLOWED:
// The appop is just allowed, plain and simple.
return PERMISSION_GRANTED;
case MODE_DEFAULT:
// Follow the permission check result.
return result == PermissionCheckerManager.PERMISSION_GRANTED
? PERMISSION_GRANTED : PERMISSION_DENIED;
case MODE_FOREGROUND:
// If the enforcement flag is OFF, we silently allow it. Or, if it's in
// the foreground only mode and we're allowing while-in-use, allow it.
return !isFgsTypeFgPermissionEnforcementEnabled() || allowWhileInUse
? PERMISSION_GRANTED : PERMISSION_DENIED;
case MODE_IGNORED:
// If it's soft denied with the mode "ignore", semantically it's a silent
// failure and no exception should be thrown, we might not want to allow
// the FGS. However, since the user has agreed with this permission
// (otherwise it's going to be a hard denial), and we're allowing
// while-in-use here, it's safe to allow the FGS run here.
return allowWhileInUse && result == PERMISSION_SOFT_DENIED
? PERMISSION_GRANTED : PERMISSION_DENIED;
default:
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(@NonNull 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 particular role an app needs to hold for a specific service type.
*/
static class RolePermission extends ForegroundServiceTypePermission {
final String mRole;
RolePermission(@NonNull String role) {
super(role);
mRole = role;
}
@Override
@PackageManager.PermissionResult
public int checkPermission(@NonNull Context context, int callerUid, int callerPid,
String packageName, boolean allowWhileInUse) {
final RoleManager rm = context.getSystemService(RoleManager.class);
final List<String> holders = rm.getRoleHoldersAsUser(mRole,
UserHandle.getUserHandleForUid(callerUid));
return holders != null && holders.contains(packageName)
? 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(@NonNull 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(@NonNull 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<>();
/**
* The map between permission enforcement flag to its permission policy info.
*/
private final ArrayMap<String, ForegroundServiceTypePolicyInfo>
mPermissionEnforcementToPolicyInfoMap = new ArrayMap<>();
/**
* Constructor
*/
public DefaultForegroundServiceTypePolicy() {
mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_MANIFEST,
FGS_TYPE_POLICY_MANIFEST);
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_SHORT_SERVICE,
FGS_TYPE_POLICY_SHORT_SERVICE);
// TODO (b/271950506): revisit it in the next release.
// Hide the file management type for now. If anyone uses it, will default to "none".
mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_SPECIAL_USE,
FGS_TYPE_POLICY_SPECIAL_USE);
for (int i = 0, size = mForegroundServiceTypePolicies.size(); i < size; i++) {
final ForegroundServiceTypePolicyInfo info =
mForegroundServiceTypePolicies.valueAt(i);
mPermissionEnforcementToPolicyInfoMap.put(info.mPermissionEnforcementFlag, info);
}
}
@Override
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_GRANTED;
// 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 policy.mPermissionEnforcementFlagValue
&& (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;
}
@Override
public void updatePermissionEnforcementFlagIfNecessary(@NonNull String flagName) {
final ForegroundServiceTypePolicyInfo info =
mPermissionEnforcementToPolicyInfoMap.get(flagName);
if (info != null) {
info.updatePermissionEnforcementFlagIfNecessary(flagName);
}
}
}
}