Loading android/app/aidl/android/bluetooth/IBluetoothLeAudio.aidl +2 −2 Original line number Diff line number Diff line Loading @@ -60,9 +60,9 @@ interface IBluetoothLeAudio { @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") void setCodecConfigPreference(in int groupId, in BluetoothLeAudioCodecConfig inputCodecConfig, in BluetoothLeAudioCodecConfig outputCodecConfig, in AttributionSource source); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") void registerCallback(in IBluetoothLeAudioCallback callback, in AttributionSource attributionSource); oneway void registerCallback(in IBluetoothLeAudioCallback callback, in AttributionSource attributionSource); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") void unregisterCallback(in IBluetoothLeAudioCallback callback, in AttributionSource attributionSource); oneway void unregisterCallback(in IBluetoothLeAudioCallback callback, in AttributionSource attributionSource); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") void setCcidInformation(in ParcelUuid userUuid, in int ccid, in int contextType, in AttributionSource attributionSource); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") Loading framework/java/android/bluetooth/BluetoothLeAudio.java +80 −132 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ package android.bluetooth; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static java.util.Objects.requireNonNull; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; Loading @@ -29,13 +31,13 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.content.AttributionSource; import android.content.Context; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.util.CloseGuard; import android.util.Log; Loading @@ -45,11 +47,9 @@ import com.android.bluetooth.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; /** * This class provides the public APIs to control the LeAudio profile. Loading @@ -65,8 +65,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean VDBG = false; private final Map<Callback, Executor> mCallbackExecutorMap = new HashMap<>(); private CloseGuard mCloseGuard; /** Loading Loading @@ -155,67 +153,41 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { } } @SuppressLint("AndroidFrameworkBluetoothPermission") private final IBluetoothLeAudioCallback mCallback = new IBluetoothLeAudioCallback.Stub() { private final CallbackWrapper<Callback, IBluetoothLeAudio> mCallbackWrapper; private final IBluetoothLeAudioCallback mCallback = new LeAudioNotifyCallback(); private class LeAudioNotifyCallback extends IBluetoothLeAudioCallback.Stub { @Override public void onCodecConfigChanged( int groupId, @NonNull BluetoothLeAudioCodecStatus status) { for (Map.Entry<BluetoothLeAudio.Callback, Executor> callbackExecutorEntry : mCallbackExecutorMap.entrySet()) { BluetoothLeAudio.Callback callback = callbackExecutorEntry.getKey(); Executor executor = callbackExecutorEntry.getValue(); executor.execute(() -> callback.onCodecConfigChanged(groupId, status)); } public void onCodecConfigChanged(int groupId, BluetoothLeAudioCodecStatus status) { mCallbackWrapper.forEach((cb) -> cb.onCodecConfigChanged(groupId, status)); } @Override public void onGroupNodeAdded(@NonNull BluetoothDevice device, int groupId) { Attributable.setAttributionSource(device, mAttributionSource); for (Map.Entry<BluetoothLeAudio.Callback, Executor> callbackExecutorEntry : mCallbackExecutorMap.entrySet()) { BluetoothLeAudio.Callback callback = callbackExecutorEntry.getKey(); Executor executor = callbackExecutorEntry.getValue(); executor.execute(() -> callback.onGroupNodeAdded(device, groupId)); } mCallbackWrapper.forEach((cb) -> cb.onGroupNodeAdded(device, groupId)); } @Override public void onGroupNodeRemoved(@NonNull BluetoothDevice device, int groupId) { Attributable.setAttributionSource(device, mAttributionSource); for (Map.Entry<BluetoothLeAudio.Callback, Executor> callbackExecutorEntry : mCallbackExecutorMap.entrySet()) { BluetoothLeAudio.Callback callback = callbackExecutorEntry.getKey(); Executor executor = callbackExecutorEntry.getValue(); executor.execute(() -> callback.onGroupNodeRemoved(device, groupId)); } mCallbackWrapper.forEach((cb) -> cb.onGroupNodeRemoved(device, groupId)); } @Override public void onGroupStatusChanged(int groupId, int groupStatus) { for (Map.Entry<BluetoothLeAudio.Callback, Executor> callbackExecutorEntry : mCallbackExecutorMap.entrySet()) { BluetoothLeAudio.Callback callback = callbackExecutorEntry.getKey(); Executor executor = callbackExecutorEntry.getValue(); executor.execute(() -> callback.onGroupStatusChanged(groupId, groupStatus)); } mCallbackWrapper.forEach((cb) -> cb.onGroupStatusChanged(groupId, groupStatus)); } @Override public void onGroupStreamStatusChanged(int groupId, int groupStreamStatus) { if (Flags.leaudioCallbackOnGroupStreamStatus()) { for (Map.Entry<BluetoothLeAudio.Callback, Executor> callbackExecutorEntry : mCallbackExecutorMap.entrySet()) { BluetoothLeAudio.Callback callback = callbackExecutorEntry.getKey(); Executor executor = callbackExecutorEntry.getValue(); executor.execute( () -> callback.onGroupStreamStatusChanged( groupId, groupStreamStatus)); mCallbackWrapper.forEach( (cb) -> cb.onGroupStreamStatusChanged(groupId, groupStreamStatus)); } } } }; /** * Intent used to broadcast the change in connection state of the LeAudio profile. Please note Loading Loading @@ -664,6 +636,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { public static final String EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS = "android.bluetooth.extra.LE_AUDIO_AVAILABLE_CONTEXTS"; private final Context mContext; private final BluetoothAdapter mAdapter; private final AttributionSource mAttributionSource; Loading Loading @@ -707,10 +680,29 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * service. */ /* package */ BluetoothLeAudio(Context context, BluetoothAdapter adapter) { mContext = requireNonNull(context); mAdapter = adapter; mAttributionSource = adapter.getAttributionSource(); mService = null; Consumer<IBluetoothLeAudio> registerConsumer = (IBluetoothLeAudio service) -> { try { service.registerCallback(mCallback, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } }; Consumer<IBluetoothLeAudio> unregisterConsumer = (IBluetoothLeAudio service) -> { try { service.unregisterCallback(mCallback, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } }; mCallbackWrapper = new CallbackWrapper(registerConsumer, unregisterConsumer); mCloseGuard = new CloseGuard(); mCloseGuard.open("close"); } Loading @@ -725,19 +717,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { @Override public void onServiceConnected(IBinder service) { mService = IBluetoothLeAudio.Stub.asInterface(service); // re-register the service-to-app callback synchronized (mCallbackExecutorMap) { if (mCallbackExecutorMap.isEmpty()) { return; } try { if (service != null) { mService.registerCallback(mCallback, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "Failed to register callback", e); } } mCallbackWrapper.registerToNewService(mService); } /** @hide */ Loading Loading @@ -960,36 +940,15 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { }) public void registerCallback( @NonNull @CallbackExecutor Executor executor, @NonNull Callback callback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(callback, "callback cannot be null"); if (DBG) log("registerCallback"); synchronized (mCallbackExecutorMap) { // If the callback map is empty, we register the service-to-app callback if (mCallbackExecutorMap.isEmpty()) { if (!mAdapter.isEnabled()) { /* If Bluetooth is off, just store callback and it will be registered * when Bluetooth is on */ mCallbackExecutorMap.put(callback, executor); return; } try { final IBluetoothLeAudio service = getService(); if (service != null) { service.registerCallback(mCallback, mAttributionSource); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } // Enforcing permission in the framework is useless from security point of view. // This is being done to help normal app developer to catch the missing permission, since // the call to the service is oneway and the SecurityException will just be logged final int pid = Process.myPid(); final int uid = Process.myUid(); mContext.enforcePermission(BLUETOOTH_CONNECT, pid, uid, null); mContext.enforcePermission(BLUETOOTH_PRIVILEGED, pid, uid, null); // Adds the passed in callback to our map of callbacks to executors if (mCallbackExecutorMap.containsKey(callback)) { throw new IllegalArgumentException("This callback has already been registered"); } mCallbackExecutorMap.put(callback, executor); } mCallbackWrapper.registerCallback(getService(), callback, executor); } /** Loading @@ -1013,26 +972,15 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { BLUETOOTH_PRIVILEGED, }) public void unregisterCallback(@NonNull Callback callback) { Objects.requireNonNull(callback, "callback cannot be null"); if (DBG) log("unregisterCallback"); // Enforcing permission in the framework is useless from security point of view. // This is being done to help normal app developer to catch the missing permission, since // the call to the service is oneway and the SecurityException will just be logged final int pid = Process.myPid(); final int uid = Process.myUid(); mContext.enforcePermission(BLUETOOTH_CONNECT, pid, uid, null); mContext.enforcePermission(BLUETOOTH_PRIVILEGED, pid, uid, null); synchronized (mCallbackExecutorMap) { if (mCallbackExecutorMap.remove(callback) == null) { throw new IllegalArgumentException("This callback has not been registered"); } } // If the callback map is empty, we unregister the service-to-app callback if (mCallbackExecutorMap.isEmpty()) { try { final IBluetoothLeAudio service = getService(); if (service != null) { service.unregisterCallback(mCallback, mAttributionSource); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } mCallbackWrapper.unregisterCallback(getService(), callback); } /** Loading Loading @@ -1411,8 +1359,8 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { @NonNull BluetoothLeAudioCodecConfig outputCodecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + groupId + ")"); Objects.requireNonNull(inputCodecConfig, " inputCodecConfig shall not be null"); Objects.requireNonNull(outputCodecConfig, " outputCodecConfig shall not be null"); requireNonNull(inputCodecConfig); requireNonNull(outputCodecConfig); final IBluetoothLeAudio service = getService(); Loading Loading
android/app/aidl/android/bluetooth/IBluetoothLeAudio.aidl +2 −2 Original line number Diff line number Diff line Loading @@ -60,9 +60,9 @@ interface IBluetoothLeAudio { @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") void setCodecConfigPreference(in int groupId, in BluetoothLeAudioCodecConfig inputCodecConfig, in BluetoothLeAudioCodecConfig outputCodecConfig, in AttributionSource source); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") void registerCallback(in IBluetoothLeAudioCallback callback, in AttributionSource attributionSource); oneway void registerCallback(in IBluetoothLeAudioCallback callback, in AttributionSource attributionSource); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") void unregisterCallback(in IBluetoothLeAudioCallback callback, in AttributionSource attributionSource); oneway void unregisterCallback(in IBluetoothLeAudioCallback callback, in AttributionSource attributionSource); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") void setCcidInformation(in ParcelUuid userUuid, in int ccid, in int contextType, in AttributionSource attributionSource); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") Loading
framework/java/android/bluetooth/BluetoothLeAudio.java +80 −132 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ package android.bluetooth; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static java.util.Objects.requireNonNull; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; Loading @@ -29,13 +31,13 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.content.AttributionSource; import android.content.Context; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.util.CloseGuard; import android.util.Log; Loading @@ -45,11 +47,9 @@ import com.android.bluetooth.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; /** * This class provides the public APIs to control the LeAudio profile. Loading @@ -65,8 +65,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean VDBG = false; private final Map<Callback, Executor> mCallbackExecutorMap = new HashMap<>(); private CloseGuard mCloseGuard; /** Loading Loading @@ -155,67 +153,41 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { } } @SuppressLint("AndroidFrameworkBluetoothPermission") private final IBluetoothLeAudioCallback mCallback = new IBluetoothLeAudioCallback.Stub() { private final CallbackWrapper<Callback, IBluetoothLeAudio> mCallbackWrapper; private final IBluetoothLeAudioCallback mCallback = new LeAudioNotifyCallback(); private class LeAudioNotifyCallback extends IBluetoothLeAudioCallback.Stub { @Override public void onCodecConfigChanged( int groupId, @NonNull BluetoothLeAudioCodecStatus status) { for (Map.Entry<BluetoothLeAudio.Callback, Executor> callbackExecutorEntry : mCallbackExecutorMap.entrySet()) { BluetoothLeAudio.Callback callback = callbackExecutorEntry.getKey(); Executor executor = callbackExecutorEntry.getValue(); executor.execute(() -> callback.onCodecConfigChanged(groupId, status)); } public void onCodecConfigChanged(int groupId, BluetoothLeAudioCodecStatus status) { mCallbackWrapper.forEach((cb) -> cb.onCodecConfigChanged(groupId, status)); } @Override public void onGroupNodeAdded(@NonNull BluetoothDevice device, int groupId) { Attributable.setAttributionSource(device, mAttributionSource); for (Map.Entry<BluetoothLeAudio.Callback, Executor> callbackExecutorEntry : mCallbackExecutorMap.entrySet()) { BluetoothLeAudio.Callback callback = callbackExecutorEntry.getKey(); Executor executor = callbackExecutorEntry.getValue(); executor.execute(() -> callback.onGroupNodeAdded(device, groupId)); } mCallbackWrapper.forEach((cb) -> cb.onGroupNodeAdded(device, groupId)); } @Override public void onGroupNodeRemoved(@NonNull BluetoothDevice device, int groupId) { Attributable.setAttributionSource(device, mAttributionSource); for (Map.Entry<BluetoothLeAudio.Callback, Executor> callbackExecutorEntry : mCallbackExecutorMap.entrySet()) { BluetoothLeAudio.Callback callback = callbackExecutorEntry.getKey(); Executor executor = callbackExecutorEntry.getValue(); executor.execute(() -> callback.onGroupNodeRemoved(device, groupId)); } mCallbackWrapper.forEach((cb) -> cb.onGroupNodeRemoved(device, groupId)); } @Override public void onGroupStatusChanged(int groupId, int groupStatus) { for (Map.Entry<BluetoothLeAudio.Callback, Executor> callbackExecutorEntry : mCallbackExecutorMap.entrySet()) { BluetoothLeAudio.Callback callback = callbackExecutorEntry.getKey(); Executor executor = callbackExecutorEntry.getValue(); executor.execute(() -> callback.onGroupStatusChanged(groupId, groupStatus)); } mCallbackWrapper.forEach((cb) -> cb.onGroupStatusChanged(groupId, groupStatus)); } @Override public void onGroupStreamStatusChanged(int groupId, int groupStreamStatus) { if (Flags.leaudioCallbackOnGroupStreamStatus()) { for (Map.Entry<BluetoothLeAudio.Callback, Executor> callbackExecutorEntry : mCallbackExecutorMap.entrySet()) { BluetoothLeAudio.Callback callback = callbackExecutorEntry.getKey(); Executor executor = callbackExecutorEntry.getValue(); executor.execute( () -> callback.onGroupStreamStatusChanged( groupId, groupStreamStatus)); mCallbackWrapper.forEach( (cb) -> cb.onGroupStreamStatusChanged(groupId, groupStreamStatus)); } } } }; /** * Intent used to broadcast the change in connection state of the LeAudio profile. Please note Loading Loading @@ -664,6 +636,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { public static final String EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS = "android.bluetooth.extra.LE_AUDIO_AVAILABLE_CONTEXTS"; private final Context mContext; private final BluetoothAdapter mAdapter; private final AttributionSource mAttributionSource; Loading Loading @@ -707,10 +680,29 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * service. */ /* package */ BluetoothLeAudio(Context context, BluetoothAdapter adapter) { mContext = requireNonNull(context); mAdapter = adapter; mAttributionSource = adapter.getAttributionSource(); mService = null; Consumer<IBluetoothLeAudio> registerConsumer = (IBluetoothLeAudio service) -> { try { service.registerCallback(mCallback, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } }; Consumer<IBluetoothLeAudio> unregisterConsumer = (IBluetoothLeAudio service) -> { try { service.unregisterCallback(mCallback, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } }; mCallbackWrapper = new CallbackWrapper(registerConsumer, unregisterConsumer); mCloseGuard = new CloseGuard(); mCloseGuard.open("close"); } Loading @@ -725,19 +717,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { @Override public void onServiceConnected(IBinder service) { mService = IBluetoothLeAudio.Stub.asInterface(service); // re-register the service-to-app callback synchronized (mCallbackExecutorMap) { if (mCallbackExecutorMap.isEmpty()) { return; } try { if (service != null) { mService.registerCallback(mCallback, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "Failed to register callback", e); } } mCallbackWrapper.registerToNewService(mService); } /** @hide */ Loading Loading @@ -960,36 +940,15 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { }) public void registerCallback( @NonNull @CallbackExecutor Executor executor, @NonNull Callback callback) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(callback, "callback cannot be null"); if (DBG) log("registerCallback"); synchronized (mCallbackExecutorMap) { // If the callback map is empty, we register the service-to-app callback if (mCallbackExecutorMap.isEmpty()) { if (!mAdapter.isEnabled()) { /* If Bluetooth is off, just store callback and it will be registered * when Bluetooth is on */ mCallbackExecutorMap.put(callback, executor); return; } try { final IBluetoothLeAudio service = getService(); if (service != null) { service.registerCallback(mCallback, mAttributionSource); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } // Enforcing permission in the framework is useless from security point of view. // This is being done to help normal app developer to catch the missing permission, since // the call to the service is oneway and the SecurityException will just be logged final int pid = Process.myPid(); final int uid = Process.myUid(); mContext.enforcePermission(BLUETOOTH_CONNECT, pid, uid, null); mContext.enforcePermission(BLUETOOTH_PRIVILEGED, pid, uid, null); // Adds the passed in callback to our map of callbacks to executors if (mCallbackExecutorMap.containsKey(callback)) { throw new IllegalArgumentException("This callback has already been registered"); } mCallbackExecutorMap.put(callback, executor); } mCallbackWrapper.registerCallback(getService(), callback, executor); } /** Loading @@ -1013,26 +972,15 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { BLUETOOTH_PRIVILEGED, }) public void unregisterCallback(@NonNull Callback callback) { Objects.requireNonNull(callback, "callback cannot be null"); if (DBG) log("unregisterCallback"); // Enforcing permission in the framework is useless from security point of view. // This is being done to help normal app developer to catch the missing permission, since // the call to the service is oneway and the SecurityException will just be logged final int pid = Process.myPid(); final int uid = Process.myUid(); mContext.enforcePermission(BLUETOOTH_CONNECT, pid, uid, null); mContext.enforcePermission(BLUETOOTH_PRIVILEGED, pid, uid, null); synchronized (mCallbackExecutorMap) { if (mCallbackExecutorMap.remove(callback) == null) { throw new IllegalArgumentException("This callback has not been registered"); } } // If the callback map is empty, we unregister the service-to-app callback if (mCallbackExecutorMap.isEmpty()) { try { final IBluetoothLeAudio service = getService(); if (service != null) { service.unregisterCallback(mCallback, mAttributionSource); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } mCallbackWrapper.unregisterCallback(getService(), callback); } /** Loading Loading @@ -1411,8 +1359,8 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { @NonNull BluetoothLeAudioCodecConfig outputCodecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + groupId + ")"); Objects.requireNonNull(inputCodecConfig, " inputCodecConfig shall not be null"); Objects.requireNonNull(outputCodecConfig, " outputCodecConfig shall not be null"); requireNonNull(inputCodecConfig); requireNonNull(outputCodecConfig); final IBluetoothLeAudio service = getService(); Loading