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

Commit f03458ab authored by Jakub Tyszkowski's avatar Jakub Tyszkowski Committed by Automerger Merge Worker
Browse files

MediaControl: Extract CCCD storage out of MediaControlGattService am: e2ce58f7

parents 62b5318a e2ce58f7
Loading
Loading
Loading
Loading
+24 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.bluetooth.IBluetoothMcpServiceManager;
import android.content.AttributionSource;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
import android.sysprop.BluetoothProperties;
import android.util.Log;

@@ -33,7 +34,9 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.util.HashMap;
import java.util.Collections;
import java.util.Map;
import java.util.List;

/**
 * Provides Media Control Profile, as a service in the Bluetooth application.
@@ -47,7 +50,7 @@ public class McpService extends ProfileService {
    private static McpService sMcpService;
    private static MediaControlProfile sGmcsForTesting;

    private Object mLock = new Object();
    private final Object mLock = new Object();
    @GuardedBy("mLock")
    private MediaControlProfile mGmcs;
    private Map<BluetoothDevice, Integer> mDeviceAuthorizations = new HashMap<>();
@@ -248,6 +251,26 @@ public class McpService extends ProfileService {
        return BluetoothDevice.ACCESS_UNKNOWN;
    }

    List<ParcelUuid> getNotificationSubscriptions(int ccid, BluetoothDevice device) {
        synchronized (mLock) {
            MediaControlProfile gmcs = getGmcsLocked();
            if (gmcs != null) {
                return gmcs.getNotificationSubscriptions(ccid, device);
            }
        }
        return Collections.emptyList();
    }

    void setNotificationSubscription(
            int ccid, BluetoothDevice device, ParcelUuid charUuid, boolean doNotify) {
        synchronized (mLock) {
            MediaControlProfile gmcs = getGmcsLocked();
            if (gmcs != null) {
                gmcs.setNotificationSubscription(ccid, device, charUuid, doNotify);
            }
        }
    }

    @GuardedBy("mLock")
    private MediaControlProfile getGmcsLocked() {
        if (sGmcsForTesting != null) {
+21 −70
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@

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;
@@ -790,13 +789,14 @@ 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));
            List<ParcelUuid> uuidList = mMcpService.getNotificationSubscriptions(mCcid, device);
            mEventLogger.logd(
                    DBG,
                    TAG,
                    "restoreCccValuesForStoredDevices: device= "
                            + device
                            + ", num_uuids= "
                            + uuidList.size());

            /* Restore CCCD values for device */
            for (ParcelUuid uuid : uuidList) {
@@ -1308,63 +1308,6 @@ 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);

        mEventLogger.logd(
                DBG,
                TAG,
                "removeUuidFromMetadata: device= " + device + ", char= " + charUuid.toString());
        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);

        mEventLogger.logd(
                DBG,
                TAG,
                "addUuidToMetadata: device= " + device + ", char= " + charUuid.toString());
        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, boolean store) {
        Map<UUID, Short> characteristicCcc = mCccDescriptorValues.get(device.getAddress());
@@ -1381,13 +1324,13 @@ public class MediaControlGattService implements MediaControlGattServiceInterface
        }

        if (Arrays.equals(value, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) {
            mEventLogger.add("setCcc: device= " + device + ", notify= " + true);
            addUuidToMetadata(new ParcelUuid(charUuid), device);
            mEventLogger.logd(DBG, TAG, "setCcc: device= " + device + ", notify: " + true);
            mMcpService.setNotificationSubscription(mCcid, device, new ParcelUuid(charUuid), true);
        } else if (Arrays.equals(value, BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)) {
            mEventLogger.add("setCcc: device= " + device + ", notify= " + false);
            removeUuidFromMetadata(new ParcelUuid(charUuid), device);
            mEventLogger.logd(DBG, TAG, "setCcc: device= " + device + ", notify: " + false);
            mMcpService.setNotificationSubscription(mCcid, device, new ParcelUuid(charUuid), false);
        } else {
            Log.e(TAG, "Not handled CCC value: " + Arrays.toString(value));
            mEventLogger.loge(TAG, "Not handled CCC value: " + Arrays.toString(value));
        }
    }

@@ -1660,6 +1603,14 @@ public class MediaControlGattService implements MediaControlGattServiceInterface
        return mCcid;
    }

    @Override
    public UUID getServiceUuid() {
        if (mGattService != null) {
            return mGattService.getUuid();
        }
        return new UUID(0, 0);
    }

    @Override
    public void onDeviceAuthorizationSet(BluetoothDevice device) {
        int auth = getDeviceAuthorization(device);
+2 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ package com.android.bluetooth.mcp;
import android.bluetooth.BluetoothDevice;

import java.util.Map;
import java.util.UUID;

/**
 * Media Control Service interface. These are sent Media Players => GATT Servers
@@ -46,6 +47,7 @@ public interface MediaControlGattServiceInterface {
    void setSearchRequestResult(SearchRequest request,
            SearchRequest.Results resultStatus, long resultObjectId);
    int getContentControlId();
    UUID getServiceUuid();
    void onDeviceAuthorizationSet(BluetoothDevice device);
    void destroy();
    void dump(StringBuilder sb);
+64 −0
Original line number Diff line number Diff line
@@ -27,16 +27,21 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.media.session.PlaybackState;
import android.os.Looper;
import android.os.ParcelUuid;
import android.os.SystemClock;
import android.util.Log;

import com.android.bluetooth.BluetoothEventLogger;
import com.android.bluetooth.Utils;
import com.android.bluetooth.audio_util.MediaData;
import com.android.bluetooth.audio_util.MediaPlayerList;
import com.android.bluetooth.audio_util.MediaPlayerWrapper;
import com.android.bluetooth.le_audio.ContentControlIdKeeper;
import com.android.internal.annotations.VisibleForTesting;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -859,6 +864,65 @@ public class MediaControlProfile implements MediaControlServiceCallbacks {
        }
    }

    private boolean isGenericMediaService(int ccid) {
        for (MediaControlGattServiceInterface svc : mServiceMap.values()) {
            if (svc.getContentControlId() == ccid) {
                return svc.getServiceUuid().equals(BluetoothUuid.GENERIC_MEDIA_CONTROL.getUuid());
            }
        }
        return false;
    }

    List<ParcelUuid> getNotificationSubscriptions(int ccid, BluetoothDevice device) {
        // TODO: Support multiple MCS instances
        if (isGenericMediaService(ccid)) {
            byte[] gmcs_cccd = device.getMetadata(BluetoothDevice.METADATA_GMCS_CCCD);
            if ((gmcs_cccd != null) && (gmcs_cccd.length != 0)) {
                return Arrays.asList(Utils.byteArrayToUuid(gmcs_cccd));
            }
        }
        return Collections.emptyList();
    }

    void setNotificationSubscription(
            int ccid, BluetoothDevice device, ParcelUuid charUuid, boolean doNotify) {
        // TODO: Support multiple MCS instances
        if (isGenericMediaService(ccid)) {
            byte[] gmcs_cccd = device.getMetadata(BluetoothDevice.METADATA_GMCS_CCCD);
            List<ParcelUuid> uuidList;

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

            boolean updateDb = false;
            if (doNotify) {
                if (!uuidList.contains(charUuid)) {
                    uuidList.add(charUuid);
                    updateDb = true;
                }
            } else if (uuidList.contains(charUuid)) {
                uuidList.remove(charUuid);
                updateDb = true;
            }

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

    public void dump(StringBuilder sb) {
        sb.append("Media Control Service instance list:\n");
        for (MediaControlGattServiceInterface svc : mServiceMap.values()) {
+1 −0
Original line number Diff line number Diff line
@@ -131,6 +131,7 @@ public class MediaControlGattServiceTest {

        doReturn(mMandatoryFeatures).when(mMockMcsCallbacks).onGetFeatureFlags();
        Assert.assertTrue(mMcpService.init(UUID_GMCS));
        Assert.assertEquals(mMcpService.getServiceUuid(), UUID_GMCS);
        Assert.assertEquals(mMcpService.getContentControlId(), TEST_CCID);

        doReturn(true).when(mMockGattServer).removeService(any(BluetoothGattService.class));
Loading