1 /* 2 * Copyright (C) 2017 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 com.android.bluetooth.gatt; 18 19 import android.bluetooth.le.AdvertiseData; 20 import android.bluetooth.le.AdvertisingSetParameters; 21 import android.bluetooth.le.IAdvertisingSetCallback; 22 import android.bluetooth.le.PeriodicAdvertisingParameters; 23 import android.os.Handler; 24 import android.os.HandlerThread; 25 import android.os.IBinder; 26 import android.os.IInterface; 27 import android.os.Looper; 28 import android.os.RemoteException; 29 import android.util.Log; 30 31 import com.android.bluetooth.btservice.AdapterService; 32 33 import java.util.Collections; 34 import java.util.HashMap; 35 import java.util.Map; 36 37 /** 38 * Manages Bluetooth LE advertising operations and interacts with bluedroid stack. TODO: add tests. 39 * 40 * @hide 41 */ 42 class AdvertiseManager { 43 private static final boolean DBG = GattServiceConfig.DBG; 44 private static final String TAG = GattServiceConfig.TAG_PREFIX + "AdvertiseManager"; 45 46 private final GattService mService; 47 private final AdapterService mAdapterService; 48 private Handler mHandler; 49 Map<IBinder, AdvertiserInfo> mAdvertisers = Collections.synchronizedMap(new HashMap<>()); 50 static int sTempRegistrationId = -1; 51 52 /** 53 * Constructor of {@link AdvertiseManager}. 54 */ AdvertiseManager(GattService service, AdapterService adapterService)55 AdvertiseManager(GattService service, AdapterService adapterService) { 56 if (DBG) { 57 Log.d(TAG, "advertise manager created"); 58 } 59 mService = service; 60 mAdapterService = adapterService; 61 } 62 63 /** 64 * Start a {@link HandlerThread} that handles advertising operations. 65 */ start()66 void start() { 67 initializeNative(); 68 HandlerThread thread = new HandlerThread("BluetoothAdvertiseManager"); 69 thread.start(); 70 mHandler = new Handler(thread.getLooper()); 71 } 72 cleanup()73 void cleanup() { 74 if (DBG) { 75 Log.d(TAG, "cleanup()"); 76 } 77 cleanupNative(); 78 mAdvertisers.clear(); 79 sTempRegistrationId = -1; 80 81 if (mHandler != null) { 82 // Shut down the thread 83 mHandler.removeCallbacksAndMessages(null); 84 Looper looper = mHandler.getLooper(); 85 if (looper != null) { 86 looper.quit(); 87 } 88 mHandler = null; 89 } 90 } 91 92 class AdvertiserInfo { 93 /* When id is negative, the registration is ongoing. When the registration finishes, id 94 * becomes equal to advertiser_id */ 95 public Integer id; 96 public AdvertisingSetDeathRecipient deathRecipient; 97 public IAdvertisingSetCallback callback; 98 AdvertiserInfo(Integer id, AdvertisingSetDeathRecipient deathRecipient, IAdvertisingSetCallback callback)99 AdvertiserInfo(Integer id, AdvertisingSetDeathRecipient deathRecipient, 100 IAdvertisingSetCallback callback) { 101 this.id = id; 102 this.deathRecipient = deathRecipient; 103 this.callback = callback; 104 } 105 } 106 toBinder(IAdvertisingSetCallback e)107 IBinder toBinder(IAdvertisingSetCallback e) { 108 return ((IInterface) e).asBinder(); 109 } 110 111 class AdvertisingSetDeathRecipient implements IBinder.DeathRecipient { 112 public IAdvertisingSetCallback callback; 113 AdvertisingSetDeathRecipient(IAdvertisingSetCallback callback)114 AdvertisingSetDeathRecipient(IAdvertisingSetCallback callback) { 115 this.callback = callback; 116 } 117 118 @Override binderDied()119 public void binderDied() { 120 if (DBG) { 121 Log.d(TAG, "Binder is dead - unregistering advertising set"); 122 } 123 stopAdvertisingSet(callback); 124 } 125 } 126 findAdvertiser(int advertiserId)127 Map.Entry<IBinder, AdvertiserInfo> findAdvertiser(int advertiserId) { 128 Map.Entry<IBinder, AdvertiserInfo> entry = null; 129 for (Map.Entry<IBinder, AdvertiserInfo> e : mAdvertisers.entrySet()) { 130 if (e.getValue().id == advertiserId) { 131 entry = e; 132 break; 133 } 134 } 135 return entry; 136 } 137 onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status)138 void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status) 139 throws Exception { 140 if (DBG) { 141 Log.d(TAG, 142 "onAdvertisingSetStarted() - regId=" + regId + ", advertiserId=" + advertiserId 143 + ", status=" + status); 144 } 145 146 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(regId); 147 148 if (entry == null) { 149 Log.i(TAG, "onAdvertisingSetStarted() - no callback found for regId " + regId); 150 // Advertising set was stopped before it was properly registered. 151 stopAdvertisingSetNative(advertiserId); 152 return; 153 } 154 155 IAdvertisingSetCallback callback = entry.getValue().callback; 156 if (status == 0) { 157 entry.setValue( 158 new AdvertiserInfo(advertiserId, entry.getValue().deathRecipient, callback)); 159 } else { 160 IBinder binder = entry.getKey(); 161 binder.unlinkToDeath(entry.getValue().deathRecipient, 0); 162 mAdvertisers.remove(binder); 163 } 164 165 callback.onAdvertisingSetStarted(advertiserId, txPower, status); 166 } 167 onAdvertisingEnabled(int advertiserId, boolean enable, int status)168 void onAdvertisingEnabled(int advertiserId, boolean enable, int status) throws Exception { 169 if (DBG) { 170 Log.d(TAG, "onAdvertisingSetEnabled() - advertiserId=" + advertiserId + ", enable=" 171 + enable + ", status=" + status); 172 } 173 174 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 175 if (entry == null) { 176 Log.i(TAG, "onAdvertisingSetEnable() - no callback found for advertiserId " 177 + advertiserId); 178 return; 179 } 180 181 IAdvertisingSetCallback callback = entry.getValue().callback; 182 callback.onAdvertisingEnabled(advertiserId, enable, status); 183 } 184 startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, AdvertiseData periodicData, int duration, int maxExtAdvEvents, IAdvertisingSetCallback callback)185 void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, 186 AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, 187 AdvertiseData periodicData, int duration, int maxExtAdvEvents, 188 IAdvertisingSetCallback callback) { 189 AdvertisingSetDeathRecipient deathRecipient = new AdvertisingSetDeathRecipient(callback); 190 IBinder binder = toBinder(callback); 191 try { 192 binder.linkToDeath(deathRecipient, 0); 193 } catch (RemoteException e) { 194 throw new IllegalArgumentException("Can't link to advertiser's death"); 195 } 196 197 String deviceName = AdapterService.getAdapterService().getName(); 198 byte[] advDataBytes = AdvertiseHelper.advertiseDataToBytes(advertiseData, deviceName); 199 byte[] scanResponseBytes = AdvertiseHelper.advertiseDataToBytes(scanResponse, deviceName); 200 byte[] periodicDataBytes = AdvertiseHelper.advertiseDataToBytes(periodicData, deviceName); 201 202 int cbId = --sTempRegistrationId; 203 mAdvertisers.put(binder, new AdvertiserInfo(cbId, deathRecipient, callback)); 204 205 if (DBG) { 206 Log.d(TAG, "startAdvertisingSet() - reg_id=" + cbId + ", callback: " + binder); 207 } 208 startAdvertisingSetNative(parameters, advDataBytes, scanResponseBytes, periodicParameters, 209 periodicDataBytes, duration, maxExtAdvEvents, cbId); 210 } 211 onOwnAddressRead(int advertiserId, int addressType, String address)212 void onOwnAddressRead(int advertiserId, int addressType, String address) 213 throws RemoteException { 214 if (DBG) { 215 Log.d(TAG, "onOwnAddressRead() advertiserId=" + advertiserId); 216 } 217 218 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 219 if (entry == null) { 220 Log.w(TAG, "onOwnAddressRead() - bad advertiserId " + advertiserId); 221 return; 222 } 223 224 IAdvertisingSetCallback callback = entry.getValue().callback; 225 callback.onOwnAddressRead(advertiserId, addressType, address); 226 } 227 getOwnAddress(int advertiserId)228 void getOwnAddress(int advertiserId) { 229 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 230 if (entry == null) { 231 Log.w(TAG, "getOwnAddress() - bad advertiserId " + advertiserId); 232 return; 233 } 234 getOwnAddressNative(advertiserId); 235 } 236 stopAdvertisingSet(IAdvertisingSetCallback callback)237 void stopAdvertisingSet(IAdvertisingSetCallback callback) { 238 IBinder binder = toBinder(callback); 239 if (DBG) { 240 Log.d(TAG, "stopAdvertisingSet() " + binder); 241 } 242 243 AdvertiserInfo adv = mAdvertisers.remove(binder); 244 if (adv == null) { 245 Log.e(TAG, "stopAdvertisingSet() - no client found for callback"); 246 return; 247 } 248 249 Integer advertiserId = adv.id; 250 binder.unlinkToDeath(adv.deathRecipient, 0); 251 252 if (advertiserId < 0) { 253 Log.i(TAG, "stopAdvertisingSet() - advertiser not finished registration yet"); 254 // Advertiser will be freed once initiated in onAdvertisingSetStarted() 255 return; 256 } 257 258 stopAdvertisingSetNative(advertiserId); 259 260 try { 261 callback.onAdvertisingSetStopped(advertiserId); 262 } catch (RemoteException e) { 263 Log.i(TAG, "error sending onAdvertisingSetStopped callback", e); 264 } 265 } 266 enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents)267 void enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents) { 268 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 269 if (entry == null) { 270 Log.w(TAG, "enableAdvertisingSet() - bad advertiserId " + advertiserId); 271 return; 272 } 273 enableAdvertisingSetNative(advertiserId, enable, duration, maxExtAdvEvents); 274 } 275 setAdvertisingData(int advertiserId, AdvertiseData data)276 void setAdvertisingData(int advertiserId, AdvertiseData data) { 277 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 278 if (entry == null) { 279 Log.w(TAG, "setAdvertisingData() - bad advertiserId " + advertiserId); 280 return; 281 } 282 String deviceName = AdapterService.getAdapterService().getName(); 283 setAdvertisingDataNative(advertiserId, 284 AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 285 } 286 setScanResponseData(int advertiserId, AdvertiseData data)287 void setScanResponseData(int advertiserId, AdvertiseData data) { 288 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 289 if (entry == null) { 290 Log.w(TAG, "setScanResponseData() - bad advertiserId " + advertiserId); 291 return; 292 } 293 String deviceName = AdapterService.getAdapterService().getName(); 294 setScanResponseDataNative(advertiserId, 295 AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 296 } 297 setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters)298 void setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters) { 299 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 300 if (entry == null) { 301 Log.w(TAG, "setAdvertisingParameters() - bad advertiserId " + advertiserId); 302 return; 303 } 304 setAdvertisingParametersNative(advertiserId, parameters); 305 } 306 setPeriodicAdvertisingParameters(int advertiserId, PeriodicAdvertisingParameters parameters)307 void setPeriodicAdvertisingParameters(int advertiserId, 308 PeriodicAdvertisingParameters parameters) { 309 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 310 if (entry == null) { 311 Log.w(TAG, "setPeriodicAdvertisingParameters() - bad advertiserId " + advertiserId); 312 return; 313 } 314 setPeriodicAdvertisingParametersNative(advertiserId, parameters); 315 } 316 setPeriodicAdvertisingData(int advertiserId, AdvertiseData data)317 void setPeriodicAdvertisingData(int advertiserId, AdvertiseData data) { 318 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 319 if (entry == null) { 320 Log.w(TAG, "setPeriodicAdvertisingData() - bad advertiserId " + advertiserId); 321 return; 322 } 323 String deviceName = AdapterService.getAdapterService().getName(); 324 setPeriodicAdvertisingDataNative(advertiserId, 325 AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 326 } 327 setPeriodicAdvertisingEnable(int advertiserId, boolean enable)328 void setPeriodicAdvertisingEnable(int advertiserId, boolean enable) { 329 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 330 if (entry == null) { 331 Log.w(TAG, "setPeriodicAdvertisingEnable() - bad advertiserId " + advertiserId); 332 return; 333 } 334 setPeriodicAdvertisingEnableNative(advertiserId, enable); 335 } 336 onAdvertisingDataSet(int advertiserId, int status)337 void onAdvertisingDataSet(int advertiserId, int status) throws Exception { 338 if (DBG) { 339 Log.d(TAG, 340 "onAdvertisingDataSet() advertiserId=" + advertiserId + ", status=" + status); 341 } 342 343 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 344 if (entry == null) { 345 Log.i(TAG, "onAdvertisingDataSet() - bad advertiserId " + advertiserId); 346 return; 347 } 348 349 IAdvertisingSetCallback callback = entry.getValue().callback; 350 callback.onAdvertisingDataSet(advertiserId, status); 351 } 352 onScanResponseDataSet(int advertiserId, int status)353 void onScanResponseDataSet(int advertiserId, int status) throws Exception { 354 if (DBG) { 355 Log.d(TAG, 356 "onScanResponseDataSet() advertiserId=" + advertiserId + ", status=" + status); 357 } 358 359 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 360 if (entry == null) { 361 Log.i(TAG, "onScanResponseDataSet() - bad advertiserId " + advertiserId); 362 return; 363 } 364 365 IAdvertisingSetCallback callback = entry.getValue().callback; 366 callback.onScanResponseDataSet(advertiserId, status); 367 } 368 onAdvertisingParametersUpdated(int advertiserId, int txPower, int status)369 void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) 370 throws Exception { 371 if (DBG) { 372 Log.d(TAG, 373 "onAdvertisingParametersUpdated() advertiserId=" + advertiserId + ", txPower=" 374 + txPower + ", status=" + status); 375 } 376 377 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 378 if (entry == null) { 379 Log.i(TAG, "onAdvertisingParametersUpdated() - bad advertiserId " + advertiserId); 380 return; 381 } 382 383 IAdvertisingSetCallback callback = entry.getValue().callback; 384 callback.onAdvertisingParametersUpdated(advertiserId, txPower, status); 385 } 386 onPeriodicAdvertisingParametersUpdated(int advertiserId, int status)387 void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) throws Exception { 388 if (DBG) { 389 Log.d(TAG, "onPeriodicAdvertisingParametersUpdated() advertiserId=" + advertiserId 390 + ", status=" + status); 391 } 392 393 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 394 if (entry == null) { 395 Log.i(TAG, 396 "onPeriodicAdvertisingParametersUpdated() - bad advertiserId " + advertiserId); 397 return; 398 } 399 400 IAdvertisingSetCallback callback = entry.getValue().callback; 401 callback.onPeriodicAdvertisingParametersUpdated(advertiserId, status); 402 } 403 onPeriodicAdvertisingDataSet(int advertiserId, int status)404 void onPeriodicAdvertisingDataSet(int advertiserId, int status) throws Exception { 405 if (DBG) { 406 Log.d(TAG, "onPeriodicAdvertisingDataSet() advertiserId=" + advertiserId + ", status=" 407 + status); 408 } 409 410 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 411 if (entry == null) { 412 Log.i(TAG, "onPeriodicAdvertisingDataSet() - bad advertiserId " + advertiserId); 413 return; 414 } 415 416 IAdvertisingSetCallback callback = entry.getValue().callback; 417 callback.onPeriodicAdvertisingDataSet(advertiserId, status); 418 } 419 onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status)420 void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status) 421 throws Exception { 422 if (DBG) { 423 Log.d(TAG, "onPeriodicAdvertisingEnabled() advertiserId=" + advertiserId + ", status=" 424 + status); 425 } 426 427 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 428 if (entry == null) { 429 Log.i(TAG, "onAdvertisingSetEnable() - bad advertiserId " + advertiserId); 430 return; 431 } 432 433 IAdvertisingSetCallback callback = entry.getValue().callback; 434 callback.onPeriodicAdvertisingEnabled(advertiserId, enable, status); 435 } 436 437 static { classInitNative()438 classInitNative(); 439 } 440 classInitNative()441 private static native void classInitNative(); 442 initializeNative()443 private native void initializeNative(); 444 cleanupNative()445 private native void cleanupNative(); 446 startAdvertisingSetNative(AdvertisingSetParameters parameters, byte[] advertiseData, byte[] scanResponse, PeriodicAdvertisingParameters periodicParameters, byte[] periodicData, int duration, int maxExtAdvEvents, int regId)447 private native void startAdvertisingSetNative(AdvertisingSetParameters parameters, 448 byte[] advertiseData, byte[] scanResponse, 449 PeriodicAdvertisingParameters periodicParameters, byte[] periodicData, int duration, 450 int maxExtAdvEvents, int regId); 451 getOwnAddressNative(int advertiserId)452 private native void getOwnAddressNative(int advertiserId); 453 stopAdvertisingSetNative(int advertiserId)454 private native void stopAdvertisingSetNative(int advertiserId); 455 enableAdvertisingSetNative(int advertiserId, boolean enable, int duration, int maxExtAdvEvents)456 private native void enableAdvertisingSetNative(int advertiserId, boolean enable, int duration, 457 int maxExtAdvEvents); 458 setAdvertisingDataNative(int advertiserId, byte[] data)459 private native void setAdvertisingDataNative(int advertiserId, byte[] data); 460 setScanResponseDataNative(int advertiserId, byte[] data)461 private native void setScanResponseDataNative(int advertiserId, byte[] data); 462 setAdvertisingParametersNative(int advertiserId, AdvertisingSetParameters parameters)463 private native void setAdvertisingParametersNative(int advertiserId, 464 AdvertisingSetParameters parameters); 465 setPeriodicAdvertisingParametersNative(int advertiserId, PeriodicAdvertisingParameters parameters)466 private native void setPeriodicAdvertisingParametersNative(int advertiserId, 467 PeriodicAdvertisingParameters parameters); 468 setPeriodicAdvertisingDataNative(int advertiserId, byte[] data)469 private native void setPeriodicAdvertisingDataNative(int advertiserId, byte[] data); 470 setPeriodicAdvertisingEnableNative(int advertiserId, boolean enable)471 private native void setPeriodicAdvertisingEnableNative(int advertiserId, boolean enable); 472 } 473