| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.device.bluetooth; | 5 package org.chromium.device.bluetooth; |
| 6 | 6 |
| 7 import android.Manifest; | 7 import android.Manifest; |
| 8 import android.annotation.TargetApi; | |
| 9 import android.bluetooth.BluetoothAdapter; | 8 import android.bluetooth.BluetoothAdapter; |
| 10 import android.bluetooth.BluetoothDevice; | |
| 11 import android.bluetooth.le.BluetoothLeScanner; | |
| 12 import android.bluetooth.le.ScanCallback; | |
| 13 import android.bluetooth.le.ScanFilter; | |
| 14 import android.bluetooth.le.ScanResult; | |
| 15 import android.bluetooth.le.ScanSettings; | |
| 16 import android.content.Context; | 9 import android.content.Context; |
| 17 import android.content.pm.PackageManager; | 10 import android.content.pm.PackageManager; |
| 18 import android.os.Build; | |
| 19 | 11 |
| 20 import org.chromium.base.CalledByNative; | 12 import org.chromium.base.CalledByNative; |
| 21 import org.chromium.base.JNINamespace; | 13 import org.chromium.base.JNINamespace; |
| 22 import org.chromium.base.Log; | 14 import org.chromium.base.Log; |
| 23 | 15 |
| 24 import java.util.ArrayList; | |
| 25 import java.util.HashMap; | |
| 26 import java.util.List; | |
| 27 | |
| 28 /** | 16 /** |
| 29 * Wrapper classes around android.bluetooth.* classes that provide an | 17 * Wrapper classes around android.bluetooth.* classes that provide an |
| 30 * indirection layer enabling fake implementations when running tests. | 18 * indirection layer enabling fake implementations when running tests. |
| 31 * | 19 * |
| 32 * Each Wrapper base class accepts an Android API object and passes through | 20 * Each Wrapper base class accepts an Android API object and passes through |
| 33 * calls to it. When under test, Fake subclasses override all methods that | 21 * calls to it. When under test, Fake subclasses override all methods that |
| 34 * pass through to the Android object and instead provide fake implementations. | 22 * pass through to the Android object and instead provide fake implementations. |
| 35 */ | 23 */ |
| 36 @JNINamespace("device") | 24 @JNINamespace("device") |
| 37 @TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
| 38 class Wrappers { | 25 class Wrappers { |
| 39 private static final String TAG = "cr.Bluetooth"; | 26 private static final String TAG = "cr.Bluetooth"; |
| 40 | 27 |
| 41 /** | 28 /** |
| 42 * Wraps android.bluetooth.BluetoothAdapter. | 29 * Wraps android.bluetooth.BluetoothAdapter. |
| 43 */ | 30 */ |
| 44 static class BluetoothAdapterWrapper { | 31 static class BluetoothAdapterWrapper { |
| 45 private final BluetoothAdapter mAdapter; | 32 private final BluetoothAdapter mAdapter; |
| 46 protected final BluetoothLeScannerWrapper mScanner; | |
| 47 | 33 |
| 48 /** | 34 /** |
| 49 * Creates a BluetoothAdapterWrapper using the default | 35 * Creates a BluetoothAdapterWrapper using the default |
| 50 * android.bluetooth.BluetoothAdapter. May fail if the default adapter | 36 * android.bluetooth.BluetoothAdapter. May fail if the default adapter |
| 51 * is not available or if the application does not have sufficient | 37 * is not available or if the application does not have sufficient |
| 52 * permissions. | 38 * permissions. |
| 53 */ | 39 */ |
| 54 @CalledByNative("BluetoothAdapterWrapper") | 40 @CalledByNative("BluetoothAdapterWrapper") |
| 55 public static BluetoothAdapterWrapper createWithDefaultAdapter(Context c
ontext) { | 41 public static BluetoothAdapterWrapper createWithDefaultAdapter(Context c
ontext) { |
| 56 final boolean hasMinAPI = Build.VERSION.SDK_INT >= Build.VERSION_COD
ES.LOLLIPOP; | |
| 57 if (!hasMinAPI) { | |
| 58 Log.i(TAG, "BluetoothAdapterWrapper.create failed: SDK version (
%d) too low.", | |
| 59 Build.VERSION.SDK_INT); | |
| 60 return null; | |
| 61 } | |
| 62 | |
| 63 final boolean hasPermissions = | 42 final boolean hasPermissions = |
| 64 context.checkCallingOrSelfPermission(Manifest.permission.BLU
ETOOTH) | 43 context.checkCallingOrSelfPermission(Manifest.permission.BLU
ETOOTH) |
| 65 == PackageManager.PERMISSION_GRANTED | 44 == PackageManager.PERMISSION_GRANTED |
| 66 && context.checkCallingOrSelfPermission(Manifest.permission.
BLUETOOTH_ADMIN) | 45 && context.checkCallingOrSelfPermission(Manifest.permission.
BLUETOOTH_ADMIN) |
| 67 == PackageManager.PERMISSION_GRANTED; | 46 == PackageManager.PERMISSION_GRANTED; |
| 68 if (!hasPermissions) { | 47 if (!hasPermissions) { |
| 69 Log.w(TAG, "BluetoothAdapterWrapper.create failed: Lacking Bluet
ooth permissions."); | 48 Log.w(TAG, "BluetoothAdapterWrapper.create failed: Lacking Bluet
ooth permissions."); |
| 70 return null; | 49 return null; |
| 71 } | 50 } |
| 72 | 51 |
| 73 // Only Low Energy currently supported, see BluetoothAdapterAndroid
class note. | |
| 74 final boolean hasLowEnergyFeature = | |
| 75 Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 | |
| 76 && context.getPackageManager().hasSystemFeature( | |
| 77 PackageManager.FEATURE_BLUETOOTH_LE); | |
| 78 if (!hasLowEnergyFeature) { | |
| 79 Log.i(TAG, "BluetoothAdapterWrapper.create failed: No Low Energy
support."); | |
| 80 return null; | |
| 81 } | |
| 82 | |
| 83 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); | 52 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); |
| 84 if (adapter == null) { | 53 if (adapter == null) { |
| 85 Log.i(TAG, "BluetoothAdapterWrapper.create failed: Default adapt
er not found."); | 54 Log.i(TAG, "BluetoothAdapterWrapper.create failed: Default adapt
er not found."); |
| 86 return null; | 55 return null; |
| 87 } else { | 56 } else { |
| 88 return new BluetoothAdapterWrapper( | 57 return new BluetoothAdapterWrapper(adapter); |
| 89 adapter, new BluetoothLeScannerWrapper(adapter.getBlueto
othLeScanner())); | |
| 90 } | 58 } |
| 91 } | 59 } |
| 92 | 60 |
| 93 public BluetoothAdapterWrapper( | 61 public BluetoothAdapterWrapper(BluetoothAdapter adapter) { |
| 94 BluetoothAdapter adapter, BluetoothLeScannerWrapper scanner) { | |
| 95 mAdapter = adapter; | 62 mAdapter = adapter; |
| 96 mScanner = scanner; | |
| 97 } | |
| 98 | |
| 99 public BluetoothLeScannerWrapper getBluetoothLeScanner() { | |
| 100 return mScanner; | |
| 101 } | 63 } |
| 102 | 64 |
| 103 public boolean isEnabled() { | 65 public boolean isEnabled() { |
| 104 return mAdapter.isEnabled(); | 66 return mAdapter.isEnabled(); |
| 105 } | 67 } |
| 106 | 68 |
| 107 public String getAddress() { | 69 public String getAddress() { |
| 108 return mAdapter.getAddress(); | 70 return mAdapter.getAddress(); |
| 109 } | 71 } |
| 110 | 72 |
| 111 public String getName() { | 73 public String getName() { |
| 112 return mAdapter.getName(); | 74 return mAdapter.getName(); |
| 113 } | 75 } |
| 114 | 76 |
| 115 public int getScanMode() { | 77 public int getScanMode() { |
| 116 return mAdapter.getScanMode(); | 78 return mAdapter.getScanMode(); |
| 117 } | 79 } |
| 118 | 80 |
| 119 public boolean isDiscovering() { | 81 public boolean isDiscovering() { |
| 120 return mAdapter.isDiscovering(); | 82 return mAdapter.isDiscovering(); |
| 121 } | 83 } |
| 122 } | 84 } |
| 123 | |
| 124 /** | |
| 125 * Wraps android.bluetooth.BluetoothLeScanner. | |
| 126 */ | |
| 127 static class BluetoothLeScannerWrapper { | |
| 128 private final BluetoothLeScanner mScanner; | |
| 129 private final HashMap<ScanCallbackWrapper, ScanCallbackImpl> mCallbacks; | |
| 130 | |
| 131 public BluetoothLeScannerWrapper(BluetoothLeScanner scanner) { | |
| 132 mScanner = scanner; | |
| 133 mCallbacks = new HashMap<ScanCallbackWrapper, ScanCallbackImpl>(); | |
| 134 } | |
| 135 | |
| 136 public void startScan( | |
| 137 List<ScanFilter> filters, int scanSettingsScanMode, ScanCallback
Wrapper callback) { | |
| 138 ScanSettings settings = | |
| 139 new ScanSettings.Builder().setScanMode(scanSettingsScanMode)
.build(); | |
| 140 | |
| 141 ScanCallbackImpl callbackImpl = new ScanCallbackImpl(callback); | |
| 142 mCallbacks.put(callback, callbackImpl); | |
| 143 | |
| 144 mScanner.startScan(filters, settings, callbackImpl); | |
| 145 } | |
| 146 | |
| 147 public void stopScan(ScanCallbackWrapper callback) { | |
| 148 ScanCallbackImpl callbackImpl = mCallbacks.remove(callback); | |
| 149 mScanner.stopScan(callbackImpl); | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 /** | |
| 154 * Implements android.bluetooth.le.ScanCallback and passes calls through to
a | |
| 155 * provided ScanCallbackWrapper instance. | |
| 156 * | |
| 157 * This class is required so that Fakes can use ScanCallbackWrapper without | |
| 158 * it extending from ScanCallback. Fakes must function even on Android | |
| 159 * versions where ScanCallback class is not defined. | |
| 160 */ | |
| 161 static class ScanCallbackImpl extends ScanCallback { | |
| 162 final ScanCallbackWrapper mWrapperCallback; | |
| 163 | |
| 164 ScanCallbackImpl(ScanCallbackWrapper wrapperCallback) { | |
| 165 mWrapperCallback = wrapperCallback; | |
| 166 } | |
| 167 | |
| 168 @Override | |
| 169 public void onBatchScanResults(List<ScanResult> results) { | |
| 170 ArrayList<ScanResultWrapper> resultsWrapped = | |
| 171 new ArrayList<ScanResultWrapper>(results.size()); | |
| 172 for (ScanResult result : results) { | |
| 173 resultsWrapped.add(new ScanResultWrapper(result)); | |
| 174 } | |
| 175 mWrapperCallback.onBatchScanResult(resultsWrapped); | |
| 176 } | |
| 177 | |
| 178 @Override | |
| 179 public void onScanResult(int callbackType, ScanResult result) { | |
| 180 mWrapperCallback.onScanResult(callbackType, new ScanResultWrapper(re
sult)); | |
| 181 } | |
| 182 | |
| 183 @Override | |
| 184 public void onScanFailed(int errorCode) { | |
| 185 mWrapperCallback.onScanFailed(errorCode); | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 /** | |
| 190 * Wraps android.bluetooth.le.ScanCallback, being called by ScanCallbackImpl
. | |
| 191 */ | |
| 192 abstract static class ScanCallbackWrapper { | |
| 193 public abstract void onBatchScanResult(List<ScanResultWrapper> results); | |
| 194 public abstract void onScanResult(int callbackType, ScanResultWrapper re
sult); | |
| 195 public abstract void onScanFailed(int errorCode); | |
| 196 } | |
| 197 | |
| 198 /** | |
| 199 * Wraps android.bluetooth.le.ScanResult. | |
| 200 */ | |
| 201 static class ScanResultWrapper { | |
| 202 private final ScanResult mScanResult; | |
| 203 | |
| 204 public ScanResultWrapper(ScanResult scanResult) { | |
| 205 mScanResult = scanResult; | |
| 206 } | |
| 207 | |
| 208 public BluetoothDeviceWrapper getDevice() { | |
| 209 return new BluetoothDeviceWrapper(mScanResult.getDevice()); | |
| 210 } | |
| 211 } | |
| 212 | |
| 213 /** | |
| 214 * Wraps android.bluetooth.BluetoothDevice. | |
| 215 */ | |
| 216 static class BluetoothDeviceWrapper { | |
| 217 private final BluetoothDevice mDevice; | |
| 218 | |
| 219 public BluetoothDeviceWrapper(BluetoothDevice device) { | |
| 220 mDevice = device; | |
| 221 } | |
| 222 | |
| 223 public String getAddress() { | |
| 224 return mDevice.getAddress(); | |
| 225 } | |
| 226 | |
| 227 public String getName() { | |
| 228 return mDevice.getName(); | |
| 229 } | |
| 230 } | |
| 231 } | 85 } |
| OLD | NEW |