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.i(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         getOwnAddressNative(advertiserId);
230     }
231 
stopAdvertisingSet(IAdvertisingSetCallback callback)232     void stopAdvertisingSet(IAdvertisingSetCallback callback) {
233         IBinder binder = toBinder(callback);
234         if (DBG) {
235             Log.d(TAG, "stopAdvertisingSet() " + binder);
236         }
237 
238         AdvertiserInfo adv = mAdvertisers.remove(binder);
239         if (adv == null) {
240             Log.e(TAG, "stopAdvertisingSet() - no client found for callback");
241             return;
242         }
243 
244         Integer advertiserId = adv.id;
245         binder.unlinkToDeath(adv.deathRecipient, 0);
246 
247         if (advertiserId < 0) {
248             Log.i(TAG, "stopAdvertisingSet() - advertiser not finished registration yet");
249             // Advertiser will be freed once initiated in onAdvertisingSetStarted()
250             return;
251         }
252 
253         stopAdvertisingSetNative(advertiserId);
254 
255         try {
256             callback.onAdvertisingSetStopped(advertiserId);
257         } catch (RemoteException e) {
258             Log.i(TAG, "error sending onAdvertisingSetStopped callback", e);
259         }
260     }
261 
enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents)262     void enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents) {
263         enableAdvertisingSetNative(advertiserId, enable, duration, maxExtAdvEvents);
264     }
265 
setAdvertisingData(int advertiserId, AdvertiseData data)266     void setAdvertisingData(int advertiserId, AdvertiseData data) {
267         String deviceName = AdapterService.getAdapterService().getName();
268         setAdvertisingDataNative(advertiserId,
269                 AdvertiseHelper.advertiseDataToBytes(data, deviceName));
270     }
271 
setScanResponseData(int advertiserId, AdvertiseData data)272     void setScanResponseData(int advertiserId, AdvertiseData data) {
273         String deviceName = AdapterService.getAdapterService().getName();
274         setScanResponseDataNative(advertiserId,
275                 AdvertiseHelper.advertiseDataToBytes(data, deviceName));
276     }
277 
setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters)278     void setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters) {
279         setAdvertisingParametersNative(advertiserId, parameters);
280     }
281 
setPeriodicAdvertisingParameters(int advertiserId, PeriodicAdvertisingParameters parameters)282     void setPeriodicAdvertisingParameters(int advertiserId,
283             PeriodicAdvertisingParameters parameters) {
284         setPeriodicAdvertisingParametersNative(advertiserId, parameters);
285     }
286 
setPeriodicAdvertisingData(int advertiserId, AdvertiseData data)287     void setPeriodicAdvertisingData(int advertiserId, AdvertiseData data) {
288         String deviceName = AdapterService.getAdapterService().getName();
289         setPeriodicAdvertisingDataNative(advertiserId,
290                 AdvertiseHelper.advertiseDataToBytes(data, deviceName));
291     }
292 
setPeriodicAdvertisingEnable(int advertiserId, boolean enable)293     void setPeriodicAdvertisingEnable(int advertiserId, boolean enable) {
294         setPeriodicAdvertisingEnableNative(advertiserId, enable);
295     }
296 
onAdvertisingDataSet(int advertiserId, int status)297     void onAdvertisingDataSet(int advertiserId, int status) throws Exception {
298         if (DBG) {
299             Log.d(TAG,
300                     "onAdvertisingDataSet() advertiserId=" + advertiserId + ", status=" + status);
301         }
302 
303         Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
304         if (entry == null) {
305             Log.i(TAG, "onAdvertisingDataSet() - bad advertiserId " + advertiserId);
306             return;
307         }
308 
309         IAdvertisingSetCallback callback = entry.getValue().callback;
310         callback.onAdvertisingDataSet(advertiserId, status);
311     }
312 
onScanResponseDataSet(int advertiserId, int status)313     void onScanResponseDataSet(int advertiserId, int status) throws Exception {
314         if (DBG) {
315             Log.d(TAG,
316                     "onScanResponseDataSet() advertiserId=" + advertiserId + ", status=" + status);
317         }
318 
319         Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
320         if (entry == null) {
321             Log.i(TAG, "onScanResponseDataSet() - bad advertiserId " + advertiserId);
322             return;
323         }
324 
325         IAdvertisingSetCallback callback = entry.getValue().callback;
326         callback.onScanResponseDataSet(advertiserId, status);
327     }
328 
onAdvertisingParametersUpdated(int advertiserId, int txPower, int status)329     void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status)
330             throws Exception {
331         if (DBG) {
332             Log.d(TAG,
333                     "onAdvertisingParametersUpdated() advertiserId=" + advertiserId + ", txPower="
334                             + txPower + ", status=" + status);
335         }
336 
337         Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
338         if (entry == null) {
339             Log.i(TAG, "onAdvertisingParametersUpdated() - bad advertiserId " + advertiserId);
340             return;
341         }
342 
343         IAdvertisingSetCallback callback = entry.getValue().callback;
344         callback.onAdvertisingParametersUpdated(advertiserId, txPower, status);
345     }
346 
onPeriodicAdvertisingParametersUpdated(int advertiserId, int status)347     void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) throws Exception {
348         if (DBG) {
349             Log.d(TAG, "onPeriodicAdvertisingParametersUpdated() advertiserId=" + advertiserId
350                     + ", status=" + status);
351         }
352 
353         Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
354         if (entry == null) {
355             Log.i(TAG,
356                     "onPeriodicAdvertisingParametersUpdated() - bad advertiserId " + advertiserId);
357             return;
358         }
359 
360         IAdvertisingSetCallback callback = entry.getValue().callback;
361         callback.onPeriodicAdvertisingParametersUpdated(advertiserId, status);
362     }
363 
onPeriodicAdvertisingDataSet(int advertiserId, int status)364     void onPeriodicAdvertisingDataSet(int advertiserId, int status) throws Exception {
365         if (DBG) {
366             Log.d(TAG, "onPeriodicAdvertisingDataSet() advertiserId=" + advertiserId + ", status="
367                     + status);
368         }
369 
370         Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
371         if (entry == null) {
372             Log.i(TAG, "onPeriodicAdvertisingDataSet() - bad advertiserId " + advertiserId);
373             return;
374         }
375 
376         IAdvertisingSetCallback callback = entry.getValue().callback;
377         callback.onPeriodicAdvertisingDataSet(advertiserId, status);
378     }
379 
onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status)380     void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status)
381             throws Exception {
382         if (DBG) {
383             Log.d(TAG, "onPeriodicAdvertisingEnabled() advertiserId=" + advertiserId + ", status="
384                     + status);
385         }
386 
387         Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
388         if (entry == null) {
389             Log.i(TAG, "onAdvertisingSetEnable() - bad advertiserId " + advertiserId);
390             return;
391         }
392 
393         IAdvertisingSetCallback callback = entry.getValue().callback;
394         callback.onPeriodicAdvertisingEnabled(advertiserId, enable, status);
395     }
396 
397     static {
classInitNative()398         classInitNative();
399     }
400 
classInitNative()401     private static native void classInitNative();
402 
initializeNative()403     private native void initializeNative();
404 
cleanupNative()405     private native void cleanupNative();
406 
startAdvertisingSetNative(AdvertisingSetParameters parameters, byte[] advertiseData, byte[] scanResponse, PeriodicAdvertisingParameters periodicParameters, byte[] periodicData, int duration, int maxExtAdvEvents, int regId)407     private native void startAdvertisingSetNative(AdvertisingSetParameters parameters,
408             byte[] advertiseData, byte[] scanResponse,
409             PeriodicAdvertisingParameters periodicParameters, byte[] periodicData, int duration,
410             int maxExtAdvEvents, int regId);
411 
getOwnAddressNative(int advertiserId)412     private native void getOwnAddressNative(int advertiserId);
413 
stopAdvertisingSetNative(int advertiserId)414     private native void stopAdvertisingSetNative(int advertiserId);
415 
enableAdvertisingSetNative(int advertiserId, boolean enable, int duration, int maxExtAdvEvents)416     private native void enableAdvertisingSetNative(int advertiserId, boolean enable, int duration,
417             int maxExtAdvEvents);
418 
setAdvertisingDataNative(int advertiserId, byte[] data)419     private native void setAdvertisingDataNative(int advertiserId, byte[] data);
420 
setScanResponseDataNative(int advertiserId, byte[] data)421     private native void setScanResponseDataNative(int advertiserId, byte[] data);
422 
setAdvertisingParametersNative(int advertiserId, AdvertisingSetParameters parameters)423     private native void setAdvertisingParametersNative(int advertiserId,
424             AdvertisingSetParameters parameters);
425 
setPeriodicAdvertisingParametersNative(int advertiserId, PeriodicAdvertisingParameters parameters)426     private native void setPeriodicAdvertisingParametersNative(int advertiserId,
427             PeriodicAdvertisingParameters parameters);
428 
setPeriodicAdvertisingDataNative(int advertiserId, byte[] data)429     private native void setPeriodicAdvertisingDataNative(int advertiserId, byte[] data);
430 
setPeriodicAdvertisingEnableNative(int advertiserId, boolean enable)431     private native void setPeriodicAdvertisingEnableNative(int advertiserId, boolean enable);
432 }
433