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.Message; 29 import android.os.RemoteException; 30 import android.util.Log; 31 import com.android.bluetooth.Utils; 32 import com.android.bluetooth.btservice.AdapterService; 33 import java.util.Collections; 34 import java.util.HashMap; 35 import java.util.HashSet; 36 import java.util.Map; 37 import java.util.Set; 38 import java.util.UUID; 39 import java.util.concurrent.CountDownLatch; 40 import java.util.concurrent.TimeUnit; 41 42 /** 43 * Manages Bluetooth LE advertising operations and interacts with bluedroid stack. TODO: add tests. 44 * 45 * @hide 46 */ 47 class 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 */ AdvertiseManager(GattService service, AdapterService adapterService)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 */ start()69 void start() { 70 initializeNative(); 71 HandlerThread thread = new HandlerThread("BluetoothAdvertiseManager"); 72 thread.start(); 73 mHandler = new Handler(thread.getLooper()); 74 } 75 cleanup()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 AdvertiserInfo(Integer id, AdvertisingSetDeathRecipient deathRecipient, IAdvertisingSetCallback callback)100 AdvertiserInfo(Integer id, AdvertisingSetDeathRecipient deathRecipient, 101 IAdvertisingSetCallback callback) { 102 this.id = id; 103 this.deathRecipient = deathRecipient; 104 this.callback = callback; 105 } 106 } 107 toBinder(IAdvertisingSetCallback e)108 IBinder toBinder(IAdvertisingSetCallback e) { 109 return ((IInterface) e).asBinder(); 110 } 111 112 class AdvertisingSetDeathRecipient implements IBinder.DeathRecipient { 113 IAdvertisingSetCallback callback; 114 AdvertisingSetDeathRecipient(IAdvertisingSetCallback callback)115 public AdvertisingSetDeathRecipient(IAdvertisingSetCallback callback) { 116 this.callback = callback; 117 } 118 119 @Override binderDied()120 public void binderDied() { 121 if (DBG) Log.d(TAG, "Binder is dead - unregistering advertising set"); 122 stopAdvertisingSet(callback); 123 } 124 } 125 findAdvertiser(int advertiser_id)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 onAdvertisingSetStarted(int reg_id, int advertiser_id, int tx_power, int status)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 onAdvertisingEnabled(int advertiser_id, boolean enable, int status)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 startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, AdvertiseData periodicData, int duration, int maxExtAdvEvents, IAdvertisingSetCallback callback)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 onOwnAddressRead(int advertiser_id, int addressType, String address)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 getOwnAddress(int advertiserId)222 void getOwnAddress(int advertiserId) { 223 getOwnAddressNative(advertiserId); 224 } 225 stopAdvertisingSet(IAdvertisingSetCallback callback)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 enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents)254 void enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents) { 255 enableAdvertisingSetNative(advertiserId, enable, duration, maxExtAdvEvents); 256 } 257 setAdvertisingData(int advertiserId, AdvertiseData data)258 void setAdvertisingData(int advertiserId, AdvertiseData data) { 259 String deviceName = AdapterService.getAdapterService().getName(); 260 setAdvertisingDataNative( 261 advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 262 } 263 setScanResponseData(int advertiserId, AdvertiseData data)264 void setScanResponseData(int advertiserId, AdvertiseData data) { 265 String deviceName = AdapterService.getAdapterService().getName(); 266 setScanResponseDataNative( 267 advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 268 } 269 setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters)270 void setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters) { 271 setAdvertisingParametersNative(advertiserId, parameters); 272 } 273 setPeriodicAdvertisingParameters( int advertiserId, PeriodicAdvertisingParameters parameters)274 void setPeriodicAdvertisingParameters( 275 int advertiserId, PeriodicAdvertisingParameters parameters) { 276 setPeriodicAdvertisingParametersNative(advertiserId, parameters); 277 } 278 setPeriodicAdvertisingData(int advertiserId, AdvertiseData data)279 void setPeriodicAdvertisingData(int advertiserId, AdvertiseData data) { 280 String deviceName = AdapterService.getAdapterService().getName(); 281 setPeriodicAdvertisingDataNative( 282 advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 283 } 284 setPeriodicAdvertisingEnable(int advertiserId, boolean enable)285 void setPeriodicAdvertisingEnable(int advertiserId, boolean enable) { 286 setPeriodicAdvertisingEnableNative(advertiserId, enable); 287 } 288 onAdvertisingDataSet(int advertiser_id, int status)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 onScanResponseDataSet(int advertiser_id, int status)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 onAdvertisingParametersUpdated(int advertiser_id, int tx_power, int status)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 onPeriodicAdvertisingParametersUpdated(int advertiser_id, int status)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 onPeriodicAdvertisingDataSet(int advertiser_id, int status)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 onPeriodicAdvertisingEnabled(int advertiser_id, boolean enable, int status)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 { classInitNative()388 classInitNative(); 389 } 390 classInitNative()391 private native static void classInitNative(); initializeNative()392 private native void initializeNative(); cleanupNative()393 private native void cleanupNative(); startAdvertisingSetNative(AdvertisingSetParameters parameters, byte[] advertiseData, byte[] scanResponse, PeriodicAdvertisingParameters periodicParameters, byte[] periodicData, int duration, int maxExtAdvEvents, int reg_id)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); getOwnAddressNative(int advertiserId)398 private native void getOwnAddressNative(int advertiserId); stopAdvertisingSetNative(int advertiser_id)399 private native void stopAdvertisingSetNative(int advertiser_id); enableAdvertisingSetNative( int advertiserId, boolean enable, int duration, int maxExtAdvEvents)400 private native void enableAdvertisingSetNative( 401 int advertiserId, boolean enable, int duration, int maxExtAdvEvents); setAdvertisingDataNative(int advertiserId, byte[] data)402 private native void setAdvertisingDataNative(int advertiserId, byte[] data); setScanResponseDataNative(int advertiserId, byte[] data)403 private native void setScanResponseDataNative(int advertiserId, byte[] data); setAdvertisingParametersNative( int advertiserId, AdvertisingSetParameters parameters)404 private native void setAdvertisingParametersNative( 405 int advertiserId, AdvertisingSetParameters parameters); setPeriodicAdvertisingParametersNative( int advertiserId, PeriodicAdvertisingParameters parameters)406 private native void setPeriodicAdvertisingParametersNative( 407 int advertiserId, PeriodicAdvertisingParameters parameters); setPeriodicAdvertisingDataNative(int advertiserId, byte[] data)408 private native void setPeriodicAdvertisingDataNative(int advertiserId, byte[] data); setPeriodicAdvertisingEnableNative(int advertiserId, boolean enable)409 private native void setPeriodicAdvertisingEnableNative(int advertiserId, boolean enable); 410 } 411