Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 781f4683 authored by Oli Lan's avatar Oli Lan Committed by Jeff Sharkey
Browse files

Replace BLUETOOTH permission checks with checks for BLUETOOTH_CONNECT.

This replaces all of the remaining checks for the BLUETOOTH and
BLUETOOTH_ADMIN permissions with checks for the new BLUETOOTH_CONNECT
permission (or in some cases, BLUETOOTH_SCAN). Permissions specified
in sendBroadcast calls are not changed in this CL, however.

PermissionChecker is used for the new checks, which can return hard
denial, soft denial, or permission granted. In the case of a soft
denial, a default value is returned as appropriate.

All of the new checks are done as ForPreflight in this CL, i.e. app
ops will be checked but not noted. Changes to make some of the
checks ForDataDelivery (i.e. noting the app op) will be made in
follow-ups - this requires additional work as the calling package
and attribution tag must be plumbed through from the app side.

Bug: 183203469
Test: atest AdapterServiceTest; atest A2dpServiceTest; atest GattServiceTest;
atest PanServiceTest; atest HidHostServiceTest...

Change-Id: I31ad6b16ac3595f45cd2e2afe50c94eabda03e19
parent 42cf113d
Loading
Loading
Loading
Loading
+35 −25
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.bluetooth;

import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_SCAN;
import static android.content.PermissionChecker.PERMISSION_HARD_DENIED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
@@ -31,7 +32,6 @@ import android.companion.Association;
import android.companion.CompanionDeviceManager;
import android.content.ContentValues;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.PermissionChecker;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -360,18 +360,6 @@ public final class Utils {
                == PackageManager.PERMISSION_GRANTED;
    }

    public static void enforceBluetoothPermission(Context context) {
        context.enforceCallingOrSelfPermission(
                android.Manifest.permission.BLUETOOTH,
                "Need BLUETOOTH permission");
    }

    public static void enforceBluetoothAdminPermission(Context context) {
        context.enforceCallingOrSelfPermission(
                android.Manifest.permission.BLUETOOTH_ADMIN,
                "Need BLUETOOTH ADMIN permission");
    }

    public static void enforceBluetoothPrivilegedPermission(Context context) {
        context.enforceCallingOrSelfPermission(
                android.Manifest.permission.BLUETOOTH_PRIVILEGED,
@@ -390,6 +378,40 @@ public final class Utils {
                "Need DUMP permission");
    }

    /**
     * Returns true if the BLUETOOTH_CONNECT permission is granted for the calling app. Returns
     * false if the result is a soft denial. Throws SecurityException if the result is a hard
     * denial.
     *
     * <p>Should be used in situations where the app op should not be noted.
     */
    public static boolean checkConnectPermissionForPreflight(Context context) {
        int permissionCheckResult = PermissionChecker.checkCallingOrSelfPermissionForPreflight(
                context, BLUETOOTH_CONNECT);
        if (permissionCheckResult == PERMISSION_HARD_DENIED) {
            throw new SecurityException("Need BLUETOOTH_CONNECT permission");
        }
        return permissionCheckResult == PERMISSION_GRANTED;
    }

    /**
     * Returns true if the BLUETOOTH_CONNECT permission is granted for the calling app. Returns
     * false if the result is a soft denial. Throws SecurityException if the result is a hard
     * denial.
     *
     * <p>Should be used in situations where data will be delivered and hence the app op should
     * be noted.
     */
    public static boolean checkConnectPermissionForDataDelivery(
            Context context, String callingPackage, String callingAttributionTag, String message) {
        int permissionCheckResult = PermissionChecker.checkCallingOrSelfPermissionForDataDelivery(
                context, BLUETOOTH_CONNECT, callingPackage, callingAttributionTag, message);
        if (permissionCheckResult == PERMISSION_HARD_DENIED) {
            throw new SecurityException("Need BLUETOOTH_CONNECT permission");
        }
        return permissionCheckResult == PERMISSION_GRANTED;
    }

    /**
     * Returns true if the BLUETOOTH_SCAN permission is granted for the calling app. Returns false
     * if the result is a soft denial. Throws SecurityException if the result is a hard denial.
@@ -497,18 +519,6 @@ public final class Utils {
        }
    }

    /**
     * Enforce the context has android.Manifest.permission.BLUETOOTH_ADMIN permission. A
     * {@link SecurityException} would be thrown if neither the calling process or the application
     * does not have BLUETOOTH_ADMIN permission.
     *
     * @param context Context for the permission check.
     */
    public static void enforceAdminPermission(ContextWrapper context) {
        context.enforceCallingOrSelfPermission(android.Manifest.permission.BLUETOOTH_ADMIN,
                "Need BLUETOOTH_ADMIN permission");
    }

    /**
     * Checks whether location is off and must be on for us to perform some operation
     */
+49 −19
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.bluetooth.a2dp;

import static com.android.bluetooth.Utils.enforceBluetoothPermission;
import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;

import android.bluetooth.BluetoothA2dp;
@@ -239,7 +238,9 @@ public class A2dpService extends ProfileService {
    }

    public boolean connect(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return false;
        }
        if (DBG) {
            Log.d(TAG, "connect(): " + device);
        }
@@ -291,7 +292,9 @@ public class A2dpService extends ProfileService {
     * @return true if profile disconnected, false if device not connected over a2dp
     */
    public boolean disconnect(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return false;
        }
        if (DBG) {
            Log.d(TAG, "disconnect(): " + device);
        }
@@ -308,7 +311,9 @@ public class A2dpService extends ProfileService {
    }

    public List<BluetoothDevice> getConnectedDevices() {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return new ArrayList<>(0);
        }
        synchronized (mStateMachines) {
            List<BluetoothDevice> devices = new ArrayList<>();
            for (A2dpStateMachine sm : mStateMachines.values()) {
@@ -389,7 +394,9 @@ public class A2dpService extends ProfileService {
    }

    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return new ArrayList<>(0);
        }
        List<BluetoothDevice> devices = new ArrayList<>();
        if (states == null) {
            return devices;
@@ -437,7 +444,9 @@ public class A2dpService extends ProfileService {
    }

    public int getConnectionState(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return BluetoothProfile.STATE_DISCONNECTED;
        }
        synchronized (mStateMachines) {
            A2dpStateMachine sm = mStateMachines.get(device);
            if (sm == null) {
@@ -513,7 +522,9 @@ public class A2dpService extends ProfileService {
     * @return true on success, otherwise false
     */
    public boolean setActiveDevice(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return false;
        }
        synchronized (mActiveSwitchingGuard) {
            if (device == null) {
                // Remove active device and continue playing audio only if necessary.
@@ -602,7 +613,9 @@ public class A2dpService extends ProfileService {
     * @return the active device or null if no device is active
     */
    public BluetoothDevice getActiveDevice() {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return null;
        }
        synchronized (mStateMachines) {
            return mActiveDevice;
        }
@@ -681,7 +694,9 @@ public class A2dpService extends ProfileService {
    }

    boolean isA2dpPlaying(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return false;
        }
        if (DBG) {
            Log.d(TAG, "isA2dpPlaying(" + device + ")");
        }
@@ -703,7 +718,9 @@ public class A2dpService extends ProfileService {
     * @hide
     */
    public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return null;
        }
        if (DBG) {
            Log.d(TAG, "getCodecStatus(" + device + ")");
        }
@@ -732,7 +749,9 @@ public class A2dpService extends ProfileService {
     */
    public void setCodecConfigPreference(BluetoothDevice device,
                                         BluetoothCodecConfig codecConfig) {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return;
        }
        if (DBG) {
            Log.d(TAG, "setCodecConfigPreference(" + device + "): "
                    + Objects.toString(codecConfig));
@@ -764,7 +783,9 @@ public class A2dpService extends ProfileService {
     * @hide
     */
    public void enableOptionalCodecs(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return;
        }
        if (DBG) {
            Log.d(TAG, "enableOptionalCodecs(" + device + ")");
        }
@@ -795,7 +816,9 @@ public class A2dpService extends ProfileService {
     * @hide
     */
    public void disableOptionalCodecs(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return;
        }
        if (DBG) {
            Log.d(TAG, "disableOptionalCodecs(" + device + ")");
        }
@@ -828,12 +851,16 @@ public class A2dpService extends ProfileService {
     * {@link OptionalCodecsSupportStatus#OPTIONAL_CODECS_SUPPORT_UNKNOWN}.
     */
    public @OptionalCodecsSupportStatus int getSupportsOptionalCodecs(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN;
        }
        return mDatabaseManager.getA2dpSupportsOptionalCodecs(device);
    }

    public void setSupportsOptionalCodecs(BluetoothDevice device, boolean doesSupport) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return;
        }
        int value = doesSupport ? BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED
                : BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED;
        mDatabaseManager.setA2dpSupportsOptionalCodecs(device, value);
@@ -849,7 +876,9 @@ public class A2dpService extends ProfileService {
     * {@link OptionalCodecsPreferenceStatus#OPTIONAL_CODECS_PREF_UNKNOWN}.
     */
    public @OptionalCodecsPreferenceStatus int getOptionalCodecsEnabled(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN;
        }
        return mDatabaseManager.getA2dpOptionalCodecsEnabled(device);
    }

@@ -864,7 +893,9 @@ public class A2dpService extends ProfileService {
     */
    public void setOptionalCodecsEnabled(BluetoothDevice device,
            @OptionalCodecsPreferenceStatus int value) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return;
        }
        if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
                && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
                && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
@@ -1320,10 +1351,9 @@ public class A2dpService extends ProfileService {
        @Override
        public int getPriority(BluetoothDevice device) {
            A2dpService service = getService();
            if (service == null) {
            if (service == null || !Utils.checkConnectPermissionForPreflight(service)) {
                return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
            }
            enforceBluetoothPermission(service);
            return service.getConnectionPolicy(device);
        }

+3 −1
Original line number Diff line number Diff line
@@ -328,7 +328,9 @@ public class A2dpSinkService extends ProfileService {
     * @return true if disconnect is successful, false otherwise.
     */
    public boolean disconnect(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
        if (!Utils.checkConnectPermissionForPreflight(this)) {
            return false;
        }
        if (DBG) {
            StringBuilder sb = new StringBuilder();
            dump(sb);
+105 −151

File changed.

Preview size limit exceeded, changes collapsed.

+17 −5
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.bluetooth.IBluetoothSocketManager;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.ParcelUuid;

import com.android.bluetooth.Utils;

class BluetoothSocketManagerBinder extends IBluetoothSocketManager.Stub {
@@ -43,7 +44,11 @@ class BluetoothSocketManagerBinder extends IBluetoothSocketManager.Stub {
    public ParcelFileDescriptor connectSocket(
            BluetoothDevice device, int type, ParcelUuid uuid, int port, int flag) {

        enforceBluetoothAndActiveUser();
        enforceActiveUser();

        if (!Utils.checkConnectPermissionForPreflight(mService)) {
            return null;
        }

        return marshalFd(mService.connectSocketNative(
            Utils.getBytesFromAddress(device.getAddress()),
@@ -58,7 +63,11 @@ class BluetoothSocketManagerBinder extends IBluetoothSocketManager.Stub {
    public ParcelFileDescriptor createSocketChannel(
            int type, String serviceName, ParcelUuid uuid, int port, int flag) {

        enforceBluetoothAndActiveUser();
        enforceActiveUser();

        if (!Utils.checkConnectPermissionForPreflight(mService)) {
            return null;
        }

        return marshalFd(mService.createSocketChannelNative(
            type,
@@ -72,16 +81,19 @@ class BluetoothSocketManagerBinder extends IBluetoothSocketManager.Stub {

    @Override
    public void requestMaximumTxDataLength(BluetoothDevice device) {
        enforceBluetoothAndActiveUser();
        enforceActiveUser();

        if (!Utils.checkConnectPermissionForPreflight(mService)) {
            return;
        }

        mService.requestMaximumTxDataLengthNative(Utils.getBytesFromAddress(device.getAddress()));
    }

    private void enforceBluetoothAndActiveUser() {
    private void enforceActiveUser() {
        if (!Utils.checkCallerAllowManagedProfiles(mService)) {
            throw new SecurityException("Not allowed for non-active user");
        }
        mService.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    }

    private static ParcelFileDescriptor marshalFd(int fd) {
Loading