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