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