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

Commit 4d39a8c3 authored by Grzegorz Kołodziejczyk's avatar Grzegorz Kołodziejczyk
Browse files

tbs: Re-register for notifications from cache

This will handle re-registering of device for notification after
re-connection of device.

Tag: #feature
Bug: 229037130
Test: atest TbsGattTest
Change-Id: Ia7863e6f7acb2a91301d1d49fdfb2542b5b63553
parent ae66a491
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.bluetooth.BluetoothProfile;
import android.content.Context;

import java.util.List;
import java.util.UUID;

/**
 * A proxy class that facilitates testing of the TbsService class.
@@ -63,6 +64,21 @@ public class BluetoothGattServerProxy {
        return mBluetoothGattServer.addService(service);
    }

    /**
     * A proxy that Returns a {@link BluetoothGattService} from the list of services offered
     * by this device.
     *
     * <p>If multiple instances of the same service (as identified by UUID)
     * exist, the first instance of the service is returned.
     *
     * @param uuid UUID of the requested service
     * @return BluetoothGattService if supported, or null if the requested service is not offered by
     * this device.
     */
    public BluetoothGattService getService(UUID uuid) {
        return mBluetoothGattServer.getService(uuid);
    }

    public boolean sendResponse(BluetoothDevice device, int requestId, int status, int offset,
            byte[] value) {
        return mBluetoothGattServer.sendResponse(device, requestId, status, offset, value);
+125 −4
Original line number Diff line number Diff line
@@ -17,17 +17,27 @@

package com.android.bluetooth.tbs;

import static android.bluetooth.BluetoothDevice.METADATA_GTBS_CCCD;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothManager;
import android.bluetooth.IBluetoothStateChangeCallback;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.Log;

import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.Utils;
import com.android.internal.annotations.VisibleForTesting;

import java.io.ByteArrayOutputStream;
@@ -35,6 +45,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

public class TbsGatt {
@@ -131,9 +142,11 @@ public class TbsGatt {
    private final GattCharacteristic mTerminationReasonCharacteristic;
    private final GattCharacteristic mIncomingCallCharacteristic;
    private final GattCharacteristic mCallFriendlyNameCharacteristic;
    private List<BluetoothDevice> mSubscribers = new ArrayList<>();
    private BluetoothGattServerProxy mBluetoothGattServer;
    private Handler mHandler;
    private Callback mCallback;
    private AdapterService mAdapterService;

    public static abstract class Callback {

@@ -144,6 +157,17 @@ public class TbsGatt {
    }

    TbsGatt(Context context) {
        mAdapterService =  Objects.requireNonNull(AdapterService.getAdapterService(),
                "AdapterService shouldn't be null when creating MediaControlCattService");
        IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
        if (mgr != null) {
            try {
                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }

        mContext = context;
        mBearerProviderNameCharacteristic = new GattCharacteristic(UUID_BEARER_PROVIDER_NAME,
                BluetoothGattCharacteristic.PROPERTY_READ
@@ -252,11 +276,55 @@ public class TbsGatt {
        return mContext;
    }

    /** Class that handles GATT characteristic notifications */
    private class BluetoothGattCharacteristicNotifier {
    private void removeUuidFromMetadata(ParcelUuid charUuid, BluetoothDevice device) {
        List<ParcelUuid> uuidList;
        byte[] gtbs_cccd = device.getMetadata(METADATA_GTBS_CCCD);

        private List<BluetoothDevice> mSubscribers = new ArrayList<>();
        if ((gtbs_cccd == null) || (gtbs_cccd.length == 0)) {
            uuidList = new ArrayList<ParcelUuid>();
        } else {
            uuidList = new ArrayList<>(Arrays.asList(Utils.byteArrayToUuid(gtbs_cccd)));

            if (!uuidList.contains(charUuid)) {
                Log.d(TAG, "Characteristic CCCD can't be removed (not cached): "
                        + charUuid.toString());
                return;
            }
        }

        uuidList.remove(charUuid);

        if (!device.setMetadata(METADATA_GTBS_CCCD,
                Utils.uuidsToByteArray(uuidList.toArray(new ParcelUuid[0])))) {
            Log.e(TAG, "Can't set CCCD for GTBS characteristic UUID: " + charUuid + ", (remove)");
        }
    }

    private void addUuidToMetadata(ParcelUuid charUuid, BluetoothDevice device) {
        List<ParcelUuid> uuidList;
        byte[] gtbs_cccd = device.getMetadata(METADATA_GTBS_CCCD);

        if ((gtbs_cccd == null) || (gtbs_cccd.length == 0)) {
            uuidList = new ArrayList<ParcelUuid>();
        } else {
            uuidList = new ArrayList<>(Arrays.asList(Utils.byteArrayToUuid(gtbs_cccd)));

            if (uuidList.contains(charUuid)) {
                Log.d(TAG, "Characteristic CCCD already add: " + charUuid.toString());
                return;
            }
        }

        uuidList.add(charUuid);

        if (!device.setMetadata(METADATA_GTBS_CCCD,
                Utils.uuidsToByteArray(uuidList.toArray(new ParcelUuid[0])))) {
            Log.e(TAG, "Can't set CCCD for GTBS characteristic UUID: " + charUuid + ", (add)");
        }
    }

    /** Class that handles GATT characteristic notifications */
    private class BluetoothGattCharacteristicNotifier {
        public int setSubscriptionConfiguration(BluetoothDevice device, byte[] configuration) {
            if (Arrays.equals(configuration, BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)) {
                mSubscribers.remove(device);
@@ -455,6 +523,14 @@ public class TbsGatt {
                return BluetoothGatt.GATT_FAILURE;
            }

            if (Arrays.equals(value, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) {
                addUuidToMetadata(new ParcelUuid(characteristic.getUuid()), device);
            } else if (Arrays.equals(value, BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)) {
                removeUuidFromMetadata(new ParcelUuid(characteristic.getUuid()), device);
            } else {
                Log.e(TAG, "Not handled CCC value: " + Arrays.toString(value));
            }

            return characteristic.setSubscriptionConfiguration(device, value);
        }
    }
@@ -659,13 +735,56 @@ public class TbsGatt {
        return UUID.fromString(UUID_PREFIX + uuid16 + UUID_SUFFIX);
    }

    private void restoreCccValuesForStoredDevices() {
        BluetoothGattService gattService = mBluetoothGattServer.getService(UUID_GTBS);

        for (BluetoothDevice device : mAdapterService.getBondedDevices()) {
            byte[] gtbs_cccd = device.getMetadata(METADATA_GTBS_CCCD);

            if ((gtbs_cccd == null) || (gtbs_cccd.length == 0)) {
                return;
            }

            List<ParcelUuid> uuidList = Arrays.asList(Utils.byteArrayToUuid(gtbs_cccd));

            /* Restore CCCD values for device */
            for (ParcelUuid uuid : uuidList) {
                BluetoothGattCharacteristic characteristic =
                        gattService.getCharacteristic(uuid.getUuid());
                if (characteristic == null) {
                    Log.e(TAG, "Invalid UUID stored in metadata: " + uuid.toString());
                    continue;
                }

                BluetoothGattDescriptor descriptor =
                        characteristic.getDescriptor(UUID_CLIENT_CHARACTERISTIC_CONFIGURATION);
                if (descriptor == null) {
                    Log.e(TAG, "Invalid characteristic, does not include CCCD");
                    continue;
                }

                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                mSubscribers.add(device);
            }
        }
    }

    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
            new IBluetoothStateChangeCallback.Stub() {
                public void onBluetoothStateChange(boolean up) {
                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
                    if (up) {
                        restoreCccValuesForStoredDevices();
                    }
                }
            };

    /**
     * Callback to handle incoming requests to the GATT server. All read/write requests for
     * characteristics and descriptors are handled here.
     */
    @VisibleForTesting
    final BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {

        @Override
        public void onServiceAdded(int status, BluetoothGattService service) {
            if (DBG) {
@@ -674,6 +793,8 @@ public class TbsGatt {
            if (mCallback != null) {
                mCallback.onServiceAdded(status == BluetoothGatt.GATT_SUCCESS);
            }

            restoreCccValuesForStoredDevices();
        }

        @Override