Chromium Code Reviews| Index: device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java |
| diff --git a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java |
| index 5775b0073f0a72dc4c815a1db2f0d511c183baf9..4526d9dd5f6b2e95bb9e51d75b7ad1bce8a3cf81 100644 |
| --- a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java |
| +++ b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java |
| @@ -4,30 +4,46 @@ |
| package org.chromium.device.bluetooth; |
| +import android.annotation.TargetApi; |
| import android.bluetooth.BluetoothAdapter; |
| +import android.bluetooth.le.ScanSettings; |
| +import android.os.Build; |
| import org.chromium.base.CalledByNative; |
| import org.chromium.base.JNINamespace; |
| import org.chromium.base.Log; |
| +import java.util.List; |
| + |
| /** |
| * Exposes android.bluetooth.BluetoothAdapter as necessary for C++ |
| * device::BluetoothAdapterAndroid, which implements the cross platform |
| * device::BluetoothAdapter. |
| */ |
| @JNINamespace("device") |
| +@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
| final class ChromeBluetoothAdapter { |
| private static final String TAG = "cr.Bluetooth"; |
| + private long mNativeBluetoothAdapterAndroid; |
| private Wrappers.BluetoothAdapterWrapper mAdapter; |
| + private int mNumDiscoverySessions; |
| + private ScanCallback mScanCallback; |
| + |
| + // --------------------------------------------------------------------------------------------- |
| + // Construction and handler for C++ object destruction. |
| /** |
| * Constructs a ChromeBluetoothAdapter. |
| + * @param nativeBluetoothAdapterAndroid Is the paired C++ |
|
armansito
2015/06/30 20:37:22
I'd say "coupled" or "associated" rather than pair
scheib
2015/06/30 21:02:14
Done.
|
| + * BluetoothAdapterAndroid pointer value. |
| * @param adapterWrapper Wraps the default android.bluetooth.BluetoothAdapter, |
| * but may be either null if an adapter is not available |
| * or a fake for testing. |
| */ |
| - private ChromeBluetoothAdapter(Wrappers.BluetoothAdapterWrapper adapterWrapper) { |
| + private ChromeBluetoothAdapter( |
| + long nativeBluetoothAdapterAndroid, Wrappers.BluetoothAdapterWrapper adapterWrapper) { |
| + mNativeBluetoothAdapterAndroid = nativeBluetoothAdapterAndroid; |
| mAdapter = adapterWrapper; |
| if (adapterWrapper == null) { |
| Log.i(TAG, "ChromeBluetoothAdapter created with no adapterWrapper."); |
| @@ -36,6 +52,15 @@ final class ChromeBluetoothAdapter { |
| } |
| } |
| + /** |
| + * Handles C++ object being destroyed. |
| + */ |
| + @CalledByNative |
| + private void onBluetoothAdapterAndroidDestruction() { |
| + stopScan(); |
| + mNativeBluetoothAdapterAndroid = 0; |
| + } |
| + |
| // --------------------------------------------------------------------------------------------- |
| // BluetoothAdapterAndroid methods implemented in java: |
| @@ -43,8 +68,10 @@ final class ChromeBluetoothAdapter { |
| // 'Object' type must be used because inner class Wrappers.BluetoothAdapterWrapper reference is |
| // not handled by jni_generator.py JavaToJni. http://crbug.com/505554 |
| @CalledByNative |
| - public static ChromeBluetoothAdapter create(Object adapterWrapper) { |
| - return new ChromeBluetoothAdapter((Wrappers.BluetoothAdapterWrapper) adapterWrapper); |
| + public static ChromeBluetoothAdapter create( |
| + long nativeBluetoothAdapterAndroid, Object adapterWrapper) { |
| + return new ChromeBluetoothAdapter( |
| + nativeBluetoothAdapterAndroid, (Wrappers.BluetoothAdapterWrapper) adapterWrapper); |
| } |
| // Implements BluetoothAdapterAndroid::GetAddress. |
| @@ -89,6 +116,123 @@ final class ChromeBluetoothAdapter { |
| // Implements BluetoothAdapterAndroid::IsDiscovering. |
| @CalledByNative |
| private boolean isDiscovering() { |
| - return isPresent() && mAdapter.isDiscovering(); |
| + return isPresent() && (mAdapter.isDiscovering() || mScanCallback != null); |
| + } |
| + |
| + // Implements BluetoothAdapterAndroid::AddDiscoverySession. |
| + @CalledByNative |
| + private boolean addDiscoverySession() { |
| + if (!isPowered()) { |
| + Log.d(TAG, "addDiscoverySession: Fails: !isPowered"); |
| + return false; |
| + } |
| + |
| + mNumDiscoverySessions++; |
| + Log.d(TAG, "addDiscoverySession: Now %d sessions.", mNumDiscoverySessions); |
| + if (mNumDiscoverySessions > 1) { |
| + return true; |
| + } |
| + |
| + if (startScan()) { |
| + return true; |
| + } else { |
|
armansito
2015/06/30 20:37:22
No need for else since you have an early return
scheib
2015/06/30 21:02:14
Done.
|
| + mNumDiscoverySessions--; |
| + return false; |
| + } |
| + } |
| + |
| + // Implements BluetoothAdapterAndroid::RemoveDiscoverySession. |
| + @CalledByNative |
| + private boolean removeDiscoverySession() { |
| + if (mNumDiscoverySessions == 0) { |
| + assert false; |
| + Log.w(TAG, "removeDiscoverySession: No scan in progress."); |
| + return false; |
| + } |
| + |
| + --mNumDiscoverySessions; |
| + |
| + if (mNumDiscoverySessions == 0) { |
| + Log.d(TAG, "removeDiscoverySession: Now 0 sessions. Stopping scan."); |
| + return stopScan(); |
|
armansito
2015/06/30 20:37:22
no need for else
scheib
2015/06/30 21:02:14
Done.
|
| + } else { |
| + Log.d(TAG, "removeDiscoverySession: Now %d sessions.", mNumDiscoverySessions); |
| + } |
| + return true; |
| + } |
| + |
| + // --------------------------------------------------------------------------------------------- |
| + // Implementation details: |
| + |
| + /** |
| + * Starts a Low Energy scan. |
| + * @return True on success. |
| + */ |
| + private boolean startScan() { |
| + // ScanSettings Note: SCAN_FAILED_FEATURE_UNSUPPORTED is caused (at least on some devices) |
| + // if setReportDelay() is used or if SCAN_MODE_LOW_LATENCY isn't used. |
| + ScanSettings scanSettings = |
| + new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); |
| + |
| + assert mScanCallback == null; |
| + mScanCallback = new ScanCallback(); |
| + |
| + try { |
| + mAdapter.getBluetoothLeScanner().startScan( |
| + null /* filters */, scanSettings, mScanCallback); |
| + } catch (IllegalArgumentException e) { |
| + Log.e(TAG, "Cannot start scan: " + e); |
| + return false; |
| + } |
| + return true; |
| + } |
| + |
| + /** |
| + * Stops the Low Energy scan. |
| + * @return True if a scan was in progress. |
| + */ |
| + private boolean stopScan() { |
| + if (mScanCallback == null) { |
| + return false; |
| + } |
| + try { |
| + mAdapter.getBluetoothLeScanner().stopScan(mScanCallback); |
| + } catch (IllegalArgumentException e) { |
| + Log.e(TAG, "Cannot stop scan: " + e); |
| + mScanCallback = null; |
| + return false; |
| + } |
| + mScanCallback = null; |
| + return true; |
| } |
| + |
| + /** |
| + * Implements callbacks used during a Low Energy scan by notifying upon |
| + * devices discovered or detecting a scan failure. |
| + */ |
| + private class ScanCallback extends Wrappers.ScanCallbackWrapper { |
| + @Override |
| + public void onBatchScanResultWrappers(List<Wrappers.ScanResultWrapper> results) { |
| + Log.v(TAG, "onBatchScanResults"); |
| + } |
| + |
| + @Override |
| + public void onScanResultWrapper(int callbackType, Wrappers.ScanResultWrapper result) { |
| + Log.v(TAG, "onScanResult %d %s %s", callbackType, result.getDevice().getAddress(), |
| + result.getDevice().getName()); |
| + } |
| + |
| + @Override |
| + public void onScanFailed(int errorCode) { |
| + Log.w(TAG, "onScanFailed: %d", errorCode); |
| + nativeOnScanFailed(mNativeBluetoothAdapterAndroid); |
| + mNumDiscoverySessions = 0; |
| + } |
| + } |
| + |
| + // --------------------------------------------------------------------------------------------- |
| + // BluetoothAdapterAndroid C++ methods declared for access from java: |
| + |
| + // Binds to BluetoothAdapterAndroid::OnScanFailed. |
| + private native void nativeOnScanFailed(long nativeBluetoothAdapterAndroid); |
| } |