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.i(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 getOwnAddressNative(advertiserId); 230 } 231 stopAdvertisingSet(IAdvertisingSetCallback callback)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 enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents)262 void enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents) { 263 enableAdvertisingSetNative(advertiserId, enable, duration, maxExtAdvEvents); 264 } 265 setAdvertisingData(int advertiserId, AdvertiseData data)266 void setAdvertisingData(int advertiserId, AdvertiseData data) { 267 String deviceName = AdapterService.getAdapterService().getName(); 268 setAdvertisingDataNative(advertiserId, 269 AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 270 } 271 setScanResponseData(int advertiserId, AdvertiseData data)272 void setScanResponseData(int advertiserId, AdvertiseData data) { 273 String deviceName = AdapterService.getAdapterService().getName(); 274 setScanResponseDataNative(advertiserId, 275 AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 276 } 277 setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters)278 void setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters) { 279 setAdvertisingParametersNative(advertiserId, parameters); 280 } 281 setPeriodicAdvertisingParameters(int advertiserId, PeriodicAdvertisingParameters parameters)282 void setPeriodicAdvertisingParameters(int advertiserId, 283 PeriodicAdvertisingParameters parameters) { 284 setPeriodicAdvertisingParametersNative(advertiserId, parameters); 285 } 286 setPeriodicAdvertisingData(int advertiserId, AdvertiseData data)287 void setPeriodicAdvertisingData(int advertiserId, AdvertiseData data) { 288 String deviceName = AdapterService.getAdapterService().getName(); 289 setPeriodicAdvertisingDataNative(advertiserId, 290 AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 291 } 292 setPeriodicAdvertisingEnable(int advertiserId, boolean enable)293 void setPeriodicAdvertisingEnable(int advertiserId, boolean enable) { 294 setPeriodicAdvertisingEnableNative(advertiserId, enable); 295 } 296 onAdvertisingDataSet(int advertiserId, int status)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 onScanResponseDataSet(int advertiserId, int status)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 onAdvertisingParametersUpdated(int advertiserId, int txPower, int status)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 onPeriodicAdvertisingParametersUpdated(int advertiserId, int status)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 onPeriodicAdvertisingDataSet(int advertiserId, int status)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 onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status)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 { classInitNative()398 classInitNative(); 399 } 400 classInitNative()401 private static native void classInitNative(); 402 initializeNative()403 private native void initializeNative(); 404 cleanupNative()405 private native void cleanupNative(); 406 startAdvertisingSetNative(AdvertisingSetParameters parameters, byte[] advertiseData, byte[] scanResponse, PeriodicAdvertisingParameters periodicParameters, byte[] periodicData, int duration, int maxExtAdvEvents, int regId)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 getOwnAddressNative(int advertiserId)412 private native void getOwnAddressNative(int advertiserId); 413 stopAdvertisingSetNative(int advertiserId)414 private native void stopAdvertisingSetNative(int advertiserId); 415 enableAdvertisingSetNative(int advertiserId, boolean enable, int duration, int maxExtAdvEvents)416 private native void enableAdvertisingSetNative(int advertiserId, boolean enable, int duration, 417 int maxExtAdvEvents); 418 setAdvertisingDataNative(int advertiserId, byte[] data)419 private native void setAdvertisingDataNative(int advertiserId, byte[] data); 420 setScanResponseDataNative(int advertiserId, byte[] data)421 private native void setScanResponseDataNative(int advertiserId, byte[] data); 422 setAdvertisingParametersNative(int advertiserId, AdvertisingSetParameters parameters)423 private native void setAdvertisingParametersNative(int advertiserId, 424 AdvertisingSetParameters parameters); 425 setPeriodicAdvertisingParametersNative(int advertiserId, PeriodicAdvertisingParameters parameters)426 private native void setPeriodicAdvertisingParametersNative(int advertiserId, 427 PeriodicAdvertisingParameters parameters); 428 setPeriodicAdvertisingDataNative(int advertiserId, byte[] data)429 private native void setPeriodicAdvertisingDataNative(int advertiserId, byte[] data); 430 setPeriodicAdvertisingEnableNative(int advertiserId, boolean enable)431 private native void setPeriodicAdvertisingEnableNative(int advertiserId, boolean enable); 432 } 433