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

Commit 0878d238 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge changes If92f6f13,I698e3a4f am: 880d674e am: 96fc8a5a am: 404c4940

parents 1568f377 404c4940
Loading
Loading
Loading
Loading
+110 −4
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@

package com.android.bluetooth.mcp;

import static android.bluetooth.BluetoothDevice.METADATA_GMCS_CCCD;
import static android.bluetooth.BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED;
import static android.bluetooth.BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED;
import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_NOTIFY;
@@ -28,6 +29,7 @@ import static java.util.Map.entry;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
@@ -37,12 +39,17 @@ import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
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 android.util.Pair;

import com.android.bluetooth.Utils;
import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.hearingaid.HearingAidService;
@@ -441,7 +448,7 @@ public class MediaControlGattService implements MediaControlGattServiceInterface
                } else {
                    status = BluetoothGatt.GATT_SUCCESS;
                    setCcc(device, op.mDescriptor.getCharacteristic().getUuid(), op.mOffset,
                            op.mValue);
                            op.mValue, true);
                }

                if (op.mResponseNeeded) {
@@ -522,6 +529,34 @@ public class MediaControlGattService implements MediaControlGattServiceInterface
        }
    }

    private void restoreCccValuesForStoredDevices() {
        for (BluetoothDevice device : mAdapterService.getBondedDevices()) {
            byte[] gmcs_cccd = device.getMetadata(METADATA_GMCS_CCCD);

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

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

            /* Restore CCCD values for device */
            for (ParcelUuid uuid : uuidList) {
                setCcc(device, uuid.getUuid(), 0,
                        BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE, false);
            }
        }
    }

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

    @VisibleForTesting
    final BluetoothGattServerCallback mServerCallback = new BluetoothGattServerCallback() {
        @Override
@@ -551,6 +586,7 @@ public class MediaControlGattService implements MediaControlGattServiceInterface

            mCharacteristics.get(CharId.CONTENT_CONTROL_ID)
                    .setValue(mCcid, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
            restoreCccValuesForStoredDevices();
            setInitialCharacteristicValuesAndNotify();
            initialStateRequest();
        }
@@ -796,6 +832,15 @@ public class MediaControlGattService implements MediaControlGattServiceInterface
        mMcpService = mcpService;
        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();
            }
        }
    }

    protected boolean init(UUID scvUuid) {
@@ -987,16 +1032,77 @@ public class MediaControlGattService implements MediaControlGattServiceInterface
        return mBluetoothGattServer.addService(mGattService);
    }

    private void removeUuidFromMetadata(ParcelUuid charUuid, BluetoothDevice device) {
        List<ParcelUuid> uuidList;
        byte[] gmcs_cccd = device.getMetadata(METADATA_GMCS_CCCD);

        if ((gmcs_cccd == null) || (gmcs_cccd.length == 0)) {
            uuidList = new ArrayList<ParcelUuid>();
        } else {
            uuidList = new ArrayList<>(Arrays.asList(Utils.byteArrayToUuid(gmcs_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_GMCS_CCCD,
                Utils.uuidsToByteArray(uuidList.toArray(new ParcelUuid[0])))) {
            Log.e(TAG, "Can't set CCCD for GMCS characteristic UUID: " + charUuid.toString()
                    + ", (remove)");
        }
    }

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

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

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

        uuidList.add(charUuid);

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

    @VisibleForTesting
    void setCcc(BluetoothDevice device, UUID charUuid, int offset, byte[] value) {
    void setCcc(BluetoothDevice device, UUID charUuid, int offset, byte[] value, boolean store) {
        HashMap<UUID, Short> characteristicCcc = mCccDescriptorValues.get(device.getAddress());
        if (characteristicCcc == null) {
            characteristicCcc = new HashMap<>();
            mCccDescriptorValues.put(device.getAddress(), characteristicCcc);
        }

        characteristicCcc.put(
                charUuid, ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getShort());
        characteristicCcc.put(charUuid,
                ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getShort());

        if (!store) {
            return;
        }

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

    private byte[] getCccBytes(BluetoothDevice device, UUID charUuid) {
+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
+2 −1
Original line number Diff line number Diff line
@@ -90,6 +90,7 @@ public class MediaControlGattServiceTest {
        mAdapter = BluetoothAdapter.getDefaultAdapter();

        doReturn(true).when(mMockGattServer).addService(any(BluetoothGattService.class));
        doReturn(new BluetoothDevice[0]).when(mAdapterService).getBondedDevices();

        mMcpService = new MediaControlGattService(mMockMcpService, mMockMcsCallbacks, TEST_CCID);
        mMcpService.setBluetoothGattServerForTesting(mMockGattServer);
@@ -122,7 +123,7 @@ public class MediaControlGattServiceTest {
        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
        devices.add(mCurrentDevice);
        doReturn(devices).when(mMockGattServer).getConnectedDevices();
        mMcpService.setCcc(mCurrentDevice, characteristic.getUuid(), 0, value);
        mMcpService.setCcc(mCurrentDevice, characteristic.getUuid(), 0, value, true);
    }

    @Test