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

Commit 3a6e65ab authored by Grzegorz Kolodziejczyk's avatar Grzegorz Kolodziejczyk Committed by Gerrit Code Review
Browse files

Merge "Add GATT authorization handling for TBS"

parents 29841453 d69d2803
Loading
Loading
Loading
Loading
+16 −21
Original line number Diff line number Diff line
@@ -2352,6 +2352,18 @@ public class LeAudioService extends ProfileService {
        return mMcpService;
    }

    void setAuthorizationForRelatedProfiles(BluetoothDevice device, boolean authorize) {
        McpService mcpService = getMcpService();
        if (mcpService != null) {
            mcpService.setDeviceAuthorized(device, authorize);
        }

        TbsService tbsService = getTbsService();
        if (tbsService != null) {
            tbsService.setDeviceAuthorized(device, authorize);
        }
    }

    /**
     * This function is called when the framework registers a callback with the service for this
     * first time. This is used as an indication that Bluetooth has been enabled.
@@ -2373,20 +2385,9 @@ public class LeAudioService extends ProfileService {
            }
        }

        McpService mcpService = getMcpService();
        if (mcpService == null) {
            Log.e(TAG, "mcpService not available ");
            return;
        }

        synchronized (mGroupLock) {
            for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> entry
                    : mDeviceDescriptors.entrySet()) {
                if (entry.getValue().mGroupId == LE_AUDIO_GROUP_ID_INVALID) {
                    continue;
                }

                mcpService.setDeviceAuthorized(entry.getKey(), true);
            for (BluetoothDevice device : mDeviceDescriptors.keySet()) {
                setAuthorizationForRelatedProfiles(device, true);
            }
        }
    }
@@ -2441,10 +2442,7 @@ public class LeAudioService extends ProfileService {
        }

        if (mBluetoothEnabled) {
            McpService mcpService = getMcpService();
            if (mcpService != null) {
                mcpService.setDeviceAuthorized(device, true);
            }
            setAuthorizationForRelatedProfiles(device, true);
        }
    }

@@ -2516,10 +2514,7 @@ public class LeAudioService extends ProfileService {
            notifyGroupNodeRemoved(device, groupId);
        }

        McpService mcpService = getMcpService();
        if (mcpService != null) {
            mcpService.setDeviceAuthorized(device, false);
        }
        setAuthorizationForRelatedProfiles(device, false);
    }

    private void notifyGroupNodeRemoved(BluetoothDevice device, int groupId) {
+71 −2
Original line number Diff line number Diff line
@@ -151,6 +151,7 @@ public class TbsGatt {
    private Callback mCallback;
    private AdapterService mAdapterService;
    private HashMap<BluetoothDevice, HashMap<UUID, Short>> mCccDescriptorValues;
    private TbsService mTbsService;

    public static abstract class Callback {

@@ -168,7 +169,17 @@ public class TbsGatt {
        public abstract boolean isInbandRingtoneEnabled(BluetoothDevice device);
    }

    TbsGatt(Context context) {
    private static class GattOpContext {
        public enum Operation {
            READ_CHARACTERISTIC,
            WRITE_CHARACTERISTIC,
            READ_DESCRIPTOR,
            WRITE_DESCRIPTOR,
        }
    }

    TbsGatt(TbsService tbsService) {
        mContext = tbsService;
        mAdapterService =  Objects.requireNonNull(AdapterService.getAdapterService(),
                "AdapterService shouldn't be null when creating MediaControlCattService");
        IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
@@ -180,7 +191,6 @@ public class TbsGatt {
            }
        }

        mContext = context;
        mBearerProviderNameCharacteristic = new GattCharacteristic(UUID_BEARER_PROVIDER_NAME,
                BluetoothGattCharacteristic.PROPERTY_READ
                        | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
@@ -227,6 +237,8 @@ public class TbsGatt {
                BluetoothGattCharacteristic.PROPERTY_READ
                        | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
                BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);

        mTbsService = tbsService;
        mBluetoothGattServer = null;
    }

@@ -888,6 +900,33 @@ public class TbsGatt {
                }
            };

    private int getDeviceAuthorization(BluetoothDevice device) {
        return mTbsService.getDeviceAuthorization(device);
    }

    private void onRejectedAuthorizationGattOperation(BluetoothDevice device,
            GattOpContext.Operation op, boolean responseNeeded, int requestId, int offset) {
        Log.w(TAG, "onRejectedAuthorizationGattOperation device: " + device + ", operation: "
                + op);

        switch (op) {
            case READ_CHARACTERISTIC:
            case READ_DESCRIPTOR:
                mBluetoothGattServer.sendResponse(device, requestId,
                        BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION, offset, null);
                break;
            case WRITE_CHARACTERISTIC:
            case WRITE_DESCRIPTOR:
                if (responseNeeded) {
                    mBluetoothGattServer.sendResponse(device, requestId,
                            BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION, offset, null);
                }
                break;
            default:
                break;
        }
    }

    /**
     * Callback to handle incoming requests to the GATT server. All read/write requests for
     * characteristics and descriptors are handled here.
@@ -912,6 +951,13 @@ public class TbsGatt {
            if (DBG) {
                Log.d(TAG, "onCharacteristicReadRequest: device=" + device);
            }

            if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) {
                onRejectedAuthorizationGattOperation(device,
                        GattOpContext.Operation.READ_CHARACTERISTIC, false, requestId, offset);
                return;
            }

            byte[] value;
            if (characteristic.getUuid().equals(UUID_STATUS_FLAGS)) {
                value = new byte[2];
@@ -949,6 +995,14 @@ public class TbsGatt {
            if (DBG) {
                Log.d(TAG, "onCharacteristicWriteRequest: device=" + device);
            }

            if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) {
                onRejectedAuthorizationGattOperation(device,
                        GattOpContext.Operation.WRITE_CHARACTERISTIC, preparedWrite, requestId,
                        offset);
                return;
            }

            GattCharacteristic gattCharacteristic = (GattCharacteristic) characteristic;
            int status;
            if (preparedWrite) {
@@ -971,6 +1025,13 @@ public class TbsGatt {
            if (DBG) {
                Log.d(TAG, "onDescriptorReadRequest: device=" + device);
            }

            if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) {
                onRejectedAuthorizationGattOperation(device,
                        GattOpContext.Operation.READ_DESCRIPTOR, false, requestId, offset);
                return;
            }

            ClientCharacteristicConfigurationDescriptor cccd =
                    (ClientCharacteristicConfigurationDescriptor) descriptor;
            byte[] value = cccd.getValue(device);
@@ -992,6 +1053,14 @@ public class TbsGatt {
            if (DBG) {
                Log.d(TAG, "onDescriptorWriteRequest: device=" + device);
            }

            if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) {
                onRejectedAuthorizationGattOperation(device,
                        GattOpContext.Operation.WRITE_DESCRIPTOR, preparedWrite, requestId,
                        offset);
                return;
            }

            ClientCharacteristicConfigurationDescriptor cccd =
                    (ClientCharacteristicConfigurationDescriptor) descriptor;
            int status;
+62 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.bluetooth.tbs;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeCall;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothLeCallControl;
import android.bluetooth.IBluetoothLeCallControlCallback;
import android.content.AttributionSource;
@@ -31,9 +32,12 @@ import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;

import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.le_audio.LeAudioService;
import com.android.internal.annotations.VisibleForTesting;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public class TbsService extends ProfileService {
@@ -42,6 +46,7 @@ public class TbsService extends ProfileService {
    private static final boolean DBG = true;

    private static TbsService sTbsService;
    private Map<BluetoothDevice, Integer> mDeviceAuthorizations = new HashMap<>();

    private final TbsGeneric mTbsGeneric = new TbsGeneric();

@@ -104,6 +109,7 @@ public class TbsService extends ProfileService {
        if (DBG) {
            Log.d(TAG, "cleanup()");
        }
        mDeviceAuthorizations.clear();
    }

    /**
@@ -133,6 +139,62 @@ public class TbsService extends ProfileService {
        sTbsService = instance;
    }

    /**
     * Sets device authorization for TBS.
     *
     * @param device device that would be authorized
     * @param isAuthorized boolean value of authorization permission
     * @hide
     */
    public void setDeviceAuthorized(BluetoothDevice device, boolean isAuthorized) {
        Log.i(TAG, "setDeviceAuthorized(): device: " + device + ", isAuthorized: " + isAuthorized);
        int authorization = isAuthorized ? BluetoothDevice.ACCESS_ALLOWED
                : BluetoothDevice.ACCESS_REJECTED;
        mDeviceAuthorizations.put(device, authorization);
    }

    /**
     * Returns authorization value for given device.
     *
     * @param device device that would be authorized
     * @return authorization value for device
     *
     * Possible authorization values:
     * {@link BluetoothDevice.ACCESS_UNKNOWN},
     * {@link BluetoothDevice.ACCESS_ALLOWED}
     * @hide
     */
    public int getDeviceAuthorization(BluetoothDevice device) {
        /* Telephony Bearer Service is allowed for
         * 1. in PTS mode
         * 2. authorized devices
         * 3. Any LeAudio devices which are allowed to connect
         */
        int authorization = mDeviceAuthorizations.getOrDefault(device, Utils.isPtsTestMode()
                ? BluetoothDevice.ACCESS_ALLOWED : BluetoothDevice.ACCESS_UNKNOWN);
        if (authorization != BluetoothDevice.ACCESS_UNKNOWN) {
            return authorization;
        }

        LeAudioService leAudioService = LeAudioService.getLeAudioService();
        if (leAudioService == null) {
            Log.e(TAG, "TBS access not permited. LeAudioService not available");
            return BluetoothDevice.ACCESS_UNKNOWN;
        }

        if (leAudioService.getConnectionPolicy(device)
                > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
            if (DBG) {
                Log.d(TAG, "TBS authorization allowed based on supported LeAudio service");
            }
            setDeviceAuthorized(device, true);
            return BluetoothDevice.ACCESS_ALLOWED;
        }

        Log.e(TAG, "TBS access not permited");
        return BluetoothDevice.ACCESS_UNKNOWN;
    }

    /**
     * Set inband ringtone for the device.
     * When set, notification will be sent to given device.
+5 −8
Original line number Diff line number Diff line
@@ -70,8 +70,6 @@ import java.util.UUID;
@MediumTest
@RunWith(AndroidJUnit4.class)
public class TbsGattTest {
    private static Context sContext;

    private BluetoothAdapter mAdapter;
    private BluetoothDevice mFirstDevice;
    private BluetoothDevice mSecondDevice;
@@ -91,6 +89,8 @@ public class TbsGattTest {
    private BluetoothGattServerProxy mMockGattServer;
    @Mock
    private TbsGatt.Callback mMockTbsGattCallback;
    @Mock
    private TbsService mMockTbsService;

    @Rule
    public final ServiceTestRule mServiceRule = new ServiceTestRule();
@@ -98,11 +98,6 @@ public class TbsGattTest {
    @Captor
    private ArgumentCaptor<BluetoothGattService> mGattServiceCaptor;

    @BeforeClass
    public static void setUpOnce() {
        sContext = getInstrumentation().getTargetContext();
    }

    @Before
    public void setUp() throws Exception {
        if (Looper.myLooper() == null) {
@@ -118,8 +113,10 @@ public class TbsGattTest {

        doReturn(true).when(mMockGattServer).addService(any(BluetoothGattService.class));
        doReturn(true).when(mMockGattServer).open(any(BluetoothGattServerCallback.class));
        doReturn(BluetoothDevice.ACCESS_ALLOWED).when(mMockTbsService)
                .getDeviceAuthorization(any(BluetoothDevice.class));

        mTbsGatt = new TbsGatt(sContext);
        mTbsGatt = new TbsGatt(mMockTbsService);
        mTbsGatt.setBluetoothGattServerForTesting(mMockGattServer);

        mFirstDevice = TestUtils.getTestDevice(mAdapter, 0);