Loading android/app/jni/com_android_bluetooth_le_audio.cpp +25 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ static jmethodID method_onConnectionStateChanged; static jmethodID method_onGroupStatus; static jmethodID method_onGroupNodeStatus; static jmethodID method_onAudioConf; static jmethodID method_onSinkAudioLocationAvailable; static struct { jclass clazz; Loading Loading @@ -129,6 +130,28 @@ class LeAudioClientCallbacksImpl : public LeAudioClientCallbacks { (jint)sink_audio_location, (jint)source_audio_location, (jint)avail_cont); } void OnSinkAudioLocationAvailable(const RawAddress& bd_addr, uint32_t sink_audio_location) override { LOG(INFO) << __func__; std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { LOG(ERROR) << "Failed to new jbyteArray bd addr for group status"; return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSinkAudioLocationAvailable, addr.get(), (jint)sink_audio_location); } }; static LeAudioClientCallbacksImpl sLeAudioClientCallbacks; Loading @@ -145,6 +168,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) { method_onGroupNodeStatus = env->GetMethodID(clazz, "onGroupNodeStatus", "([BII)V"); method_onAudioConf = env->GetMethodID(clazz, "onAudioConf", "(IIIII)V"); method_onSinkAudioLocationAvailable = env->GetMethodID(clazz, "onSinkAudioLocationAvailable", "([BI)V"); method_onConnectionStateChanged = env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); } Loading android/app/src/com/android/bluetooth/le_audio/LeAudioNativeInterface.java +12 −0 Original line number Diff line number Diff line Loading @@ -141,6 +141,18 @@ public class LeAudioNativeInterface { sendMessageToService(event); } private void onSinkAudioLocationAvailable(byte[] address, int sinkAudioLocation) { LeAudioStackEvent event = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE); event.device = getDevice(address); event.valueInt1 = sinkAudioLocation; if (DBG) { Log.d(TAG, "onSinkAudioLocationAvailable: " + event); } sendMessageToService(event); } /** * Initializes the native interface. * Loading android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +43 −0 Original line number Diff line number Diff line Loading @@ -143,6 +143,7 @@ public class LeAudioService extends ProfileService { private final Map<BluetoothDevice, LeAudioStateMachine> mStateMachines = new LinkedHashMap<>(); private final Map<BluetoothDevice, Integer> mDeviceGroupIdMap = new ConcurrentHashMap<>(); private final Map<BluetoothDevice, Integer> mDeviceAudioLocationMap = new ConcurrentHashMap<>(); private final int mContextSupportingInputAudio = BluetoothLeAudio.CONTEXT_TYPE_COMMUNICATION | Loading Loading @@ -203,6 +204,7 @@ public class LeAudioService extends ProfileService { mStateMachinesThread.start(); mDeviceGroupIdMap.clear(); mDeviceAudioLocationMap.clear(); mBroadcastStateMap.clear(); mBroadcastIdMap.clear(); mBroadcastMetadataList.clear(); Loading Loading @@ -290,6 +292,7 @@ public class LeAudioService extends ProfileService { } mDeviceGroupIdMap.clear(); mDeviceAudioLocationMap.clear(); mGroupDescriptors.clear(); if (mBroadcastCallbacks != null) { Loading Loading @@ -1049,6 +1052,17 @@ public class LeAudioService extends ProfileService { intent.putExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_SINK_LOCATION, snk_audio_location); intent.putExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_SOURCE_LOCATION, src_audio_location); intent.putExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS, available_contexts); } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE) { Objects.requireNonNull(stackEvent.device, "Device should never be null, event: " + stackEvent); int sink_audio_location = stackEvent.valueInt1; mDeviceAudioLocationMap.put(device, sink_audio_location); if (DBG) { Log.i(TAG, "EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE:" + device + " audio location:" + sink_audio_location); } } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED) { int group_id = stackEvent.valueInt1; int group_status = stackEvent.valueInt2; Loading Loading @@ -1263,6 +1277,7 @@ public class LeAudioService extends ProfileService { } mDeviceGroupIdMap.remove(device); mDeviceAudioLocationMap.remove(device); synchronized (mStateMachines) { LeAudioStateMachine sm = mStateMachines.get(device); if (sm == null) { Loading Loading @@ -1423,6 +1438,19 @@ public class LeAudioService extends ProfileService { return true; } /** * Get device audio location. * @param device LE Audio capable device * @return the sink audioi location that this device currently exposed */ public int getAudioLocation(BluetoothDevice device) { if (device == null) { return BluetoothLeAudio.AUDIO_LOCATION_INVALID; } return mDeviceAudioLocationMap.getOrDefault(device, BluetoothLeAudio.AUDIO_LOCATION_INVALID); } /** * Set connection policy of the profile and connects it if connectionPolicy is * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is Loading Loading @@ -1887,6 +1915,21 @@ public class LeAudioService extends ProfileService { } } @Override public void getAudioLocation(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { LeAudioService service = getService(source); int defaultValue = BluetoothLeAudio.AUDIO_LOCATION_INVALID; if (service != null) { defaultValue = service.getAudioLocation(device); } receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source, SynchronousResultReceiver receiver) { Loading android/app/src/com/android/bluetooth/le_audio/LeAudioStackEvent.java +6 −1 Original line number Diff line number Diff line Loading @@ -30,8 +30,9 @@ public class LeAudioStackEvent { public static final int EVENT_TYPE_GROUP_STATUS_CHANGED = 2; public static final int EVENT_TYPE_GROUP_NODE_STATUS_CHANGED = 3; public static final int EVENT_TYPE_AUDIO_CONF_CHANGED = 4; public static final int EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE = 5; // -------- DO NOT PUT ANY NEW UNICAST EVENTS BELOW THIS LINE------------- public static final int EVENT_TYPE_UNICAST_MAX = 5; public static final int EVENT_TYPE_UNICAST_MAX = 6; // Broadcast related events public static final int EVENT_TYPE_BROADCAST_CREATED = EVENT_TYPE_UNICAST_MAX + 1; Loading Loading @@ -103,6 +104,8 @@ public class LeAudioStackEvent { return "EVENT_TYPE_GROUP_NODE_STATUS_CHANGED"; case EVENT_TYPE_AUDIO_CONF_CHANGED: return "EVENT_TYPE_AUDIO_CONF_CHANGED"; case EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE: return "EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE"; case EVENT_TYPE_BROADCAST_CREATED: return "EVENT_TYPE_BROADCAST_CREATED"; case EVENT_TYPE_BROADCAST_DESTROYED: Loading Loading @@ -138,6 +141,8 @@ public class LeAudioStackEvent { case EVENT_TYPE_AUDIO_CONF_CHANGED: // FIXME: It should have proper direction names here return "{direction:" + value + "}"; case EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE: return "{sink_audio_location:" + value + "}"; case EVENT_TYPE_BROADCAST_CREATED: return "{instance_id:" + value + "}"; case EVENT_TYPE_BROADCAST_DESTROYED: Loading framework/java/android/bluetooth/BluetoothLeAudio.java +8 −3 Original line number Diff line number Diff line Loading @@ -1268,9 +1268,14 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { if (service == null) { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); } else if (mAdapter.isEnabled()) { //TODO: add the implementation. if (VDBG) log("getAudioLocation() from LE audio service"); } else if (mAdapter.isEnabled() && isValidDevice(device)) { try { final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); service.getAudioLocation(device, mAttributionSource, recv); return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultLocation); } catch (RemoteException | TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } return defaultLocation; } Loading Loading
android/app/jni/com_android_bluetooth_le_audio.cpp +25 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ static jmethodID method_onConnectionStateChanged; static jmethodID method_onGroupStatus; static jmethodID method_onGroupNodeStatus; static jmethodID method_onAudioConf; static jmethodID method_onSinkAudioLocationAvailable; static struct { jclass clazz; Loading Loading @@ -129,6 +130,28 @@ class LeAudioClientCallbacksImpl : public LeAudioClientCallbacks { (jint)sink_audio_location, (jint)source_audio_location, (jint)avail_cont); } void OnSinkAudioLocationAvailable(const RawAddress& bd_addr, uint32_t sink_audio_location) override { LOG(INFO) << __func__; std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return; ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { LOG(ERROR) << "Failed to new jbyteArray bd addr for group status"; return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSinkAudioLocationAvailable, addr.get(), (jint)sink_audio_location); } }; static LeAudioClientCallbacksImpl sLeAudioClientCallbacks; Loading @@ -145,6 +168,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) { method_onGroupNodeStatus = env->GetMethodID(clazz, "onGroupNodeStatus", "([BII)V"); method_onAudioConf = env->GetMethodID(clazz, "onAudioConf", "(IIIII)V"); method_onSinkAudioLocationAvailable = env->GetMethodID(clazz, "onSinkAudioLocationAvailable", "([BI)V"); method_onConnectionStateChanged = env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); } Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioNativeInterface.java +12 −0 Original line number Diff line number Diff line Loading @@ -141,6 +141,18 @@ public class LeAudioNativeInterface { sendMessageToService(event); } private void onSinkAudioLocationAvailable(byte[] address, int sinkAudioLocation) { LeAudioStackEvent event = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE); event.device = getDevice(address); event.valueInt1 = sinkAudioLocation; if (DBG) { Log.d(TAG, "onSinkAudioLocationAvailable: " + event); } sendMessageToService(event); } /** * Initializes the native interface. * Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +43 −0 Original line number Diff line number Diff line Loading @@ -143,6 +143,7 @@ public class LeAudioService extends ProfileService { private final Map<BluetoothDevice, LeAudioStateMachine> mStateMachines = new LinkedHashMap<>(); private final Map<BluetoothDevice, Integer> mDeviceGroupIdMap = new ConcurrentHashMap<>(); private final Map<BluetoothDevice, Integer> mDeviceAudioLocationMap = new ConcurrentHashMap<>(); private final int mContextSupportingInputAudio = BluetoothLeAudio.CONTEXT_TYPE_COMMUNICATION | Loading Loading @@ -203,6 +204,7 @@ public class LeAudioService extends ProfileService { mStateMachinesThread.start(); mDeviceGroupIdMap.clear(); mDeviceAudioLocationMap.clear(); mBroadcastStateMap.clear(); mBroadcastIdMap.clear(); mBroadcastMetadataList.clear(); Loading Loading @@ -290,6 +292,7 @@ public class LeAudioService extends ProfileService { } mDeviceGroupIdMap.clear(); mDeviceAudioLocationMap.clear(); mGroupDescriptors.clear(); if (mBroadcastCallbacks != null) { Loading Loading @@ -1049,6 +1052,17 @@ public class LeAudioService extends ProfileService { intent.putExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_SINK_LOCATION, snk_audio_location); intent.putExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_SOURCE_LOCATION, src_audio_location); intent.putExtra(BluetoothLeAudio.EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS, available_contexts); } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE) { Objects.requireNonNull(stackEvent.device, "Device should never be null, event: " + stackEvent); int sink_audio_location = stackEvent.valueInt1; mDeviceAudioLocationMap.put(device, sink_audio_location); if (DBG) { Log.i(TAG, "EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE:" + device + " audio location:" + sink_audio_location); } } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED) { int group_id = stackEvent.valueInt1; int group_status = stackEvent.valueInt2; Loading Loading @@ -1263,6 +1277,7 @@ public class LeAudioService extends ProfileService { } mDeviceGroupIdMap.remove(device); mDeviceAudioLocationMap.remove(device); synchronized (mStateMachines) { LeAudioStateMachine sm = mStateMachines.get(device); if (sm == null) { Loading Loading @@ -1423,6 +1438,19 @@ public class LeAudioService extends ProfileService { return true; } /** * Get device audio location. * @param device LE Audio capable device * @return the sink audioi location that this device currently exposed */ public int getAudioLocation(BluetoothDevice device) { if (device == null) { return BluetoothLeAudio.AUDIO_LOCATION_INVALID; } return mDeviceAudioLocationMap.getOrDefault(device, BluetoothLeAudio.AUDIO_LOCATION_INVALID); } /** * Set connection policy of the profile and connects it if connectionPolicy is * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is Loading Loading @@ -1887,6 +1915,21 @@ public class LeAudioService extends ProfileService { } } @Override public void getAudioLocation(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { LeAudioService service = getService(source); int defaultValue = BluetoothLeAudio.AUDIO_LOCATION_INVALID; if (service != null) { defaultValue = service.getAudioLocation(device); } receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source, SynchronousResultReceiver receiver) { Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioStackEvent.java +6 −1 Original line number Diff line number Diff line Loading @@ -30,8 +30,9 @@ public class LeAudioStackEvent { public static final int EVENT_TYPE_GROUP_STATUS_CHANGED = 2; public static final int EVENT_TYPE_GROUP_NODE_STATUS_CHANGED = 3; public static final int EVENT_TYPE_AUDIO_CONF_CHANGED = 4; public static final int EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE = 5; // -------- DO NOT PUT ANY NEW UNICAST EVENTS BELOW THIS LINE------------- public static final int EVENT_TYPE_UNICAST_MAX = 5; public static final int EVENT_TYPE_UNICAST_MAX = 6; // Broadcast related events public static final int EVENT_TYPE_BROADCAST_CREATED = EVENT_TYPE_UNICAST_MAX + 1; Loading Loading @@ -103,6 +104,8 @@ public class LeAudioStackEvent { return "EVENT_TYPE_GROUP_NODE_STATUS_CHANGED"; case EVENT_TYPE_AUDIO_CONF_CHANGED: return "EVENT_TYPE_AUDIO_CONF_CHANGED"; case EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE: return "EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE"; case EVENT_TYPE_BROADCAST_CREATED: return "EVENT_TYPE_BROADCAST_CREATED"; case EVENT_TYPE_BROADCAST_DESTROYED: Loading Loading @@ -138,6 +141,8 @@ public class LeAudioStackEvent { case EVENT_TYPE_AUDIO_CONF_CHANGED: // FIXME: It should have proper direction names here return "{direction:" + value + "}"; case EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE: return "{sink_audio_location:" + value + "}"; case EVENT_TYPE_BROADCAST_CREATED: return "{instance_id:" + value + "}"; case EVENT_TYPE_BROADCAST_DESTROYED: Loading
framework/java/android/bluetooth/BluetoothLeAudio.java +8 −3 Original line number Diff line number Diff line Loading @@ -1268,9 +1268,14 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { if (service == null) { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); } else if (mAdapter.isEnabled()) { //TODO: add the implementation. if (VDBG) log("getAudioLocation() from LE audio service"); } else if (mAdapter.isEnabled() && isValidDevice(device)) { try { final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); service.getAudioLocation(device, mAttributionSource, recv); return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultLocation); } catch (RemoteException | TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } return defaultLocation; } Loading