AdvertiseManager.java revision 792b3b63ea8109fb0d30135e9249256f3d891d9e
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 248 void enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents) { 249 enableAdvertisingSetNative(advertiserId, enable, duration, maxExtAdvEvents); 250 } 251 252 void setAdvertisingData(int advertiserId, AdvertiseData data) { 253 String deviceName = AdapterService.getAdapterService().getName(); 254 setAdvertisingDataNative( 255 advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 256 } 257 258 void setScanResponseData(int advertiserId, AdvertiseData data) { 259 String deviceName = AdapterService.getAdapterService().getName(); 260 setScanResponseDataNative( 261 advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 262 } 263 264 void setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters) { 265 setAdvertisingParametersNative(advertiserId, parameters); 266 } 267 268 void setPeriodicAdvertisingParameters( 269 int advertiserId, PeriodicAdvertisingParameters parameters) { 270 setPeriodicAdvertisingParametersNative(advertiserId, parameters); 271 } 272 273 void setPeriodicAdvertisingData(int advertiserId, AdvertiseData data) { 274 String deviceName = AdapterService.getAdapterService().getName(); 275 setPeriodicAdvertisingDataNative( 276 advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 277 } 278 279 void setPeriodicAdvertisingEnable(int advertiserId, boolean enable) { 280 setPeriodicAdvertisingEnableNative(advertiserId, enable); 281 } 282 283 void onAdvertisingDataSet(int advertiser_id, int status) throws Exception { 284 if (DBG) { 285 Log.d(TAG, 286 "onAdvertisingDataSet() advertiser_id=" + advertiser_id + ", status=" + status); 287 } 288 289 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiser_id); 290 if (entry == null) { 291 Log.i(TAG, "onAdvertisingDataSet() - bad advertiser_id " + advertiser_id); 292 return; 293 } 294 295 IAdvertisingSetCallback callback = entry.getValue().callback; 296 callback.onAdvertisingDataSet(advertiser_id, status); 297 } 298 299 void onScanResponseDataSet(int advertiser_id, int status) throws Exception { 300 if (DBG) 301 Log.d(TAG, "onScanResponseDataSet() advertiser_id=" + advertiser_id + ", status=" 302 + status); 303 304 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiser_id); 305 if (entry == null) { 306 Log.i(TAG, "onScanResponseDataSet() - bad advertiser_id " + advertiser_id); 307 return; 308 } 309 310 IAdvertisingSetCallback callback = entry.getValue().callback; 311 callback.onScanResponseDataSet(advertiser_id, status); 312 } 313 314 void onAdvertisingParametersUpdated(int advertiser_id, int tx_power, int status) 315 throws Exception { 316 if (DBG) { 317 Log.d(TAG, "onAdvertisingParametersUpdated() advertiser_id=" + advertiser_id 318 + ", tx_power=" + tx_power + ", status=" + status); 319 } 320 321 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiser_id); 322 if (entry == null) { 323 Log.i(TAG, "onAdvertisingParametersUpdated() - bad advertiser_id " + advertiser_id); 324 return; 325 } 326 327 IAdvertisingSetCallback callback = entry.getValue().callback; 328 callback.onAdvertisingParametersUpdated(advertiser_id, tx_power, status); 329 } 330 331 void onPeriodicAdvertisingParametersUpdated(int advertiser_id, int status) throws Exception { 332 if (DBG) { 333 Log.d(TAG, "onPeriodicAdvertisingParametersUpdated() advertiser_id=" + advertiser_id 334 + ", status=" + status); 335 } 336 337 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiser_id); 338 if (entry == null) { 339 Log.i(TAG, "onPeriodicAdvertisingParametersUpdated() - bad advertiser_id " 340 + advertiser_id); 341 return; 342 } 343 344 IAdvertisingSetCallback callback = entry.getValue().callback; 345 callback.onPeriodicAdvertisingParametersUpdated(advertiser_id, status); 346 } 347 348 void onPeriodicAdvertisingDataSet(int advertiser_id, int status) throws Exception { 349 if (DBG) { 350 Log.d(TAG, "onPeriodicAdvertisingDataSet() advertiser_id=" + advertiser_id + ", status=" 351 + status); 352 } 353 354 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiser_id); 355 if (entry == null) { 356 Log.i(TAG, "onPeriodicAdvertisingDataSet() - bad advertiser_id " + advertiser_id); 357 return; 358 } 359 360 IAdvertisingSetCallback callback = entry.getValue().callback; 361 callback.onPeriodicAdvertisingDataSet(advertiser_id, status); 362 } 363 364 void onPeriodicAdvertisingEnabled(int advertiser_id, boolean enable, int status) 365 throws Exception { 366 if (DBG) { 367 Log.d(TAG, "onPeriodicAdvertisingEnabled() advertiser_id=" + advertiser_id + ", status=" 368 + status); 369 } 370 371 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiser_id); 372 if (entry == null) { 373 Log.i(TAG, "onAdvertisingSetEnable() - bad advertiser_id " + advertiser_id); 374 return; 375 } 376 377 IAdvertisingSetCallback callback = entry.getValue().callback; 378 callback.onPeriodicAdvertisingEnabled(advertiser_id, enable, status); 379 } 380 381 static { 382 classInitNative(); 383 } 384 385 private native static void classInitNative(); 386 private native void initializeNative(); 387 private native void cleanupNative(); 388 private native void startAdvertisingSetNative(AdvertisingSetParameters parameters, 389 byte[] advertiseData, byte[] scanResponse, 390 PeriodicAdvertisingParameters periodicParameters, byte[] periodicData, int duration, 391 int maxExtAdvEvents, int reg_id); 392 private native void getOwnAddressNative(int advertiserId); 393 private native void stopAdvertisingSetNative(int advertiser_id); 394 private native void enableAdvertisingSetNative( 395 int advertiserId, boolean enable, int duration, int maxExtAdvEvents); 396 private native void setAdvertisingDataNative(int advertiserId, byte[] data); 397 private native void setScanResponseDataNative(int advertiserId, byte[] data); 398 private native void setAdvertisingParametersNative( 399 int advertiserId, AdvertisingSetParameters parameters); 400 private native void setPeriodicAdvertisingParametersNative( 401 int advertiserId, PeriodicAdvertisingParameters parameters); 402 private native void setPeriodicAdvertisingDataNative(int advertiserId, byte[] data); 403 private native void setPeriodicAdvertisingEnableNative(int advertiserId, boolean enable); 404} 405