BluetoothLeAdvertiser.java revision 29230ceab9ea7b6913f0a898411b213f199e18c2
1f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root/* 2f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * Copyright (C) 2014 The Android Open Source Project 3f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * 4f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * Licensed under the Apache License, Version 2.0 (the "License"); 5f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * you may not use this file except in compliance with the License. 6f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * You may obtain a copy of the License at 7f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * 8f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * http://www.apache.org/licenses/LICENSE-2.0 9f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * 10f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * Unless required by applicable law or agreed to in writing, software 11f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * distributed under the License is distributed on an "AS IS" BASIS, 12f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * See the License for the specific language governing permissions and 14f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root * limitations under the License. 15f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root */ 16f8d0f095e34f8d661ca5b7d555d8610272099bffKenny Root 17829778843cf459384841f9f4ecafe862b6228d6eDianne Hackbornpackage android.bluetooth.le; 1890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn 1990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.bluetooth.BluetoothAdapter; 2090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.bluetooth.BluetoothGatt; 2190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.bluetooth.BluetoothGattCallbackWrapper; 228d60866e2100db70ecf0502c14768a384514d7e9Jeff Brownimport android.bluetooth.BluetoothUuid; 2390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.bluetooth.IBluetoothGatt; 24af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brownimport android.bluetooth.IBluetoothManager; 25af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brownimport android.os.Handler; 269eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brownimport android.os.Looper; 2790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport android.os.ParcelUuid; 288d60866e2100db70ecf0502c14768a384514d7e9Jeff Brownimport android.os.RemoteException; 29c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brownimport android.util.Log; 3090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn 3190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport java.util.HashMap; 3290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport java.util.Map; 3390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackbornimport java.util.UUID; 34c9c487e6d6e9cf91e8ad30958ddf361158815cabMichael Wright 35cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown/** 3690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn * This class provides a way to perform Bluetooth LE advertise operations, such as starting and 3790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn * stopping advertising. An advertiser can broadcast up to 31 bytes of advertisement data 3890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn * represented by {@link AdvertiseData}. 39c9c487e6d6e9cf91e8ad30958ddf361158815cabMichael Wright * <p> 40c9c487e6d6e9cf91e8ad30958ddf361158815cabMichael Wright * To get an instance of {@link BluetoothLeAdvertiser}, call the 418d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method. 429eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown * <p> 439eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown * <b>Note:</b> Most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} 449eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown * permission. 459eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown * 469eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown * @see AdvertiseData 4790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn */ 488d60866e2100db70ecf0502c14768a384514d7e9Jeff Brownpublic final class BluetoothLeAdvertiser { 498d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown 508d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown private static final String TAG = "BluetoothLeAdvertiser"; 5176936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright 528d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown private static final int MAX_ADVERTISING_DATA_BYTES = 31; 538d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown // Each fields need one byte for field length and another byte for field type. 548d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown private static final int OVERHEAD_BYTES_PER_FIELD = 2; 5590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn // Flags field will be set by system. 568d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown private static final int FLAGS_FIELD_BYTES = 3; 578d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown private static final int MANUFACTURER_SPECIFIC_DATA_LENGTH = 2; 58cc0c159e9b3dd4e0f48da0ce3e33d2c68a651413Jeff Brown private static final int SERVICE_DATA_UUID_LENGTH = 2; 5965fd251c3913fc921468a3dad190810db19eb9dfJeff Brown 608d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown private final IBluetoothManager mBluetoothManager; 618d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown private final Handler mHandler; 629e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown private BluetoothAdapter mBluetoothAdapter; 639e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown private final Map<AdvertiseCallback, AdvertiseCallbackWrapper> 649eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown mLeAdvertisers = new HashMap<AdvertiseCallback, AdvertiseCallbackWrapper>(); 659eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown 66b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown /** 6786172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright * Use BluetoothAdapter.getLeAdvertiser() instead. 6886172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright * 6986172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management 7086172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright * @hide 7186172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright */ 7286172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright public BluetoothLeAdvertiser(IBluetoothManager bluetoothManager) { 7386172f6252fe2ed49a1cdea5cafd0ba2e049255dMichael Wright mBluetoothManager = bluetoothManager; 74b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 75b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown mHandler = new Handler(Looper.getMainLooper()); 769eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown } 77b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown 788d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown /** 798d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown * Start Bluetooth LE Advertising. On success, the {@code advertiseData} will be broadcasted. 808d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown * Returns immediately, the operation status is delivered through {@code callback}. 818d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown * <p> 8276936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. 838d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown * 848d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown * @param settings Settings for Bluetooth LE advertising. 858d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown * @param advertiseData Advertisement data to be broadcasted. 868d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown * @param callback Callback for advertising status. 878d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown */ 888d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown public void startAdvertising(AdvertiseSettings settings, 898d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown AdvertiseData advertiseData, final AdvertiseCallback callback) { 908d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown startAdvertising(settings, advertiseData, null, callback); 918d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown } 928d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown 9376936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright /** 9476936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright * Start Bluetooth LE Advertising. The {@code advertiseData} will be broadcasted if the 9576936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright * operation succeeds. The {@code scanResponse} is returned when a scanning device sends an 9676936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright * active scan request. This method returns immediately, the operation status is delivered 978d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown * through {@code callback}. 988d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown * <p> 998d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 1008d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown * 10176936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright * @param settings Settings for Bluetooth LE advertising. 1028d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown * @param advertiseData Advertisement data to be advertised in advertisement packet. 1038d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown * @param scanResponse Scan response associated with the advertisement data. 10490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn * @param callback Callback for advertising status. 10590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn */ 106b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown public void startAdvertising(AdvertiseSettings settings, 107b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown AdvertiseData advertiseData, AdvertiseData scanResponse, 108b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown final AdvertiseCallback callback) { 109b59ab9f41faafb358afb4f951de96f34a656e0b4Jeff Brown synchronized (mLeAdvertisers) { 110af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); 111af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown if (callback == null) { 11290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn throw new IllegalArgumentException("callback cannot be null"); 11390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn } 11490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn if (!mBluetoothAdapter.isMultipleAdvertisementSupported() && 11590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn !mBluetoothAdapter.isPeripheralModeSupported()) { 11690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn postStartFailure(callback, 11776936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED); 11890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn return; 11990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn } 12090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn boolean isConnectable = settings.isConnectable(); 12190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn if (totalBytes(advertiseData, isConnectable) > MAX_ADVERTISING_DATA_BYTES || 12290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn totalBytes(scanResponse, isConnectable) > MAX_ADVERTISING_DATA_BYTES) { 12390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE); 12490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn return; 125d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown } 1268d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown if (mLeAdvertisers.containsKey(callback)) { 12765fd251c3913fc921468a3dad190810db19eb9dfJeff Brown postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED); 12890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn return; 1299e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown } 1309eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown 1319eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown IBluetoothGatt gatt; 1328d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown try { 1338d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown gatt = mBluetoothManager.getBluetoothGatt(); 13490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn } catch (RemoteException e) { 13590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn Log.e(TAG, "Failed to get Bluetooth gatt - ", e); 13690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); 13790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn return; 138c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown } 139c3fe7669d6276b43e82c802743a7e8974bae52a5Jeff Brown AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData, 140af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown scanResponse, settings, gatt); 141af9e8d38184c6ba4d2d3eb5bde7014a66dd8a78bJeff Brown wrapper.startRegisteration(); 14290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn } 14390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn } 14490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn 14590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn /** 14690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn * Stop Bluetooth LE advertising. The {@code callback} must be the same one use in 14790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn * {@link BluetoothLeAdvertiser#startAdvertising}. 14890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn * <p> 14990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. 15090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn * 15190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn * @param callback {@link AdvertiseCallback} identifies the advertising instance to stop. 15290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn */ 15390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn public void stopAdvertising(final AdvertiseCallback callback) { 15490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn synchronized (mLeAdvertisers) { 15590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); 15690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn if (callback == null) { 15790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn throw new IllegalArgumentException("callback cannot be null"); 15890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn } 15976936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(callback); 16076936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright if (wrapper == null) return; 16176936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright wrapper.stopAdvertising(); 16276936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright } 16376936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright } 16490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn 16590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn /** 16690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn * Cleans up advertise clients. Should be called when bluetooth is down. 16790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn * 16890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn * @hide 16990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn */ 17090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn public void cleanup() { 17190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn mLeAdvertisers.clear(); 17290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn } 17390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn 17490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn // Compute the size of the advertise data. 175d1e0c371a651f024b3de62a686c56e369e3f361eJeff Brown private int totalBytes(AdvertiseData data, boolean isConnectable) { 1769e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown if (data == null) return 0; 1779e2ad36be87f2703b3d737189944d82f93bd4f27Jeff Brown // Flags field is omitted if the advertising is not connectable. 1789eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown int size = isConnectable ? FLAGS_FIELD_BYTES : 0; 1799eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown if (data.getServiceUuids() != null) { 1809eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown int num16BitUuids = 0; 1819eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown int num32BitUuids = 0; 1829eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown int num128BitUuids = 0; 1839eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown for (ParcelUuid uuid : data.getServiceUuids()) { 1849eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown if (BluetoothUuid.is16BitUuid(uuid)) { 1859eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown ++num16BitUuids; 18690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn } else if (BluetoothUuid.is32BitUuid(uuid)) { 18790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn ++num32BitUuids; 18890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn } else { 18990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn ++num128BitUuids; 19090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn } 19190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn } 19290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn // 16 bit service uuids are grouped into one field when doing advertising. 19390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn if (num16BitUuids != 0) { 19490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn size += OVERHEAD_BYTES_PER_FIELD + 19590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT; 19690d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn } 19790d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn // 32 bit service uuids are grouped into one field when doing advertising. 19890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn if (num32BitUuids != 0) { 19990d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn size += OVERHEAD_BYTES_PER_FIELD + 20090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT; 20190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn } 20290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn // 128 bit service uuids are grouped into one field when doing advertising. 20390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn if (num128BitUuids != 0) { 20490d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn size += OVERHEAD_BYTES_PER_FIELD + 2058d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; 2068d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown } 2078d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown } 2088d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown for (ParcelUuid uuid : data.getServiceData().keySet()) { 2098d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown size += OVERHEAD_BYTES_PER_FIELD + SERVICE_DATA_UUID_LENGTH 2108d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown + byteLength(data.getServiceData().get(uuid)); 2118d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown } 2125068ad8d2396d6d7bfbdb1c2c3fe57104744f1f9Jeff Brown for (int i = 0; i < data.getManufacturerSpecificData().size(); ++i) { 2138d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown size += OVERHEAD_BYTES_PER_FIELD + MANUFACTURER_SPECIFIC_DATA_LENGTH + 2148d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown byteLength(data.getManufacturerSpecificData().valueAt(i)); 2158d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown } 2168d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown if (data.getIncludeTxPowerLevel()) { 2178d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte. 2188d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown } 2198d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown if (data.getIncludeDeviceName() && mBluetoothAdapter.getName() != null) { 22090d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn size += OVERHEAD_BYTES_PER_FIELD + mBluetoothAdapter.getName().length(); 22190d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn } 22290d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn return size; 22370825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 22470825161b5bf51ed48319e142751a9c88b104994Jeff Brown 22570825161b5bf51ed48319e142751a9c88b104994Jeff Brown private int byteLength(byte[] array) { 22670825161b5bf51ed48319e142751a9c88b104994Jeff Brown return array == null ? 0 : array.length; 22770825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 22870825161b5bf51ed48319e142751a9c88b104994Jeff Brown 22970825161b5bf51ed48319e142751a9c88b104994Jeff Brown /** 23070825161b5bf51ed48319e142751a9c88b104994Jeff Brown * Bluetooth GATT interface callbacks for advertising. 23170825161b5bf51ed48319e142751a9c88b104994Jeff Brown */ 23270825161b5bf51ed48319e142751a9c88b104994Jeff Brown private class AdvertiseCallbackWrapper extends BluetoothGattCallbackWrapper { 23390d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000; 23470825161b5bf51ed48319e142751a9c88b104994Jeff Brown private final AdvertiseCallback mAdvertiseCallback; 23570825161b5bf51ed48319e142751a9c88b104994Jeff Brown private final AdvertiseData mAdvertisement; 23670825161b5bf51ed48319e142751a9c88b104994Jeff Brown private final AdvertiseData mScanResponse; 23770825161b5bf51ed48319e142751a9c88b104994Jeff Brown private final AdvertiseSettings mSettings; 23870825161b5bf51ed48319e142751a9c88b104994Jeff Brown private final IBluetoothGatt mBluetoothGatt; 23970825161b5bf51ed48319e142751a9c88b104994Jeff Brown 24070825161b5bf51ed48319e142751a9c88b104994Jeff Brown // mClientIf 0: not registered 24170825161b5bf51ed48319e142751a9c88b104994Jeff Brown // -1: scan stopped 24270825161b5bf51ed48319e142751a9c88b104994Jeff Brown // >0: registered and scan started 2438d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown private int mClientIf; 24470825161b5bf51ed48319e142751a9c88b104994Jeff Brown private boolean mIsAdvertising = false; 24570825161b5bf51ed48319e142751a9c88b104994Jeff Brown 24670825161b5bf51ed48319e142751a9c88b104994Jeff Brown public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback, 2478d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown AdvertiseData advertiseData, AdvertiseData scanResponse, 24870825161b5bf51ed48319e142751a9c88b104994Jeff Brown AdvertiseSettings settings, 24970825161b5bf51ed48319e142751a9c88b104994Jeff Brown IBluetoothGatt bluetoothGatt) { 25070825161b5bf51ed48319e142751a9c88b104994Jeff Brown mAdvertiseCallback = advertiseCallback; 25170825161b5bf51ed48319e142751a9c88b104994Jeff Brown mAdvertisement = advertiseData; 25270825161b5bf51ed48319e142751a9c88b104994Jeff Brown mScanResponse = scanResponse; 25370825161b5bf51ed48319e142751a9c88b104994Jeff Brown mSettings = settings; 25470825161b5bf51ed48319e142751a9c88b104994Jeff Brown mBluetoothGatt = bluetoothGatt; 25570825161b5bf51ed48319e142751a9c88b104994Jeff Brown mClientIf = 0; 2568d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown } 25770825161b5bf51ed48319e142751a9c88b104994Jeff Brown 25870825161b5bf51ed48319e142751a9c88b104994Jeff Brown public void startRegisteration() { 25970825161b5bf51ed48319e142751a9c88b104994Jeff Brown synchronized (this) { 26070825161b5bf51ed48319e142751a9c88b104994Jeff Brown if (mClientIf == -1) return; 26170825161b5bf51ed48319e142751a9c88b104994Jeff Brown 2628d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown try { 26370825161b5bf51ed48319e142751a9c88b104994Jeff Brown UUID uuid = UUID.randomUUID(); 26470825161b5bf51ed48319e142751a9c88b104994Jeff Brown mBluetoothGatt.registerClient(new ParcelUuid(uuid), this); 26590d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn wait(LE_CALLBACK_TIMEOUT_MILLIS); 26670825161b5bf51ed48319e142751a9c88b104994Jeff Brown } catch (InterruptedException | RemoteException e) { 26770825161b5bf51ed48319e142751a9c88b104994Jeff Brown Log.e(TAG, "Failed to start registeration", e); 26870825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 26970825161b5bf51ed48319e142751a9c88b104994Jeff Brown if (mClientIf > 0 && mIsAdvertising) { 27070825161b5bf51ed48319e142751a9c88b104994Jeff Brown mLeAdvertisers.put(mAdvertiseCallback, this); 27170825161b5bf51ed48319e142751a9c88b104994Jeff Brown } else if (mClientIf <= 0) { 27270825161b5bf51ed48319e142751a9c88b104994Jeff Brown // Post internal error if registration failed. 27370825161b5bf51ed48319e142751a9c88b104994Jeff Brown postStartFailure(mAdvertiseCallback, 27470825161b5bf51ed48319e142751a9c88b104994Jeff Brown AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); 27570825161b5bf51ed48319e142751a9c88b104994Jeff Brown } else { 27670825161b5bf51ed48319e142751a9c88b104994Jeff Brown // Unregister application if it's already registered but advertise failed. 27770825161b5bf51ed48319e142751a9c88b104994Jeff Brown try { 27870825161b5bf51ed48319e142751a9c88b104994Jeff Brown mBluetoothGatt.unregisterClient(mClientIf); 27970825161b5bf51ed48319e142751a9c88b104994Jeff Brown mClientIf = -1; 28070825161b5bf51ed48319e142751a9c88b104994Jeff Brown } catch (RemoteException e) { 28170825161b5bf51ed48319e142751a9c88b104994Jeff Brown Log.e(TAG, "remote exception when unregistering", e); 28270825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 28370825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 28470825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 28570825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 28670825161b5bf51ed48319e142751a9c88b104994Jeff Brown 28770825161b5bf51ed48319e142751a9c88b104994Jeff Brown public void stopAdvertising() { 28870825161b5bf51ed48319e142751a9c88b104994Jeff Brown synchronized (this) { 28970825161b5bf51ed48319e142751a9c88b104994Jeff Brown try { 29070825161b5bf51ed48319e142751a9c88b104994Jeff Brown mBluetoothGatt.stopMultiAdvertising(mClientIf); 29170825161b5bf51ed48319e142751a9c88b104994Jeff Brown wait(LE_CALLBACK_TIMEOUT_MILLIS); 29270825161b5bf51ed48319e142751a9c88b104994Jeff Brown } catch (InterruptedException | RemoteException e) { 29370825161b5bf51ed48319e142751a9c88b104994Jeff Brown Log.e(TAG, "Failed to stop advertising", e); 29470825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 29570825161b5bf51ed48319e142751a9c88b104994Jeff Brown // Advertise callback should have been removed from LeAdvertisers when 29670825161b5bf51ed48319e142751a9c88b104994Jeff Brown // onMultiAdvertiseCallback was called. In case onMultiAdvertiseCallback is never 29770825161b5bf51ed48319e142751a9c88b104994Jeff Brown // invoked and wait timeout expires, remove callback here. 29870825161b5bf51ed48319e142751a9c88b104994Jeff Brown if (mLeAdvertisers.containsKey(mAdvertiseCallback)) { 29970825161b5bf51ed48319e142751a9c88b104994Jeff Brown mLeAdvertisers.remove(mAdvertiseCallback); 30070825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 30170825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 30270825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 30370825161b5bf51ed48319e142751a9c88b104994Jeff Brown 30470825161b5bf51ed48319e142751a9c88b104994Jeff Brown /** 30570825161b5bf51ed48319e142751a9c88b104994Jeff Brown * Application interface registered - app is ready to go 30670825161b5bf51ed48319e142751a9c88b104994Jeff Brown */ 30770825161b5bf51ed48319e142751a9c88b104994Jeff Brown @Override 30890d2db3d21d07c2a4b4cbbc558f5ec59d20098c3Dianne Hackborn public void onClientRegistered(int status, int clientIf) { 30970825161b5bf51ed48319e142751a9c88b104994Jeff Brown Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf); 31070825161b5bf51ed48319e142751a9c88b104994Jeff Brown synchronized (this) { 31176936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright if (status == BluetoothGatt.GATT_SUCCESS) { 31276936eb96acdaf7feb221000be3e0d154fa5000cMichael Wright mClientIf = clientIf; 31370825161b5bf51ed48319e142751a9c88b104994Jeff Brown try { 3148d60866e2100db70ecf0502c14768a384514d7e9Jeff Brown mBluetoothGatt.startMultiAdvertising(mClientIf, mAdvertisement, 31570825161b5bf51ed48319e142751a9c88b104994Jeff Brown mScanResponse, mSettings); 31670825161b5bf51ed48319e142751a9c88b104994Jeff Brown return; 31770825161b5bf51ed48319e142751a9c88b104994Jeff Brown } catch (RemoteException e) { 31870825161b5bf51ed48319e142751a9c88b104994Jeff Brown Log.e(TAG, "failed to start advertising", e); 31970825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 32070825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 32170825161b5bf51ed48319e142751a9c88b104994Jeff Brown // Registration failed. 32270825161b5bf51ed48319e142751a9c88b104994Jeff Brown mClientIf = -1; 32370825161b5bf51ed48319e142751a9c88b104994Jeff Brown notifyAll(); 32470825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 32570825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 32670825161b5bf51ed48319e142751a9c88b104994Jeff Brown 32770825161b5bf51ed48319e142751a9c88b104994Jeff Brown @Override 32870825161b5bf51ed48319e142751a9c88b104994Jeff Brown public void onMultiAdvertiseCallback(int status, boolean isStart, 32970825161b5bf51ed48319e142751a9c88b104994Jeff Brown AdvertiseSettings settings) { 33070825161b5bf51ed48319e142751a9c88b104994Jeff Brown synchronized (this) { 33170825161b5bf51ed48319e142751a9c88b104994Jeff Brown if (isStart) { 33270825161b5bf51ed48319e142751a9c88b104994Jeff Brown if (status == AdvertiseCallback.ADVERTISE_SUCCESS) { 33370825161b5bf51ed48319e142751a9c88b104994Jeff Brown // Start success 33470825161b5bf51ed48319e142751a9c88b104994Jeff Brown mIsAdvertising = true; 33570825161b5bf51ed48319e142751a9c88b104994Jeff Brown postStartSuccess(mAdvertiseCallback, settings); 33670825161b5bf51ed48319e142751a9c88b104994Jeff Brown } else { 33770825161b5bf51ed48319e142751a9c88b104994Jeff Brown // Start failure. 3389eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown postStartFailure(mAdvertiseCallback, status); 3399eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown } 3409eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown } else { 3419eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown // unregister client for stop. 3429eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown try { 3439eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown mBluetoothGatt.unregisterClient(mClientIf); 3449eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown mClientIf = -1; 3459eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown mIsAdvertising = false; 3469eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown mLeAdvertisers.remove(mAdvertiseCallback); 3479eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown } catch (RemoteException e) { 3489eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown Log.e(TAG, "remote exception when unregistering", e); 3499eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown } 3509eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown } 3519eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown notifyAll(); 3529eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown } 3539eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown 3549eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown } 3559eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown } 3569eb7d86181729c3eb769d71123c4ce9ffc868f08Jeff Brown 35770825161b5bf51ed48319e142751a9c88b104994Jeff Brown private void postStartFailure(final AdvertiseCallback callback, final int error) { 35870825161b5bf51ed48319e142751a9c88b104994Jeff Brown mHandler.post(new Runnable() { 35970825161b5bf51ed48319e142751a9c88b104994Jeff Brown @Override 36070825161b5bf51ed48319e142751a9c88b104994Jeff Brown public void run() { 36170825161b5bf51ed48319e142751a9c88b104994Jeff Brown callback.onStartFailure(error); 36270825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 36370825161b5bf51ed48319e142751a9c88b104994Jeff Brown }); 36470825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 36570825161b5bf51ed48319e142751a9c88b104994Jeff Brown 36670825161b5bf51ed48319e142751a9c88b104994Jeff Brown private void postStartSuccess(final AdvertiseCallback callback, 36770825161b5bf51ed48319e142751a9c88b104994Jeff Brown final AdvertiseSettings settings) { 36870825161b5bf51ed48319e142751a9c88b104994Jeff Brown mHandler.post(new Runnable() { 36970825161b5bf51ed48319e142751a9c88b104994Jeff Brown 37070825161b5bf51ed48319e142751a9c88b104994Jeff Brown @Override 37170825161b5bf51ed48319e142751a9c88b104994Jeff Brown public void run() { 37270825161b5bf51ed48319e142751a9c88b104994Jeff Brown callback.onStartSuccess(settings); 37370825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 37470825161b5bf51ed48319e142751a9c88b104994Jeff Brown }); 37570825161b5bf51ed48319e142751a9c88b104994Jeff Brown } 37670825161b5bf51ed48319e142751a9c88b104994Jeff Brown} 37770825161b5bf51ed48319e142751a9c88b104994Jeff Brown