AdvertiseManager.java revision 335fb7b425b0a585c271ae34901ea443ec88be38
1/*
2 * Copyright (C) 2014 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.AdvertiseCallback;
20import android.bluetooth.le.AdvertiseSettings;
21import android.os.Handler;
22import android.os.HandlerThread;
23import android.os.Looper;
24import android.os.Message;
25import android.os.RemoteException;
26import android.util.Log;
27import com.android.bluetooth.Utils;
28import com.android.bluetooth.btservice.AdapterService;
29import java.util.HashSet;
30import java.util.Set;
31import java.util.UUID;
32import java.util.concurrent.CountDownLatch;
33import java.util.concurrent.TimeUnit;
34
35/**
36 * Manages Bluetooth LE advertising operations and interacts with bluedroid stack. TODO: add tests.
37 *
38 * @hide
39 */
40class AdvertiseManager {
41    private static final boolean DBG = GattServiceConfig.DBG;
42    private static final String TAG = GattServiceConfig.TAG_PREFIX + "AdvertiseManager";
43
44    // Timeout for each controller operation.
45    private static final int OPERATION_TIME_OUT_MILLIS = 500;
46
47    // Message for advertising operations.
48    private static final int MSG_START_ADVERTISING = 0;
49    private static final int MSG_STOP_ADVERTISING = 1;
50
51    private final GattService mService;
52    private final AdapterService mAdapterService;
53    private final Set<AdvertiseClient> mAdvertiseClients;
54    private final AdvertiseNative mAdvertiseNative;
55
56    // Handles advertise operations.
57    private ClientHandler mHandler;
58
59    // CountDownLatch for blocking advertise operations.
60    private CountDownLatch mLatch;
61
62    /**
63     * Constructor of {@link AdvertiseManager}.
64     */
65    AdvertiseManager(GattService service, AdapterService adapterService) {
66        logd("advertise manager created");
67        mService = service;
68        mAdapterService = adapterService;
69        mAdvertiseClients = new HashSet<AdvertiseClient>();
70        mAdvertiseNative = new AdvertiseNative();
71    }
72
73    /**
74     * Start a {@link HandlerThread} that handles advertising operations.
75     */
76    void start() {
77        HandlerThread thread = new HandlerThread("BluetoothAdvertiseManager");
78        thread.start();
79        mHandler = new ClientHandler(thread.getLooper());
80    }
81
82    void cleanup() {
83        logd("advertise clients cleared");
84        mAdvertiseClients.clear();
85
86        if (mHandler != null) {
87            // Shut down the thread
88            mHandler.removeCallbacksAndMessages(null);
89            Looper looper = mHandler.getLooper();
90            if (looper != null) {
91                looper.quit();
92            }
93            mHandler = null;
94        }
95    }
96
97    void registerAdvertiser(UUID uuid) {
98        mAdvertiseNative.registerAdvertiserNative(
99            uuid.getLeastSignificantBits(), uuid.getMostSignificantBits());
100    }
101
102    void unregisterAdvertiser(int advertiserId) {
103        mAdvertiseNative.unregisterAdvertiserNative(advertiserId);
104    }
105
106    /**
107     * Start BLE advertising.
108     *
109     * @param client Advertise client.
110     */
111    void startAdvertising(AdvertiseClient client) {
112        if (client == null) {
113            return;
114        }
115        Message message = new Message();
116        message.what = MSG_START_ADVERTISING;
117        message.obj = client;
118        mHandler.sendMessage(message);
119    }
120
121    /**
122     * Stop BLE advertising.
123     */
124    void stopAdvertising(AdvertiseClient client) {
125        if (client == null) {
126            return;
127        }
128        Message message = new Message();
129        message.what = MSG_STOP_ADVERTISING;
130        message.obj = client;
131        mHandler.sendMessage(message);
132    }
133
134    /**
135     * Signals the callback is received.
136     *
137     * @param advertiserId Identifier for the client.
138     * @param status Status of the callback.
139     */
140    void callbackDone(int advertiserId, int status) {
141        if (status == AdvertiseCallback.ADVERTISE_SUCCESS) {
142            mLatch.countDown();
143        } else {
144            // Note in failure case we'll wait for the latch to timeout(which takes 100ms) and
145            // the mClientHandler thread will be blocked till timeout.
146            postCallback(advertiserId, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
147        }
148    }
149
150    // Post callback status to app process.
151    private void postCallback(int advertiserId, int status) {
152        try {
153            AdvertiseClient client = getAdvertiseClient(advertiserId);
154            AdvertiseSettings settings = (client == null) ? null : client.settings;
155            boolean isStart = true;
156            mService.onMultipleAdvertiseCallback(advertiserId, status, isStart, settings);
157        } catch (RemoteException e) {
158            loge("failed onMultipleAdvertiseCallback", e);
159        }
160    }
161
162    private AdvertiseClient getAdvertiseClient(int advertiserId) {
163        for (AdvertiseClient client : mAdvertiseClients) {
164            if (client.advertiserId == advertiserId) {
165                return client;
166            }
167        }
168        return null;
169    }
170
171    // Handler class that handles BLE advertising operations.
172    private class ClientHandler extends Handler {
173
174        ClientHandler(Looper looper) {
175            super(looper);
176        }
177
178        @Override
179        public void handleMessage(Message msg) {
180            logd("message : " + msg.what);
181            AdvertiseClient client = (AdvertiseClient) msg.obj;
182            switch (msg.what) {
183                case MSG_START_ADVERTISING:
184                    handleStartAdvertising(client);
185                    break;
186                case MSG_STOP_ADVERTISING:
187                    handleStopAdvertising(client);
188                    break;
189                default:
190                    // Shouldn't happen.
191                    Log.e(TAG, "recieve an unknown message : " + msg.what);
192                    break;
193            }
194        }
195
196        private void handleStartAdvertising(AdvertiseClient client) {
197            Utils.enforceAdminPermission(mService);
198            int advertiserId = client.advertiserId;
199            if (mAdvertiseClients.contains(client)) {
200                postCallback(advertiserId, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
201                return;
202            }
203
204            if (mAdvertiseClients.size() >= maxAdvertiseInstances()) {
205                postCallback(advertiserId,
206                        AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS);
207                return;
208            }
209            if (!mAdvertiseNative.startAdverising(client)) {
210                postCallback(advertiserId, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
211                return;
212            }
213
214            mAdvertiseClients.add(client);
215        }
216
217        // Handles stop advertising.
218        private void handleStopAdvertising(AdvertiseClient client) {
219            Utils.enforceAdminPermission(mService);
220            if (client == null) {
221                return;
222            }
223            logd("stop advertise for client " + client.advertiserId);
224            mAdvertiseNative.stopAdvertising(client);
225            if (client.appDied) {
226                logd("app died - unregistering client : " + client.advertiserId);
227                mAdvertiseNative.unregisterAdvertiserNative(client.advertiserId);
228            }
229            if (mAdvertiseClients.contains(client)) {
230                mAdvertiseClients.remove(client);
231            }
232        }
233
234        // Returns maximum advertise instances supported by controller.
235        int maxAdvertiseInstances() {
236            return mAdapterService.getNumOfAdvertisementInstancesSupported();
237        }
238    }
239
240    // Class that wraps advertise native related constants, methods etc.
241    private class AdvertiseNative {
242
243        // Add some randomness to the advertising min/max interval so the controller can do some
244        // optimization.
245        private static final int ADVERTISING_INTERVAL_DELTA_UNIT = 10;
246
247        // The following constants should be kept the same as those defined in bt stack.
248        private static final int ADVERTISING_CHANNEL_37 = 1 << 0;
249        private static final int ADVERTISING_CHANNEL_38 = 1 << 1;
250        private static final int ADVERTISING_CHANNEL_39 = 1 << 2;
251        private static final int ADVERTISING_CHANNEL_ALL =
252            ADVERTISING_CHANNEL_37 | ADVERTISING_CHANNEL_38 | ADVERTISING_CHANNEL_39;
253
254        boolean startAdverising(AdvertiseClient client) {
255            logd("starting advertising");
256
257            int advertiserId = client.advertiserId;
258            int minAdvertiseUnit = (int) AdvertiseHelper.getAdvertisingIntervalUnit(client.settings);
259            int maxAdvertiseUnit = minAdvertiseUnit + ADVERTISING_INTERVAL_DELTA_UNIT;
260            int advertiseEventType = AdvertiseHelper.getAdvertisingEventType(client);
261            int txPowerLevel = AdvertiseHelper.getTxPowerLevel(client.settings);
262
263            byte [] adv_data = AdvertiseHelper.advertiseDataToBytes(client.advertiseData,
264                                                                    mAdapterService.getName());
265            byte [] scan_resp_data = AdvertiseHelper.advertiseDataToBytes(client.scanResponse,
266                                                                          mAdapterService.getName());
267
268            int advertiseTimeoutSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(
269                    client.settings.getTimeout());
270
271            resetCountDownLatch();
272
273            startAdvertiserNative(advertiserId, minAdvertiseUnit, maxAdvertiseUnit,
274                    advertiseEventType, ADVERTISING_CHANNEL_ALL, txPowerLevel, adv_data,
275                    scan_resp_data, advertiseTimeoutSeconds);
276            if (!waitForCallback()) {
277                return false;
278            }
279
280            return true;
281        }
282
283        void stopAdvertising(AdvertiseClient client) {
284            gattClientEnableAdvNative(client.advertiserId, false, 0);
285        }
286
287        private void resetCountDownLatch() {
288            mLatch = new CountDownLatch(1);
289        }
290
291        // Returns true if mLatch reaches 0, false if timeout or interrupted.
292        private boolean waitForCallback() {
293            try {
294                return mLatch.await(OPERATION_TIME_OUT_MILLIS, TimeUnit.MILLISECONDS);
295            } catch (InterruptedException e) {
296                return false;
297            }
298        }
299
300        // Native functions
301
302        private native void registerAdvertiserNative(long app_uuid_lsb,
303                                                     long app_uuid_msb);
304
305        private native void unregisterAdvertiserNative(int advertiserId);
306
307        private native void gattClientEnableAdvNative(int advertiserId,
308                boolean enable, int timeout_s);
309
310        private native void startAdvertiserNative(int advertiserId,
311                int min_interval, int max_interval, int adv_type, int chnl_map, int tx_power,
312                byte[] adv_data, byte[] scan_resp_data, int timeout_s);
313
314    }
315
316    private void logd(String s) {
317        if (DBG) {
318            Log.d(TAG, s);
319        }
320    }
321
322    private void loge(String s, Exception e) {
323        Log.e(TAG, s, e);
324    }
325
326}
327