1 /* 2 * Copyright 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at: 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.bluetooth; 18 19 import android.annotation.NonNull; 20 import android.os.Binder; 21 import android.os.RemoteException; 22 import android.util.Log; 23 24 import java.util.HashMap; 25 import java.util.Map; 26 import java.util.concurrent.Executor; 27 28 /** @hide */ 29 public class BluetoothLeBroadcastAssistantCallback 30 extends IBluetoothLeBroadcastAssistantCallback.Stub { 31 private static final String TAG = BluetoothLeBroadcastAssistantCallback.class.getSimpleName(); 32 private boolean mIsRegistered = false; 33 private final Map<BluetoothLeBroadcastAssistant.Callback, Executor> mCallbackMap = 34 new HashMap<>(); 35 IBluetoothLeBroadcastAssistant mAdapter; 36 BluetoothLeBroadcastAssistantCallback(IBluetoothLeBroadcastAssistant adapter)37 public BluetoothLeBroadcastAssistantCallback(IBluetoothLeBroadcastAssistant adapter) { 38 mAdapter = adapter; 39 } 40 41 /** 42 * @hide 43 * @param executor an {@link Executor} to execute given callback 44 * @param callback user implementation of the {@link BluetoothLeBroadcastAssistant#Callback} 45 * @throws IllegalArgumentException if the same <var>callback<var> is already registered. 46 */ register( @onNull Executor executor, @NonNull BluetoothLeBroadcastAssistant.Callback callback)47 public void register( 48 @NonNull Executor executor, @NonNull BluetoothLeBroadcastAssistant.Callback callback) { 49 synchronized (this) { 50 if (mCallbackMap.containsKey(callback)) { 51 throw new IllegalArgumentException("callback is already registered"); 52 } 53 mCallbackMap.put(callback, executor); 54 55 if (!mIsRegistered) { 56 try { 57 mAdapter.registerCallback(this); 58 mIsRegistered = true; 59 } catch (RemoteException e) { 60 Log.w(TAG, "Failed to register broadcast assistant callback"); 61 Log.e(TAG, Log.getStackTraceString(new Throwable())); 62 } 63 } 64 } 65 } 66 67 /** 68 * @hide 69 * @param callback user implementation of the {@link BluetoothLeBroadcastAssistant#Callback} 70 * @throws IllegalArgumentException if <var>callback</var> was not registered before 71 */ unregister(@onNull BluetoothLeBroadcastAssistant.Callback callback)72 public void unregister(@NonNull BluetoothLeBroadcastAssistant.Callback callback) { 73 synchronized (this) { 74 if (!mCallbackMap.containsKey(callback)) { 75 throw new IllegalArgumentException("callback was not registered before"); 76 } 77 mCallbackMap.remove(callback); 78 if (mCallbackMap.isEmpty() && mIsRegistered) { 79 try { 80 mAdapter.unregisterCallback(this); 81 mIsRegistered = false; 82 } catch (RemoteException e) { 83 Log.w(TAG, "Failed to unregister callback with service"); 84 Log.e(TAG, Log.getStackTraceString(new Throwable())); 85 } 86 } 87 } 88 } 89 90 /** 91 * Check if at least one callback is registered from this App 92 * 93 * @return true if at least one callback is registered 94 * @hide 95 */ isAtLeastOneCallbackRegistered()96 public boolean isAtLeastOneCallbackRegistered() { 97 synchronized (this) { 98 return !mCallbackMap.isEmpty(); 99 } 100 } 101 102 @Override onSearchStarted(int reason)103 public void onSearchStarted(int reason) { 104 synchronized (this) { 105 for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { 106 Executor executor = mCallbackMap.get(cb); 107 final long identity = Binder.clearCallingIdentity(); 108 try { 109 executor.execute(() -> cb.onSearchStarted(reason)); 110 } finally { 111 Binder.restoreCallingIdentity(identity); 112 } 113 } 114 } 115 } 116 117 @Override onSearchStartFailed(int reason)118 public void onSearchStartFailed(int reason) { 119 synchronized (this) { 120 for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { 121 Executor executor = mCallbackMap.get(cb); 122 final long identity = Binder.clearCallingIdentity(); 123 try { 124 executor.execute(() -> cb.onSearchStartFailed(reason)); 125 } finally { 126 Binder.restoreCallingIdentity(identity); 127 } 128 } 129 } 130 } 131 132 @Override onSearchStopped(int reason)133 public void onSearchStopped(int reason) { 134 synchronized (this) { 135 for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { 136 Executor executor = mCallbackMap.get(cb); 137 final long identity = Binder.clearCallingIdentity(); 138 try { 139 executor.execute(() -> cb.onSearchStopped(reason)); 140 } finally { 141 Binder.restoreCallingIdentity(identity); 142 } 143 } 144 } 145 } 146 147 @Override onSearchStopFailed(int reason)148 public void onSearchStopFailed(int reason) { 149 synchronized (this) { 150 for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { 151 Executor executor = mCallbackMap.get(cb); 152 final long identity = Binder.clearCallingIdentity(); 153 try { 154 executor.execute(() -> cb.onSearchStopFailed(reason)); 155 } finally { 156 Binder.restoreCallingIdentity(identity); 157 } 158 } 159 } 160 } 161 162 @Override onSourceFound(BluetoothLeBroadcastMetadata source)163 public void onSourceFound(BluetoothLeBroadcastMetadata source) { 164 synchronized (this) { 165 for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { 166 Executor executor = mCallbackMap.get(cb); 167 final long identity = Binder.clearCallingIdentity(); 168 try { 169 executor.execute(() -> cb.onSourceFound(source)); 170 } finally { 171 Binder.restoreCallingIdentity(identity); 172 } 173 } 174 } 175 } 176 177 @Override onSourceAdded(BluetoothDevice sink, int sourceId, int reason)178 public void onSourceAdded(BluetoothDevice sink, int sourceId, int reason) { 179 synchronized (this) { 180 for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { 181 Executor executor = mCallbackMap.get(cb); 182 final long identity = Binder.clearCallingIdentity(); 183 try { 184 executor.execute(() -> cb.onSourceAdded(sink, sourceId, reason)); 185 } finally { 186 Binder.restoreCallingIdentity(identity); 187 } 188 } 189 } 190 } 191 192 @Override onSourceAddFailed( BluetoothDevice sink, BluetoothLeBroadcastMetadata source, int reason)193 public void onSourceAddFailed( 194 BluetoothDevice sink, BluetoothLeBroadcastMetadata source, int reason) { 195 synchronized (this) { 196 for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { 197 Executor executor = mCallbackMap.get(cb); 198 final long identity = Binder.clearCallingIdentity(); 199 try { 200 executor.execute(() -> cb.onSourceAddFailed(sink, source, reason)); 201 } finally { 202 Binder.restoreCallingIdentity(identity); 203 } 204 } 205 } 206 } 207 208 @Override onSourceModified(BluetoothDevice sink, int sourceId, int reason)209 public void onSourceModified(BluetoothDevice sink, int sourceId, int reason) { 210 synchronized (this) { 211 for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { 212 Executor executor = mCallbackMap.get(cb); 213 final long identity = Binder.clearCallingIdentity(); 214 try { 215 executor.execute(() -> cb.onSourceModified(sink, sourceId, reason)); 216 } finally { 217 Binder.restoreCallingIdentity(identity); 218 } 219 } 220 } 221 } 222 223 @Override onSourceModifyFailed(BluetoothDevice sink, int sourceId, int reason)224 public void onSourceModifyFailed(BluetoothDevice sink, int sourceId, int reason) { 225 synchronized (this) { 226 for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { 227 Executor executor = mCallbackMap.get(cb); 228 final long identity = Binder.clearCallingIdentity(); 229 try { 230 executor.execute(() -> cb.onSourceModifyFailed(sink, sourceId, reason)); 231 } finally { 232 Binder.restoreCallingIdentity(identity); 233 } 234 } 235 } 236 } 237 238 @Override onSourceRemoved(BluetoothDevice sink, int sourceId, int reason)239 public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) { 240 synchronized (this) { 241 for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { 242 Executor executor = mCallbackMap.get(cb); 243 final long identity = Binder.clearCallingIdentity(); 244 try { 245 executor.execute(() -> cb.onSourceRemoved(sink, sourceId, reason)); 246 } finally { 247 Binder.restoreCallingIdentity(identity); 248 } 249 } 250 } 251 } 252 253 @Override onSourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason)254 public void onSourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason) { 255 synchronized (this) { 256 for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { 257 Executor executor = mCallbackMap.get(cb); 258 final long identity = Binder.clearCallingIdentity(); 259 try { 260 executor.execute(() -> cb.onSourceRemoveFailed(sink, sourceId, reason)); 261 } finally { 262 Binder.restoreCallingIdentity(identity); 263 } 264 } 265 } 266 } 267 268 @Override onReceiveStateChanged( BluetoothDevice sink, int sourceId, BluetoothLeBroadcastReceiveState state)269 public void onReceiveStateChanged( 270 BluetoothDevice sink, int sourceId, BluetoothLeBroadcastReceiveState state) { 271 synchronized (this) { 272 for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { 273 Executor executor = mCallbackMap.get(cb); 274 final long identity = Binder.clearCallingIdentity(); 275 try { 276 executor.execute(() -> cb.onReceiveStateChanged(sink, sourceId, state)); 277 } finally { 278 Binder.restoreCallingIdentity(identity); 279 } 280 } 281 } 282 } 283 284 @Override onSourceLost(int broadcastId)285 public void onSourceLost(int broadcastId) { 286 synchronized (this) { 287 for (BluetoothLeBroadcastAssistant.Callback cb : mCallbackMap.keySet()) { 288 Executor executor = mCallbackMap.get(cb); 289 final long identity = Binder.clearCallingIdentity(); 290 try { 291 executor.execute(() -> cb.onSourceLost(broadcastId)); 292 } finally { 293 Binder.restoreCallingIdentity(identity); 294 } 295 } 296 } 297 } 298 } 299