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..a29c9d8c2515d0af3f7709e50ec581becd3218f2 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 associated C++ |
+ * 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) { |
+ public 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); |
+ private static ChromeBluetoothAdapter create( |
+ long nativeBluetoothAdapterAndroid, Object adapterWrapper) { |
+ return new ChromeBluetoothAdapter( |
+ nativeBluetoothAdapterAndroid, (Wrappers.BluetoothAdapterWrapper) adapterWrapper); |
} |
// Implements BluetoothAdapterAndroid::GetAddress. |
@@ -89,6 +116,120 @@ 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()) { |
+ mNumDiscoverySessions--; |
+ return false; |
+ } |
+ return true; |
} |
+ |
+ // 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(); |
+ } |
+ |
+ Log.d(TAG, "removeDiscoverySession: Now %d sessions.", mNumDiscoverySessions); |
+ return true; |
+ } |
+ |
+ // --------------------------------------------------------------------------------------------- |
+ // Implementation details: |
+ |
+ /** |
+ * Starts a Low Energy scan. |
+ * @return True on success. |
+ */ |
+ private boolean startScan() { |
+ // scanMode 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. |
+ int scanMode = ScanSettings.SCAN_MODE_LOW_LATENCY; |
+ |
+ assert mScanCallback == null; |
+ mScanCallback = new ScanCallback(); |
+ |
+ try { |
+ mAdapter.getBluetoothLeScanner().startScan(null /* filters */, scanMode, 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 onBatchScanResult(List<Wrappers.ScanResultWrapper> results) { |
+ Log.v(TAG, "onBatchScanResults"); |
+ } |
+ |
+ @Override |
+ public void onScanResult(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); |
} |