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

Commit dcf43b85 authored by Jakub Pawłowski's avatar Jakub Pawłowski Committed by Gerrit Code Review
Browse files

Merge changes Ia7863e6f,I1f2c3c19,I34ce32d4

* changes:
  tbs: Re-register for notifications from cache
  mcs: Re-register for notifications from cache
  Extend Metadata Database with GMCS, GTBS CCC data
parents d9f80505 4d39a8c3
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -47,6 +47,8 @@ class CustomizedMetadataEntity {
    public byte[] spatial_audio;
    public byte[] fastpair_customized;
    public byte[] le_audio;
    public byte[] gmcs_cccd;
    public byte[] gtbs_cccd;

    public String toString() {
        StringBuilder builder = new StringBuilder();
@@ -103,7 +105,11 @@ class CustomizedMetadataEntity {
                .append("|fastpair_customized=")
                .append(metadataToString(fastpair_customized))
                .append("|le_audio=")
                .append(metadataToString(le_audio));
                .append(metadataToString(le_audio))
                .append("|gmcs_cccd=")
                .append(metadataToString(gmcs_cccd))
                .append("|gtbs_cccd=")
                .append(metadataToString(gtbs_cccd));


        return builder.toString();
+12 −0
Original line number Diff line number Diff line
@@ -297,6 +297,12 @@ class Metadata {
            case BluetoothDevice.METADATA_LE_AUDIO:
                publicMetadata.le_audio = value;
                break;
            case BluetoothDevice.METADATA_GMCS_CCCD:
                publicMetadata.gmcs_cccd = value;
                break;
            case BluetoothDevice.METADATA_GTBS_CCCD:
                publicMetadata.gtbs_cccd = value;
                break;
        }
    }

@@ -384,6 +390,12 @@ class Metadata {
            case BluetoothDevice.METADATA_LE_AUDIO:
                value = publicMetadata.le_audio;
                break;
            case BluetoothDevice.METADATA_GMCS_CCCD:
                value = publicMetadata.gmcs_cccd;
                break;
            case BluetoothDevice.METADATA_GTBS_CCCD:
                value = publicMetadata.gtbs_cccd;
                break;
        }
        return value;
    }
+19 −1
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@ import java.util.List;
/**
 * MetadataDatabase is a Room database stores Bluetooth persistence data
 */
@Database(entities = {Metadata.class}, version = 116)
@Database(entities = {Metadata.class}, version = 117)
public abstract class MetadataDatabase extends RoomDatabase {
    /**
     * The metadata database file name
@@ -69,6 +69,7 @@ public abstract class MetadataDatabase extends RoomDatabase {
                .addMigrations(MIGRATION_113_114)
                .addMigrations(MIGRATION_114_115)
                .addMigrations(MIGRATION_115_116)
                .addMigrations(MIGRATION_116_117)
                .allowMainThreadQueries()
                .build();
    }
@@ -548,4 +549,21 @@ public abstract class MetadataDatabase extends RoomDatabase {
            }
        }
    };

    @VisibleForTesting
    static final Migration MIGRATION_116_117 = new Migration(116, 117) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            try {
                database.execSQL("ALTER TABLE metadata ADD COLUMN `gmcs_cccd` BLOB");
                database.execSQL("ALTER TABLE metadata ADD COLUMN `gtbs_cccd` BLOB");
            } catch (SQLException ex) {
                // Check if user has new schema, but is just missing the version update
                Cursor cursor = database.query("SELECT * FROM metadata");
                if (cursor == null || cursor.getColumnIndex("gmcs_cccd") == -1) {
                    throw ex;
                }
            }
        }
    };
}
+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);
Loading