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

Commit 90f4f35c authored by Grzegorz Kolodziejczyk's avatar Grzegorz Kolodziejczyk Committed by Automerger Merge Worker
Browse files

Merge "Add GATT authorization handling for TBS" am: 3a6e65ab am: e6d9b82d

parents 630d3fd1 e6d9b82d
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);