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