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 |