AdvertiseManager.java revision 7f4a31f44f61bf765dad1d3b570e314fa1a98f85
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 logd("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 logd("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 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(reg_id); 144 145 if (entry == null) { 146 Log.i(TAG, "onAdvertisingSetStarted() - no callback found for reg_id " + reg_id); 147 // Advertising set was stopped before it was properly registered. 148 stopAdvertisingSetNative(advertiser_id); 149 return; 150 } 151 152 IAdvertisingSetCallback callback = entry.getValue().callback; 153 if (status == 0) { 154 entry.setValue( 155 new AdvertiserInfo(advertiser_id, entry.getValue().deathRecipient, callback)); 156 } else { 157 IBinder binder = entry.getKey(); 158 binder.unlinkToDeath(entry.getValue().deathRecipient, 0); 159 mAdvertisers.remove(binder); 160 } 161 162 callback.onAdvertisingSetStarted(advertiser_id, tx_power, status); 163 } 164 165 void onAdvertisingEnabled(int advertiser_id, boolean enable, int status) throws Exception { 166 logd("onAdvertisingSetEnabled() - advertiser_id=" + advertiser_id + ", enable=" + enable 167 + ", status=" + status); 168 169 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiser_id); 170 if (entry == null) { 171 Log.i(TAG, "onAdvertisingSetEnable() - no callback found for advertiser_id " 172 + advertiser_id); 173 return; 174 } 175 176 IAdvertisingSetCallback callback = entry.getValue().callback; 177 callback.onAdvertisingEnabled(advertiser_id, enable, status); 178 } 179 180 void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, 181 AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, 182 AdvertiseData periodicData, int duration, int maxExtAdvEvents, 183 IAdvertisingSetCallback callback) { 184 AdvertisingSetDeathRecipient deathRecipient = new AdvertisingSetDeathRecipient(callback); 185 IBinder binder = toBinder(callback); 186 try { 187 binder.linkToDeath(deathRecipient, 0); 188 } catch (RemoteException e) { 189 throw new IllegalArgumentException("Can't link to advertiser's death"); 190 } 191 192 String deviceName = AdapterService.getAdapterService().getName(); 193 byte[] adv_data = AdvertiseHelper.advertiseDataToBytes(advertiseData, deviceName); 194 byte[] scan_response = AdvertiseHelper.advertiseDataToBytes(scanResponse, deviceName); 195 byte[] periodic_data = AdvertiseHelper.advertiseDataToBytes(periodicData, deviceName); 196 197 int cb_id = --sTempRegistrationId; 198 mAdvertisers.put(binder, new AdvertiserInfo(cb_id, deathRecipient, callback)); 199 200 logd("startAdvertisingSet() - reg_id=" + cb_id + ", callback: " + binder); 201 startAdvertisingSetNative(parameters, adv_data, scan_response, periodicParameters, 202 periodic_data, duration, maxExtAdvEvents, cb_id); 203 } 204 205 void stopAdvertisingSet(IAdvertisingSetCallback callback) { 206 IBinder binder = toBinder(callback); 207 if (DBG) Log.d(TAG, "stopAdvertisingSet() " + binder); 208 209 AdvertiserInfo adv = mAdvertisers.remove(binder); 210 if (adv == null) { 211 Log.e(TAG, "stopAdvertisingSet() - no client found for callback"); 212 return; 213 } 214 215 Integer advertiser_id = adv.id; 216 binder.unlinkToDeath(adv.deathRecipient, 0); 217 218 if (advertiser_id < 0) { 219 Log.i(TAG, "stopAdvertisingSet() - advertiser not finished registration yet"); 220 // Advertiser will be freed once initiated in onAdvertisingSetStarted() 221 return; 222 } 223 224 stopAdvertisingSetNative(advertiser_id); 225 } 226 227 void enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents) { 228 enableAdvertisingSetNative(advertiserId, enable, duration, maxExtAdvEvents); 229 } 230 231 void setAdvertisingData(int advertiserId, AdvertiseData data) { 232 String deviceName = AdapterService.getAdapterService().getName(); 233 setAdvertisingDataNative( 234 advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 235 } 236 237 void setScanResponseData(int advertiserId, AdvertiseData data) { 238 String deviceName = AdapterService.getAdapterService().getName(); 239 setScanResponseDataNative( 240 advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 241 } 242 243 void setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters) { 244 setAdvertisingParametersNative(advertiserId, parameters); 245 } 246 247 void setPeriodicAdvertisingParameters( 248 int advertiserId, PeriodicAdvertisingParameters parameters) { 249 setPeriodicAdvertisingParametersNative(advertiserId, parameters); 250 } 251 252 void setPeriodicAdvertisingData(int advertiserId, AdvertiseData data) { 253 String deviceName = AdapterService.getAdapterService().getName(); 254 setPeriodicAdvertisingDataNative( 255 advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName)); 256 } 257 258 void setPeriodicAdvertisingEnable(int advertiserId, boolean enable) { 259 setPeriodicAdvertisingEnableNative(advertiserId, enable); 260 } 261 262 void onAdvertisingDataSet(int advertiser_id, int status) throws Exception { 263 logd("onAdvertisingDataSet() advertiser_id=" + advertiser_id + ", status=" + status); 264 265 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiser_id); 266 if (entry == null) { 267 Log.i(TAG, "onAdvertisingDataSet() - bad advertiser_id " + advertiser_id); 268 return; 269 } 270 271 IAdvertisingSetCallback callback = entry.getValue().callback; 272 callback.onAdvertisingDataSet(advertiser_id, status); 273 } 274 275 void onScanResponseDataSet(int advertiser_id, int status) throws Exception { 276 logd("onScanResponseDataSet() advertiser_id=" + advertiser_id + ", status=" + status); 277 278 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiser_id); 279 if (entry == null) { 280 Log.i(TAG, "onScanResponseDataSet() - bad advertiser_id " + advertiser_id); 281 return; 282 } 283 284 IAdvertisingSetCallback callback = entry.getValue().callback; 285 callback.onScanResponseDataSet(advertiser_id, status); 286 } 287 288 void onAdvertisingParametersUpdated(int advertiser_id, int tx_power, int status) 289 throws Exception { 290 logd("onAdvertisingParametersUpdated() advertiser_id=" + advertiser_id + ", tx_power=" 291 + tx_power + ", status=" + status); 292 293 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiser_id); 294 if (entry == null) { 295 Log.i(TAG, "onAdvertisingParametersUpdated() - bad advertiser_id " + advertiser_id); 296 return; 297 } 298 299 IAdvertisingSetCallback callback = entry.getValue().callback; 300 callback.onAdvertisingParametersUpdated(advertiser_id, tx_power, status); 301 } 302 303 void onPeriodicAdvertisingParametersUpdated(int advertiser_id, int status) throws Exception { 304 logd("onPeriodicAdvertisingParametersUpdated() advertiser_id=" + advertiser_id + ", status=" 305 + status); 306 307 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiser_id); 308 if (entry == null) { 309 Log.i(TAG, "onPeriodicAdvertisingParametersUpdated() - bad advertiser_id " 310 + advertiser_id); 311 return; 312 } 313 314 IAdvertisingSetCallback callback = entry.getValue().callback; 315 callback.onPeriodicAdvertisingParametersUpdated(advertiser_id, status); 316 } 317 318 void onPeriodicAdvertisingDataSet(int advertiser_id, int status) throws Exception { 319 logd("onPeriodicAdvertisingDataSet() advertiser_id=" + advertiser_id + ", status=" 320 + status); 321 322 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiser_id); 323 if (entry == null) { 324 Log.i(TAG, "onPeriodicAdvertisingDataSet() - bad advertiser_id " + advertiser_id); 325 return; 326 } 327 328 IAdvertisingSetCallback callback = entry.getValue().callback; 329 callback.onPeriodicAdvertisingDataSet(advertiser_id, status); 330 } 331 332 void onPeriodicAdvertisingEnabled(int advertiser_id, boolean enable, int status) 333 throws Exception { 334 logd("onPeriodicAdvertisingEnabled() advertiser_id=" + advertiser_id + ", status=" 335 + status); 336 337 Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiser_id); 338 if (entry == null) { 339 Log.i(TAG, "onAdvertisingSetEnable() - bad advertiser_id " + advertiser_id); 340 return; 341 } 342 343 IAdvertisingSetCallback callback = entry.getValue().callback; 344 callback.onPeriodicAdvertisingEnabled(advertiser_id, enable, status); 345 } 346 347 private void logd(String s) { 348 if (DBG) { 349 Log.d(TAG, s); 350 } 351 } 352 353 static { 354 classInitNative(); 355 } 356 357 private native static void classInitNative(); 358 private native void initializeNative(); 359 private native void cleanupNative(); 360 private native void startAdvertisingSetNative(AdvertisingSetParameters parameters, 361 byte[] advertiseData, byte[] scanResponse, 362 PeriodicAdvertisingParameters periodicParameters, byte[] periodicData, int duration, 363 int maxExtAdvEvents, int reg_id); 364 private native void stopAdvertisingSetNative(int advertiser_id); 365 private native void enableAdvertisingSetNative( 366 int advertiserId, boolean enable, int duration, int maxExtAdvEvents); 367 private native void setAdvertisingDataNative(int advertiserId, byte[] data); 368 private native void setScanResponseDataNative(int advertiserId, byte[] data); 369 private native void setAdvertisingParametersNative( 370 int advertiserId, AdvertisingSetParameters parameters); 371 private native void setPeriodicAdvertisingParametersNative( 372 int advertiserId, PeriodicAdvertisingParameters parameters); 373 private native void setPeriodicAdvertisingDataNative(int advertiserId, byte[] data); 374 private native void setPeriodicAdvertisingEnableNative(int advertiserId, boolean enable); 375} 376