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