AdvertiseManager.java revision 4d6c42f3481f1ef7657b10f1ba5198216b67c498
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    // Message for advertising operations.
45    private static final int MSG_START_ADVERTISING = 0;
46    private static final int MSG_STOP_ADVERTISING = 1;
47
48    private final GattService mService;
49    private final AdapterService mAdapterService;
50    private final Set<AdvertiseClient> mAdvertiseClients;
51    private final AdvertiseNative mAdvertiseNative;
52
53    // Handles advertise operations.
54    private ClientHandler mHandler;
55
56    /**
57     * Constructor of {@link AdvertiseManager}.
58     */
59    AdvertiseManager(GattService service, AdapterService adapterService) {
60        logd("advertise manager created");
61        mService = service;
62        mAdapterService = adapterService;
63        mAdvertiseClients = new HashSet<AdvertiseClient>();
64        mAdvertiseNative = new AdvertiseNative();
65    }
66
67    /**
68     * Start a {@link HandlerThread} that handles advertising operations.
69     */
70    void start() {
71        HandlerThread thread = new HandlerThread("BluetoothAdvertiseManager");
72        thread.start();
73        mHandler = new ClientHandler(thread.getLooper());
74    }
75
76    void cleanup() {
77        logd("advertise clients cleared");
78        mAdvertiseClients.clear();
79
80        if (mHandler != null) {
81            // Shut down the thread
82            mHandler.removeCallbacksAndMessages(null);
83            Looper looper = mHandler.getLooper();
84            if (looper != null) {
85                looper.quit();
86            }
87            mHandler = null;
88        }
89    }
90
91    void registerAdvertiser(UUID uuid) {
92        mAdvertiseNative.registerAdvertiserNative(
93            uuid.getLeastSignificantBits(), uuid.getMostSignificantBits());
94    }
95
96    void unregisterAdvertiser(int advertiserId) {
97        mAdvertiseNative.unregisterAdvertiserNative(advertiserId);
98    }
99
100    /**
101     * Start BLE advertising.
102     *
103     * @param client Advertise client.
104     */
105    void startAdvertising(AdvertiseClient client) {
106        if (client == null) {
107            return;
108        }
109        Message message = new Message();
110        message.what = MSG_START_ADVERTISING;
111        message.obj = client;
112        mHandler.sendMessage(message);
113    }
114
115    /**
116     * Stop BLE advertising.
117     */
118    void stopAdvertising(AdvertiseClient client) {
119        if (client == null) {
120            return;
121        }
122        Message message = new Message();
123        message.what = MSG_STOP_ADVERTISING;
124        message.obj = client;
125        mHandler.sendMessage(message);
126    }
127
128    // Post callback status to app process.
129    private void postCallback(int advertiserId, int status) {
130        try {
131            AdvertiseClient client = getAdvertiseClient(advertiserId);
132            AdvertiseSettings settings = (client == null) ? null : client.settings;
133            boolean isStart = true;
134            mService.onMultipleAdvertiseCallback(advertiserId, status, isStart, settings);
135        } catch (RemoteException e) {
136            loge("failed onMultipleAdvertiseCallback", e);
137        }
138    }
139
140    public AdvertiseClient getAdvertiseClient(int advertiserId) {
141        for (AdvertiseClient client : mAdvertiseClients) {
142            if (client.advertiserId == advertiserId) {
143                return client;
144            }
145        }
146        return null;
147    }
148
149    // Handler class that handles BLE advertising operations.
150    private class ClientHandler extends Handler {
151
152        ClientHandler(Looper looper) {
153            super(looper);
154        }
155
156        @Override
157        public void handleMessage(Message msg) {
158            logd("message : " + msg.what);
159            AdvertiseClient client = (AdvertiseClient) msg.obj;
160            switch (msg.what) {
161                case MSG_START_ADVERTISING:
162                    handleStartAdvertising(client);
163                    break;
164                case MSG_STOP_ADVERTISING:
165                    handleStopAdvertising(client);
166                    break;
167                default:
168                    // Shouldn't happen.
169                    Log.e(TAG, "recieve an unknown message : " + msg.what);
170                    break;
171            }
172        }
173
174        private void handleStartAdvertising(AdvertiseClient client) {
175            Utils.enforceAdminPermission(mService);
176            int advertiserId = client.advertiserId;
177            if (mAdvertiseClients.contains(client)) {
178                postCallback(advertiserId, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
179                return;
180            }
181
182            mAdvertiseNative.startAdverising(client);
183            mAdvertiseClients.add(client);
184        }
185
186        // Handles stop advertising.
187        private void handleStopAdvertising(AdvertiseClient client) {
188            Utils.enforceAdminPermission(mService);
189            if (client == null) {
190                return;
191            }
192            logd("stop advertise for client " + client.advertiserId);
193            mAdvertiseNative.stopAdvertising(client);
194            if (client.appDied) {
195                logd("app died - unregistering client : " + client.advertiserId);
196                mAdvertiseNative.unregisterAdvertiserNative(client.advertiserId);
197            }
198            if (mAdvertiseClients.contains(client)) {
199                mAdvertiseClients.remove(client);
200            }
201        }
202    }
203
204    // Class that wraps advertise native related constants, methods etc.
205    private class AdvertiseNative {
206
207        // Add some randomness to the advertising min/max interval so the controller can do some
208        // optimization.
209        private static final int ADVERTISING_INTERVAL_DELTA_UNIT = 10;
210
211        // The following constants should be kept the same as those defined in bt stack.
212        private static final int ADVERTISING_CHANNEL_37 = 1 << 0;
213        private static final int ADVERTISING_CHANNEL_38 = 1 << 1;
214        private static final int ADVERTISING_CHANNEL_39 = 1 << 2;
215        private static final int ADVERTISING_CHANNEL_ALL =
216            ADVERTISING_CHANNEL_37 | ADVERTISING_CHANNEL_38 | ADVERTISING_CHANNEL_39;
217
218        void startAdverising(AdvertiseClient client) {
219            logd("starting advertising");
220
221            int advertiserId = client.advertiserId;
222            int minAdvertiseUnit = (int) AdvertiseHelper.getAdvertisingIntervalUnit(client.settings);
223            int maxAdvertiseUnit = minAdvertiseUnit + ADVERTISING_INTERVAL_DELTA_UNIT;
224            int advertiseEventType = AdvertiseHelper.getAdvertisingEventType(client);
225            int txPowerLevel = AdvertiseHelper.getTxPowerLevel(client.settings);
226
227            byte [] adv_data = AdvertiseHelper.advertiseDataToBytes(client.advertiseData,
228                                                                    mAdapterService.getName());
229            byte [] scan_resp_data = AdvertiseHelper.advertiseDataToBytes(client.scanResponse,
230                                                                          mAdapterService.getName());
231
232            int advertiseTimeoutSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(
233                    client.settings.getTimeout());
234
235            startAdvertiserNative(advertiserId, minAdvertiseUnit, maxAdvertiseUnit,
236                    advertiseEventType, ADVERTISING_CHANNEL_ALL, txPowerLevel, adv_data,
237                    scan_resp_data, advertiseTimeoutSeconds);
238        }
239
240        void stopAdvertising(AdvertiseClient client) {
241            gattClientEnableAdvNative(client.advertiserId, false, 0);
242        }
243
244        // Native functions
245        private native void registerAdvertiserNative(long app_uuid_lsb,
246                                                     long app_uuid_msb);
247
248        private native void unregisterAdvertiserNative(int advertiserId);
249
250        private native void gattClientEnableAdvNative(int advertiserId,
251                boolean enable, int timeout_s);
252
253        private native void startAdvertiserNative(int advertiserId,
254                int min_interval, int max_interval, int adv_type, int chnl_map, int tx_power,
255                byte[] adv_data, byte[] scan_resp_data, int timeout_s);
256
257    }
258
259    private void logd(String s) {
260        if (DBG) {
261            Log.d(TAG, s);
262        }
263    }
264
265    private void loge(String s, Exception e) {
266        Log.e(TAG, s, e);
267    }
268
269}
270