| /* | |
| * Copyright 2022 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at: | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| package android.bluetooth; | |
| import android.annotation.NonNull; | |
| import android.os.Binder; | |
| import android.os.RemoteException; | |
| import android.util.Log; | |
| import java.util.HashMap; | |
| import java.util.Map; | |
| import java.util.concurrent.Executor; | |
| /** @hide */ | |
| public class BluetoothLeBroadcastAssistantCallback | |
| extends IBluetoothLeBroadcastAssistantCallback.Stub { | |
| private static final String TAG = BluetoothLeBroadcastAssistantCallback.class.getSimpleName(); | |
| private boolean mIsRegistered = false; | |
| private final Map<BluetoothLeBroadcastAssistant.Callback, Executor> mCallbackMap = | |
| new HashMap<>(); | |
| IBluetoothLeBroadcastAssistant mAdapter; | |
| public BluetoothLeBroadcastAssistantCallback(IBluetoothLeBroadcastAssistant adapter) { | |
| mAdapter = adapter; | |
| } | |
| /** | |
| * @hide | |
| * @param executor an {@link Executor} to execute given callback | |
| * @param callback user implementation of the {@link BluetoothLeBroadcastAssistant#Callback} | |
| * @throws IllegalArgumentException if the same <var>callback<var> is already registered. | |
| */ | |
| public void register( | |
| @NonNull Executor executor, @NonNull BluetoothLeBroadcastAssistant.Callback callback) { | |
| synchronized (this) { | |
| if (mCallbackMap.containsKey(callback)) { | |
| throw new IllegalArgumentException("callback is already registered"); | |
| } | |
| mCallbackMap.put(callback, executor); | |
| if (!mIsRegistered) { | |
| try { | |
| mAdapter.registerCallback(this); | |
| mIsRegistered = true; | |
| } catch (RemoteException e) { | |
| Log.w(TAG, "Failed to register broadcast assistant callback"); | |
| Log.e(TAG, Log.getStackTraceString(new Throwable())); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * @hide | |
| * @param callback user implementation of the {@link BluetoothLeBroadcastAssistant#Callback} | |
| * @throws IllegalArgumentException if <var>callback</var> was not registered before | |
| */ | |
| public void unregister(@NonNull BluetoothLeBroadcastAssistant.Callback callback) { | |
| synchronized (this) { | |
| if (!mCallbackMap.containsKey(callback)) { | |
| throw new IllegalArgumentException("callback was not registered before"); | |
| } | |
| mCallbackMap.remove(callback); | |
| if (mCallbackMap.isEmpty() && mIsRegistered) { | |
| try { | |
| mAdapter.unregisterCallback(this); | |
| mIsRegistered = false; | |
| } catch (RemoteException e) { | |
| Log.w(TAG, "Failed to unregister callback with service"); | |
| Log.e(TAG, Log.getStackTraceString(new Throwable())); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Check if at least one callback is registered from this App | |
| * | |
| * @return true if at least one callback is registered | |
| * @hide | |
| */ | |
| public boolean isAtLeastOneCallbackRegistered() { | |
| synchronized (this) { | |
| return !mCallbackMap.isEmpty(); | |
| } | |
| } | |
| @Override | |
| public void onSearchStarted(int reason) { | |
| synchronized (this) { | |
| for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { | |
| Executor executor = mCallbackMap.get(cb); | |
| final long identity = Binder.clearCallingIdentity(); | |
| try { | |
| executor.execute(() -> cb.onSearchStarted(reason)); | |
| } finally { | |
| Binder.restoreCallingIdentity(identity); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| public void onSearchStartFailed(int reason) { | |
| synchronized (this) { | |
| for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { | |
| Executor executor = mCallbackMap.get(cb); | |
| final long identity = Binder.clearCallingIdentity(); | |
| try { | |
| executor.execute(() -> cb.onSearchStartFailed(reason)); | |
| } finally { | |
| Binder.restoreCallingIdentity(identity); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| public void onSearchStopped(int reason) { | |
| synchronized (this) { | |
| for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { | |
| Executor executor = mCallbackMap.get(cb); | |
| final long identity = Binder.clearCallingIdentity(); | |
| try { | |
| executor.execute(() -> cb.onSearchStopped(reason)); | |
| } finally { | |
| Binder.restoreCallingIdentity(identity); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| public void onSearchStopFailed(int reason) { | |
| synchronized (this) { | |
| for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { | |
| Executor executor = mCallbackMap.get(cb); | |
| final long identity = Binder.clearCallingIdentity(); | |
| try { | |
| executor.execute(() -> cb.onSearchStopFailed(reason)); | |
| } finally { | |
| Binder.restoreCallingIdentity(identity); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| public void onSourceFound(BluetoothLeBroadcastMetadata source) { | |
| synchronized (this) { | |
| for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { | |
| Executor executor = mCallbackMap.get(cb); | |
| final long identity = Binder.clearCallingIdentity(); | |
| try { | |
| executor.execute(() -> cb.onSourceFound(source)); | |
| } finally { | |
| Binder.restoreCallingIdentity(identity); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| public void onSourceAdded(BluetoothDevice sink, int sourceId, int reason) { | |
| synchronized (this) { | |
| for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { | |
| Executor executor = mCallbackMap.get(cb); | |
| final long identity = Binder.clearCallingIdentity(); | |
| try { | |
| executor.execute(() -> cb.onSourceAdded(sink, sourceId, reason)); | |
| } finally { | |
| Binder.restoreCallingIdentity(identity); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| public void onSourceAddFailed( | |
| BluetoothDevice sink, BluetoothLeBroadcastMetadata source, int reason) { | |
| synchronized (this) { | |
| for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { | |
| Executor executor = mCallbackMap.get(cb); | |
| final long identity = Binder.clearCallingIdentity(); | |
| try { | |
| executor.execute(() -> cb.onSourceAddFailed(sink, source, reason)); | |
| } finally { | |
| Binder.restoreCallingIdentity(identity); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| public void onSourceModified(BluetoothDevice sink, int sourceId, int reason) { | |
| synchronized (this) { | |
| for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { | |
| Executor executor = mCallbackMap.get(cb); | |
| final long identity = Binder.clearCallingIdentity(); | |
| try { | |
| executor.execute(() -> cb.onSourceModified(sink, sourceId, reason)); | |
| } finally { | |
| Binder.restoreCallingIdentity(identity); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| public void onSourceModifyFailed(BluetoothDevice sink, int sourceId, int reason) { | |
| synchronized (this) { | |
| for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { | |
| Executor executor = mCallbackMap.get(cb); | |
| final long identity = Binder.clearCallingIdentity(); | |
| try { | |
| executor.execute(() -> cb.onSourceModifyFailed(sink, sourceId, reason)); | |
| } finally { | |
| Binder.restoreCallingIdentity(identity); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) { | |
| synchronized (this) { | |
| for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { | |
| Executor executor = mCallbackMap.get(cb); | |
| final long identity = Binder.clearCallingIdentity(); | |
| try { | |
| executor.execute(() -> cb.onSourceRemoved(sink, sourceId, reason)); | |
| } finally { | |
| Binder.restoreCallingIdentity(identity); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| public void onSourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason) { | |
| synchronized (this) { | |
| for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { | |
| Executor executor = mCallbackMap.get(cb); | |
| final long identity = Binder.clearCallingIdentity(); | |
| try { | |
| executor.execute(() -> cb.onSourceRemoveFailed(sink, sourceId, reason)); | |
| } finally { | |
| Binder.restoreCallingIdentity(identity); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| public void onReceiveStateChanged( | |
| BluetoothDevice sink, int sourceId, BluetoothLeBroadcastReceiveState state) { | |
| synchronized (this) { | |
| for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { | |
| Executor executor = mCallbackMap.get(cb); | |
| final long identity = Binder.clearCallingIdentity(); | |
| try { | |
| executor.execute(() -> cb.onReceiveStateChanged(sink, sourceId, state)); | |
| } finally { | |
| Binder.restoreCallingIdentity(identity); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| public void onSourceLost(int broadcastId) { | |
| synchronized (this) { | |
| for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { | |
| Executor executor = mCallbackMap.get(cb); | |
| final long identity = Binder.clearCallingIdentity(); | |
| try { | |
| executor.execute(() -> cb.onSourceLost(broadcastId)); | |
| } finally { | |
| Binder.restoreCallingIdentity(identity); | |
| } | |
| } | |
| } | |
| } | |
| } |