blob: 94d03e533dffa4fe45137568ee78d4a8258abd94 [file] [log] [blame]
Wei Wang6d811182014-05-22 12:10:25 -07001/*
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 android.bluetooth.le;
18
19import android.bluetooth.BluetoothAdapter;
20import android.bluetooth.BluetoothGatt;
Wei Wang0e81ca22014-07-23 20:33:31 -070021import android.bluetooth.BluetoothUuid;
Wei Wang6d811182014-05-22 12:10:25 -070022import android.bluetooth.IBluetoothGatt;
Wei Wang9fb17912014-07-01 15:10:06 -070023import android.bluetooth.IBluetoothManager;
Jakub Pawlowskia480f7f2016-08-05 06:40:31 -070024import android.bluetooth.le.IAdvertiserCallback;
Wei Wang6d811182014-05-22 12:10:25 -070025import android.os.Handler;
26import android.os.Looper;
27import android.os.ParcelUuid;
28import android.os.RemoteException;
29import android.util.Log;
30
31import java.util.HashMap;
32import java.util.Map;
33import java.util.UUID;
34
35/**
Wei Wangaf74e662014-07-09 14:03:42 -070036 * This class provides a way to perform Bluetooth LE advertise operations, such as starting and
37 * stopping advertising. An advertiser can broadcast up to 31 bytes of advertisement data
38 * represented by {@link AdvertiseData}.
Wei Wang6d811182014-05-22 12:10:25 -070039 * <p>
40 * To get an instance of {@link BluetoothLeAdvertiser}, call the
41 * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method.
42 * <p>
Wei Wangaf74e662014-07-09 14:03:42 -070043 * <b>Note:</b> Most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
Wei Wang6d811182014-05-22 12:10:25 -070044 * permission.
45 *
Wei Wangaf74e662014-07-09 14:03:42 -070046 * @see AdvertiseData
Wei Wang6d811182014-05-22 12:10:25 -070047 */
48public final class BluetoothLeAdvertiser {
49
50 private static final String TAG = "BluetoothLeAdvertiser";
51
Wei Wang0e81ca22014-07-23 20:33:31 -070052 private static final int MAX_ADVERTISING_DATA_BYTES = 31;
53 // Each fields need one byte for field length and another byte for field type.
54 private static final int OVERHEAD_BYTES_PER_FIELD = 2;
55 // Flags field will be set by system.
56 private static final int FLAGS_FIELD_BYTES = 3;
57 private static final int MANUFACTURER_SPECIFIC_DATA_LENGTH = 2;
58 private static final int SERVICE_DATA_UUID_LENGTH = 2;
59
Wei Wang9fb17912014-07-01 15:10:06 -070060 private final IBluetoothManager mBluetoothManager;
Wei Wang6d811182014-05-22 12:10:25 -070061 private final Handler mHandler;
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -070062 private BluetoothAdapter mBluetoothAdapter;
Wei Wang6d811182014-05-22 12:10:25 -070063 private final Map<AdvertiseCallback, AdvertiseCallbackWrapper>
64 mLeAdvertisers = new HashMap<AdvertiseCallback, AdvertiseCallbackWrapper>();
65
66 /**
67 * Use BluetoothAdapter.getLeAdvertiser() instead.
Wei Wang685c17582014-07-16 22:02:03 -070068 *
69 * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management
Wei Wang6d811182014-05-22 12:10:25 -070070 * @hide
71 */
Wei Wang9fb17912014-07-01 15:10:06 -070072 public BluetoothLeAdvertiser(IBluetoothManager bluetoothManager) {
73 mBluetoothManager = bluetoothManager;
Prerepa Viswanadham8e5270f2014-07-08 16:33:34 -070074 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Wei Wang6d811182014-05-22 12:10:25 -070075 mHandler = new Handler(Looper.getMainLooper());
76 }
77
78 /**
Wei Wang685c17582014-07-16 22:02:03 -070079 * Start Bluetooth LE Advertising. On success, the {@code advertiseData} will be broadcasted.
80 * Returns immediately, the operation status is delivered through {@code callback}.
Wei Wang6d811182014-05-22 12:10:25 -070081 * <p>
82 * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
83 *
84 * @param settings Settings for Bluetooth LE advertising.
85 * @param advertiseData Advertisement data to be broadcasted.
86 * @param callback Callback for advertising status.
87 */
88 public void startAdvertising(AdvertiseSettings settings,
Wei Wangaf74e662014-07-09 14:03:42 -070089 AdvertiseData advertiseData, final AdvertiseCallback callback) {
Wei Wang6d811182014-05-22 12:10:25 -070090 startAdvertising(settings, advertiseData, null, callback);
91 }
92
93 /**
Wei Wangaf74e662014-07-09 14:03:42 -070094 * Start Bluetooth LE Advertising. The {@code advertiseData} will be broadcasted if the
Wei Wang685c17582014-07-16 22:02:03 -070095 * operation succeeds. The {@code scanResponse} is returned when a scanning device sends an
96 * active scan request. This method returns immediately, the operation status is delivered
97 * through {@code callback}.
Wei Wang6d811182014-05-22 12:10:25 -070098 * <p>
99 * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
100 *
101 * @param settings Settings for Bluetooth LE advertising.
102 * @param advertiseData Advertisement data to be advertised in advertisement packet.
103 * @param scanResponse Scan response associated with the advertisement data.
104 * @param callback Callback for advertising status.
105 */
106 public void startAdvertising(AdvertiseSettings settings,
Wei Wangaf74e662014-07-09 14:03:42 -0700107 AdvertiseData advertiseData, AdvertiseData scanResponse,
Wei Wang6d811182014-05-22 12:10:25 -0700108 final AdvertiseCallback callback) {
Wei Wang9a974be2014-08-18 21:58:32 -0700109 synchronized (mLeAdvertisers) {
Wei Wang833559d2014-08-29 10:26:13 -0700110 BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
Wei Wang9a974be2014-08-18 21:58:32 -0700111 if (callback == null) {
112 throw new IllegalArgumentException("callback cannot be null");
Wei Wang6d811182014-05-22 12:10:25 -0700113 }
Tom Turney29230ce2014-11-12 16:26:41 -0800114 boolean isConnectable = settings.isConnectable();
115 if (totalBytes(advertiseData, isConnectable) > MAX_ADVERTISING_DATA_BYTES ||
Prerepa Viswanadhame77adab2015-01-16 10:40:11 -0800116 totalBytes(scanResponse, false) > MAX_ADVERTISING_DATA_BYTES) {
Wei Wang9a974be2014-08-18 21:58:32 -0700117 postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE);
118 return;
119 }
120 if (mLeAdvertisers.containsKey(callback)) {
121 postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
122 return;
123 }
124
125 IBluetoothGatt gatt;
126 try {
127 gatt = mBluetoothManager.getBluetoothGatt();
128 } catch (RemoteException e) {
129 Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
130 postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
131 return;
132 }
133 AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData,
134 scanResponse, settings, gatt);
135 wrapper.startRegisteration();
Wei Wang6d811182014-05-22 12:10:25 -0700136 }
137 }
138
139 /**
140 * Stop Bluetooth LE advertising. The {@code callback} must be the same one use in
141 * {@link BluetoothLeAdvertiser#startAdvertising}.
142 * <p>
143 * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
144 *
Wei Wangaf74e662014-07-09 14:03:42 -0700145 * @param callback {@link AdvertiseCallback} identifies the advertising instance to stop.
Wei Wang6d811182014-05-22 12:10:25 -0700146 */
147 public void stopAdvertising(final AdvertiseCallback callback) {
Wei Wang9a974be2014-08-18 21:58:32 -0700148 synchronized (mLeAdvertisers) {
Wei Wang9a974be2014-08-18 21:58:32 -0700149 if (callback == null) {
150 throw new IllegalArgumentException("callback cannot be null");
Wei Wang6d811182014-05-22 12:10:25 -0700151 }
Wei Wang9a974be2014-08-18 21:58:32 -0700152 AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(callback);
153 if (wrapper == null) return;
154 wrapper.stopAdvertising();
Wei Wang6d811182014-05-22 12:10:25 -0700155 }
156 }
157
Wei Wangee809222014-08-12 22:16:32 -0700158 /**
Jakub Pawlowskia480f7f2016-08-05 06:40:31 -0700159 * Cleans up advertisers. Should be called when bluetooth is down.
Wei Wangee809222014-08-12 22:16:32 -0700160 *
161 * @hide
162 */
163 public void cleanup() {
164 mLeAdvertisers.clear();
165 }
166
Prerepa Viswanadhame77adab2015-01-16 10:40:11 -0800167 // Compute the size of advertisement data or scan resp
168 private int totalBytes(AdvertiseData data, boolean isFlagsIncluded) {
Wei Wang9a974be2014-08-18 21:58:32 -0700169 if (data == null) return 0;
Tom Turney29230ce2014-11-12 16:26:41 -0800170 // Flags field is omitted if the advertising is not connectable.
Prerepa Viswanadhame77adab2015-01-16 10:40:11 -0800171 int size = (isFlagsIncluded) ? FLAGS_FIELD_BYTES : 0;
Wei Wang0e81ca22014-07-23 20:33:31 -0700172 if (data.getServiceUuids() != null) {
173 int num16BitUuids = 0;
174 int num32BitUuids = 0;
175 int num128BitUuids = 0;
176 for (ParcelUuid uuid : data.getServiceUuids()) {
177 if (BluetoothUuid.is16BitUuid(uuid)) {
178 ++num16BitUuids;
179 } else if (BluetoothUuid.is32BitUuid(uuid)) {
180 ++num32BitUuids;
181 } else {
182 ++num128BitUuids;
183 }
184 }
185 // 16 bit service uuids are grouped into one field when doing advertising.
186 if (num16BitUuids != 0) {
187 size += OVERHEAD_BYTES_PER_FIELD +
188 num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT;
189 }
190 // 32 bit service uuids are grouped into one field when doing advertising.
191 if (num32BitUuids != 0) {
192 size += OVERHEAD_BYTES_PER_FIELD +
193 num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT;
194 }
195 // 128 bit service uuids are grouped into one field when doing advertising.
196 if (num128BitUuids != 0) {
197 size += OVERHEAD_BYTES_PER_FIELD +
198 num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
199 }
200 }
Wei Wang6bf513d2014-08-01 11:12:37 -0700201 for (ParcelUuid uuid : data.getServiceData().keySet()) {
Wei Wang0e81ca22014-07-23 20:33:31 -0700202 size += OVERHEAD_BYTES_PER_FIELD + SERVICE_DATA_UUID_LENGTH
Wei Wang6bf513d2014-08-01 11:12:37 -0700203 + byteLength(data.getServiceData().get(uuid));
Wei Wang0e81ca22014-07-23 20:33:31 -0700204 }
Wei Wang6bf513d2014-08-01 11:12:37 -0700205 for (int i = 0; i < data.getManufacturerSpecificData().size(); ++i) {
Wei Wang0e81ca22014-07-23 20:33:31 -0700206 size += OVERHEAD_BYTES_PER_FIELD + MANUFACTURER_SPECIFIC_DATA_LENGTH +
Wei Wang6bf513d2014-08-01 11:12:37 -0700207 byteLength(data.getManufacturerSpecificData().valueAt(i));
Wei Wang0e81ca22014-07-23 20:33:31 -0700208 }
209 if (data.getIncludeTxPowerLevel()) {
210 size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte.
211 }
212 if (data.getIncludeDeviceName() && mBluetoothAdapter.getName() != null) {
213 size += OVERHEAD_BYTES_PER_FIELD + mBluetoothAdapter.getName().length();
214 }
215 return size;
216 }
217
218 private int byteLength(byte[] array) {
219 return array == null ? 0 : array.length;
220 }
221
Wei Wang6d811182014-05-22 12:10:25 -0700222 /**
223 * Bluetooth GATT interface callbacks for advertising.
224 */
Jakub Pawlowskia480f7f2016-08-05 06:40:31 -0700225 private class AdvertiseCallbackWrapper extends IAdvertiserCallback.Stub {
Wei Wang6d811182014-05-22 12:10:25 -0700226 private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000;
227 private final AdvertiseCallback mAdvertiseCallback;
Wei Wangaf74e662014-07-09 14:03:42 -0700228 private final AdvertiseData mAdvertisement;
229 private final AdvertiseData mScanResponse;
Wei Wang6d811182014-05-22 12:10:25 -0700230 private final AdvertiseSettings mSettings;
231 private final IBluetoothGatt mBluetoothGatt;
232
Jakub Pawlowski3d83b0e2016-11-04 15:25:57 -0700233 // mAdvertiserId -1: not registered
234 // -2: advertise stopped or registration timeout
235 // >=0: registered and advertising started
Jakub Pawlowskia480f7f2016-08-05 06:40:31 -0700236 private int mAdvertiserId;
Wei Wange0d4afb2014-07-29 21:34:25 -0700237 private boolean mIsAdvertising = false;
Jakub Pawlowski76f517a2016-11-22 12:44:22 -0800238 private int registrationError = AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR;
Wei Wang6d811182014-05-22 12:10:25 -0700239
240 public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback,
Wei Wangaf74e662014-07-09 14:03:42 -0700241 AdvertiseData advertiseData, AdvertiseData scanResponse,
Wei Wang6d811182014-05-22 12:10:25 -0700242 AdvertiseSettings settings,
243 IBluetoothGatt bluetoothGatt) {
244 mAdvertiseCallback = advertiseCallback;
245 mAdvertisement = advertiseData;
246 mScanResponse = scanResponse;
247 mSettings = settings;
248 mBluetoothGatt = bluetoothGatt;
Jakub Pawlowski3d83b0e2016-11-04 15:25:57 -0700249 mAdvertiserId = -1;
Wei Wang6d811182014-05-22 12:10:25 -0700250 }
251
Wei Wang9a974be2014-08-18 21:58:32 -0700252 public void startRegisteration() {
Wei Wang6d811182014-05-22 12:10:25 -0700253 synchronized (this) {
Jakub Pawlowski3d83b0e2016-11-04 15:25:57 -0700254 if (mAdvertiserId == -2) return;
Wei Wang9a974be2014-08-18 21:58:32 -0700255
Wei Wang6d811182014-05-22 12:10:25 -0700256 try {
Jakub Pawlowskia480f7f2016-08-05 06:40:31 -0700257 mBluetoothGatt.registerAdvertiser(this);
Wei Wang6d811182014-05-22 12:10:25 -0700258 wait(LE_CALLBACK_TIMEOUT_MILLIS);
Wei Wang9a974be2014-08-18 21:58:32 -0700259 } catch (InterruptedException | RemoteException e) {
260 Log.e(TAG, "Failed to start registeration", e);
Wei Wang6d811182014-05-22 12:10:25 -0700261 }
Jakub Pawlowski3d83b0e2016-11-04 15:25:57 -0700262 if (mAdvertiserId >= 0 && mIsAdvertising) {
Wei Wang9a974be2014-08-18 21:58:32 -0700263 mLeAdvertisers.put(mAdvertiseCallback, this);
Jakub Pawlowski3d83b0e2016-11-04 15:25:57 -0700264 } else if (mAdvertiserId < 0) {
Wei Wang02bc0082015-11-09 19:45:53 -0800265
Jakub Pawlowski76f517a2016-11-22 12:44:22 -0800266 // Registration timeout, reset mClientIf to -2 so no subsequent operations can
Wei Wang02bc0082015-11-09 19:45:53 -0800267 // proceed.
Jakub Pawlowski76f517a2016-11-22 12:44:22 -0800268 if (mAdvertiserId == -1) mAdvertiserId = -2;
Wei Wang833559d2014-08-29 10:26:13 -0700269 // Post internal error if registration failed.
Jakub Pawlowski76f517a2016-11-22 12:44:22 -0800270 postStartFailure(mAdvertiseCallback, registrationError);
Wei Wang833559d2014-08-29 10:26:13 -0700271 } else {
272 // Unregister application if it's already registered but advertise failed.
273 try {
Jakub Pawlowskia480f7f2016-08-05 06:40:31 -0700274 mBluetoothGatt.unregisterAdvertiser(mAdvertiserId);
Jakub Pawlowski3d83b0e2016-11-04 15:25:57 -0700275 mAdvertiserId = -2;
Wei Wang833559d2014-08-29 10:26:13 -0700276 } catch (RemoteException e) {
277 Log.e(TAG, "remote exception when unregistering", e);
278 }
Wei Wang9a974be2014-08-18 21:58:32 -0700279 }
Wei Wang6d811182014-05-22 12:10:25 -0700280 }
Wei Wang6d811182014-05-22 12:10:25 -0700281 }
282
Wei Wang9a974be2014-08-18 21:58:32 -0700283 public void stopAdvertising() {
Wei Wang6d811182014-05-22 12:10:25 -0700284 synchronized (this) {
285 try {
Jakub Pawlowskia480f7f2016-08-05 06:40:31 -0700286 mBluetoothGatt.stopMultiAdvertising(mAdvertiserId);
Wei Wang6d811182014-05-22 12:10:25 -0700287 wait(LE_CALLBACK_TIMEOUT_MILLIS);
Wei Wang9a974be2014-08-18 21:58:32 -0700288 } catch (InterruptedException | RemoteException e) {
289 Log.e(TAG, "Failed to stop advertising", e);
Wei Wang6d811182014-05-22 12:10:25 -0700290 }
Wei Wang9a974be2014-08-18 21:58:32 -0700291 // Advertise callback should have been removed from LeAdvertisers when
292 // onMultiAdvertiseCallback was called. In case onMultiAdvertiseCallback is never
293 // invoked and wait timeout expires, remove callback here.
294 if (mLeAdvertisers.containsKey(mAdvertiseCallback)) {
295 mLeAdvertisers.remove(mAdvertiseCallback);
296 }
Wei Wang6d811182014-05-22 12:10:25 -0700297 }
298 }
299
300 /**
Jakub Pawlowskia480f7f2016-08-05 06:40:31 -0700301 * Advertiser interface registered - app is ready to go
Wei Wang6d811182014-05-22 12:10:25 -0700302 */
303 @Override
Jakub Pawlowskia480f7f2016-08-05 06:40:31 -0700304 public void onAdvertiserRegistered(int status, int advertiserId) {
305 Log.d(TAG, "onAdvertiserRegistered() - status=" + status + " advertiserId=" + advertiserId);
Wei Wang6d811182014-05-22 12:10:25 -0700306 synchronized (this) {
307 if (status == BluetoothGatt.GATT_SUCCESS) {
Wei Wang6d811182014-05-22 12:10:25 -0700308 try {
Jakub Pawlowski3d83b0e2016-11-04 15:25:57 -0700309 if (mAdvertiserId == -2) {
Jakub Pawlowskia480f7f2016-08-05 06:40:31 -0700310 // Registration succeeds after timeout, unregister advertiser.
311 mBluetoothGatt.unregisterAdvertiser(advertiserId);
Wei Wang02bc0082015-11-09 19:45:53 -0800312 } else {
Jakub Pawlowskia480f7f2016-08-05 06:40:31 -0700313 mAdvertiserId = advertiserId;
314 mBluetoothGatt.startMultiAdvertising(mAdvertiserId, mAdvertisement,
Wei Wang02bc0082015-11-09 19:45:53 -0800315 mScanResponse, mSettings);
316 }
Wei Wang9a974be2014-08-18 21:58:32 -0700317 return;
Wei Wang6d811182014-05-22 12:10:25 -0700318 } catch (RemoteException e) {
Wei Wang9a974be2014-08-18 21:58:32 -0700319 Log.e(TAG, "failed to start advertising", e);
Wei Wang6d811182014-05-22 12:10:25 -0700320 }
Jakub Pawlowski76f517a2016-11-22 12:44:22 -0800321 } else if (status == AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) {
322 registrationError = status;
Wei Wang6d811182014-05-22 12:10:25 -0700323 }
Wei Wang9a974be2014-08-18 21:58:32 -0700324 // Registration failed.
Jakub Pawlowski3d83b0e2016-11-04 15:25:57 -0700325 mAdvertiserId = -2;
Wei Wang9a974be2014-08-18 21:58:32 -0700326 notifyAll();
Wei Wang6d811182014-05-22 12:10:25 -0700327 }
328 }
329
330 @Override
Wei Wange0d4afb2014-07-29 21:34:25 -0700331 public void onMultiAdvertiseCallback(int status, boolean isStart,
332 AdvertiseSettings settings) {
Wei Wang6d811182014-05-22 12:10:25 -0700333 synchronized (this) {
Wei Wange0d4afb2014-07-29 21:34:25 -0700334 if (isStart) {
335 if (status == AdvertiseCallback.ADVERTISE_SUCCESS) {
336 // Start success
Wei Wange0d4afb2014-07-29 21:34:25 -0700337 mIsAdvertising = true;
Wei Wang9a974be2014-08-18 21:58:32 -0700338 postStartSuccess(mAdvertiseCallback, settings);
Wei Wangaf74e662014-07-09 14:03:42 -0700339 } else {
Wei Wange0d4afb2014-07-29 21:34:25 -0700340 // Start failure.
Wei Wang9a974be2014-08-18 21:58:32 -0700341 postStartFailure(mAdvertiseCallback, status);
Wei Wang6d811182014-05-22 12:10:25 -0700342 }
Wei Wang6d811182014-05-22 12:10:25 -0700343 } else {
Jakub Pawlowskia480f7f2016-08-05 06:40:31 -0700344 // unregister advertiser for stop.
Wei Wange0d4afb2014-07-29 21:34:25 -0700345 try {
Jakub Pawlowskia480f7f2016-08-05 06:40:31 -0700346 mBluetoothGatt.unregisterAdvertiser(mAdvertiserId);
Jakub Pawlowski3d83b0e2016-11-04 15:25:57 -0700347 mAdvertiserId = -2;
Wei Wange0d4afb2014-07-29 21:34:25 -0700348 mIsAdvertising = false;
Wei Wang9a974be2014-08-18 21:58:32 -0700349 mLeAdvertisers.remove(mAdvertiseCallback);
Wei Wange0d4afb2014-07-29 21:34:25 -0700350 } catch (RemoteException e) {
351 Log.e(TAG, "remote exception when unregistering", e);
352 }
Wei Wang6d811182014-05-22 12:10:25 -0700353 }
354 notifyAll();
355 }
356
357 }
Wei Wang6d811182014-05-22 12:10:25 -0700358 }
359
Wei Wang9a974be2014-08-18 21:58:32 -0700360 private void postStartFailure(final AdvertiseCallback callback, final int error) {
Wei Wang6d811182014-05-22 12:10:25 -0700361 mHandler.post(new Runnable() {
Wei Wang9a974be2014-08-18 21:58:32 -0700362 @Override
Wei Wang6d811182014-05-22 12:10:25 -0700363 public void run() {
Wei Wangaf74e662014-07-09 14:03:42 -0700364 callback.onStartFailure(error);
Wei Wang6d811182014-05-22 12:10:25 -0700365 }
366 });
367 }
Wei Wang9a974be2014-08-18 21:58:32 -0700368
369 private void postStartSuccess(final AdvertiseCallback callback,
370 final AdvertiseSettings settings) {
371 mHandler.post(new Runnable() {
372
373 @Override
374 public void run() {
375 callback.onStartSuccess(settings);
376 }
377 });
378 }
Wei Wang6d811182014-05-22 12:10:25 -0700379}