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 Original line Diff line number Diff line
@@ -2352,6 +2352,18 @@ public class LeAudioService extends ProfileService {
        return mMcpService;
        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
     * 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.
     * 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) {
        synchronized (mGroupLock) {
            for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> entry
            for (BluetoothDevice device : mDeviceDescriptors.keySet()) {
                    : mDeviceDescriptors.entrySet()) {
                setAuthorizationForRelatedProfiles(device, true);
                if (entry.getValue().mGroupId == LE_AUDIO_GROUP_ID_INVALID) {
                    continue;
                }

                mcpService.setDeviceAuthorized(entry.getKey(), true);
            }
            }
        }
        }
    }
    }
@@ -2441,10 +2442,7 @@ public class LeAudioService extends ProfileService {
        }
        }


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


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


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


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


    public static abstract class Callback {
    public static abstract class Callback {


@@ -168,7 +169,17 @@ public class TbsGatt {
        public abstract boolean isInbandRingtoneEnabled(BluetoothDevice device);
        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(),
        mAdapterService =  Objects.requireNonNull(AdapterService.getAdapterService(),
                "AdapterService shouldn't be null when creating MediaControlCattService");
                "AdapterService shouldn't be null when creating MediaControlCattService");
        IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
        IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
@@ -180,7 +191,6 @@ public class TbsGatt {
            }
            }
        }
        }


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

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

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

            byte[] value;
            byte[] value;
            if (characteristic.getUuid().equals(UUID_STATUS_FLAGS)) {
            if (characteristic.getUuid().equals(UUID_STATUS_FLAGS)) {
                value = new byte[2];
                value = new byte[2];
@@ -949,6 +995,14 @@ public class TbsGatt {
            if (DBG) {
            if (DBG) {
                Log.d(TAG, "onCharacteristicWriteRequest: device=" + device);
                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;
            GattCharacteristic gattCharacteristic = (GattCharacteristic) characteristic;
            int status;
            int status;
            if (preparedWrite) {
            if (preparedWrite) {
@@ -971,6 +1025,13 @@ public class TbsGatt {
            if (DBG) {
            if (DBG) {
                Log.d(TAG, "onDescriptorReadRequest: device=" + device);
                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 cccd =
                    (ClientCharacteristicConfigurationDescriptor) descriptor;
                    (ClientCharacteristicConfigurationDescriptor) descriptor;
            byte[] value = cccd.getValue(device);
            byte[] value = cccd.getValue(device);
@@ -992,6 +1053,14 @@ public class TbsGatt {
            if (DBG) {
            if (DBG) {
                Log.d(TAG, "onDescriptorWriteRequest: device=" + device);
                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 cccd =
                    (ClientCharacteristicConfigurationDescriptor) descriptor;
                    (ClientCharacteristicConfigurationDescriptor) descriptor;
            int status;
            int status;
+62 −0
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.bluetooth.tbs;


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


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


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


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


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


    private final TbsGeneric mTbsGeneric = new TbsGeneric();
    private final TbsGeneric mTbsGeneric = new TbsGeneric();


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


    /**
    /**
@@ -133,6 +139,62 @@ public class TbsService extends ProfileService {
        sTbsService = instance;
        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.
     * Set inband ringtone for the device.
     * When set, notification will be sent to given device.
     * When set, notification will be sent to given device.
+5 −8
Original line number Original line Diff line number Diff line
@@ -70,8 +70,6 @@ import java.util.UUID;
@MediumTest
@MediumTest
@RunWith(AndroidJUnit4.class)
@RunWith(AndroidJUnit4.class)
public class TbsGattTest {
public class TbsGattTest {
    private static Context sContext;

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


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


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

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


        doReturn(true).when(mMockGattServer).addService(any(BluetoothGattService.class));
        doReturn(true).when(mMockGattServer).addService(any(BluetoothGattService.class));
        doReturn(true).when(mMockGattServer).open(any(BluetoothGattServerCallback.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);
        mTbsGatt.setBluetoothGattServerForTesting(mMockGattServer);


        mFirstDevice = TestUtils.getTestDevice(mAdapter, 0);
        mFirstDevice = TestUtils.getTestDevice(mAdapter, 0);