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