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

Commit 005ebae4 authored by Łukasz Rymanowski's avatar Łukasz Rymanowski
Browse files

LeAudioService: Provide Ccid to the native code

This patch extends ContentControlIdKeeper with more information about
the user of the ccid e.g. Context type and userUuid.
The userUuid should be a GATT service uuid which used Ccid or any other
uuid. Note that LeAudioService filters ccid with GTBS and GMCS uuid.

This patch also introduce onInitialized() callback from native to java.
This is needed for LeAudioService to find a good moment to feed native
with known Ccids

Bug: 230551601
Test: atest BluetoothInstrumentationTests
Test: connect audio and verify logs in ASE Enable command

Change-Id: I55d8674c86575ba8a91d9c2a1885d02e1009b919
(cherry picked from commit bcd9ce70e5de8e18fff8f428bfad3ccc0673da6e)
parent 89a12d30
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ using bluetooth::le_audio::LeAudioClientCallbacks;
using bluetooth::le_audio::LeAudioClientInterface;

namespace android {
static jmethodID method_onInitialized;
static jmethodID method_onConnectionStateChanged;
static jmethodID method_onGroupStatus;
static jmethodID method_onGroupNodeStatus;
@@ -125,6 +126,14 @@ class LeAudioClientCallbacksImpl : public LeAudioClientCallbacks {
 public:
  ~LeAudioClientCallbacksImpl() = default;

  void OnInitialized(void) override {
    LOG(INFO) << __func__;
    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onInitialized);
  }

  void OnConnectionState(ConnectionState state,
                         const RawAddress& bd_addr) override {
    LOG(INFO) << __func__ << ", state:" << int(state);
@@ -280,6 +289,7 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
  method_onAudioConf = env->GetMethodID(clazz, "onAudioConf", "(IIIII)V");
  method_onSinkAudioLocationAvailable =
      env->GetMethodID(clazz, "onSinkAudioLocationAvailable", "([BI)V");
  method_onInitialized = env->GetMethodID(clazz, "onInitialized", "()V");
  method_onConnectionStateChanged =
      env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
  method_onAudioLocalCodecCapabilities =
+49 −2
Original line number Diff line number Diff line
@@ -17,6 +17,15 @@

package com.android.bluetooth.le_audio;

import android.bluetooth.BluetoothLeAudio;
import android.os.ParcelUuid;
import android.util.Pair;

import com.android.bluetooth.btservice.ServiceFactory;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;

@@ -30,8 +39,19 @@ public class ContentControlIdKeeper {
    public static final int CCID_MAX = 0xFF;

    private static SortedSet<Integer> sAssignedCcidList = new TreeSet();
    private static HashMap<ParcelUuid, Pair<Integer, Integer>> sUserMap = new HashMap();
    private static ServiceFactory sServiceFactory = null;

    public static synchronized int acquireCcid() {
    /**
     * Functions is used to acquire Content Control ID (Ccid). Ccid is connected
     * with a context type  and the user uuid. In most of cases user uuid is the GATT service
     * UUID which makes use of Ccid
     *
     * @param userUuid user identifier (GATT service)
     * @param contextType the context types as defined in {@link BluetoothLeAudio}
     * @return ccid to be used in the Gatt service Ccid characteristic.
    */
    public static synchronized int acquireCcid(ParcelUuid userUuid, int contextType) {
        int ccid = CCID_INVALID;

        if (sAssignedCcidList.size() == 0) {
@@ -51,11 +71,38 @@ public class ContentControlIdKeeper {
            }
        }

        if (ccid != CCID_INVALID) sAssignedCcidList.add(ccid);
        if (ccid != CCID_INVALID)  {
            sAssignedCcidList.add(ccid);
            sUserMap.put(userUuid, new Pair(ccid, contextType));

            if (sServiceFactory == null) {
                sServiceFactory = new ServiceFactory();
            }
            /* Notify LeAudioService about new ccid  */
            LeAudioService service = sServiceFactory.getLeAudioService();
            if (service != null) {
                service.setCcidInformation(userUuid, ccid, contextType);
            }
        }
        return ccid;
    }

    /**
     * Release the acquired Ccid
     *
     * @param value Ccid value to release
     */
    public static synchronized void releaseCcid(int value) {
        sAssignedCcidList.remove(value);
        sUserMap.entrySet().removeIf(entry -> entry.getValue().first.equals(value));
    }

    /**
     * Get Ccid information.
     *
     * @return Map of acquired ccids along with the user information.
     */
    public static synchronized Map<ParcelUuid, Pair<Integer, Integer>> getUserCcidMap() {
        return Collections.unmodifiableMap(sUserMap);
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -90,6 +90,16 @@ public class LeAudioNativeInterface {
    // Callbacks from the native stack back into the Java framework.
    // All callbacks are routed via the Service which will disambiguate which
    // state machine the message should be routed to.
    private void onInitialized() {
        LeAudioStackEvent event =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED);

        if (DBG) {
            Log.d(TAG, "onInitialized: " + event);
        }
        sendMessageToService(event);
    }

    private void onConnectionStateChanged(int state, byte[] address) {
        LeAudioStackEvent event =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+69 −0
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.sysprop.BluetoothProperties;
import android.util.Log;
import android.util.Pair;

import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
@@ -59,6 +60,7 @@ import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ServiceFactory;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.mcp.McpService;
import com.android.bluetooth.tbs.TbsGatt;
import com.android.bluetooth.vc.VolumeControlService;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -119,6 +121,7 @@ public class LeAudioService extends ProfileService {
    ServiceFactory mServiceFactory = new ServiceFactory();

    LeAudioNativeInterface mLeAudioNativeInterface;
    boolean mLeAudioNativeIsInitialized = false;
    LeAudioBroadcasterNativeInterface mLeAudioBroadcasterNativeInterface = null;
    @VisibleForTesting
    AudioManager mAudioManager;
@@ -328,6 +331,7 @@ public class LeAudioService extends ProfileService {
        // Cleanup native interfaces
        mLeAudioNativeInterface.cleanup();
        mLeAudioNativeInterface = null;
        mLeAudioNativeIsInitialized = false;

        // Set the service and BLE devices as inactive
        setLeAudioService(null);
@@ -553,6 +557,10 @@ public class LeAudioService extends ProfileService {
     * @return true on success, otherwise false
     */
    boolean groupAddNode(int groupId, BluetoothDevice device) {
        if (!mLeAudioNativeIsInitialized) {
            Log.e(TAG, "Le Audio not initialized properly.");
            return false;
        }
        return mLeAudioNativeInterface.groupAddNode(groupId, device);
    }

@@ -563,6 +571,10 @@ public class LeAudioService extends ProfileService {
     * @return true on success, otherwise false
     */
    boolean groupRemoveNode(int groupId, BluetoothDevice device) {
        if (!mLeAudioNativeIsInitialized) {
            Log.e(TAG, "Le Audio not initialized properly.");
            return false;
        }
        return mLeAudioNativeInterface.groupRemoveNode(groupId, device);
    }

@@ -952,6 +964,10 @@ public class LeAudioService extends ProfileService {
            return;
        }

        if (!mLeAudioNativeIsInitialized) {
            Log.e(TAG, "Le Audio not initialized properly.");
            return;
        }
        mLeAudioNativeInterface.groupSetActive(groupId);
    }

@@ -1346,6 +1362,14 @@ public class LeAudioService extends ProfileService {
                mBroadcastMetadataList.put(broadcastId, stackEvent.broadcastMetadata);
                notifyBroadcastMetadataChanged(broadcastId, stackEvent.broadcastMetadata);
            }
        } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED) {
            mLeAudioNativeIsInitialized = true;
            for (Map.Entry<ParcelUuid, Pair<Integer, Integer>> entry :
                    ContentControlIdKeeper.getUserCcidMap().entrySet()) {
                ParcelUuid userUuid = entry.getKey();
                Pair<Integer, Integer> ccidInformation = entry.getValue();
                setCcidInformation(userUuid, ccidInformation.first, ccidInformation.second);
            }
        }

        if (intent != null) {
@@ -1694,6 +1718,25 @@ public class LeAudioService extends ProfileService {
        }
    }

    /**
     * Set the user application ccid along with used context type
     * @param userUuid user uuid
     * @param ccid content control id
     * @param contextType context type
     */
    public void setCcidInformation(ParcelUuid userUuid, int ccid, int contextType) {
        /* for the moment we care only for GMCS and GTBS */
        if (userUuid != BluetoothUuid.GENERIC_MEDIA_CONTROL
                && userUuid.getUuid() != TbsGatt.UUID_GTBS) {
            return;
        }
        if (!mLeAudioNativeIsInitialized) {
            Log.e(TAG, "Le Audio not initialized properly.");
            return;
        }
        mLeAudioNativeInterface.setCcidInformation(ccid, contextType);
    }

    /**
     * Set volume for streaming devices
     * @param volume volume to set
@@ -1987,6 +2030,11 @@ public class LeAudioService extends ProfileService {
            return;
        }

        if (!mLeAudioNativeIsInitialized) {
            Log.e(TAG, "Le Audio not initialized properly.");
            return;
        }

        mLeAudioNativeInterface.setCodecConfigPreference(groupId,
                                inputCodecConfig, outputCodecConfig);
    }
@@ -2224,6 +2272,27 @@ public class LeAudioService extends ProfileService {
            }
        }

        @Override
        public void setCcidInformation(ParcelUuid userUuid, int ccid, int contextType,
                AttributionSource source,
                SynchronousResultReceiver receiver) {
            try {
                Objects.requireNonNull(userUuid, "userUuid cannot be null");
                Objects.requireNonNull(source, "source cannot be null");
                Objects.requireNonNull(receiver, "receiver cannot be null");

                LeAudioService service = getService(source);
                if (service == null) {
                    throw new IllegalStateException("service is null");
                }
                enforceBluetoothPrivilegedPermission(service);
                service.setCcidInformation(userUuid, ccid, contextType);
                receiver.send(null);
            } catch (RuntimeException e) {
                receiver.propagateException(e);
            }
        }

        @Override
        public void getGroupId(BluetoothDevice device, AttributionSource source,
                SynchronousResultReceiver receiver) {
+4 −1
Original line number Diff line number Diff line
@@ -36,8 +36,9 @@ public class LeAudioStackEvent {
    public static final int EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE = 5;
    public static final int EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED = 6;
    public static final int EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED = 7;
    public static final int EVENT_TYPE_NATIVE_INITIALIZED = 8;
        // -------- DO NOT PUT ANY NEW UNICAST EVENTS BELOW THIS LINE-------------
    public static final int EVENT_TYPE_UNICAST_MAX = 8;
    public static final int EVENT_TYPE_UNICAST_MAX = 9;

    // Broadcast related events
    public static final int EVENT_TYPE_BROADCAST_CREATED = EVENT_TYPE_UNICAST_MAX + 1;
@@ -136,6 +137,8 @@ public class LeAudioStackEvent {
                return "EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED";
            case EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED:
                return "EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED";
            case EVENT_TYPE_NATIVE_INITIALIZED:
                return "EVENT_TYPE_NATIVE_INITIALIZED";
            default:
                return "EVENT_TYPE_UNKNOWN:" + type;
        }
Loading