Index: device/bluetooth/android/java/src/org/chromium/device/bluetooth/Wrappers.java |
diff --git a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/Wrappers.java b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/Wrappers.java |
index 36387f47c8f198db5f34bc93def243130375f5f1..4ae298d746a5e1732a78913396f455c55530c37b 100644 |
--- a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/Wrappers.java |
+++ b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/Wrappers.java |
@@ -5,14 +5,26 @@ |
package org.chromium.device.bluetooth; |
import android.Manifest; |
+import android.annotation.TargetApi; |
import android.bluetooth.BluetoothAdapter; |
+import android.bluetooth.BluetoothDevice; |
+import android.bluetooth.le.BluetoothLeScanner; |
+import android.bluetooth.le.ScanCallback; |
+import android.bluetooth.le.ScanFilter; |
+import android.bluetooth.le.ScanResult; |
+import android.bluetooth.le.ScanSettings; |
import android.content.Context; |
import android.content.pm.PackageManager; |
+import android.os.Build; |
import org.chromium.base.CalledByNative; |
import org.chromium.base.JNINamespace; |
import org.chromium.base.Log; |
+import java.util.ArrayList; |
+import java.util.HashMap; |
+import java.util.List; |
+ |
/** |
* Wrapper classes around android.bluetooth.* classes that provide an |
* indirection layer enabling fake implementations when running tests. |
@@ -22,6 +34,7 @@ import org.chromium.base.Log; |
* pass through to the Android object and instead provide fake implementations. |
*/ |
@JNINamespace("device") |
+@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
class Wrappers { |
private static final String TAG = "cr.Bluetooth"; |
@@ -30,6 +43,7 @@ class Wrappers { |
*/ |
static class BluetoothAdapterWrapper { |
private final BluetoothAdapter mAdapter; |
+ protected final BluetoothLeScannerWrapper mScanner; |
/** |
* Creates a BluetoothAdapterWrapper using the default |
@@ -39,6 +53,13 @@ class Wrappers { |
*/ |
@CalledByNative("BluetoothAdapterWrapper") |
public static BluetoothAdapterWrapper createWithDefaultAdapter(Context context) { |
+ final boolean hasMinAPI = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; |
+ if (!hasMinAPI) { |
+ Log.i(TAG, "BluetoothAdapterWrapper.create failed: SDK version (%d) too low.", |
+ Build.VERSION.SDK_INT); |
+ return null; |
+ } |
+ |
final boolean hasPermissions = |
context.checkCallingOrSelfPermission(Manifest.permission.BLUETOOTH) |
== PackageManager.PERMISSION_GRANTED |
@@ -49,17 +70,34 @@ class Wrappers { |
return null; |
} |
+ // Only Low Energy currently supported, see BluetoothAdapterAndroid class note. |
+ final boolean hasLowEnergyFeature = |
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 |
+ && context.getPackageManager().hasSystemFeature( |
+ PackageManager.FEATURE_BLUETOOTH_LE); |
+ if (!hasLowEnergyFeature) { |
+ Log.i(TAG, "BluetoothAdapterWrapper.create failed: No Low Energy support."); |
+ return null; |
+ } |
+ |
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); |
if (adapter == null) { |
Log.i(TAG, "BluetoothAdapterWrapper.create failed: Default adapter not found."); |
return null; |
} else { |
- return new BluetoothAdapterWrapper(adapter); |
+ return new BluetoothAdapterWrapper( |
+ adapter, new BluetoothLeScannerWrapper(adapter.getBluetoothLeScanner())); |
} |
} |
- public BluetoothAdapterWrapper(BluetoothAdapter adapter) { |
+ public BluetoothAdapterWrapper( |
+ BluetoothAdapter adapter, BluetoothLeScannerWrapper scanner) { |
mAdapter = adapter; |
+ mScanner = scanner; |
+ } |
+ |
+ public BluetoothLeScannerWrapper getBluetoothLeScanner() { |
+ return mScanner; |
} |
public boolean isEnabled() { |
@@ -82,4 +120,112 @@ class Wrappers { |
return mAdapter.isDiscovering(); |
} |
} |
+ |
+ /** |
+ * Wraps android.bluetooth.BluetoothLeScanner. |
+ */ |
+ static class BluetoothLeScannerWrapper { |
+ private final BluetoothLeScanner mScanner; |
+ private final HashMap<ScanCallbackWrapper, ScanCallbackImpl> mCallbacks; |
+ |
+ public BluetoothLeScannerWrapper(BluetoothLeScanner scanner) { |
+ mScanner = scanner; |
+ mCallbacks = new HashMap<ScanCallbackWrapper, ScanCallbackImpl>(); |
+ } |
+ |
+ public void startScan( |
+ List<ScanFilter> filters, int scanSettingsScanMode, ScanCallbackWrapper callback) { |
+ ScanSettings settings = |
+ new ScanSettings.Builder().setScanMode(scanSettingsScanMode).build(); |
+ |
+ ScanCallbackImpl callbackImpl = new ScanCallbackImpl(callback); |
+ mCallbacks.put(callback, callbackImpl); |
+ |
+ mScanner.startScan(filters, settings, callbackImpl); |
+ } |
+ |
+ public void stopScan(ScanCallbackWrapper callback) { |
+ ScanCallbackImpl callbackImpl = mCallbacks.remove(callback); |
+ mScanner.stopScan(callbackImpl); |
+ } |
+ } |
+ |
+ /** |
+ * Implements android.bluetooth.le.ScanCallback and passes calls through to a |
+ * provided ScanCallbackWrapper instance. |
+ * |
+ * This class is required so that Fakes can use ScanCallbackWrapper without |
+ * it extending from ScanCallback. Fakes must function even on Android |
+ * versions where ScanCallback class is not defined. |
+ */ |
+ static class ScanCallbackImpl extends ScanCallback { |
+ final ScanCallbackWrapper mWrapperCallback; |
+ |
+ ScanCallbackImpl(ScanCallbackWrapper wrapperCallback) { |
+ mWrapperCallback = wrapperCallback; |
+ } |
+ |
+ @Override |
+ public void onBatchScanResults(List<ScanResult> results) { |
+ ArrayList<ScanResultWrapper> resultsWrapped = |
+ new ArrayList<ScanResultWrapper>(results.size()); |
+ for (ScanResult result : results) { |
+ resultsWrapped.add(new ScanResultWrapper(result)); |
+ } |
+ mWrapperCallback.onBatchScanResult(resultsWrapped); |
+ } |
+ |
+ @Override |
+ public void onScanResult(int callbackType, ScanResult result) { |
+ mWrapperCallback.onScanResult(callbackType, new ScanResultWrapper(result)); |
+ } |
+ |
+ @Override |
+ public void onScanFailed(int errorCode) { |
+ mWrapperCallback.onScanFailed(errorCode); |
+ } |
+ } |
+ |
+ /** |
+ * Wraps android.bluetooth.le.ScanCallback, being called by ScanCallbackImpl. |
+ */ |
+ abstract static class ScanCallbackWrapper { |
+ public abstract void onBatchScanResult(List<ScanResultWrapper> results); |
+ public abstract void onScanResult(int callbackType, ScanResultWrapper result); |
+ public abstract void onScanFailed(int errorCode); |
+ } |
+ |
+ /** |
+ * Wraps android.bluetooth.le.ScanResult. |
+ */ |
+ static class ScanResultWrapper { |
+ private final ScanResult mScanResult; |
+ |
+ public ScanResultWrapper(ScanResult scanResult) { |
+ mScanResult = scanResult; |
+ } |
+ |
+ public BluetoothDeviceWrapper getDevice() { |
+ return new BluetoothDeviceWrapper(mScanResult.getDevice()); |
+ } |
+ } |
+ |
+ /** |
+ * Wraps android.bluetooth.BluetoothDevice. |
+ */ |
+ static class BluetoothDeviceWrapper { |
+ private final BluetoothDevice mDevice; |
+ |
+ public BluetoothDeviceWrapper(BluetoothDevice device) { |
+ mDevice = device; |
+ } |
+ |
+ public String getAddress() { |
+ return mDevice.getAddress(); |
+ } |
+ |
+ public String getName() { |
+ return mDevice.getName(); |
+ } |
+ } |
} |