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

Commit 85ec3740 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Tmap: Allow basic GATT ops before authorized by LeAudioService" am: f8577a6e

parents 8bb9c7b5 f8577a6e
Loading
Loading
Loading
Loading
+181 −2
Original line number Diff line number Diff line
@@ -478,6 +478,7 @@ public class TbsGatt {

        private void notifyCharacteristicChanged(BluetoothDevice device,
                BluetoothGattCharacteristic characteristic) {
            if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) return;
            if (mBluetoothGattServer != null) {
                mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false);
            }
@@ -566,9 +567,18 @@ public class TbsGatt {
        }

        public boolean notifyWithValue(BluetoothDevice device, byte[] value) {
            if (isNotifiable()) {
                mNotifier.notifyWithValue(device, this, value);
                return true;
            }
            return false;
        }

        public void notify(BluetoothDevice device) {
            if (isNotifiable()) {
                mNotifier.notify(device, this);
            }
        }

        public boolean clearValue(boolean notify) {
            boolean success = super.setValue(new byte[0]);
@@ -1010,11 +1020,136 @@ public class TbsGatt {
        }
    }

    private void onUnauthorizedCharRead(BluetoothDevice device, GattOpContext op) {
        UUID charUuid = op.mCharacteristic.getUuid();
        boolean allowToReadRealValue = false;
        byte[] buffer = null;

        /* Allow only some informations to be disclosed at this stage. */
        if (charUuid.equals(UUID_BEARER_PROVIDER_NAME)) {
            ByteBuffer bb = ByteBuffer.allocate(0).order(ByteOrder.LITTLE_ENDIAN);
            bb.put("".getBytes());
            buffer = bb.array();

        } else if (charUuid.equals(UUID_BEARER_UCI)) {
            buffer = "E.164".getBytes();

        } else if (charUuid.equals(UUID_BEARER_TECHNOLOGY)) {
            allowToReadRealValue = true;

        } else if (charUuid.equals(UUID_BEARER_URI_SCHEMES_SUPPORTED_LIST)) {
            ByteBuffer bb = ByteBuffer.allocate(0).order(ByteOrder.LITTLE_ENDIAN);
            bb.put("".getBytes());
            buffer = bb.array();

        } else if (charUuid.equals(UUID_BEARER_LIST_CURRENT_CALLS)) {
            ByteBuffer bb = ByteBuffer.allocate(0).order(ByteOrder.LITTLE_ENDIAN);
            bb.put("".getBytes());
            buffer = bb.array();

        } else if (charUuid.equals(UUID_CONTENT_CONTROL_ID)) {
            allowToReadRealValue = true;

        } else if (charUuid.equals(UUID_STATUS_FLAGS)) {
            allowToReadRealValue = true;

        } else if (charUuid.equals(UUID_CALL_STATE)) {
            ByteBuffer bb = ByteBuffer.allocate(0).order(ByteOrder.LITTLE_ENDIAN);
            bb.put("".getBytes());
            buffer = bb.array();

        } else if (charUuid.equals(UUID_CALL_CONTROL_POINT)) {
            // No read is available on this characteristic

        } else if (charUuid.equals(UUID_CALL_CONTROL_POINT_OPTIONAL_OPCODES)) {
            ByteBuffer bb = ByteBuffer.allocate(1).order(ByteOrder.LITTLE_ENDIAN);
            bb.put((byte) 0x00);
            buffer = bb.array();

        } else if (charUuid.equals(UUID_TERMINATION_REASON)) {
            // No read is available on this characteristic

        } else if (charUuid.equals(UUID_INCOMING_CALL)) {
            ByteBuffer bb = ByteBuffer.allocate(0).order(ByteOrder.LITTLE_ENDIAN);
            bb.put("".getBytes());
            buffer = bb.array();

        } else if (charUuid.equals(UUID_CALL_FRIENDLY_NAME)) {
            ByteBuffer bb = ByteBuffer.allocate(1).order(ByteOrder.LITTLE_ENDIAN);
            bb.put((byte) 0x00);
            buffer = bb.array();
        }

        if (allowToReadRealValue) {
            if (op.mCharacteristic.getValue() != null) {
                buffer =
                        Arrays.copyOfRange(
                                op.mCharacteristic.getValue(),
                                op.mOffset,
                                op.mCharacteristic.getValue().length);
            }
        }

        if (buffer != null) {
            mBluetoothGattServer.sendResponse(
                    device, op.mRequestId, BluetoothGatt.GATT_SUCCESS, op.mOffset, buffer);
        } else {
            mBluetoothGattServer.sendResponse(
                    device,
                    op.mRequestId,
                    BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH,
                    op.mOffset,
                    buffer);
        }
    }

    private void onUnauthorizedGattOperation(BluetoothDevice device, GattOpContext op) {
        if (DBG) {
            Log.d(TAG, "onUnauthorizedGattOperation device: " + device);
        }

        int status = BluetoothGatt.GATT_SUCCESS;

        switch (op.mOperation) {
            /* Allow not yet authorized devices to subscribe for notifications */
            case READ_DESCRIPTOR:
                byte[] value = getCccBytes(device, op.mDescriptor.getCharacteristic().getUuid());
                if (value.length < op.mOffset) {
                    status = BluetoothGatt.GATT_INVALID_OFFSET;
                } else {
                    value = Arrays.copyOfRange(value, op.mOffset, value.length);
                    status = BluetoothGatt.GATT_SUCCESS;
                }

                mBluetoothGattServer.sendResponse(device, op.mRequestId, status, op.mOffset, value);
                return;
            case WRITE_DESCRIPTOR:
                if (op.mPreparedWrite) {
                    status = BluetoothGatt.GATT_FAILURE;
                } else if (op.mOffset > 0) {
                    status = BluetoothGatt.GATT_INVALID_OFFSET;
                } else if (op.mValue.length != 2) {
                    status = BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH;
                } else {
                    status = BluetoothGatt.GATT_SUCCESS;
                    setCcc(device, op.mDescriptor.getCharacteristic().getUuid(), op.mValue);
                }

                if (op.mResponseNeeded) {
                    mBluetoothGattServer.sendResponse(
                            device, op.mRequestId, status, op.mOffset, op.mValue);
                }
                return;
            case READ_CHARACTERISTIC:
                onUnauthorizedCharRead(device, op);
                return;
            case WRITE_CHARACTERISTIC:
                // store as pending operation
                break;
            default:
                break;
        }

        synchronized (mPendingGattOperationsLock) {
            List<GattOpContext> operations = mPendingGattOperations.get(device);
            if (operations == null) {
@@ -1160,6 +1295,38 @@ public class TbsGatt {
        }
    }

    private GattCharacteristic getLocalCharacteristicWrapper(UUID uuid) {
        if (uuid.equals(UUID_BEARER_PROVIDER_NAME)) {
            return mBearerProviderNameCharacteristic;
        } else if (uuid.equals(UUID_BEARER_UCI)) {
            return mBearerUciCharacteristic;
        } else if (uuid.equals(UUID_BEARER_TECHNOLOGY)) {
            return mBearerTechnologyCharacteristic;
        } else if (uuid.equals(UUID_BEARER_URI_SCHEMES_SUPPORTED_LIST)) {
            return mBearerUriSchemesSupportedListCharacteristic;
        } else if (uuid.equals(UUID_BEARER_LIST_CURRENT_CALLS)) {
            return mBearerListCurrentCallsCharacteristic;
        } else if (uuid.equals(UUID_CONTENT_CONTROL_ID)) {
            return mContentControlIdCharacteristic;
        } else if (uuid.equals(UUID_STATUS_FLAGS)) {
            return mStatusFlagsCharacteristic;
        } else if (uuid.equals(UUID_CALL_STATE)) {
            return mCallStateCharacteristic;
        } else if (uuid.equals(UUID_CALL_CONTROL_POINT)) {
            return mCallControlPointCharacteristic;
        } else if (uuid.equals(UUID_CALL_CONTROL_POINT_OPTIONAL_OPCODES)) {
            return mCallControlPointOptionalOpcodesCharacteristic;
        } else if (uuid.equals(UUID_TERMINATION_REASON)) {
            return mTerminationReasonCharacteristic;
        } else if (uuid.equals(UUID_INCOMING_CALL)) {
            return mIncomingCallCharacteristic;
        } else if (uuid.equals(UUID_CALL_FRIENDLY_NAME)) {
            return mCallFriendlyNameCharacteristic;
        }

        return null;
    }

    /**
     * Callback for TBS GATT instance about authorization change for device.
     *
@@ -1167,6 +1334,18 @@ public class TbsGatt {
     */
    public void onDeviceAuthorizationSet(BluetoothDevice device) {
        processPendingGattOperations(device);

        BluetoothGattService gattService = mBluetoothGattServer.getService(UUID_GTBS);
        if (gattService != null) {
            List<BluetoothGattCharacteristic> characteristics = gattService.getCharacteristics();
            for (BluetoothGattCharacteristic characteristic : characteristics) {
                GattCharacteristic wrapper =
                        getLocalCharacteristicWrapper(characteristic.getUuid());
                if (wrapper != null) {
                    wrapper.notify(device);
                }
            }
        }
    }

    private void clearUnauthorizedGattOperationss(BluetoothDevice device) {
+3 −16
Original line number Diff line number Diff line
@@ -23,15 +23,9 @@ import static org.mockito.AdditionalMatchers.*;
import android.bluetooth.*;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.IBluetoothManager;
import android.bluetooth.IBluetoothLeCallControl;
import android.bluetooth.IBluetoothLeCallControlCallback;
import android.content.Context;
import android.os.Looper;
import android.util.Pair;
import android.util.Log;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.rule.ServiceTestRule;
import androidx.test.runner.AndroidJUnit4;
@@ -44,26 +38,19 @@ import com.google.common.primitives.Bytes;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;

@@ -850,7 +837,7 @@ public class TbsGattTest {
        mTbsGatt.mGattServerCallback.onCharacteristicReadRequest(
                mFirstDevice, 1, 0, characteristic);

        verify(mMockTbsService).onDeviceUnauthorized(eq(mFirstDevice));
        verify(mMockTbsService, times(0)).onDeviceUnauthorized(eq(mFirstDevice));
    }

    @Test
@@ -928,7 +915,7 @@ public class TbsGattTest {

        mTbsGatt.mGattServerCallback.onDescriptorReadRequest(mFirstDevice, 1, 0, descriptor);

        verify(mMockTbsService).onDeviceUnauthorized(eq(mFirstDevice));
        verify(mMockTbsService, times(0)).onDeviceUnauthorized(eq(mFirstDevice));
    }

    @Test
@@ -972,6 +959,6 @@ public class TbsGattTest {
        mTbsGatt.mGattServerCallback.onDescriptorWriteRequest(
                mFirstDevice, 1, descriptor, false, true, 0, value);

        verify(mMockTbsService).onDeviceUnauthorized(eq(mFirstDevice));
        verify(mMockTbsService, times(0)).onDeviceUnauthorized(eq(mFirstDevice));
    }
}