• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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