Loading framework/java/android/bluetooth/BluetoothAdapter.java +26 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.annotation.SystemApi; import android.app.ActivityThread; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.PeriodicAdvertisingManager; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanRecord; Loading Loading @@ -525,6 +526,7 @@ public final class BluetoothAdapter { private static BluetoothLeScanner sBluetoothLeScanner; private static BluetoothLeAdvertiser sBluetoothLeAdvertiser; private static PeriodicAdvertisingManager sPeriodicAdvertisingManager; private final IBluetoothManager mManagerService; private IBluetooth mService; Loading Loading @@ -629,6 +631,30 @@ public final class BluetoothAdapter { return sBluetoothLeAdvertiser; } /** * Returns a {@link PeriodicAdvertisingManager} object for Bluetooth LE Periodic Advertising * operations. Will return null if Bluetooth is turned off or if Bluetooth LE Periodic * Advertising is not supported on this device. * <p> * Use {@link #isLePeriodicAdvertisingSupported()} to check whether LE Periodic Advertising is * supported on this device before calling this method. */ public PeriodicAdvertisingManager getPeriodicAdvertisingManager() { if (!getLeAccess()) return null; if (!isLePeriodicAdvertisingSupported()) return null; synchronized (mLock) { if (sPeriodicAdvertisingManager == null) { sPeriodicAdvertisingManager = new PeriodicAdvertisingManager(mManagerService); } } return sPeriodicAdvertisingManager; } /** * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations. */ Loading framework/java/android/bluetooth/IBluetoothGatt.aidl +5 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.bluetooth.BluetoothGattService; import android.bluetooth.le.AdvertiseSettings; import android.bluetooth.le.AdvertiseData; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.bluetooth.le.ResultStorageDescriptor; import android.os.ParcelUuid; Loading @@ -29,6 +30,7 @@ import android.os.WorkSource; import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothGattServerCallback; import android.bluetooth.le.IAdvertiserCallback; import android.bluetooth.le.IPeriodicAdvertisingCallback; import android.bluetooth.le.IScannerCallback; /** Loading @@ -53,6 +55,9 @@ interface IBluetoothGatt { in AdvertiseSettings settings); void stopMultiAdvertising(in int advertiserId); void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback); void unregisterSync(in IPeriodicAdvertisingCallback callback); void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport); Loading framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl 0 → 100644 +31 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.bluetooth.le; import android.bluetooth.BluetoothDevice; import android.bluetooth.le.PeriodicAdvertisingReport; /** * Callback definitions for interacting with Periodic Advertising * @hide */ oneway interface IPeriodicAdvertisingCallback { void onSyncEstablished(in int syncHandle, in BluetoothDevice device, in int advertisingSid, in int skip, in int timeout, in int status); void onPeriodicAdvertisingReport(in PeriodicAdvertisingReport report); void onSyncLost(in int syncHandle); } framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.bluetooth.le; import android.bluetooth.BluetoothDevice; /** * Bluetooth LE periodic advertising callbacks, used to deliver periodic * advertising operation status. * * @see PeriodicAdvertisingManager#createSync */ public abstract class PeriodicAdvertisingCallback { /** * The requested operation was successful. * * @hide */ public static final int SYNC_SUCCESS = 0; /** * Sync failed to be established because remote device did not respond. */ public static final int SYNC_NO_RESPONSE = 1; /** * Sync failed to be established because controller can't support more syncs. */ public static final int SYNC_NO_RESOURCES = 2; /** * Callback when synchronization was established. * * @param syncHandle handle used to identify this synchronization. * @param device remote device. * @param advertisingSid synchronized advertising set id. * @param skip The number of periodic advertising packets that can be skipped * after a successful receive in force. @see PeriodicAdvertisingManager#createSync * @param timeout Synchronization timeout for the periodic advertising in force. One * unit is 10ms. @see PeriodicAdvertisingManager#createSync * @param timeout * @param status operation status. */ public void onSyncEstablished(int syncHandle, BluetoothDevice device, int advertisingSid, int skip, int timeout, int status) {} /** * Callback when periodic advertising report is received. * * @param report periodic advertising report. */ public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {} /** * Callback when periodic advertising synchronization was lost. * * @param syncHandle handle used to identify this synchronization. */ public void onSyncLost(int syncHandle) {} } framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java 0 → 100644 +237 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package android.bluetooth.le; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.util.Log; import java.util.IdentityHashMap; import java.util.Map; /** * This class provides methods to perform periodic advertising related * operations. An application can register for periodic advertisements using * {@link PeriodicAdvertisingManager#registerSync}. * <p> * Use {@link BluetoothAdapter#getPeriodicAdvertisingManager()} to get an * instance of {@link PeriodicAdvertisingManager}. * <p> * <b>Note:</b> Most of the methods here require * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. */ public final class PeriodicAdvertisingManager { private static final String TAG = "PeriodicAdvertisingManager"; private static final int SKIP_MIN = 0; private static final int SKIP_MAX = 499; private static final int TIMEOUT_MIN = 10; private static final int TIMEOUT_MAX = 16384; private static final int SYNC_STARTING = -1; private final IBluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; /* maps callback, to callback wrapper and sync handle */ Map<PeriodicAdvertisingCallback, IPeriodicAdvertisingCallback /* callbackWrapper */> callbackWrappers; /** * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. * * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management. * @hide */ public PeriodicAdvertisingManager(IBluetoothManager bluetoothManager) { mBluetoothManager = bluetoothManager; mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); callbackWrappers = new IdentityHashMap<>(); } /** * Synchronize with periodic advertising pointed to by the {@code scanResult}. * The {@code scanResult} used must contain a valid advertisingSid. First * call to registerSync will use the {@code skip} and {@code timeout} provided. * Subsequent calls from other apps, trying to sync with same set will reuse * existing sync, thus {@code skip} and {@code timeout} values will not take * effect. The values in effect will be returned in * {@link PeriodicAdvertisingCallback#onSyncEstablished}. * * @param scanResult Scan result containing advertisingSid. * @param skip The number of periodic advertising packets that can be skipped * after a successful receive. Must be between 0 and 499. * @param timeout Synchronization timeout for the periodic advertising. One * unit is 10ms. Must be between 10 (100ms) and 16384 (163.84s). * @param callback Callback used to deliver all operations status. * @throws IllegalArgumentException if {@code scanResult} is null or {@code * skip} is invalid or {@code timeout} is invalid or {@code callback} is null. */ public void registerSync(ScanResult scanResult, int skip, int timeout, PeriodicAdvertisingCallback callback) { registerSync(scanResult, skip, timeout, callback, null); } /** * Synchronize with periodic advertising pointed to by the {@code scanResult}. * The {@code scanResult} used must contain a valid advertisingSid. First * call to registerSync will use the {@code skip} and {@code timeout} provided. * Subsequent calls from other apps, trying to sync with same set will reuse * existing sync, thus {@code skip} and {@code timeout} values will not take * effect. The values in effect will be returned in * {@link PeriodicAdvertisingCallback#onSyncEstablished}. * * @param scanResult Scan result containing advertisingSid. * @param skip The number of periodic advertising packets that can be skipped * after a successful receive. Must be between 0 and 499. * @param timeout Synchronization timeout for the periodic advertising. One * unit is 10ms. Must be between 10 (100ms) and 16384 (163.84s). * @param callback Callback used to deliver all operations status. * @param handler thread upon which the callbacks will be invoked. * @throws IllegalArgumentException if {@code scanResult} is null or {@code * skip} is invalid or {@code timeout} is invalid or {@code callback} is null. */ public void registerSync(ScanResult scanResult, int skip, int timeout, PeriodicAdvertisingCallback callback, Handler handler) { if (callback == null) { throw new IllegalArgumentException("callback can't be null"); } if (scanResult == null) { throw new IllegalArgumentException("scanResult can't be null"); } if (scanResult.getAdvertisingSid() == ScanResult.SID_NOT_PRESENT) { throw new IllegalArgumentException("scanResult must contain a valid sid"); } if (skip < SKIP_MIN || skip > SKIP_MAX) { throw new IllegalArgumentException( "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX); } if (timeout < TIMEOUT_MIN || timeout > TIMEOUT_MAX) { throw new IllegalArgumentException( "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX); } IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); } catch (RemoteException e) { Log.e(TAG, "Failed to get Bluetooth gatt - ", e); callback.onSyncEstablished(0, scanResult.getDevice(), scanResult.getAdvertisingSid(), skip, timeout, PeriodicAdvertisingCallback.SYNC_NO_RESOURCES); return; } if (handler == null) handler = new Handler(Looper.getMainLooper()); IPeriodicAdvertisingCallback wrapped = wrap(callback, handler); callbackWrappers.put(callback, wrapped); try { gatt.registerSync(scanResult, skip, timeout, wrapped); } catch (RemoteException e) { Log.e(TAG, "Failed to register sync - ", e); return; } } /** * Cancel pending attempt to create sync, or terminate existing sync. * * @param callback Callback used to deliver all operations status. * @throws IllegalArgumentException if {@code callback} is null, or not a properly * registered callback. */ public void unregisterSync(PeriodicAdvertisingCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback can't be null"); } IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); } catch (RemoteException e) { Log.e(TAG, "Failed to get Bluetooth gatt - ", e); return; } IPeriodicAdvertisingCallback wrapper = callbackWrappers.remove(callback); if (wrapper == null) { throw new IllegalArgumentException("callback was not properly registered"); } try { gatt.unregisterSync(wrapper); } catch (RemoteException e) { Log.e(TAG, "Failed to cancel sync creation - ", e); return; } } private IPeriodicAdvertisingCallback wrap(PeriodicAdvertisingCallback callback, Handler handler) { return new IPeriodicAdvertisingCallback.Stub() { public void onSyncEstablished(int syncHandle, BluetoothDevice device, int advertisingSid, int skip, int timeout, int status) { handler.post(new Runnable() { @Override public void run() { callback.onSyncEstablished(syncHandle, device, advertisingSid, skip, timeout, status); if (status != PeriodicAdvertisingCallback.SYNC_SUCCESS) { // App can still unregister the sync until notified it failed. Remove callback // after app was notifed. callbackWrappers.remove(callback); } } }); } public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) { handler.post(new Runnable() { @Override public void run() { callback.onPeriodicAdvertisingReport(report); } }); } public void onSyncLost(int syncHandle) { handler.post(new Runnable() { @Override public void run() { callback.onSyncLost(syncHandle); // App can still unregister the sync until notified it's lost. Remove callback after // app was notifed. callbackWrappers.remove(callback); } }); } }; } } Loading
framework/java/android/bluetooth/BluetoothAdapter.java +26 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.annotation.SystemApi; import android.app.ActivityThread; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.PeriodicAdvertisingManager; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanRecord; Loading Loading @@ -525,6 +526,7 @@ public final class BluetoothAdapter { private static BluetoothLeScanner sBluetoothLeScanner; private static BluetoothLeAdvertiser sBluetoothLeAdvertiser; private static PeriodicAdvertisingManager sPeriodicAdvertisingManager; private final IBluetoothManager mManagerService; private IBluetooth mService; Loading Loading @@ -629,6 +631,30 @@ public final class BluetoothAdapter { return sBluetoothLeAdvertiser; } /** * Returns a {@link PeriodicAdvertisingManager} object for Bluetooth LE Periodic Advertising * operations. Will return null if Bluetooth is turned off or if Bluetooth LE Periodic * Advertising is not supported on this device. * <p> * Use {@link #isLePeriodicAdvertisingSupported()} to check whether LE Periodic Advertising is * supported on this device before calling this method. */ public PeriodicAdvertisingManager getPeriodicAdvertisingManager() { if (!getLeAccess()) return null; if (!isLePeriodicAdvertisingSupported()) return null; synchronized (mLock) { if (sPeriodicAdvertisingManager == null) { sPeriodicAdvertisingManager = new PeriodicAdvertisingManager(mManagerService); } } return sPeriodicAdvertisingManager; } /** * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations. */ Loading
framework/java/android/bluetooth/IBluetoothGatt.aidl +5 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.bluetooth.BluetoothGattService; import android.bluetooth.le.AdvertiseSettings; import android.bluetooth.le.AdvertiseData; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.bluetooth.le.ResultStorageDescriptor; import android.os.ParcelUuid; Loading @@ -29,6 +30,7 @@ import android.os.WorkSource; import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothGattServerCallback; import android.bluetooth.le.IAdvertiserCallback; import android.bluetooth.le.IPeriodicAdvertisingCallback; import android.bluetooth.le.IScannerCallback; /** Loading @@ -53,6 +55,9 @@ interface IBluetoothGatt { in AdvertiseSettings settings); void stopMultiAdvertising(in int advertiserId); void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback); void unregisterSync(in IPeriodicAdvertisingCallback callback); void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport); Loading
framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl 0 → 100644 +31 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.bluetooth.le; import android.bluetooth.BluetoothDevice; import android.bluetooth.le.PeriodicAdvertisingReport; /** * Callback definitions for interacting with Periodic Advertising * @hide */ oneway interface IPeriodicAdvertisingCallback { void onSyncEstablished(in int syncHandle, in BluetoothDevice device, in int advertisingSid, in int skip, in int timeout, in int status); void onPeriodicAdvertisingReport(in PeriodicAdvertisingReport report); void onSyncLost(in int syncHandle); }
framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.bluetooth.le; import android.bluetooth.BluetoothDevice; /** * Bluetooth LE periodic advertising callbacks, used to deliver periodic * advertising operation status. * * @see PeriodicAdvertisingManager#createSync */ public abstract class PeriodicAdvertisingCallback { /** * The requested operation was successful. * * @hide */ public static final int SYNC_SUCCESS = 0; /** * Sync failed to be established because remote device did not respond. */ public static final int SYNC_NO_RESPONSE = 1; /** * Sync failed to be established because controller can't support more syncs. */ public static final int SYNC_NO_RESOURCES = 2; /** * Callback when synchronization was established. * * @param syncHandle handle used to identify this synchronization. * @param device remote device. * @param advertisingSid synchronized advertising set id. * @param skip The number of periodic advertising packets that can be skipped * after a successful receive in force. @see PeriodicAdvertisingManager#createSync * @param timeout Synchronization timeout for the periodic advertising in force. One * unit is 10ms. @see PeriodicAdvertisingManager#createSync * @param timeout * @param status operation status. */ public void onSyncEstablished(int syncHandle, BluetoothDevice device, int advertisingSid, int skip, int timeout, int status) {} /** * Callback when periodic advertising report is received. * * @param report periodic advertising report. */ public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {} /** * Callback when periodic advertising synchronization was lost. * * @param syncHandle handle used to identify this synchronization. */ public void onSyncLost(int syncHandle) {} }
framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java 0 → 100644 +237 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package android.bluetooth.le; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.util.Log; import java.util.IdentityHashMap; import java.util.Map; /** * This class provides methods to perform periodic advertising related * operations. An application can register for periodic advertisements using * {@link PeriodicAdvertisingManager#registerSync}. * <p> * Use {@link BluetoothAdapter#getPeriodicAdvertisingManager()} to get an * instance of {@link PeriodicAdvertisingManager}. * <p> * <b>Note:</b> Most of the methods here require * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. */ public final class PeriodicAdvertisingManager { private static final String TAG = "PeriodicAdvertisingManager"; private static final int SKIP_MIN = 0; private static final int SKIP_MAX = 499; private static final int TIMEOUT_MIN = 10; private static final int TIMEOUT_MAX = 16384; private static final int SYNC_STARTING = -1; private final IBluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; /* maps callback, to callback wrapper and sync handle */ Map<PeriodicAdvertisingCallback, IPeriodicAdvertisingCallback /* callbackWrapper */> callbackWrappers; /** * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. * * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management. * @hide */ public PeriodicAdvertisingManager(IBluetoothManager bluetoothManager) { mBluetoothManager = bluetoothManager; mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); callbackWrappers = new IdentityHashMap<>(); } /** * Synchronize with periodic advertising pointed to by the {@code scanResult}. * The {@code scanResult} used must contain a valid advertisingSid. First * call to registerSync will use the {@code skip} and {@code timeout} provided. * Subsequent calls from other apps, trying to sync with same set will reuse * existing sync, thus {@code skip} and {@code timeout} values will not take * effect. The values in effect will be returned in * {@link PeriodicAdvertisingCallback#onSyncEstablished}. * * @param scanResult Scan result containing advertisingSid. * @param skip The number of periodic advertising packets that can be skipped * after a successful receive. Must be between 0 and 499. * @param timeout Synchronization timeout for the periodic advertising. One * unit is 10ms. Must be between 10 (100ms) and 16384 (163.84s). * @param callback Callback used to deliver all operations status. * @throws IllegalArgumentException if {@code scanResult} is null or {@code * skip} is invalid or {@code timeout} is invalid or {@code callback} is null. */ public void registerSync(ScanResult scanResult, int skip, int timeout, PeriodicAdvertisingCallback callback) { registerSync(scanResult, skip, timeout, callback, null); } /** * Synchronize with periodic advertising pointed to by the {@code scanResult}. * The {@code scanResult} used must contain a valid advertisingSid. First * call to registerSync will use the {@code skip} and {@code timeout} provided. * Subsequent calls from other apps, trying to sync with same set will reuse * existing sync, thus {@code skip} and {@code timeout} values will not take * effect. The values in effect will be returned in * {@link PeriodicAdvertisingCallback#onSyncEstablished}. * * @param scanResult Scan result containing advertisingSid. * @param skip The number of periodic advertising packets that can be skipped * after a successful receive. Must be between 0 and 499. * @param timeout Synchronization timeout for the periodic advertising. One * unit is 10ms. Must be between 10 (100ms) and 16384 (163.84s). * @param callback Callback used to deliver all operations status. * @param handler thread upon which the callbacks will be invoked. * @throws IllegalArgumentException if {@code scanResult} is null or {@code * skip} is invalid or {@code timeout} is invalid or {@code callback} is null. */ public void registerSync(ScanResult scanResult, int skip, int timeout, PeriodicAdvertisingCallback callback, Handler handler) { if (callback == null) { throw new IllegalArgumentException("callback can't be null"); } if (scanResult == null) { throw new IllegalArgumentException("scanResult can't be null"); } if (scanResult.getAdvertisingSid() == ScanResult.SID_NOT_PRESENT) { throw new IllegalArgumentException("scanResult must contain a valid sid"); } if (skip < SKIP_MIN || skip > SKIP_MAX) { throw new IllegalArgumentException( "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX); } if (timeout < TIMEOUT_MIN || timeout > TIMEOUT_MAX) { throw new IllegalArgumentException( "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX); } IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); } catch (RemoteException e) { Log.e(TAG, "Failed to get Bluetooth gatt - ", e); callback.onSyncEstablished(0, scanResult.getDevice(), scanResult.getAdvertisingSid(), skip, timeout, PeriodicAdvertisingCallback.SYNC_NO_RESOURCES); return; } if (handler == null) handler = new Handler(Looper.getMainLooper()); IPeriodicAdvertisingCallback wrapped = wrap(callback, handler); callbackWrappers.put(callback, wrapped); try { gatt.registerSync(scanResult, skip, timeout, wrapped); } catch (RemoteException e) { Log.e(TAG, "Failed to register sync - ", e); return; } } /** * Cancel pending attempt to create sync, or terminate existing sync. * * @param callback Callback used to deliver all operations status. * @throws IllegalArgumentException if {@code callback} is null, or not a properly * registered callback. */ public void unregisterSync(PeriodicAdvertisingCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback can't be null"); } IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); } catch (RemoteException e) { Log.e(TAG, "Failed to get Bluetooth gatt - ", e); return; } IPeriodicAdvertisingCallback wrapper = callbackWrappers.remove(callback); if (wrapper == null) { throw new IllegalArgumentException("callback was not properly registered"); } try { gatt.unregisterSync(wrapper); } catch (RemoteException e) { Log.e(TAG, "Failed to cancel sync creation - ", e); return; } } private IPeriodicAdvertisingCallback wrap(PeriodicAdvertisingCallback callback, Handler handler) { return new IPeriodicAdvertisingCallback.Stub() { public void onSyncEstablished(int syncHandle, BluetoothDevice device, int advertisingSid, int skip, int timeout, int status) { handler.post(new Runnable() { @Override public void run() { callback.onSyncEstablished(syncHandle, device, advertisingSid, skip, timeout, status); if (status != PeriodicAdvertisingCallback.SYNC_SUCCESS) { // App can still unregister the sync until notified it failed. Remove callback // after app was notifed. callbackWrappers.remove(callback); } } }); } public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) { handler.post(new Runnable() { @Override public void run() { callback.onPeriodicAdvertisingReport(report); } }); } public void onSyncLost(int syncHandle) { handler.post(new Runnable() { @Override public void run() { callback.onSyncLost(syncHandle); // App can still unregister the sync until notified it's lost. Remove callback after // app was notifed. callbackWrappers.remove(callback); } }); } }; } }