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 */ 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 */ 66 void start() { 67 initializeNative(); 68 HandlerThread thread = new HandlerThread("BluetoothAdvertiseManager"); 69 thread.start(); 70 mHandler = new Handler(thread.getLooper()); 71 } 72 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 99 AdvertiserInfo(Integer id, AdvertisingSetDeathRecipient deathRecipient, 100 IAdvertisingSetCallback callback) { 101 this.id = id; 102 this.deathRecipient = deathRecipient; 103 this.callback = callback; 104 } 105 } 106 107 IBinder toBinder(IAdvertisingSetCallback e) { 108 return ((IInterface) e).asBinder(); 109 } 110 111 class AdvertisingSetDeathRecipient implements IBinder.DeathRecipient { 112 public IAdvertisingSetCallback callback; 113 114 AdvertisingSetDeathRecipient(IAdvertisingSetCallback callback) { 115 this.callback = callback; 116 } 117 118 @Override 119 public void binderDied() { 120 if (DBG) { 121 Log.d(TAG, "Binder is dead - unregistering advertising set"); 122 } 123 stopAdvertisingSet(callback); 124 } 125 } 126 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 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 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 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 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.i(TAG, "onOwnAddressRead() - bad advertiserId " + advertiserId); 221 return; 222 } 223 224 IAdvertisingSetCallback callback = entry.getValue().callback; 225 callback.onOwnAddressRead(advertiserId, addressType, address); 226 } 227 228 void getOwnAddress(int advertiserId) { 229 getOwnAddressNative(advertiserId); 230 } 231 232 void stopAdvertisingSet(IAdvertisingSetCallback callback) { 233 IBinder binder = toBinder(callback); 234 if (DBG) { 235 Log.d(TAG, "stopAdvertisingSet() " + binder); 236 } 237 238 AdvertiserInfo adv = mAdvertisers.remove(binder); 239 if (adv == null) { 240 Log.e(TAG, "stopAdvertisingSet() - no client found for callback"); 241 return; 242 } 243 244 Integer advertiserId = adv.id; 245 binder.unlinkToDeath(adv.deathRecipient, 0); 246 247 if (advertiserId < 0) { 248 Log.i(TAG, "stopAdvertisingSet() - advertiser not finished registration yet"); 249 // Advertiser will be freed once initiated in onAdvertisingSetStarted() 250 return; 251 } 252 253 stopAdvertisingSetNative(advertiserId); 254 255 try { 256 callback.onAdvertisingSetStopped(advertiserId); 257 } catch (RemoteException e) { 258 Log.i(TAG, "error sending onAdvertisingSetStopped callback", e); 259 } 260 } 261 262 void enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents) { 263 enableAdvertisingSetNative(advertiserId, enable, duration, maxExtAdvEvents); 264 } 265 266 void setAdvertisingData(int advertiserId, AdvertiseData data) { 267 String deviceName = AdapterService.getAdapterService().getName(); 268 setAdvertisingDataNative(advertiserId, 269 AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 270 } 271 272 void setScanResponseData(int advertiserId, AdvertiseData data) { 273 String deviceName = AdapterService.getAdapterService().getName(); 274 setScanResponseDataNative(advertiserId, 275 AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 276 } 277 278 void setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters) { 279 setAdvertisingParametersNative(advertiserId, parameters); 280 } 281 282 void setPeriodicAdvertisingParameters(int advertiserId, 283 PeriodicAdvertisingParameters parameters) { 284 setPeriodicAdvertisingParametersNative(advertiserId, parameters); 285 } 286 287 void setPeriodicAdvertisingData(int advertiserId, AdvertiseData data) { 288 String deviceName = AdapterService.getAdapterService().getName(); 289 setPeriodicAdvertisingDataNative(advertiserId, 290 AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 291 } 292 293 void setPeriodicAdvertisingEnable(int advertiserId, boolean enable) { 294 setPeriodicAdvertisingEnableNative(advertiserId, enable); 295 } 296 297 void onAdvertisingDataSet(int advertiserId, int status) throws Exception { 298 if (DBG) { 299 Log.d(TAG, 300 "onAdvertisingDataSet() advertiserId=" + advertiserId + ", status=" + status); 301 } 302 303 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 304 if (entry == null) { 305 Log.i(TAG, "onAdvertisingDataSet() - bad advertiserId " + advertiserId); 306 return; 307 } 308 309 IAdvertisingSetCallback callback = entry.getValue().callback; 310 callback.onAdvertisingDataSet(advertiserId, status); 311 } 312 313 void onScanResponseDataSet(int advertiserId, int status) throws Exception { 314 if (DBG) { 315 Log.d(TAG, 316 "onScanResponseDataSet() advertiserId=" + advertiserId + ", status=" + status); 317 } 318 319 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 320 if (entry == null) { 321 Log.i(TAG, "onScanResponseDataSet() - bad advertiserId " + advertiserId); 322 return; 323 } 324 325 IAdvertisingSetCallback callback = entry.getValue().callback; 326 callback.onScanResponseDataSet(advertiserId, status); 327 } 328 329 void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) 330 throws Exception { 331 if (DBG) { 332 Log.d(TAG, 333 "onAdvertisingParametersUpdated() advertiserId=" + advertiserId + ", txPower=" 334 + txPower + ", status=" + status); 335 } 336 337 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 338 if (entry == null) { 339 Log.i(TAG, "onAdvertisingParametersUpdated() - bad advertiserId " + advertiserId); 340 return; 341 } 342 343 IAdvertisingSetCallback callback = entry.getValue().callback; 344 callback.onAdvertisingParametersUpdated(advertiserId, txPower, status); 345 } 346 347 void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) throws Exception { 348 if (DBG) { 349 Log.d(TAG, "onPeriodicAdvertisingParametersUpdated() advertiserId=" + advertiserId 350 + ", status=" + status); 351 } 352 353 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 354 if (entry == null) { 355 Log.i(TAG, 356 "onPeriodicAdvertisingParametersUpdated() - bad advertiserId " + advertiserId); 357 return; 358 } 359 360 IAdvertisingSetCallback callback = entry.getValue().callback; 361 callback.onPeriodicAdvertisingParametersUpdated(advertiserId, status); 362 } 363 364 void onPeriodicAdvertisingDataSet(int advertiserId, int status) throws Exception { 365 if (DBG) { 366 Log.d(TAG, "onPeriodicAdvertisingDataSet() advertiserId=" + advertiserId + ", status=" 367 + status); 368 } 369 370 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 371 if (entry == null) { 372 Log.i(TAG, "onPeriodicAdvertisingDataSet() - bad advertiserId " + advertiserId); 373 return; 374 } 375 376 IAdvertisingSetCallback callback = entry.getValue().callback; 377 callback.onPeriodicAdvertisingDataSet(advertiserId, status); 378 } 379 380 void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status) 381 throws Exception { 382 if (DBG) { 383 Log.d(TAG, "onPeriodicAdvertisingEnabled() advertiserId=" + advertiserId + ", status=" 384 + status); 385 } 386 387 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); 388 if (entry == null) { 389 Log.i(TAG, "onAdvertisingSetEnable() - bad advertiserId " + advertiserId); 390 return; 391 } 392 393 IAdvertisingSetCallback callback = entry.getValue().callback; 394 callback.onPeriodicAdvertisingEnabled(advertiserId, enable, status); 395 } 396 397 static { 398 classInitNative(); 399 } 400 401 private static native void classInitNative(); 402 403 private native void initializeNative(); 404 405 private native void cleanupNative(); 406 407 private native void startAdvertisingSetNative(AdvertisingSetParameters parameters, 408 byte[] advertiseData, byte[] scanResponse, 409 PeriodicAdvertisingParameters periodicParameters, byte[] periodicData, int duration, 410 int maxExtAdvEvents, int regId); 411 412 private native void getOwnAddressNative(int advertiserId); 413 414 private native void stopAdvertisingSetNative(int advertiserId); 415 416 private native void enableAdvertisingSetNative(int advertiserId, boolean enable, int duration, 417 int maxExtAdvEvents); 418 419 private native void setAdvertisingDataNative(int advertiserId, byte[] data); 420 421 private native void setScanResponseDataNative(int advertiserId, byte[] data); 422 423 private native void setAdvertisingParametersNative(int advertiserId, 424 AdvertisingSetParameters parameters); 425 426 private native void setPeriodicAdvertisingParametersNative(int advertiserId, 427 PeriodicAdvertisingParameters parameters); 428 429 private native void setPeriodicAdvertisingDataNative(int advertiserId, byte[] data); 430 431 private native void setPeriodicAdvertisingEnableNative(int advertiserId, boolean enable); 432 } 433