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