16d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang/* 26d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * Copyright (C) 2014 The Android Open Source Project 36d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * 46d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * Licensed under the Apache License, Version 2.0 (the "License"); 56d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * you may not use this file except in compliance with the License. 66d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * You may obtain a copy of the License at 76d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * 86d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * http://www.apache.org/licenses/LICENSE-2.0 96d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * 106d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * Unless required by applicable law or agreed to in writing, software 116d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * distributed under the License is distributed on an "AS IS" BASIS, 126d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * See the License for the specific language governing permissions and 146d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * limitations under the License. 156d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang */ 166d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 176d81118032b92caa0f5cfebe11af02a98f819d5eWei Wangpackage android.bluetooth.le; 186d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 196d81118032b92caa0f5cfebe11af02a98f819d5eWei Wangimport android.bluetooth.BluetoothAdapter; 206d81118032b92caa0f5cfebe11af02a98f819d5eWei Wangimport android.bluetooth.BluetoothGatt; 21e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wangimport android.bluetooth.BluetoothGattCallbackWrapper; 220e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wangimport android.bluetooth.BluetoothUuid; 236d81118032b92caa0f5cfebe11af02a98f819d5eWei Wangimport android.bluetooth.IBluetoothGatt; 249fb1791e1a6859bfb14006a6d101cdecc88f3f95Wei Wangimport android.bluetooth.IBluetoothManager; 256d81118032b92caa0f5cfebe11af02a98f819d5eWei Wangimport android.os.Handler; 266d81118032b92caa0f5cfebe11af02a98f819d5eWei Wangimport android.os.Looper; 276d81118032b92caa0f5cfebe11af02a98f819d5eWei Wangimport android.os.ParcelUuid; 286d81118032b92caa0f5cfebe11af02a98f819d5eWei Wangimport android.os.RemoteException; 296d81118032b92caa0f5cfebe11af02a98f819d5eWei Wangimport android.util.Log; 306d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 316d81118032b92caa0f5cfebe11af02a98f819d5eWei Wangimport java.util.HashMap; 326d81118032b92caa0f5cfebe11af02a98f819d5eWei Wangimport java.util.Map; 336d81118032b92caa0f5cfebe11af02a98f819d5eWei Wangimport java.util.UUID; 346d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 356d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang/** 36af74e66e29a518157cb78fcef4b4fc532b7f60b0Wei Wang * This class provides a way to perform Bluetooth LE advertise operations, such as starting and 37af74e66e29a518157cb78fcef4b4fc532b7f60b0Wei Wang * stopping advertising. An advertiser can broadcast up to 31 bytes of advertisement data 38af74e66e29a518157cb78fcef4b4fc532b7f60b0Wei Wang * represented by {@link AdvertiseData}. 396d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * <p> 406d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * To get an instance of {@link BluetoothLeAdvertiser}, call the 416d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method. 426d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * <p> 43af74e66e29a518157cb78fcef4b4fc532b7f60b0Wei Wang * <b>Note:</b> Most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} 446d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * permission. 456d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * 46af74e66e29a518157cb78fcef4b4fc532b7f60b0Wei Wang * @see AdvertiseData 476d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang */ 486d81118032b92caa0f5cfebe11af02a98f819d5eWei Wangpublic final class BluetoothLeAdvertiser { 496d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 506d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang private static final String TAG = "BluetoothLeAdvertiser"; 516d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 520e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang private static final int MAX_ADVERTISING_DATA_BYTES = 31; 530e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang // Each fields need one byte for field length and another byte for field type. 540e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang private static final int OVERHEAD_BYTES_PER_FIELD = 2; 550e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang // Flags field will be set by system. 560e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang private static final int FLAGS_FIELD_BYTES = 3; 570e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang private static final int MANUFACTURER_SPECIFIC_DATA_LENGTH = 2; 580e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang private static final int SERVICE_DATA_UUID_LENGTH = 2; 590e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang 609fb1791e1a6859bfb14006a6d101cdecc88f3f95Wei Wang private final IBluetoothManager mBluetoothManager; 616d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang private final Handler mHandler; 628e5270fdf5639461d67e9a898a85520abac6053dPrerepa Viswanadham private BluetoothAdapter mBluetoothAdapter; 636d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang private final Map<AdvertiseCallback, AdvertiseCallbackWrapper> 646d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang mLeAdvertisers = new HashMap<AdvertiseCallback, AdvertiseCallbackWrapper>(); 656d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 666d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang /** 676d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * Use BluetoothAdapter.getLeAdvertiser() instead. 68685c1758902a42a7beb030d8bbaed3f7ce6f6135Wei Wang * 69685c1758902a42a7beb030d8bbaed3f7ce6f6135Wei Wang * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management 706d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * @hide 716d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang */ 729fb1791e1a6859bfb14006a6d101cdecc88f3f95Wei Wang public BluetoothLeAdvertiser(IBluetoothManager bluetoothManager) { 739fb1791e1a6859bfb14006a6d101cdecc88f3f95Wei Wang mBluetoothManager = bluetoothManager; 748e5270fdf5639461d67e9a898a85520abac6053dPrerepa Viswanadham mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 756d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang mHandler = new Handler(Looper.getMainLooper()); 766d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 776d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 786d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang /** 79685c1758902a42a7beb030d8bbaed3f7ce6f6135Wei Wang * Start Bluetooth LE Advertising. On success, the {@code advertiseData} will be broadcasted. 80685c1758902a42a7beb030d8bbaed3f7ce6f6135Wei Wang * Returns immediately, the operation status is delivered through {@code callback}. 816d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * <p> 826d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. 836d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * 846d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * @param settings Settings for Bluetooth LE advertising. 856d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * @param advertiseData Advertisement data to be broadcasted. 866d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * @param callback Callback for advertising status. 876d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang */ 886d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang public void startAdvertising(AdvertiseSettings settings, 89af74e66e29a518157cb78fcef4b4fc532b7f60b0Wei Wang AdvertiseData advertiseData, final AdvertiseCallback callback) { 906d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang startAdvertising(settings, advertiseData, null, callback); 916d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 926d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 936d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang /** 94af74e66e29a518157cb78fcef4b4fc532b7f60b0Wei Wang * Start Bluetooth LE Advertising. The {@code advertiseData} will be broadcasted if the 95685c1758902a42a7beb030d8bbaed3f7ce6f6135Wei Wang * operation succeeds. The {@code scanResponse} is returned when a scanning device sends an 96685c1758902a42a7beb030d8bbaed3f7ce6f6135Wei Wang * active scan request. This method returns immediately, the operation status is delivered 97685c1758902a42a7beb030d8bbaed3f7ce6f6135Wei Wang * through {@code callback}. 986d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * <p> 996d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 1006d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * 1016d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * @param settings Settings for Bluetooth LE advertising. 1026d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * @param advertiseData Advertisement data to be advertised in advertisement packet. 1036d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * @param scanResponse Scan response associated with the advertisement data. 1046d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * @param callback Callback for advertising status. 1056d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang */ 1066d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang public void startAdvertising(AdvertiseSettings settings, 107af74e66e29a518157cb78fcef4b4fc532b7f60b0Wei Wang AdvertiseData advertiseData, AdvertiseData scanResponse, 1086d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang final AdvertiseCallback callback) { 1099a974be631a3ff2e7e754f79167eb024ee55925eWei Wang synchronized (mLeAdvertisers) { 110833559d9f3f0bd6ddb1cf9c1571975751830e045Wei Wang BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); 1119a974be631a3ff2e7e754f79167eb024ee55925eWei Wang if (callback == null) { 1129a974be631a3ff2e7e754f79167eb024ee55925eWei Wang throw new IllegalArgumentException("callback cannot be null"); 1139a974be631a3ff2e7e754f79167eb024ee55925eWei Wang } 11435cd4c853f748e295352bda708b26ee39544d06dWei Wang if (!mBluetoothAdapter.isMultipleAdvertisementSupported() && 11535cd4c853f748e295352bda708b26ee39544d06dWei Wang !mBluetoothAdapter.isPeripheralModeSupported()) { 1169a974be631a3ff2e7e754f79167eb024ee55925eWei Wang postStartFailure(callback, 1179a974be631a3ff2e7e754f79167eb024ee55925eWei Wang AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED); 1189a974be631a3ff2e7e754f79167eb024ee55925eWei Wang return; 1199a974be631a3ff2e7e754f79167eb024ee55925eWei Wang } 12029230ceab9ea7b6913f0a898411b213f199e18c2Tom Turney boolean isConnectable = settings.isConnectable(); 12129230ceab9ea7b6913f0a898411b213f199e18c2Tom Turney if (totalBytes(advertiseData, isConnectable) > MAX_ADVERTISING_DATA_BYTES || 122e77adabeafa16f9b401a6ad67405205bd96f2219Prerepa Viswanadham totalBytes(scanResponse, false) > MAX_ADVERTISING_DATA_BYTES) { 1239a974be631a3ff2e7e754f79167eb024ee55925eWei Wang postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE); 1249a974be631a3ff2e7e754f79167eb024ee55925eWei Wang return; 1256d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 1269a974be631a3ff2e7e754f79167eb024ee55925eWei Wang if (mLeAdvertisers.containsKey(callback)) { 1279a974be631a3ff2e7e754f79167eb024ee55925eWei Wang postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED); 1289a974be631a3ff2e7e754f79167eb024ee55925eWei Wang return; 1299a974be631a3ff2e7e754f79167eb024ee55925eWei Wang } 1309a974be631a3ff2e7e754f79167eb024ee55925eWei Wang 1319a974be631a3ff2e7e754f79167eb024ee55925eWei Wang IBluetoothGatt gatt; 1329a974be631a3ff2e7e754f79167eb024ee55925eWei Wang try { 1339a974be631a3ff2e7e754f79167eb024ee55925eWei Wang gatt = mBluetoothManager.getBluetoothGatt(); 1349a974be631a3ff2e7e754f79167eb024ee55925eWei Wang } catch (RemoteException e) { 1359a974be631a3ff2e7e754f79167eb024ee55925eWei Wang Log.e(TAG, "Failed to get Bluetooth gatt - ", e); 1369a974be631a3ff2e7e754f79167eb024ee55925eWei Wang postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); 1379a974be631a3ff2e7e754f79167eb024ee55925eWei Wang return; 1389a974be631a3ff2e7e754f79167eb024ee55925eWei Wang } 1399a974be631a3ff2e7e754f79167eb024ee55925eWei Wang AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData, 1409a974be631a3ff2e7e754f79167eb024ee55925eWei Wang scanResponse, settings, gatt); 1419a974be631a3ff2e7e754f79167eb024ee55925eWei Wang wrapper.startRegisteration(); 1426d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 1436d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 1446d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 1456d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang /** 1466d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * Stop Bluetooth LE advertising. The {@code callback} must be the same one use in 1476d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * {@link BluetoothLeAdvertiser#startAdvertising}. 1486d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * <p> 1496d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. 1506d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * 151af74e66e29a518157cb78fcef4b4fc532b7f60b0Wei Wang * @param callback {@link AdvertiseCallback} identifies the advertising instance to stop. 1526d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang */ 1536d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang public void stopAdvertising(final AdvertiseCallback callback) { 1549a974be631a3ff2e7e754f79167eb024ee55925eWei Wang synchronized (mLeAdvertisers) { 1559a974be631a3ff2e7e754f79167eb024ee55925eWei Wang if (callback == null) { 1569a974be631a3ff2e7e754f79167eb024ee55925eWei Wang throw new IllegalArgumentException("callback cannot be null"); 1576d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 1589a974be631a3ff2e7e754f79167eb024ee55925eWei Wang AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(callback); 1599a974be631a3ff2e7e754f79167eb024ee55925eWei Wang if (wrapper == null) return; 1609a974be631a3ff2e7e754f79167eb024ee55925eWei Wang wrapper.stopAdvertising(); 1616d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 1626d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 1636d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 164ee80922c6a1228886589dcd4598a1cadf0bd1ff8Wei Wang /** 165ee80922c6a1228886589dcd4598a1cadf0bd1ff8Wei Wang * Cleans up advertise clients. Should be called when bluetooth is down. 166ee80922c6a1228886589dcd4598a1cadf0bd1ff8Wei Wang * 167ee80922c6a1228886589dcd4598a1cadf0bd1ff8Wei Wang * @hide 168ee80922c6a1228886589dcd4598a1cadf0bd1ff8Wei Wang */ 169ee80922c6a1228886589dcd4598a1cadf0bd1ff8Wei Wang public void cleanup() { 170ee80922c6a1228886589dcd4598a1cadf0bd1ff8Wei Wang mLeAdvertisers.clear(); 171ee80922c6a1228886589dcd4598a1cadf0bd1ff8Wei Wang } 172ee80922c6a1228886589dcd4598a1cadf0bd1ff8Wei Wang 173e77adabeafa16f9b401a6ad67405205bd96f2219Prerepa Viswanadham // Compute the size of advertisement data or scan resp 174e77adabeafa16f9b401a6ad67405205bd96f2219Prerepa Viswanadham private int totalBytes(AdvertiseData data, boolean isFlagsIncluded) { 1759a974be631a3ff2e7e754f79167eb024ee55925eWei Wang if (data == null) return 0; 17629230ceab9ea7b6913f0a898411b213f199e18c2Tom Turney // Flags field is omitted if the advertising is not connectable. 177e77adabeafa16f9b401a6ad67405205bd96f2219Prerepa Viswanadham int size = (isFlagsIncluded) ? FLAGS_FIELD_BYTES : 0; 1780e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang if (data.getServiceUuids() != null) { 1790e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang int num16BitUuids = 0; 1800e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang int num32BitUuids = 0; 1810e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang int num128BitUuids = 0; 1820e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang for (ParcelUuid uuid : data.getServiceUuids()) { 1830e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang if (BluetoothUuid.is16BitUuid(uuid)) { 1840e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang ++num16BitUuids; 1850e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang } else if (BluetoothUuid.is32BitUuid(uuid)) { 1860e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang ++num32BitUuids; 1870e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang } else { 1880e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang ++num128BitUuids; 1890e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang } 1900e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang } 1910e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang // 16 bit service uuids are grouped into one field when doing advertising. 1920e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang if (num16BitUuids != 0) { 1930e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang size += OVERHEAD_BYTES_PER_FIELD + 1940e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT; 1950e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang } 1960e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang // 32 bit service uuids are grouped into one field when doing advertising. 1970e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang if (num32BitUuids != 0) { 1980e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang size += OVERHEAD_BYTES_PER_FIELD + 1990e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT; 2000e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang } 2010e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang // 128 bit service uuids are grouped into one field when doing advertising. 2020e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang if (num128BitUuids != 0) { 2030e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang size += OVERHEAD_BYTES_PER_FIELD + 2040e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; 2050e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang } 2060e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang } 2076bf513d32db7fbc157681bd642e12a201cf20a89Wei Wang for (ParcelUuid uuid : data.getServiceData().keySet()) { 2080e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang size += OVERHEAD_BYTES_PER_FIELD + SERVICE_DATA_UUID_LENGTH 2096bf513d32db7fbc157681bd642e12a201cf20a89Wei Wang + byteLength(data.getServiceData().get(uuid)); 2100e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang } 2116bf513d32db7fbc157681bd642e12a201cf20a89Wei Wang for (int i = 0; i < data.getManufacturerSpecificData().size(); ++i) { 2120e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang size += OVERHEAD_BYTES_PER_FIELD + MANUFACTURER_SPECIFIC_DATA_LENGTH + 2136bf513d32db7fbc157681bd642e12a201cf20a89Wei Wang byteLength(data.getManufacturerSpecificData().valueAt(i)); 2140e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang } 2150e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang if (data.getIncludeTxPowerLevel()) { 2160e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte. 2170e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang } 2180e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang if (data.getIncludeDeviceName() && mBluetoothAdapter.getName() != null) { 2190e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang size += OVERHEAD_BYTES_PER_FIELD + mBluetoothAdapter.getName().length(); 2200e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang } 2210e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang return size; 2220e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang } 2230e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang 2240e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang private int byteLength(byte[] array) { 2250e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang return array == null ? 0 : array.length; 2260e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang } 2270e81ca25432b3606eba841206b7a4a6ed74bd54bWei Wang 2286d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang /** 2296d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * Bluetooth GATT interface callbacks for advertising. 2306d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang */ 2319a974be631a3ff2e7e754f79167eb024ee55925eWei Wang private class AdvertiseCallbackWrapper extends BluetoothGattCallbackWrapper { 2326d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000; 2336d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang private final AdvertiseCallback mAdvertiseCallback; 234af74e66e29a518157cb78fcef4b4fc532b7f60b0Wei Wang private final AdvertiseData mAdvertisement; 235af74e66e29a518157cb78fcef4b4fc532b7f60b0Wei Wang private final AdvertiseData mScanResponse; 2366d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang private final AdvertiseSettings mSettings; 2376d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang private final IBluetoothGatt mBluetoothGatt; 2386d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 239685c1758902a42a7beb030d8bbaed3f7ce6f6135Wei Wang // mClientIf 0: not registered 24002bc0086071b5faff260566b4d3713861703eee3Wei Wang // -1: advertise stopped or registration timeout 24102bc0086071b5faff260566b4d3713861703eee3Wei Wang // >0: registered and advertising started 242685c1758902a42a7beb030d8bbaed3f7ce6f6135Wei Wang private int mClientIf; 243e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang private boolean mIsAdvertising = false; 2446d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 2456d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback, 246af74e66e29a518157cb78fcef4b4fc532b7f60b0Wei Wang AdvertiseData advertiseData, AdvertiseData scanResponse, 2476d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang AdvertiseSettings settings, 2486d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang IBluetoothGatt bluetoothGatt) { 2496d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang mAdvertiseCallback = advertiseCallback; 2506d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang mAdvertisement = advertiseData; 2516d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang mScanResponse = scanResponse; 2526d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang mSettings = settings; 2536d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang mBluetoothGatt = bluetoothGatt; 254685c1758902a42a7beb030d8bbaed3f7ce6f6135Wei Wang mClientIf = 0; 2556d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 2566d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 2579a974be631a3ff2e7e754f79167eb024ee55925eWei Wang public void startRegisteration() { 2586d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang synchronized (this) { 2599a974be631a3ff2e7e754f79167eb024ee55925eWei Wang if (mClientIf == -1) return; 2609a974be631a3ff2e7e754f79167eb024ee55925eWei Wang 2616d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang try { 2629a974be631a3ff2e7e754f79167eb024ee55925eWei Wang UUID uuid = UUID.randomUUID(); 2639a974be631a3ff2e7e754f79167eb024ee55925eWei Wang mBluetoothGatt.registerClient(new ParcelUuid(uuid), this); 2646d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang wait(LE_CALLBACK_TIMEOUT_MILLIS); 2659a974be631a3ff2e7e754f79167eb024ee55925eWei Wang } catch (InterruptedException | RemoteException e) { 2669a974be631a3ff2e7e754f79167eb024ee55925eWei Wang Log.e(TAG, "Failed to start registeration", e); 2679a974be631a3ff2e7e754f79167eb024ee55925eWei Wang } 2689a974be631a3ff2e7e754f79167eb024ee55925eWei Wang if (mClientIf > 0 && mIsAdvertising) { 2699a974be631a3ff2e7e754f79167eb024ee55925eWei Wang mLeAdvertisers.put(mAdvertiseCallback, this); 270833559d9f3f0bd6ddb1cf9c1571975751830e045Wei Wang } else if (mClientIf <= 0) { 27102bc0086071b5faff260566b4d3713861703eee3Wei Wang 27202bc0086071b5faff260566b4d3713861703eee3Wei Wang // Registration timeout, reset mClientIf to -1 so no subsequent operations can 27302bc0086071b5faff260566b4d3713861703eee3Wei Wang // proceed. 27402bc0086071b5faff260566b4d3713861703eee3Wei Wang if (mClientIf == 0) mClientIf = -1; 275833559d9f3f0bd6ddb1cf9c1571975751830e045Wei Wang // Post internal error if registration failed. 2769a974be631a3ff2e7e754f79167eb024ee55925eWei Wang postStartFailure(mAdvertiseCallback, 2779a974be631a3ff2e7e754f79167eb024ee55925eWei Wang AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); 278833559d9f3f0bd6ddb1cf9c1571975751830e045Wei Wang } else { 279833559d9f3f0bd6ddb1cf9c1571975751830e045Wei Wang // Unregister application if it's already registered but advertise failed. 280833559d9f3f0bd6ddb1cf9c1571975751830e045Wei Wang try { 281833559d9f3f0bd6ddb1cf9c1571975751830e045Wei Wang mBluetoothGatt.unregisterClient(mClientIf); 282833559d9f3f0bd6ddb1cf9c1571975751830e045Wei Wang mClientIf = -1; 283833559d9f3f0bd6ddb1cf9c1571975751830e045Wei Wang } catch (RemoteException e) { 284833559d9f3f0bd6ddb1cf9c1571975751830e045Wei Wang Log.e(TAG, "remote exception when unregistering", e); 285833559d9f3f0bd6ddb1cf9c1571975751830e045Wei Wang } 2866d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 2876d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 2886d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 2896d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 2909a974be631a3ff2e7e754f79167eb024ee55925eWei Wang public void stopAdvertising() { 2916d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang synchronized (this) { 2926d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang try { 2939a974be631a3ff2e7e754f79167eb024ee55925eWei Wang mBluetoothGatt.stopMultiAdvertising(mClientIf); 2946d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang wait(LE_CALLBACK_TIMEOUT_MILLIS); 2959a974be631a3ff2e7e754f79167eb024ee55925eWei Wang } catch (InterruptedException | RemoteException e) { 2969a974be631a3ff2e7e754f79167eb024ee55925eWei Wang Log.e(TAG, "Failed to stop advertising", e); 2979a974be631a3ff2e7e754f79167eb024ee55925eWei Wang } 2989a974be631a3ff2e7e754f79167eb024ee55925eWei Wang // Advertise callback should have been removed from LeAdvertisers when 2999a974be631a3ff2e7e754f79167eb024ee55925eWei Wang // onMultiAdvertiseCallback was called. In case onMultiAdvertiseCallback is never 3009a974be631a3ff2e7e754f79167eb024ee55925eWei Wang // invoked and wait timeout expires, remove callback here. 3019a974be631a3ff2e7e754f79167eb024ee55925eWei Wang if (mLeAdvertisers.containsKey(mAdvertiseCallback)) { 3029a974be631a3ff2e7e754f79167eb024ee55925eWei Wang mLeAdvertisers.remove(mAdvertiseCallback); 3036d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 3046d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 3056d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 3066d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 3076d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang /** 3086d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang * Application interface registered - app is ready to go 3096d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang */ 3106d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang @Override 3116d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang public void onClientRegistered(int status, int clientIf) { 3126d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf); 3136d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang synchronized (this) { 3146d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang if (status == BluetoothGatt.GATT_SUCCESS) { 3156d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang try { 31602bc0086071b5faff260566b4d3713861703eee3Wei Wang if (mClientIf == -1) { 31702bc0086071b5faff260566b4d3713861703eee3Wei Wang // Registration succeeds after timeout, unregister client. 31802bc0086071b5faff260566b4d3713861703eee3Wei Wang mBluetoothGatt.unregisterClient(clientIf); 31902bc0086071b5faff260566b4d3713861703eee3Wei Wang } else { 32002bc0086071b5faff260566b4d3713861703eee3Wei Wang mClientIf = clientIf; 32102bc0086071b5faff260566b4d3713861703eee3Wei Wang mBluetoothGatt.startMultiAdvertising(mClientIf, mAdvertisement, 32202bc0086071b5faff260566b4d3713861703eee3Wei Wang mScanResponse, mSettings); 32302bc0086071b5faff260566b4d3713861703eee3Wei Wang } 3249a974be631a3ff2e7e754f79167eb024ee55925eWei Wang return; 3256d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } catch (RemoteException e) { 3269a974be631a3ff2e7e754f79167eb024ee55925eWei Wang Log.e(TAG, "failed to start advertising", e); 3276d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 3286d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 3299a974be631a3ff2e7e754f79167eb024ee55925eWei Wang // Registration failed. 3309a974be631a3ff2e7e754f79167eb024ee55925eWei Wang mClientIf = -1; 3319a974be631a3ff2e7e754f79167eb024ee55925eWei Wang notifyAll(); 3326d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 3336d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 3346d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 3356d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang @Override 336e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang public void onMultiAdvertiseCallback(int status, boolean isStart, 337e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang AdvertiseSettings settings) { 3386d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang synchronized (this) { 339e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang if (isStart) { 340e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang if (status == AdvertiseCallback.ADVERTISE_SUCCESS) { 341e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang // Start success 342e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang mIsAdvertising = true; 3439a974be631a3ff2e7e754f79167eb024ee55925eWei Wang postStartSuccess(mAdvertiseCallback, settings); 344af74e66e29a518157cb78fcef4b4fc532b7f60b0Wei Wang } else { 345e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang // Start failure. 3469a974be631a3ff2e7e754f79167eb024ee55925eWei Wang postStartFailure(mAdvertiseCallback, status); 3476d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 3486d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } else { 349e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang // unregister client for stop. 350e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang try { 351e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang mBluetoothGatt.unregisterClient(mClientIf); 352e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang mClientIf = -1; 353e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang mIsAdvertising = false; 3549a974be631a3ff2e7e754f79167eb024ee55925eWei Wang mLeAdvertisers.remove(mAdvertiseCallback); 355e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang } catch (RemoteException e) { 356e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang Log.e(TAG, "remote exception when unregistering", e); 357e0d4afb2d4caecb264852a35f6e3cfc1248e08c4Wei Wang } 3586d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 3596d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang notifyAll(); 3606d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 3616d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 3626d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 3636d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 3646d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang 3659a974be631a3ff2e7e754f79167eb024ee55925eWei Wang private void postStartFailure(final AdvertiseCallback callback, final int error) { 3666d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang mHandler.post(new Runnable() { 3679a974be631a3ff2e7e754f79167eb024ee55925eWei Wang @Override 3686d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang public void run() { 369af74e66e29a518157cb78fcef4b4fc532b7f60b0Wei Wang callback.onStartFailure(error); 3706d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 3716d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang }); 3726d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang } 3739a974be631a3ff2e7e754f79167eb024ee55925eWei Wang 3749a974be631a3ff2e7e754f79167eb024ee55925eWei Wang private void postStartSuccess(final AdvertiseCallback callback, 3759a974be631a3ff2e7e754f79167eb024ee55925eWei Wang final AdvertiseSettings settings) { 3769a974be631a3ff2e7e754f79167eb024ee55925eWei Wang mHandler.post(new Runnable() { 3779a974be631a3ff2e7e754f79167eb024ee55925eWei Wang 3789a974be631a3ff2e7e754f79167eb024ee55925eWei Wang @Override 3799a974be631a3ff2e7e754f79167eb024ee55925eWei Wang public void run() { 3809a974be631a3ff2e7e754f79167eb024ee55925eWei Wang callback.onStartSuccess(settings); 3819a974be631a3ff2e7e754f79167eb024ee55925eWei Wang } 3829a974be631a3ff2e7e754f79167eb024ee55925eWei Wang }); 3839a974be631a3ff2e7e754f79167eb024ee55925eWei Wang } 3846d81118032b92caa0f5cfebe11af02a98f819d5eWei Wang} 385