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