diff --git a/.gitignore b/.gitignore
index 9c9035b0ff2709ba6291bbd67c5cb34b2822afa6..603b14077394cd2294ac6922fe619669630ef3ab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,29 +1,14 @@
-# Built application files
-*.apk
-*.ap_
-
-# Files for the Dalvik VM
-*.dex
-
-# Java class files
-*.class
-
-# Generated files
-bin/
-gen/
-
-# Gradle files
-.gradle/
-build/
-/*/build/
-
-# Local configuration file (sdk path, etc)
-local.properties
-/.idea
*.iml
-
-# Proguard folder generated by Eclipse
-proguard/
-
-# Log Files
-*.log
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
diff --git a/.idea/$PROJECT_FILE$ b/.idea/$PROJECT_FILE$
new file mode 100644
index 0000000000000000000000000000000000000000..58b7e3e570f8289344d345f6e3634608e74c690d
--- /dev/null
+++ b/.idea/$PROJECT_FILE$
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..26d33521af10bcc7fd8cea344038eaaeb78d0ef5
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/checkstyle-idea.xml b/.idea/checkstyle-idea.xml
new file mode 100644
index 0000000000000000000000000000000000000000..04546e080a2c37b6f2fcf1d259c8ef4222b41d82
--- /dev/null
+++ b/.idea/checkstyle-idea.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/checkstyleidea-libs/readme.txt b/.idea/checkstyleidea-libs/readme.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3bb2010fe0dc9fe399aa172ffa6552cf028f47b8
--- /dev/null
+++ b/.idea/checkstyleidea-libs/readme.txt
@@ -0,0 +1,6 @@
+This folder contains libraries copied from the "ohos-Scanner-Compat-Library-master" project.
+It is managed by the CheckStyle-IDEA IDE plugin.
+Do not modify this folder while the IDE is running.
+When the IDE is stopped, you may delete this folder at any time. It will be recreated as needed.
+In order to prevent the CheckStyle-IDEA IDE plugin from creating this folder,
+uncheck the "Copy libraries from project directory" option in the CheckStyle-IDEA settings dialog.
diff --git a/.idea/code-check/java/codemars.log b/.idea/code-check/java/codemars.log
new file mode 100644
index 0000000000000000000000000000000000000000..03b4dec34c39a35cebdbf6795ef79375f38d1ce1
--- /dev/null
+++ b/.idea/code-check/java/codemars.log
@@ -0,0 +1,30 @@
+2021-05-11 19:29:31.986 [main] INFO . - user input: D:\soft\deveco\DevEco Studio 2.1.0.301\tools\openjdk\bin\java,-j,-source,@D:/ohos/trunk/SOW-24/ohos-Scanner-Compat-Library-master/.idea/code-check/java/detect.txt,-output,D:/ohos/trunk/SOW-24/ohos-Scanner-Compat-Library-master/.idea/code-check/java/output.xml
+2021-05-11 19:29:31.989 [main] INFO . - CodeMars Version:2.1.2.sp4
+2021-05-11 19:29:32.033 [main] INFO . - starting analyzing.
+2021-05-11 19:29:32.040 [main] INFO . - start collecting report.
+2021-05-11 19:29:32.044 [CodeMars1] INFO . - Command: "D:\soft\deveco\DevEco Studio 2.1.0.301\plugins\codecheck\lib\CodeMars\engines\SecFinder-J\bin\run_SecFinder-J.bat",-filelist,D:\ohos\trunk\SOW-24\ohos-Scanner-Compat-Library-master\.idea\code-check\java\filelist_2021_05_11_19_29_32_040_5.txt,-f,xml,-default,-progress,-r,D:\ohos\trunk\SOW-24\ohos-Scanner-Compat-Library-master\.idea\code-check\java\\errorreport_2021_05_11_19_29_31_910_33.xml,-ruleclasspath,file:///D:\ohos\trunk\SOW-24\ohos-Scanner-Compat-Library-master\.idea\code-check\java\ruleclasspath.txt
+2021-05-11 19:29:32.164 [Thread-2] INFO . - 五月 11, 2021 7:29:32 下午 com.huawei.secfinderj.SecFinderJ needScan
+2021-05-11 19:29:32.164 [Thread-2] INFO . - 信息: SecFinder-J Version: 2.1.3
+2021-05-11 19:29:32.233 [Thread-1] INFO . - 2021-05-11 19:29:32.197: SecFinder-J Output: Inspect start...
+2021-05-11 19:29:32.285 [Thread-1] INFO . - 2021-05-11 19:29:32.284: SecFinder-J Output: Load checkers...
+2021-05-11 19:29:32.409 [Thread-1] INFO . - 2021-05-11 19:29:32.409: SecFinder-J Output: Load config...
+2021-05-11 19:29:32.436 [Thread-1] INFO . - 2021-05-11 19:29:32.436: SecFinder-J Output: step 1/4: Find files
+2021-05-11 19:29:32.459 [Thread-1] INFO . - 2021-05-11 19:29:32.459: SecFinder-J Output: step 2/4: Process files
+2021-05-11 19:29:32.498 [Thread-1] INFO . - 2021-05-11 19:29:32.498: SecFinder-J Output: step 3/4: Run analysis...
+2021-05-11 19:29:32.499 [Thread-1] INFO . - 2021-05-11 19:29:32.499: SecFinder-J Output: [SecFinder-J--Thread--1] - during processing of [BluetoothScannerImplV5.java]
+2021-05-11 19:29:32.954 [Thread-1] INFO . - 2021-05-11 19:29:32.954: SecFinder-J Output: step 4/4: Result output...
+2021-05-11 19:29:32.955 [Thread-1] INFO . - 2021-05-11 19:29:32.954: SecFinder-J Output: Inspect finish...
+2021-05-11 19:29:32.955 [Thread-1] INFO . - Analysis result:
+2021-05-11 19:29:32.955 [Thread-1] INFO . - files analyzed : 1
+2021-05-11 19:29:32.955 [Thread-1] INFO . - lines analyzed : 266
+2021-05-11 19:29:32.955 [Thread-1] INFO . - rules used : 59
+2021-05-11 19:29:32.955 [Thread-1] INFO . - issues detected : 0
+2021-05-11 19:29:32.955 [Thread-1] INFO . - time cost(sec) : 0
+2021-05-11 19:29:32.955 [Thread-1] INFO . -
+2021-05-11 19:29:32.955 [Thread-2] INFO . - 五月 11, 2021 7:29:32 下午 com.huawei.secfinderj.override.HwPmd end
+2021-05-11 19:29:32.955 [Thread-2] INFO . - 信息: SecFinder-J run successed!
+2021-05-11 19:29:33.025 [CodeMars1] INFO . - start parse errorreport xml
+2021-05-11 19:29:33.026 [CodeMars1] INFO . - parse xml time : 2
+2021-05-11 19:29:33.026 [CodeMars1] INFO . - end parse errorreport xml
+2021-05-11 19:29:33.027 [main] INFO . - end collecting report.
+2021-05-11 19:29:33.027 [main] INFO . - end analyzing.
diff --git a/.idea/code-check/java/detect.txt b/.idea/code-check/java/detect.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0229ee19e9051be81cde403bf67a629839a33fdb
--- /dev/null
+++ b/.idea/code-check/java/detect.txt
@@ -0,0 +1 @@
+D:/ohos/trunk/SOW-24/ohos-Scanner-Compat-Library-master/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothScannerImplV5.java
diff --git a/.idea/code-check/java/output.xml b/.idea/code-check/java/output.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8cf7ac8cd0e3e8f262172cde6cc1c04a3a429fc3
--- /dev/null
+++ b/.idea/code-check/java/output.xml
@@ -0,0 +1,9 @@
+
+
+
+
+1
+266
+0
+0
+
diff --git a/.idea/code-check/java/ruleclasspath.txt b/.idea/code-check/java/ruleclasspath.txt
new file mode 100644
index 0000000000000000000000000000000000000000..02ab4392ec8cef8562dbd257ef87b98f2bad6e6d
--- /dev/null
+++ b/.idea/code-check/java/ruleclasspath.txt
@@ -0,0 +1 @@
+D:\soft\deveco\DevEco Studio 2.1.0.301\plugins\codecheck\lib\CodeMars\engines\SecFinder-J\rule\
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000000000000000000000000000000000000..61a9130cd9669c3843e6445dfe1fee2d493869bc
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d964c316c7d424bdbdd31ef2398357d13742d2e6
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ba2e7443424bc5abb3b2c16dd9751cfceb69faed
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/markdown-navigator-enh.xml b/.idea/markdown-navigator-enh.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a8fcc84db3668cadd75348be61bda65a8fc5ea21
--- /dev/null
+++ b/.idea/markdown-navigator-enh.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/markdown-navigator.xml b/.idea/markdown-navigator.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2aba66280be8440b9ca797afa0ac90fd06503ced
--- /dev/null
+++ b/.idea/markdown-navigator.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000000000000000000000000000000000000..53f05c51c5d431697f23ce9abedc264ed3a5fa65
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/previewer/phone/phoneSettingConfig_1341911773.json b/.idea/previewer/phone/phoneSettingConfig_1341911773.json
new file mode 100644
index 0000000000000000000000000000000000000000..f505e6b9ca3d41faf1f0a39a496db58b117fbd60
--- /dev/null
+++ b/.idea/previewer/phone/phoneSettingConfig_1341911773.json
@@ -0,0 +1,25 @@
+{
+ "setting": {
+ "1.0.1": {
+ "Language": {
+ "args": {
+ "Language": "zh-CN"
+ }
+ }
+ }
+ },
+ "frontend": {
+ "1.0.0": {
+ "Resolution": {
+ "args": {
+ "Resolution": "360*780"
+ }
+ },
+ "DeviceType": {
+ "args": {
+ "DeviceType": "phone"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/.idea/previewer/previewConfig.json b/.idea/previewer/previewConfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..29f1efdaec0a48216f012617082546b7918e7ce2
--- /dev/null
+++ b/.idea/previewer/previewConfig.json
@@ -0,0 +1,9 @@
+{
+ "1.0.0": {
+ "LastPreviewDevice": {
+ "D:\\ohos\\trunk\\SOW-24\\ohos-Scanner-Compat-Library-master\\entry": [
+ "phone"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/.idea/qaplug_profiles.xml b/.idea/qaplug_profiles.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6c583dfa8f3e7ef50535af521d43726b0f0303c3
--- /dev/null
+++ b/.idea/qaplug_profiles.xml
@@ -0,0 +1,183 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000000000000000000000000000000000000..35eb1ddfbbc029bcab630581847471d7f238ec53
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
index 0000000000000000000000000000000000000000..e2e988d658b10742d8e5e6d86a69f86b38c392e4
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,33 @@
+## 0.0.1-SNAPSHOT
+* 鍙戝竷beta鐗堟湰
+
+Version 1.4.3
+Filtering based on service data
+
+Version 1.4.2
+Crashing when disabling scanning after Bluetooth has been turned OFF.
+
+Version 1.4.1
+#58 - memory leak when using PendingIntent fixed.
+Migrated to Android Studio 3.4.2, Gradle wrapper 5.1.1.
+Improved publication script.
+
+Version 1.4.0
+Feature equal to 1.3.1, but migrated to Android Jetpack (androidX).
+
+Version 1.3.1
+#45 - removing a task from Recents is killing the background service. Not any more. Watch out for Doze mode!
+
+Version 1.3.0
+#33 - Scanning with PendingIntent supported (and emulated on older platforms using a background service).
+#44 - SCAN_MODE_OPPORTUNISTIC falls back to SCAN_MODE_LOW_POWER on Lollipop and to power save settings on older devices.
+#39 - fixed infinite scanning with #40.
+
+Version 1.2.0
+#32, #34, #36 - Fixed race conditions, supported worker thread, simplified code
+
+Version 1.1.0
+Support for Android Oreo added
+
+Version 1.0.0
+JUnit tests added
\ No newline at end of file
diff --git a/README.md b/README.md
index ef3839a6e65472d346ee99e20ef53abf541f1691..3ad04bd24bef4a0ccdf3efc90a3f61386cb35be2 100644
--- a/README.md
+++ b/README.md
@@ -1,61 +1,51 @@
-# Android BLE Scanner Compat library
+# Scanner-Compat-Library
-[  ](https://bintray.com/nordic/android/no.nordicsemi.android.support.v18%3Ascanner/_latestVersion)
+**鏈」鐩熀浜庡紑婧愰」鐩產ndroid-Scanner-Compat-Library 杩涜楦胯挋鍖栫殑绉绘鍜屽紑鍙戯紝鍙互閫氳繃椤圭洰鏍囩浠ュ強github鍦板潃锛锛夎拷韪埌鍘熷畨鍗撻」鐩増鏈**
-The Scanner Compat library solves the problem with scanning for Bluetooth Low Energy devices on Android.
-The scanner API, initially created in Android 4.3, has changed in Android 5.0 and has been extended in 6.0 and 8.0.
-This library allows to use modern API even on older phones, emulating not supported features. If a feature
-(for example offloaded filtering or batching) is not available natively, it will be emulated by
-the compat library. Also, native filtering, batching and reporting first match or match lost may
-be disabled if you find them not working on some devices. Advertising Extension (`ScanSetting#setLegacy(boolean)`
-or `setPhy(int)`) is available only on Android Oreo or newer and such calls will be ignored on
-older platforms where only legacy advertising packets on PHY LE 1M will be reported,
-due to the Bluetooth chipset capabilities.
+#### 椤圭洰浠嬬粛
+- 椤圭洰鍚嶇О锛氳摑鐗欏簱
+- 鎵灞炵郴鍒楋細楦胯挋鐨勭涓夋柟缁勪欢閫傞厤绉绘
+- 鍔熻兘锛氬叧浜庤摑鐗欑殑鎿嶄綔
+- 椤圭洰绉绘鐘舵侊細涓诲姛鑳藉畬鎴
+- 璋冪敤宸紓锛氭棤
+- 寮鍙戠増鏈細sdk5锛孌evEco Studio2.1 beta4
+- 椤圭洰浣滆呭拰缁存姢浜猴細璧垫棴涓
+- 鑱旂郴鏂瑰紡锛歾haoxudong073@chinasoftinc.com
+- 鍘熼」鐩瓺oc鍦板潃锛
+- 鍩虹嚎鐗堟湰锛歍ag v1.4.3
-### Background scanning
-`SCAN_MODE_LOW_POWER` or `SCAN_MODE_OPPORTUNISTIC` should be used when scanning in background.
-Note, that newer Android versions will enforce using low power mode in background, even if another one has been set.
-This library allows to emulate [scanning with PendingIntent](https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner.html#startScan(java.util.List%3Candroid.bluetooth.le.ScanFilter%3E,%20android.bluetooth.le.ScanSettings,%20android.app.PendingIntent))
-on pre-Oreo devices by starting a background service that will scan with requested scan mode.
-This is much less battery friendly than when the original method is used, but works and saves
-a lot of development time if such feature should be implemented anyway. Please read below
-for more details.
+#### 鏁堟灉婕旂ず
+![鏁堟灉婕旂ず]
-## Usage
-
-The compat library may be found on jcenter repository. Add it to your project by adding the
-following dependency:
-
-```Groovy
-implementation 'no.nordicsemi.android.support.v18:scanner:1.4.2'
+#### 瀹夎鏁欑▼
+1.鍦ㄩ」鐩牴鐩綍涓嬬殑build.gradle鏂囦欢涓坊鍔
```
-
-Projects not migrated to Android Jetpack should use version 1.3.1, which is feature-equal to 1.4.0.
-
-## API
-
-The Scanner Compat API is very similar to the original one, known from Android Oreo.
-
-Instead of getting it from the **BluetoothAdapter**, acquire the scanner instance using:
-
-```java
-BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
+allprojects {
+ repositories {
+ maven {
+ url 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
+ }
+ }
+}
```
-
-You also need to change the packets for **ScanSettings**, **ScanFilter** and **ScanCallback**
-classes to:
-
-```java
-no.nordicsemi.android.support.v18.scanner
+2.鍦╡ntry妯″潡涓嬬殑build.gradle鏂囦欢涓坊鍔犱緷璧栥
+```
+dependencies {
+ implementation('com.gitee.chinasoft_ohos:Scanner-Compat-Library:0.0.1-SNAPSHOT')
+ ......
+}
```
+鍦╯dk4锛孌evEco Studio2.1 beta2涓嬮」鐩彲鐩存帴杩愯
+濡傛棤娉曡繍琛岋紝鍒犻櫎椤圭洰.gradle,.idea,build,gradle,build.gradle鏂囦欢锛
+骞朵緷鎹嚜宸辩殑鐗堟湰鍒涘缓鏂伴」鐩紝灏嗘柊椤圭洰鐨勫搴旀枃浠跺鍒跺埌鏍圭洰褰曚笅
-## Sample
+#### 浣跨敤璇存槑
-To start scanning use (example):
+浣跨敤璇ュ簱闈炲父绠鍗曪紝鍙渶鏌ョ湅鎻愪緵鐨勭ず渚嬬殑婧愪唬鐮併
```java
- BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
+ BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
ScanSettings settings = new ScanSettings.Builder()
.setLegacy(false)
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
@@ -67,133 +57,24 @@ To start scanning use (example):
scanner.startScan(filters, settings, scanCallback);
```
-to stop scanning use:
-```java
- BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
- scanner.stopScan(scanCallback);
-```
+#### 娴嬭瘯淇℃伅
-### Scanning modes
-
-There are 4 scanning modes available in native [ScanSettings](https://developer.android.com/reference/android/bluetooth/le/ScanSettings).
-3 of them are available since Android Lollipop while the opportunistic scan mode has been added in Marshmallow.
-This library tries to emulate them on platforms where they are not supported natively.
-1. [SCAN_MODE_LOW_POWER](https://developer.android.com/reference/android/bluetooth/le/ScanSettings#SCAN_MODE_LOW_POWER) -
-Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the least power.
-The scanner will scan for 0.5 second and rest for 4.5 seconds. A Bluetooth LE device should advertise
-very often (at least once per 100 ms) in order to be found with this mode, otherwise the scanning interval may miss some or even all
-advertising events. This mode may be enforced if the scanning application is not in foreground.
-2. [SCAN_MODE_BALANCED](https://developer.android.com/reference/android/bluetooth/le/ScanSettings#SCAN_MODE_BALANCED) -
-Perform Bluetooth LE scan in balanced power mode. Scan results are returned at a rate that provides a
-good trade-off between scan frequency and power consumption. The scanner will scan for 2 seconds followed
-by 3 seconds of idle.
-3. [SCAN_MODE_LOW_LATENCY](https://developer.android.com/reference/android/bluetooth/le/ScanSettings#SCAN_MODE_LOW_LATENCY) -
-Scan using highest duty cycle. It's recommended to only use this mode when the application is running in the foreground.
-4. [SCAN_MODE_OPPORTUNISTIC](https://developer.android.com/reference/android/bluetooth/le/ScanSettings#SCAN_MODE_OPPORTUNISTIC) -
-A special Bluetooth LE scan mode. Applications using this scan mode will passively listen for other scan results
-without starting BLE scans themselves.
-
-3 first modes are emulated on Android 4.3 and 4.4.x by starting a handler task that scans for a period of time
-and rests in between. To set scanning and rest intervals use `Builder#setPowerSave(long,long)`.
-
-Opportunistic scanning is not possible to emulate and will fallback to `SCAN_MODE_LOW_POWER` on Lollipop and
-power save settings on pre-Lollipop devices. That means that this library actually will initiate scanning
-on its own. This may have impact on battery consumption and should be used with care.
-
-### Scan filters and batching
-
-Offloaded filtering is available on Lollipop or newer devices where
-[BluetoothAdapter#isOffloadedFilteringSupported()](https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#isOffloadedFilteringSupported())
-returns *true* (when Bluetooth is enabled). If it is not supported, this library will scan without a filter and
-apply the filter to the results. If you find offloaded filtering unreliable you may force using compat filtering by calling
-`Builder#useHardwareFilteringIfSupported(false)`. Keep in mind that, newer Android versions may prohibit
-background scanning without native filters to save battery, so this method should be used with care.
-
-Android Scanner Compat Library may also emulate batching. To enable scan batching call `Builder#setScanDelay(interval)`
-with an interval greater than 0. For intervals less 5 seconds the actual interval may vary.
-If you want to get results in lower intervals, call `Builder#useHardwareBatchingIfSupported(false)`, which will
-start a normal scan and report results in given interval. Emulated batching uses significantly more battery
-than offloaded as it wakes CPU with every device found.
-
-### Scanning with Pending Intent
-
-Android 8.0 Oreo introduced [Background Execution Limits](https://developer.android.com/about/versions/oreo/background)
-which made background running services short-lived. At the same time, to make background scanning possible, a new
-[method](https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner.html#startScan(java.util.List%3Candroid.bluetooth.le.ScanFilter%3E,%20android.bluetooth.le.ScanSettings,%20android.app.PendingIntent))
-was added to [BluetoothLeScanner](https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner.html)
-which allows registering a [PendingIntent](https://developer.android.com/reference/android/app/PendingIntent)
-that will be sent whenever a device matching filter criteria is found. This will also work after
-your application has been killed (the receiver must be added in *AndroidManifest* and the
-`PendingIntent` must be created with an explicit Intent).
-
-Starting from version 1.3.0, this library may emulate such feature on older Android versions.
-In order to do that, a background service will be started after calling
-`scanner.startScan(filters, settings, context, pendingIntent)`, which will be scanning in
-background with given settings and will send the given `PendingIntent` when a device
-matching filter is found. To lower battery consumption it is recommended to set
-`ScanSettings.SCAN_MODE_LOW_POWER` scanning mode and use filter, but even with those conditions fulfilled
-**the battery consumption will be significantly higher than on Oreo+**. To stop scanning call
-`scanner.stopScan(context, pendingIntent)` with
-[the same](https://developer.android.com/reference/android/app/PendingIntent) intent in parameter.
-The service will be stopped when the last scan was stopped.
-
-On Android Oreo or newer this library will use the native scanning mechanism. However, as it may also
-emulate batching or apply filtering (when `useHardwareBatchingIfSupported` or `useHardwareFilteringIfSupported`
-were called with parameter *false*) the library will register its own broadcast
-receiver that will translate results from native to compat classes.
-
-The receiver and service will be added automatically to the manifest even if they are not used by
-the application. No changes are required to make it work.
-
-To use this feature:
+CodeCheck浠g爜娴嬭瘯鏃犲紓甯
-```java
- Intent intent = new Intent(context, MyReceiver.class); // explicite intent
- intent.setAction("com.example.ACTION_FOUND");
- intent.putExtra("some.extra", value); // optional
- PendingIntent pendingIntent = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
-
- BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
- ScanSettings settings = new ScanSettings.Builder()
- .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
- .setReportDelay(10000)
- .build();
- List filters = new ArrayList<>();
- filters.add(new ScanFilter.Builder().setServiceUuid(mUuid).build());
- scanner.startScan(filters, settings, context, pendingIntent);
-```
-
-Add your `MyRecever` to *AndroidManifest*, as the application context might have been released
-and all broadcast receivers registered to it together with it.
+CloudTest浠g爜娴嬭瘯鏃犲紓甯
-To stop scanning call:
+鐏粧瀹夊叏鐥呮瘨瀹夊叏妫娴嬮氳繃
-```java
- // To stop scanning use the same or an equal PendingIntent (check PendingIntent documentation)
- Intent intent = new Intent(context, MyReceiver.class);
- intent.setAction("com.example.ACTION_FOUND");
- PendingIntent pendingIntent = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_CANCEL_CURRENT);
-
- BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
- scanner.stopScan(context, pendingIntent);
-```
+褰撳墠鐗堟湰demo鍔熻兘涓庡畨鍗撳師缁勪欢鍩烘湰鏃犲樊寮
-**Note:** Android versions 6 and 7 will not report any advertising packets when in Doze mode.
-Read more about it here: https://developer.android.com/training/monitoring-device-state/doze-standby
+娴嬭瘯鍛橈細浣欎附椋
-## Background scanning guidelines
+#### 鐗堟湰杩唬
-To save power it is recommended to use as low power settings as possible and and use filters.
-However, the more battery friendly settings are used, the longest time to finding a device.
-In general, scanning with `PendingIntent` and `SCAN_MODE_LOW_POWER` or `SCAN_MODE_OPPORTUNISTIC`
-should be used, together with report delay set and filters used.
-`useHardwareFilteringIfSupported` and `useHardwareBatchingIfSupported` should be set to *true* (default).
+- v0.0.1_SNAPSHOT
-Background scanning on Android 4.3 and 4.4.x will use a lot of power, as all those properties
-will have to be emulated. It is recommended to scan in background only on Lollipop or newer, or
-even Oreo or newer devices and giving the user an option to disable this feature.
+#### 鐗堟潈鍜岃鍙俊鎭
-## License
+ The Scanner Compat library is available under BSD 3-Clause license. See the LICENSE file for more info.
-The Scanner Compat library is available under BSD 3-Clause license. See the LICENSE file for more info.
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 8da322c631295ac04d26e5ed4126a1165424ac37..0f37d1b371f1632aaf2af4a2814463596cc0ddf0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,22 +1,37 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
+apply plugin: 'com.huawei.ohos.app'
+
+ohos {
+ compileSdkVersion 5
+ defaultConfig {
+ compatibleSdkVersion 5
+ }
+}
buildscript {
repositories {
- google()
+ maven {
+ url 'https://mirrors.huaweicloud.com/repository/maven/'
+ }
+ maven {
+ url 'https://developer.huawei.com/repo/'
+ }
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.2'
-
- classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
+ classpath 'com.huawei.ohos:hap:2.4.2.5'
+ classpath 'com.huawei.ohos:decctest:1.0.0.6'
}
}
allprojects {
repositories {
- google()
+ maven {
+ url 'https://mirrors.huaweicloud.com/repository/maven/'
+ }
+ maven {
+ url 'https://developer.huawei.com/repo/'
+ }
jcenter()
}
-}
\ No newline at end of file
+}
diff --git a/entry/.gitignore b/entry/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..796b96d1c402326528b4ba3c12ee9d92d0e212e9
--- /dev/null
+++ b/entry/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/entry/build.gradle b/entry/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..18fad72ab26a49aad281ab3dea45b0cab0c36801
--- /dev/null
+++ b/entry/build.gradle
@@ -0,0 +1,14 @@
+apply plugin: 'com.huawei.ohos.hap'
+ohos {
+ compileSdkVersion 5
+ defaultConfig {
+ compatibleSdkVersion 5
+ }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
+ testCompile 'junit:junit:4.12'
+ implementation project(':scanner')
+}
diff --git a/entry/src/main/config.json b/entry/src/main/config.json
new file mode 100644
index 0000000000000000000000000000000000000000..838029d36775dce26637fc2d19042c434e718f44
--- /dev/null
+++ b/entry/src/main/config.json
@@ -0,0 +1,110 @@
+{
+ "app": {
+ "bundleName": "com.example.myapplication",
+ "vendor": "example",
+ "version": {
+ "code": 1,
+ "name": "1.0"
+ },
+ "apiVersion": {
+ "compatible": 5,
+ "target": 5
+ }
+ },
+ "deviceConfig": {},
+ "module": {
+ "package": "com.example.myapplication",
+ "name": ".MyApplication",
+ "deviceType": [
+ "tv"
+ ],
+ "distro": {
+ "deliveryWithInstall": true,
+ "moduleName": "entry",
+ "moduleType": "entry"
+ },
+ "abilities": [
+ {
+ "skills": [
+ {
+ "entities": [
+ "entity.system.home"
+ ],
+ "actions": [
+ "action.system.home"
+ ]
+ }
+ ],
+ "orientation": "unspecified",
+ "name": "com.example.myapplication.MainAbility",
+ "icon": "$media:icon",
+ "description": "$string:mainability_description",
+ "label": "MyApplication",
+ "type": "page",
+ "launchType": "standard"
+ },
+ {
+ "name": "no.nordicsemi.android.support.v18.scanner.ScannerService",
+ "type": "service",
+ "visible": true
+ }
+ ],
+ "reqPermissions": [
+ {
+ "name": "ohos.permission.USE_BLUETOOTH",
+ "reason": "浣跨敤钃濈墮鏉冮檺",
+ "usedScene": {
+ "ability": [
+ "com.example.myapplication.slice.MainAbility",
+ "no.nordicsemi.android.support.v18.scanner.ScannerService"
+ ],
+ "when": "always"
+ }
+ },
+ {
+ "name": "ohos.permission.DISCOVER_BLUETOOTH",
+ "reason": "鎼滅储钃濈墮鏉冮檺",
+ "usedScene": {
+ "ability": [
+ "com.example.myapplication.slice.MainAbility",
+ "no.nordicsemi.android.support.v18.scanner.ScannerService"
+ ],
+ "when": "always"
+ }
+ },
+ {
+ "name": "ohos.permission.LOCATION",
+ "reason": "鍏佽鍓嶅彴鑾峰彇鏉冮檺",
+ "usedScene": {
+ "ability": [
+ "com.example.myapplication.slice.MainAbility",
+ "no.nordicsemi.android.support.v18.scanner.ScannerService"
+ ],
+ "when": "always"
+ }
+ },
+ {
+ "name": "ohos.permission.LOCATION_IN_BACKGROUND",
+ "reason": "鍏佽鍚庡彴鑾峰彇鏉冮檺",
+ "usedScene": {
+ "ability": [
+ "com.example.myapplication.slice.MainAbility",
+ "no.nordicsemi.android.support.v18.scanner.ScannerService"
+ ],
+ "when": "always"
+ }
+ },
+ {
+ "name": "ohos.permission.GET_NETWORK_INFO",
+ "reason": "鍏佽搴旂敤鑾峰彇鏁版嵁缃戠粶淇℃伅",
+ "usedScene": {
+ "ability": [
+ "com.example.myapplication.slice.MainAbility",
+ "no.nordicsemi.android.support.v18.scanner.ScannerService"
+ ],
+ "when": "always"
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/java/com/example/myapplication/HistoryBean.java b/entry/src/main/java/com/example/myapplication/HistoryBean.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c5894396a1fcaaf26565b635ae4764fa13abc70
--- /dev/null
+++ b/entry/src/main/java/com/example/myapplication/HistoryBean.java
@@ -0,0 +1,16 @@
+package com.example.myapplication;
+
+public class HistoryBean {
+
+ private String name;
+ public HistoryBean(String name) {
+ this.name = name;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
diff --git a/entry/src/main/java/com/example/myapplication/HistoryItemProvider.java b/entry/src/main/java/com/example/myapplication/HistoryItemProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..f573c855496a9a53a409ec88587d263e54dcd19b
--- /dev/null
+++ b/entry/src/main/java/com/example/myapplication/HistoryItemProvider.java
@@ -0,0 +1,71 @@
+package com.example.myapplication;
+
+import no.nordicsemi.android.support.v18.scanner.ScanResult;
+import ohos.aafwk.ability.AbilitySlice;
+import ohos.agp.components.*;
+import ohos.bluetooth.ble.BleScanResult;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class HistoryItemProvider extends BaseItemProvider {
+ private List list;
+ private AbilitySlice slice;
+ private int layout;
+
+ public HistoryItemProvider(List list, AbilitySlice slice, int layout) {
+ this.list = list;
+ this.slice = slice;
+ this.layout = layout;
+ }
+
+ @Override
+ public int getCount() {
+ return list == null ? 0 : list.size();
+ }
+
+ public List getList() {
+ if (list == null) {
+ return new ArrayList<>();
+ }
+ return list;
+ }
+
+ @Override
+ public Object getItem(int i) {
+ if (list != null && i >= 0 && i < list.size()) {
+ return list.get(i);
+ }
+ return null;
+ }
+
+ @Override
+ public long getItemId(int i) {
+ return i;
+ }
+
+ @Override
+ public Component getComponent(int i, Component component, ComponentContainer componentContainer) {
+ final Component comp;
+ if (component == null) {
+ comp = LayoutScatter.getInstance(slice).parse(layout, null, false);
+ } else {
+ comp = component;
+ }
+ List list = getList();
+ ScanResult historyBean = list.get(i);
+ BleScanResult bleScanResult = (BleScanResult) historyBean.getDevice();
+ String str = bleScanResult.getPeripheralDevice().getDeviceName().toString();
+ str = str.replace("Optional[", "");
+ str = str.replace("]", "");
+ Text text = (Text) comp.findComponentById(ResourceTable.Id_text);
+ if (str.equals("Optional.empty")) {
+ String strs = bleScanResult.getPeripheralDevice().getDeviceAddr().toString();
+ text.setText(strs);
+ } else {
+ text.setText(str);
+ }
+
+ return comp;
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/java/com/example/myapplication/MainAbility.java b/entry/src/main/java/com/example/myapplication/MainAbility.java
new file mode 100644
index 0000000000000000000000000000000000000000..d184331d82957d055552c81610186524a42ba1db
--- /dev/null
+++ b/entry/src/main/java/com/example/myapplication/MainAbility.java
@@ -0,0 +1,15 @@
+package com.example.myapplication;
+
+import com.example.myapplication.slice.MainAbilitySlice;
+import ohos.aafwk.ability.Ability;
+import ohos.aafwk.content.Intent;
+import ohos.aafwk.content.Operation;
+
+public class MainAbility extends Ability {
+ @Override
+ public void onStart(Intent intent) {
+ super.onStart(intent);
+ super.setMainRoute(MainAbilitySlice.class.getName());
+ }
+
+}
diff --git a/entry/src/main/java/com/example/myapplication/MyApplication.java b/entry/src/main/java/com/example/myapplication/MyApplication.java
new file mode 100644
index 0000000000000000000000000000000000000000..33915e2cc691b0fa169fd0a13e2262ac3925bafd
--- /dev/null
+++ b/entry/src/main/java/com/example/myapplication/MyApplication.java
@@ -0,0 +1,10 @@
+package com.example.myapplication;
+
+import ohos.aafwk.ability.AbilityPackage;
+
+public class MyApplication extends AbilityPackage {
+ @Override
+ public void onInitialize() {
+ super.onInitialize();
+ }
+}
diff --git a/entry/src/main/java/com/example/myapplication/ScannerService.java b/entry/src/main/java/com/example/myapplication/ScannerService.java
new file mode 100644
index 0000000000000000000000000000000000000000..62890b645bc40151a526c312bfd9f30a6fae125f
--- /dev/null
+++ b/entry/src/main/java/com/example/myapplication/ScannerService.java
@@ -0,0 +1,129 @@
+package com.example.myapplication;
+
+import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat;
+import no.nordicsemi.android.support.v18.scanner.ScanCallback;
+import no.nordicsemi.android.support.v18.scanner.ScanSettings;
+import ohos.aafwk.ability.Ability;
+import ohos.aafwk.content.Intent;
+import ohos.app.Context;
+import ohos.bluetooth.ble.BleScanFilter;
+import ohos.event.intentagent.IntentAgent;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+
+public class ScannerService extends Ability {
+
+ /* package */ static final String EXTRA_PENDING_INTENT = "ohos.permission.GET_NETWORK_INFO";
+ /* package */ static final String EXTRA_FILTERS = "no.nordicsemi.android.support.v18.EXTRA_FILTERS";
+ /* package */ static final String EXTRA_SETTINGS = "no.nordicsemi.android.support.v18.EXTRA_SETTINGS";
+ /* package */ static final String EXTRA_START = "no.nordicsemi.android.support.v18.EXTRA_START";
+
+ private HashMap callbacks;
+ Handler handler;
+ private final Object LOCK = new Object();
+ Context context = getContext();
+
+ @Override
+ protected void onStart(Intent intent) {
+ super.onStart(intent);
+ callbacks = new HashMap<>();
+ handler = new Handler() {
+ @Override
+ public void publish(LogRecord logRecord) {
+ }
+ @Override
+ public void flush() {
+ }
+ @Override
+ public void close() throws SecurityException {
+ }
+ };
+
+ }
+
+ @Override
+ public void onCommand(final Intent intent,final boolean restart,final int startId) {
+ super.onCommand(intent, restart, startId);
+// final IntentAgent callbackIntent = intent.getSequenceableParam(EXTRA_PENDING_INTENT);
+ final boolean start = intent.getBooleanParam(EXTRA_START, false);
+ final boolean stop = !start;
+// if (callbackIntent == null) {
+// boolean shouldStop;
+// synchronized (LOCK) {
+// shouldStop = callbacks.isEmpty();
+// }
+//
+// if (shouldStop)
+// stopScan(callbackIntent);
+//// return 2;
+// }
+ boolean knownCallback;
+ synchronized (LOCK) {
+// knownCallback = callbacks.containsKey(callbackIntent);
+ }
+
+// if (start && !knownCallback) {
+// final ArrayList filters = intent.getSequenceableArrayListParam(EXTRA_FILTERS);
+// final ScanSettings settings = intent.getSequenceableParam(EXTRA_SETTINGS);
+// startScan(filters != null ? filters : Collections.emptyList(),
+// settings != null ? settings : new ScanSettings.Builder().build(),
+// callbackIntent);
+// } else if (stop && knownCallback) {
+// stopScan(callbackIntent);
+// }
+// return 2;
+ }
+
+ private void startScan(List bleScanFilters, ScanSettings scanSettings, IntentAgent callbackIntent) {
+// final PendingIntentExecutor executor =
+// new PendingIntentExecutor(callbackIntent, scanSettings,this);
+ synchronized (LOCK) {
+// callbacks.put(callbackIntent, executor);
+ }
+
+ try {
+ final BluetoothLeScannerCompat scannerCompat = BluetoothLeScannerCompat.getScanner();
+ } catch (final Exception e) {
+ }
+ }
+
+ private void stopScan(final IntentAgent callbackIntent) {
+ ScanCallback callback;
+ boolean shouldStop;
+ synchronized (LOCK) {
+ callback = callbacks.remove(callbackIntent);
+ shouldStop = callbacks.isEmpty();
+ }
+ if (callback == null)
+ return;
+ try {
+ final BluetoothLeScannerCompat scannerCompat = BluetoothLeScannerCompat.getScanner();
+ scannerCompat.stopScan(callback);
+ } catch (final Exception e) {
+ }
+ if (shouldStop)
+ stopScan(callbackIntent);
+ }
+
+ @Override
+ protected void onStop() {
+ final BluetoothLeScannerCompat scannerCompat = BluetoothLeScannerCompat.getScanner();
+ for (final ScanCallback callback : callbacks.values()) {
+ try {
+ scannerCompat.stopScan(callback);
+ } catch (final Exception e) {
+ // Ignore
+ }
+ }
+ callbacks.clear();
+ callbacks = null;
+ handler = null;
+ super.onStop();
+ super.onStop();
+ }
+}
diff --git a/entry/src/main/java/com/example/myapplication/slice/MainAbilitySlice.java b/entry/src/main/java/com/example/myapplication/slice/MainAbilitySlice.java
new file mode 100644
index 0000000000000000000000000000000000000000..42e5d26de37fd0b4cde369390585f28ae0423a42
--- /dev/null
+++ b/entry/src/main/java/com/example/myapplication/slice/MainAbilitySlice.java
@@ -0,0 +1,278 @@
+package com.example.myapplication.slice;
+
+import com.example.myapplication.HistoryItemProvider;
+import com.example.myapplication.ResourceTable;
+import no.nordicsemi.android.support.v18.scanner.*;
+import ohos.aafwk.ability.AbilitySlice;
+import ohos.aafwk.content.Intent;
+import ohos.agp.components.*;
+import ohos.agp.window.dialog.ToastDialog;
+import ohos.app.Context;
+import ohos.bluetooth.ble.BleScanFilter;
+import ohos.bluetooth.ble.BleScanResult;
+import ohos.bundle.IBundleManager;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+
+import java.util.*;
+
+public class MainAbilitySlice extends AbilitySlice implements Component.ClickedListener {
+
+ private Button btnStartScan, btnStopScan;
+ private RadioButton one, two, three, four;
+ private RadioContainer rabut;
+ private BluetoothLeScannerCompat scanner;
+
+ private List beans = new ArrayList<>();
+ Context context;
+ private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, "MY_TAG");
+ //璁剧疆钃濈墮鎵弿杩囨护鍣ㄩ泦鍚
+ private List scanFilterList;
+ //璁剧疆钃濈墮鎵弿杩囨护鍣
+ private ScanFilter.Builder scanFilterBuilder;
+ //璁剧疆钃濈墮鎵弿璁剧疆
+ private ScanSettings.Builder scanSettingBuilder;
+ private ListContainer dialog;
+ private HistoryItemProvider historyItemProvider;
+ private boolean isBut = false;
+ private boolean isPD = false;
+// private ToastDialog toastDialog;
+
+
+ @Override
+ public void onStart(Intent intent) {
+ super.onStart(intent);
+ super.setUIContent(ResourceTable.Layout_ability_main);
+ initView();
+ initClick();
+ String[] permission = {"ohos.permission.LOCATION", "ohos.permission.USE_BLUETOOTH"
+ , "ohos.permission.DISCOVER_BLUETOOTH", "ohos.permission.LOCATION_IN_BACKGROUND"
+ , "ohos.permission.GET_NETWORK_INFO"};
+ requestPermissionsFromUser(permission, 0);
+ if (verifySelfPermission("ohos.permission.LOCATION_IN_BACKGROUND") != IBundleManager.PERMISSION_GRANTED) {
+ return;
+ } else {
+ return;
+ }
+
+ }
+
+ private void initView() {
+ dialog = (ListContainer) findComponentById(ResourceTable.Id_dialog);
+ btnStartScan = (Button) findComponentById(ResourceTable.Id_btn_start_scan);
+ btnStopScan = (Button) findComponentById(ResourceTable.Id_btn_stop_scan);
+ one = (RadioButton) findComponentById(ResourceTable.Id_one);
+ two = (RadioButton) findComponentById(ResourceTable.Id_two);
+ three = (RadioButton) findComponentById(ResourceTable.Id_three);
+ four = (RadioButton) findComponentById(ResourceTable.Id_four);
+ rabut = (RadioContainer) findComponentById(ResourceTable.Id_rabut);
+ scanner = BluetoothLeScannerCompat.getScanner();
+ historyItemProvider = new HistoryItemProvider(beans, MainAbilitySlice.this, ResourceTable.Layout_item_layout);
+ dialog.setItemProvider(historyItemProvider);
+ rabut.mark(0);
+ }
+
+ private void initClick() {
+ btnStartScan.setClickedListener(this);
+ btnStopScan.setClickedListener(this);
+
+ rabut.setMarkChangedListener(new RadioContainer.CheckedStateChangedListener() {
+ @Override
+ public void onCheckedChanged(RadioContainer radioContainer, int i) {
+ if (i == 0) {
+ if (isBut == false) {
+ ScanSettings settings = new ScanSettings.Builder()
+ .setLegacy(false)
+ .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
+ .setReportDelay(10000)
+ .setUseHardwareBatchingIfSupported(false)
+ .build();
+ } else {
+ new ToastDialog(getContext())
+ .setText("鎼滅储涓,璇风粨鏉熷綋鍓嶆悳绱")
+ .show();
+ }
+ } else if (i == 1) {
+ if (isBut == false) {
+ ScanSettings settings = new ScanSettings.Builder()
+ .setLegacy(false)
+ .setScanMode(ScanSettings.SCAN_MODE_BALANCED)
+ .setReportDelay(10000)
+ .setUseHardwareBatchingIfSupported(false)
+ .build();
+ } else {
+ new ToastDialog(getContext())
+ .setText("鎼滅储涓,璇风粨鏉熷綋鍓嶆悳绱")
+ .show();
+ }
+ } else if (i == 2) {
+ if (isBut == false) {
+ ScanSettings settings = new ScanSettings.Builder()
+ .setLegacy(false)
+ .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+ .setReportDelay(10000)
+ .setUseHardwareBatchingIfSupported(false)
+ .build();
+ } else {
+ new ToastDialog(getContext())
+ .setText("鎼滅储涓,璇风粨鏉熷綋鍓嶆悳绱")
+ .show();
+ }
+ } else {
+ if (isBut == false) {
+ ScanSettings settings = new ScanSettings.Builder()
+ .setLegacy(false)
+ .setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC)
+ .setReportDelay(10000)
+ .setUseHardwareBatchingIfSupported(false)
+ .build();
+ } else {
+ new ToastDialog(getContext())
+ .setText("鎼滅储涓,璇风粨鏉熷綋鍓嶆悳绱")
+ .show();
+ }
+ }
+ }
+ });
+ }
+
+
+ @Override
+ public void onActive() {
+ super.onActive();
+ }
+
+ @Override
+ public void onForeground(Intent intent) {
+ super.onForeground(intent);
+ }
+
+ private ScanSettings buildScanSettings() {
+ scanSettingBuilder = new ScanSettings.Builder();
+ scanSettingBuilder.setUseHardwareBatchingIfSupported(false);
+ //璁剧疆钃濈墮LE鎵弿鐨勬壂鎻忔ā寮忋
+ //浣跨敤鏈楂樺崰绌烘瘮杩涜鎵弿銆傚缓璁彧鍦ㄥ簲鐢ㄧ▼搴忓浜庢妯″紡鏃朵娇鐢ㄦ妯″紡鍦ㄥ墠鍙拌繍琛
+ scanSettingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
+ //璁剧疆钃濈墮LE鎵弿婊ゆ尝鍣ㄧ‖浠跺尮閰嶇殑鍖归厤妯″紡
+ //鍦ㄤ富鍔ㄦā寮忎笅锛屽嵆浣夸俊鍙峰己搴﹁緝寮憋紝hw涔熶細鏇村揩鍦扮‘瀹氬尮閰.鍦ㄤ竴娈垫椂闂村唴寰堝皯鏈夌洰鍑/鍖归厤銆
+ scanSettingBuilder.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE);
+ //璁剧疆钃濈墮LE鎵弿鐨勫洖璋冪被鍨
+ //涓烘瘡涓涓尮閰嶈繃婊ゆ潯浠剁殑钃濈墮骞垮憡瑙﹀彂涓涓洖璋冦傚鏋滄病鏈夎繃婊ゅ櫒鏄椿鍔ㄧ殑锛屾墍鏈夌殑骞垮憡鍖呰鎶ュ憡
+ scanSettingBuilder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
+ scanSettingBuilder.setReportDelay(10000);
+ scanSettingBuilder.setUseHardwareBatchingIfSupported(false);
+ return scanSettingBuilder.build();
+ }
+
+ @Override
+ public void onClick(Component component) {
+ switch (component.getId()) {
+ case ResourceTable.Id_btn_start_scan:
+ if (isBut == false) {
+ start();
+ new ToastDialog(getContext())
+ .setText("寮濮")
+ .show();
+ } else {
+ new ToastDialog(getContext())
+ .setText("鎼滅储涓")
+ .show();
+ }
+ break;
+ case ResourceTable.Id_btn_stop_scan:
+ if (isBut == true) {
+ stop();
+ new ToastDialog(getContext())
+ .setText("缁撴潫")
+ .show();
+ } else {
+ new ToastDialog(getContext())
+ .setText("宸茬粨鏉")
+ .show();
+ }
+ break;
+ }
+ }
+
+ private void start() {
+ isBut = true;
+ List filters = new ArrayList<>();
+ UUID uuid = UUID.randomUUID();
+ scanner.startScan(this, null, buildScanSettings(), scanCallback);
+ scanner.startScan(filters, buildScanSettings(), this, null);
+ }
+
+ private void stop() {
+ isBut = false;
+ scanner.stopScan(scanCallback);
+ beans.clear();
+ getUITaskDispatcher().asyncDispatch(new Runnable() {
+ @Override
+ public void run() {
+ historyItemProvider.notifyDataChanged();
+ }
+ });
+ }
+
+ private ScanCallback scanCallback = new ScanCallback() {
+ @Override
+ public void onScanResult(int callbackType, ScanResult result) {
+ super.onScanResult(callbackType, result);
+ System.out.println("LOGLIST" + "/**/" + "RESULTDEVICE-------------------------------" + result.getDevice());
+ }
+
+
+ @Override
+ public void onBatchScanResults(List results) {
+ super.onBatchScanResults(results);
+ if (results.size() > 0) {
+ beans.clear();
+ beans.addAll(results);
+ }
+
+ getUITaskDispatcher().asyncDispatch(new Runnable() {
+ @Override
+ public void run() {
+ if (results.size() > 0) {
+ int size = results.size();
+ }
+ }
+ });
+
+ for (ScanResult scanResult : results) {
+
+ String deviceName = scanResult.getScanRecord().getDeviceName();
+ if (deviceName != null && !deviceName.isEmpty()) {
+// System.out.println("LOGLISTALL" + "/--/--/ "
+// + "DEVICE =" + ((BleScanResult)scanResult.getDevice()).getPeripheralDevice().getDeviceAddr()+ ", "
+// +"DEVICENAME ="+scanResult.getScanRecord().getDeviceName()+", "
+// +"DATASTATUS ="+scanResult.getDataStatus());
+ }
+ int size = results.size();
+ System.out.println("LOGLISTALL" + "/**/" + "ALLSIZE = " + size);
+ }
+ getUITaskDispatcher().asyncDispatch(new Runnable() {
+ @Override
+ public void run() {
+ historyItemProvider.notifyDataChanged();
+ }
+ });
+ }
+
+ @Override
+ public void onScanFailed(int errorCode) {
+ super.onScanFailed(errorCode);
+ System.out.println("onScanFailed");
+ }
+ };
+
+ @Override
+ protected void onBackground() {
+ super.onBackground();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..6becfe43fc3852c05cb8654d79518be7487f6947
--- /dev/null
+++ b/entry/src/main/resources/base/element/string.json
@@ -0,0 +1,16 @@
+{
+ "string": [
+ {
+ "name": "app_name",
+ "value": "MyApplication"
+ },
+ {
+ "name": "mainability_description",
+ "value": "Java_Phone_Empty Feature Ability"
+ },
+ {
+ "name": "HelloWorld",
+ "value": "Hello World"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/entry/src/main/resources/base/graphic/background_ability_main.xml b/entry/src/main/resources/base/graphic/background_ability_main.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c0c0a3df480fa387a452b9c40ca191cc918a3fc0
--- /dev/null
+++ b/entry/src/main/resources/base/graphic/background_ability_main.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/entry/src/main/resources/base/graphic/background_list.xml b/entry/src/main/resources/base/graphic/background_list.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1629bd1a5ce8d90ff3c1a9c9ab543bb6079c7a0d
--- /dev/null
+++ b/entry/src/main/resources/base/graphic/background_list.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/entry/src/main/resources/base/layout/ability_main.xml b/entry/src/main/resources/base/layout/ability_main.xml
new file mode 100644
index 0000000000000000000000000000000000000000..84da3f7f39bcfac4b040cb6dc5f0fa8c319e9bd5
--- /dev/null
+++ b/entry/src/main/resources/base/layout/ability_main.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/entry/src/main/resources/base/layout/item_layout.xml b/entry/src/main/resources/base/layout/item_layout.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1d3f0ff637e78ce025dd9ccdf5f43ea911e21bd1
--- /dev/null
+++ b/entry/src/main/resources/base/layout/item_layout.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/entry/src/main/resources/base/media/icon.png b/entry/src/main/resources/base/media/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c
Binary files /dev/null and b/entry/src/main/resources/base/media/icon.png differ
diff --git a/entry/src/test/java/com/example/myapplication/ExampleTest.java b/entry/src/test/java/com/example/myapplication/ExampleTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..160d520624e9b69a368ef2d9903f914e1be01474
--- /dev/null
+++ b/entry/src/test/java/com/example/myapplication/ExampleTest.java
@@ -0,0 +1,9 @@
+package com.example.myapplication;
+
+import org.junit.Test;
+
+public class ExampleTest {
+ @Test
+ public void onStart() {
+ }
+}
diff --git a/gradle.properties b/gradle.properties
index 82c67a044c8dbf081f6acef7e2faa8624c9ec665..0daf1830fbdef07e50a44d74210c8c82f1b66278 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,34 +1,10 @@
# Project-wide Gradle settings.
-
-# IDE (e.g. Android Studio) users:
+# IDE (e.g. DevEco Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
-
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
-
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
-# Default value: -Xmx10248m -XX:MaxPermSize=256m
-# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
-
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
-android.enableJetifier=true
-android.useAndroidX=true
-
-VERSION_NAME=1.4.3
-GROUP=no.nordicsemi.android.support.v18
-
-POM_DESCRIPTION=Android Bluetooth LE Scanner Compat library
-POM_URL=https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library
-POM_SCM_URL=https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library
-POM_SCM_CONNECTION=scm:git@github.com:NordicSemiconductor/Android-Scanner-Compat-Library.git
-POM_SCM_DEV_CONNECTION=scm:git@github.com:NordicSemiconductor/Android-Scanner-Compat-Library.git
-POM_LICENCE=BSD 3-Clause
-POM_LICENCE_NAME=The BSD 3-Clause License
-POM_LICENCE_URL=http://opensource.org/licenses/BSD-3-Clause
-POM_DEVELOPER_ID=nordic
-POM_DEVELOPER_NAME=Nordic Semiconductor ASA
\ No newline at end of file
+# If the Chinese output is garbled, please configure the following parameter.
+# org.gradle.jvmargs=-Dfile.encoding=GBK
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 8c0fb64a8698b08ecc4158d828ca593c4928e9dd..490fda8577df6c95960ba7077c43220e5bb2c0d9 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 4c6e8dab8b284da1ee0dacbde0a58ad55eac95f1..f59159e865d4b59feb1b8c44b001f62fc5d58df4 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Fri Nov 15 09:17:12 CET 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
+distributionUrl=https\://repo.huaweicloud.com/gradle/gradle-6.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
diff --git a/gradlew b/gradlew
index 91a7e269e19dfc62e27137a0b57ef3e430cee4fd..2fe81a7d95e4f9ad2c9b2a046707d36ceb3980b3 100644
--- a/gradlew
+++ b/gradlew
@@ -1,4 +1,20 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
##############################################################################
##
@@ -6,20 +22,38 @@
##
##############################################################################
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
-warn ( ) {
+warn () {
echo "$*"
}
-die ( ) {
+die () {
echo
echo "$*"
echo
@@ -30,6 +64,7 @@ die ( ) {
cygwin=false
msys=false
darwin=false
+nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
@@ -40,31 +75,11 @@ case "`uname`" in
MINGW* )
msys=true
;;
+ NONSTOP* )
+ nonstop=true
+ ;;
esac
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
-APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -90,7 +105,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -110,10 +125,11 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
@@ -138,27 +154,30 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
- i=$((i+1))
+ i=`expr $i + 1`
done
case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
- JVM_OPTS=("$@")
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index 8a0b282aa6885fb573c106b3551f7275c5f17e8e..62bd9b9ccefea2b65ae41e5d9a545e2021b90a1d 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,3 +1,19 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -8,14 +24,17 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,10 +65,9 @@ echo location of your Java installation.
goto fail
:init
-@rem Get command-line arguments, handling Windowz variants
+@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@@ -60,11 +78,6 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
diff --git a/scanner/.gitignore b/scanner/.gitignore
index d0b97c6bc8c77d4b244d7f2959072336230fc44c..796b96d1c402326528b4ba3c12ee9d92d0e212e9 100644
--- a/scanner/.gitignore
+++ b/scanner/.gitignore
@@ -1,2 +1 @@
/build
-*.iml
\ No newline at end of file
diff --git a/scanner/build.gradle b/scanner/build.gradle
index 152ec5f2063f087a248d8af989cce46955c6cb38..76741aef809a5b900f4c25a8644fd155b411554b 100644
--- a/scanner/build.gradle
+++ b/scanner/build.gradle
@@ -1,44 +1,14 @@
-apply plugin: 'com.android.library'
-
-android {
- compileSdkVersion 29
-
+apply plugin: 'com.huawei.ohos.library'
+ohos {
+ compileSdkVersion 5
defaultConfig {
- minSdkVersion 18
- targetSdkVersion 29
- versionCode 21
- versionName VERSION_NAME
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- }
- buildTypes {
- release {
- minifyEnabled false
- consumerProguardFiles 'scanner-proguard-rules.pro'
- }
- debug {
- testCoverageEnabled true
- }
+ compatibleSdkVersion 5
}
+
}
dependencies {
- implementation 'androidx.annotation:annotation:1.1.0'
-
- androidTestImplementation 'androidx.test:runner:1.3.0-alpha02'
- androidTestImplementation 'androidx.test:rules:1.3.0-alpha02'
- androidTestImplementation 'org.hamcrest:hamcrest-library:2.1'
- androidTestImplementation ('junit:junit:4.13-beta-3') {
- exclude module: 'hamcrest-core'
- }
-
- testImplementation 'org.hamcrest:hamcrest-library:2.1'
- testImplementation ('junit:junit:4.13-beta-3') {
- exclude module: 'hamcrest-core'
- }
- testImplementation "org.mockito:mockito-core:2.18.0"
- testImplementation "org.powermock:powermock-module-junit4:1.7.4"
- testImplementation "org.powermock:powermock-api-mockito:1.7.4"
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ testCompile 'junit:junit:4.12'
+ implementation 'org.jetbrains:annotations:15.0'
}
-
-//apply from: rootProject.file('gradle/gradle-bintray-push.gradle')
\ No newline at end of file
diff --git a/scanner/src/main/config.json b/scanner/src/main/config.json
new file mode 100644
index 0000000000000000000000000000000000000000..576e6849b05793890d6d14884f9a829e860cd006
--- /dev/null
+++ b/scanner/src/main/config.json
@@ -0,0 +1,27 @@
+{
+ "app": {
+ "bundleName": "no.nordicsemi.android.support.v18.scanner",
+ "vendor": "example",
+ "version": {
+ "code": 1,
+ "name": "1.0"
+ },
+ "apiVersion": {
+ "compatible": 5,
+ "target": 5
+ }
+ },
+ "deviceConfig": {},
+ "module": {
+ "package": "no.nordicsemi.android.support.v18.scanner",
+ "abilities": [],
+ "deviceType": [
+ "tv"
+ ],
+ "distro": {
+ "deliveryWithInstall": true,
+ "moduleName": "scanner",
+ "moduleType": "har"
+ }
+ }
+}
\ No newline at end of file
diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerCompat.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerCompat.java
index 65ee77de372e2796edf54a99b104340728c78611..c61dd1ee9da5f29656ca8eac5be83ea1257044ba 100644
--- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerCompat.java
+++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerCompat.java
@@ -1,47 +1,14 @@
-/*
- * Copyright (c) 2018, Nordic Semiconductor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
package no.nordicsemi.android.support.v18.scanner;
-import android.Manifest;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresPermission;
+import ohos.agp.components.Clock;
+import ohos.app.Context;
+import ohos.bluetooth.ble.BleScanFilter;
+import ohos.event.intentagent.IntentAgent;
+import ohos.eventhandler.EventHandler;
+import ohos.eventhandler.EventRunner;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
/**
* This class provides methods to perform scan related operations for Bluetooth LE devices. An
@@ -52,472 +19,392 @@ import androidx.annotation.RequiresPermission;
*
* Since version 1.3.0 of the Scanner Compar Library library,
* {@link PendingIntentReceiver} and {@link ScannerService} will be added to AndroidManifest
- * whether the scanning with {@link PendingIntent} feature is used or not.
+ * whether the scanning with {@link IntentAgent} feature is used or not.
* The {@link ScannerService} is used to emulate such scanning on platforms
* before Oreo, while {@link PendingIntentReceiver} is used to translate from native
- * {@link android.bluetooth.le.ScanResult} to compat {@link ScanResult} and apply additional
+ * {@link ScanResult} to compat {@link ScanResult} and apply additional
* filtering.
*
* Note: Most of the scan methods here require
- * {@link Manifest.permission#BLUETOOTH_ADMIN} permission.
*
* @see ScanFilter
*/
@SuppressWarnings("WeakerAccess")
public abstract class BluetoothLeScannerCompat {
- /**
- * Extra containing a list of ScanResults. It can have one or more results if there was no
- * error. In case of error, {@link #EXTRA_ERROR_CODE} will contain the error code and this
- * extra will not be available.
- */
- public static final String EXTRA_LIST_SCAN_RESULT =
- "android.bluetooth.le.extra.LIST_SCAN_RESULT";
-
- /**
- * Optional extra indicating the error code, if any. The error code will be one of the
- * SCAN_FAILED_* codes in {@link android.bluetooth.le.ScanCallback}.
- */
- public static final String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE";
-
- /**
- * Optional extra indicating the callback type, which will be one of
- * CALLBACK_TYPE_* constants in {@link android.bluetooth.le.ScanSettings}.
- *
- * @see android.bluetooth.le.ScanCallback#onScanResult(int, android.bluetooth.le.ScanResult)
- */
- public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
-
- private static BluetoothLeScannerCompat instance;
-
- /**
- * Returns the scanner compat object
- * @return scanner implementation
- */
- @NonNull
- public synchronized static BluetoothLeScannerCompat getScanner() {
- if (instance != null)
- return instance;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
- return instance = new BluetoothLeScannerImplOreo();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
- return instance = new BluetoothLeScannerImplMarshmallow();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
- return instance = new BluetoothLeScannerImplLollipop();
- return instance = new BluetoothLeScannerImplJB();
- }
-
- /* package */ BluetoothLeScannerCompat() {}
-
- /**
- * Start Bluetooth LE scan with default parameters and no filters. The scan results will be
- * delivered through {@code callback}.
- *
- * Requires {@link Manifest.permission#BLUETOOTH_ADMIN} permission.
- * An app must hold
- * {@link Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_FINE_LOCATION} permission
- * in order to get results.
- *
- * @param callback Callback used to deliver scan results.
- * @throws IllegalArgumentException If {@code callback} is null.
- */
- @SuppressWarnings("WeakerAccess")
- @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH})
- public final void startScan(@NonNull final ScanCallback callback) {
- //noinspection ConstantConditions
- if (callback == null) {
- throw new IllegalArgumentException("callback is null");
- }
- final Handler handler = new Handler(Looper.getMainLooper());
- startScanInternal(Collections.emptyList(), new ScanSettings.Builder().build(),
- callback, handler);
- }
-
- /**
- * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}.
- *
- * Requires {@link Manifest.permission#BLUETOOTH_ADMIN} permission.
- * An app must hold
- * {@link Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_FINE_LOCATION} permission
- * in order to get results.
- *
- * @param filters {@link ScanFilter}s for finding exact BLE devices.
- * @param settings Optional settings for the scan.
- * @param callback Callback used to deliver scan results.
- * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
- */
- @SuppressWarnings("unused")
- @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH})
- public final void startScan(@Nullable final List filters,
- @Nullable final ScanSettings settings,
- @NonNull final ScanCallback callback) {
- //noinspection ConstantConditions
- if (callback == null) {
- throw new IllegalArgumentException("callback is null");
- }
- final Handler handler = new Handler(Looper.getMainLooper());
- startScanInternal(filters != null ? filters : Collections.emptyList(),
- settings != null ? settings : new ScanSettings.Builder().build(),
- callback, handler);
- }
-
- /**
- * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}.
- *
- * Requires {@link Manifest.permission#BLUETOOTH_ADMIN} permission.
- * An app must hold
- * {@link Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_FINE_LOCATION} permission
- * in order to get results.
- *
- * @param filters {@link ScanFilter}s for finding exact BLE devices.
- * @param settings Optional settings for the scan.
- * @param callback Callback used to deliver scan results.
- * @param handler Optional handler used to deliver results.
- * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
- */
- @SuppressWarnings("unused")
- @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH})
- public final void startScan(@Nullable final List filters,
- @Nullable final ScanSettings settings,
- @NonNull final ScanCallback callback,
- @Nullable final Handler handler) {
- //noinspection ConstantConditions
- if (callback == null) {
- throw new IllegalArgumentException("callback is null");
- }
- startScanInternal(filters != null ? filters : Collections.emptyList(),
- settings != null ? settings : new ScanSettings.Builder().build(),
- callback, handler != null ? handler : new Handler(Looper.getMainLooper()));
- }
-
- /**
- * Stops an ongoing Bluetooth LE scan.
- *
- * Requires {@link Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
- * @param callback The callback used to start scanning.
- */
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public final void stopScan(@NonNull final ScanCallback callback) {
- //noinspection ConstantConditions
- if (callback == null) {
- throw new IllegalArgumentException("callback is null");
- }
- stopScanInternal(callback);
- }
-
- /**
- * Starts Bluetooth LE scan. Its implementation depends on the Android version.
- *
- * @param filters {@link ScanFilter}s for finding exact BLE devices.
- * @param settings Settings for the scan.
- * @param callback Callback used to deliver scan results.
- * @param handler Handler used to deliver results.
- */
- @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH})
- /* package */ abstract void startScanInternal(@NonNull List filters,
- @NonNull ScanSettings settings,
- @NonNull ScanCallback callback,
- @NonNull Handler handler);
-
- /**
- * Stops an ongoing Bluetooth LE scan. Its implementation depends on the Android version.
- *
- * @param callback The callback used to start scanning.
- */
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- /* package */ abstract void stopScanInternal(@NonNull ScanCallback callback);
-
- /**
- * Start Bluetooth LE scan using a {@link PendingIntent}. The scan results will be delivered
- * via the PendingIntent. On platforms before Oreo this will start {@link ScannerService}
- * which will scan in background using given settings.
- *
- * This method of scanning is intended to work in background. Long running scanning may
- * consume a lot of battery, so it is recommended to use low power mode in settings,
- * offloaded filtering and batching. However, the library may emulate batching, filtering or
- * callback types {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} and
- * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST} if they are not supported.
- *
- * A {@link PendingIntentReceiver} and {@link ScannerService} will be added to AndroidManifest
- * whether this feature is used or not.
- *
- * Requires {@link Manifest.permission#BLUETOOTH_ADMIN} permission.
- * An app must hold
- * {@link Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_FINE_LOCATION} permission
- * in order to get results.
- *
- * When the PendingIntent is delivered, the Intent passed to the receiver or activity will
- * contain one or more of the extras {@link #EXTRA_CALLBACK_TYPE}, {@link #EXTRA_ERROR_CODE} and
- * {@link #EXTRA_LIST_SCAN_RESULT} to indicate the result of the scan.
- *
- * @param filters {@link ScanFilter}s for finding exact BLE devices.
- * @param settings Optional settings for the scan.
- * @param context Context used to start {@link ScannerService}.
- * @param callbackIntent The PendingIntent to deliver the result to.
- * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
- */
- @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH})
- public final void startScan(@Nullable final List filters,
- @Nullable final ScanSettings settings,
- @NonNull final Context context,
- @NonNull final PendingIntent callbackIntent) {
- //noinspection ConstantConditions
- if (callbackIntent == null) {
- throw new IllegalArgumentException("callbackIntent is null");
- }
- //noinspection ConstantConditions
- if (context == null) {
- throw new IllegalArgumentException("context is null");
- }
- startScanInternal(filters != null ? filters : Collections.emptyList(),
- settings != null ? settings : new ScanSettings.Builder().build(),
- context, callbackIntent);
- }
-
- /**
- * Stops an ongoing Bluetooth LE scan.
- *
- * Requires {@link Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
- * @param context Context used to stop {@link ScannerService}.
- * @param callbackIntent The PendingIntent that was used to start the scan.
- */
- @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH})
- public final void stopScan(@NonNull final Context context,
- @NonNull final PendingIntent callbackIntent) {
- //noinspection ConstantConditions
- if (callbackIntent == null) {
- throw new IllegalArgumentException("callbackIntent is null");
- }
- //noinspection ConstantConditions
- if (context == null) {
- throw new IllegalArgumentException("context is null");
- }
- stopScanInternal(context, callbackIntent);
- }
-
- /**
- * Starts Bluetooth LE scan using PendingIntent.
- * Its implementation depends on the Android version.
- *
- * @param filters {@link ScanFilter}s for finding exact BLE devices.
- * @param settings Settings for the scan.
- * @param context Context used to start {@link ScannerService}.
- * @param callbackIntent The PendingIntent to deliver the result to.
- */
- @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH})
- /* package */ abstract void startScanInternal(@NonNull List filters,
- @NonNull ScanSettings settings,
- @NonNull Context context,
- @NonNull PendingIntent callbackIntent);
-
- /**
- * Stops an ongoing Bluetooth LE scan.
- *
- * @param context Context used to stop {@link ScannerService}.
- * @param callbackIntent The PendingIntent that was used to start the scan.
- */
- @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH})
- /* package */ abstract void stopScanInternal(@NonNull Context context,
- @NonNull PendingIntent callbackIntent);
-
- /**
- * Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth
- * LE scan results batched on Bluetooth controller. Returns immediately, batch scan results data
- * will be delivered through the {@code callback}.
- *
- * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
- * used to start scan.
- */
- @SuppressWarnings("unused")
- public abstract void flushPendingScanResults(@NonNull ScanCallback callback);
-
- /* package */ static class ScanCallbackWrapper {
-
- @NonNull private final Object LOCK = new Object();
-
- private final boolean emulateFiltering;
- private final boolean emulateBatching;
- private final boolean emulateFoundOrLostCallbackType;
- private boolean scanningStopped;
-
- @NonNull final List filters;
- @NonNull final ScanSettings scanSettings;
- @NonNull final ScanCallback scanCallback;
- @NonNull final Handler handler;
-
- @NonNull private final List scanResults = new ArrayList<>();
-
- @NonNull private final Set devicesInBatch = new HashSet<>();
-
- /** A collection of scan result of devices in range. */
- @NonNull private final Map devicesInRange = new HashMap<>();
-
- @NonNull
- private final Runnable flushPendingScanResultsTask = new Runnable() {
- @Override
- public void run() {
- if (!scanningStopped) {
- flushPendingScanResults();
- handler.postDelayed(this, scanSettings.getReportDelayMillis());
- }
- }
- };
-
- /** A task, called periodically, that notifies about match lost. */
- @NonNull
- private final Runnable matchLostNotifierTask = new Runnable() {
- @Override
- public void run() {
- final long now = SystemClock.elapsedRealtimeNanos();
-
- synchronized (LOCK) {
- final Iterator iterator = devicesInRange.values().iterator();
- while (iterator.hasNext()) {
- final ScanResult result = iterator.next();
- if (result.getTimestampNanos() < now - scanSettings.getMatchLostDeviceTimeout()) {
- iterator.remove();
- handler.post(new Runnable() {
- @Override
- public void run() {
- scanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, result);
- }
- });
- }
- }
-
- if (!devicesInRange.isEmpty()) {
- handler.postDelayed(this, scanSettings.getMatchLostTaskInterval());
- }
- }
- }
- };
-
- /* package */ ScanCallbackWrapper(final boolean offloadedBatchingSupported,
- final boolean offloadedFilteringSupported,
- @NonNull final List filters,
- @NonNull final ScanSettings settings,
- @NonNull final ScanCallback callback,
- @NonNull final Handler handler) {
- this.filters = Collections.unmodifiableList(filters);
- this.scanSettings = settings;
- this.scanCallback = callback;
- this.handler = handler;
- this.scanningStopped = false;
-
- // Emulate other callback types
- final boolean callbackTypesSupported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
- emulateFoundOrLostCallbackType = settings.getCallbackType() != ScanSettings.CALLBACK_TYPE_ALL_MATCHES
- && (!callbackTypesSupported || !settings.getUseHardwareCallbackTypesIfSupported());
-
- // Emulate filtering
- emulateFiltering = !filters.isEmpty() && (!offloadedFilteringSupported || !settings.getUseHardwareFilteringIfSupported());
-
- // Emulate batching
- final long delay = settings.getReportDelayMillis();
- emulateBatching = delay > 0 && (!offloadedBatchingSupported || !settings.getUseHardwareBatchingIfSupported());
- if (emulateBatching) {
- handler.postDelayed(flushPendingScanResultsTask, delay);
- }
- }
-
- /* package */ void close() {
- scanningStopped = true;
- handler.removeCallbacksAndMessages(null);
- synchronized (LOCK) {
- devicesInRange.clear();
- devicesInBatch.clear();
- scanResults.clear();
- }
- }
-
- /* package */ void flushPendingScanResults() {
- if (emulateBatching && !scanningStopped) {
- synchronized (LOCK) {
- scanCallback.onBatchScanResults(new ArrayList<>(scanResults));
- scanResults.clear();
- devicesInBatch.clear();
- }
- }
- }
-
- /* package */ void handleScanResult(final int callbackType,
- @NonNull final ScanResult scanResult) {
- if (scanningStopped || !filters.isEmpty() && !matches(scanResult))
- return;
-
- final String deviceAddress = scanResult.getDevice().getAddress();
-
- // Notify if a new device was found and callback type is FIRST MATCH
- if (emulateFoundOrLostCallbackType) { // -> Callback type != ScanSettings.CALLBACK_TYPE_ALL_MATCHES
- ScanResult previousResult;
- boolean firstResult;
- synchronized (devicesInRange) {
- // The periodic task will be started only on the first result
- firstResult = devicesInRange.isEmpty();
- // Save the first result or update the old one with new data
- previousResult = devicesInRange.put(deviceAddress, scanResult);
- }
-
- if (previousResult == null) {
- if ((scanSettings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) > 0) {
- scanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, scanResult);
- }
- }
-
- // In case user wants to be notified about match lost, we need to start a task that
- // will check the timestamp periodically
- if (firstResult) {
- if ((scanSettings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST) > 0) {
- handler.removeCallbacks(matchLostNotifierTask);
- handler.postDelayed(matchLostNotifierTask, scanSettings.getMatchLostTaskInterval());
- }
- }
- } else {
- // A callback type may not contain CALLBACK_TYPE_ALL_MATCHES and any other value.
- // If devicesInRange is empty, report delay > 0 means we are emulating hardware
- // batching. Otherwise handleScanResults(List) is called, not this method.
- if (emulateBatching) {
- synchronized (LOCK) {
- if (!devicesInBatch.contains(deviceAddress)) { // add only the first record from the device, others will be skipped
- scanResults.add(scanResult);
- devicesInBatch.add(deviceAddress);
- }
- }
- return;
- }
-
- scanCallback.onScanResult(callbackType, scanResult);
- }
- }
-
- /* package */ void handleScanResults(@NonNull final List results) {
- if (scanningStopped)
- return;
-
- List filteredResults = results;
-
- if (emulateFiltering) {
- filteredResults = new ArrayList<>();
- for (final ScanResult result : results)
- if (matches(result))
- filteredResults.add(result);
- }
-
- scanCallback.onBatchScanResults(filteredResults);
- }
-
- /* package */ void handleScanError(final int errorCode) {
- scanCallback.onScanFailed(errorCode);
- }
-
- private boolean matches(@NonNull final ScanResult result) {
- for (final ScanFilter filter : filters) {
- if (filter.matches(result))
- return true;
- }
- return false;
- }
- }
+ /**
+ * Extra containing a list of ScanResults. It can have one or more results if there was no
+ * error. In case of error, {@link #EXTRA_ERROR_CODE} will contain the error code and this
+ * extra will not be available.
+ */
+ public static final String EXTRA_LIST_SCAN_RESULT =
+ "android.bluetooth.le.extra.LIST_SCAN_RESULT";
+ /**
+ * Optional extra indicating the error code, if any. The error code will be one of the
+ * SCAN_FAILED_* codes in {@link ScanCallback}.
+ */
+ public static final String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE";
+ /**
+ * Optional extra indicating the callback type, which will be one of
+ * CALLBACK_TYPE_* constants in {@link ScanSettings}.
+ */
+ public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
+ private static BluetoothLeScannerCompat instance;
+
+ /**
+ * Returns the scanner compat object
+ *
+ * @return scanner implementation
+ */
+ public BluetoothLeScannerCompat() {
+ }
+
+ public synchronized static BluetoothLeScannerCompat getScanner() {
+ if (instance != null)
+ return instance;
+ instance = new BluetoothScannerImplV5();
+ return instance;
+ }
+
+ /**
+ * Start Bluetooth LE scan with default parameters and no filters. The scan results will be
+ * delivered through {@code callback}.
+ *
+ * An app must hold
+ * in order to get results.
+ *
+ * @param callback Callback used to deliver scan results.
+ * @throws IllegalArgumentException If {@code callback} is null.
+ */
+ @SuppressWarnings("WeakerAccess")
+ public final void startScan(Context context, final ScanCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback is null");
+ }
+ final EventHandler handler = new EventHandler(EventRunner.getMainEventRunner());
+ startScanInternal(context, Collections.emptyList(), new ScanSettings.Builder().build(),
+ callback, handler);
+ }
+
+ /**
+ * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}.
+ *
+ * An app must hold
+ * in order to get results.
+ *
+ * @param filters {@link ScanFilter}s for finding exact BLE devices.
+ * @param settings Optional settings for the scan.
+ * @param callback Callback used to deliver scan results.
+ * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
+ */
+ public final void startScan(Context context, @Nullable final List filters,
+ @Nullable final ScanSettings settings,
+ final ScanCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback is null");
+ }
+ final EventHandler handler = new EventHandler(EventRunner.getMainEventRunner());
+ startScanInternal(context, filters != null ? filters : Collections.emptyList(),
+ settings != null ? settings : new ScanSettings.Builder().build(),
+ callback, handler);
+ }
+
+ /**
+ * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}.
+ *
+ * An app must hold
+ * in order to get results.
+ *
+ * @param callback Callback used to deliver scan results.
+ * @param handler Optional handler used to deliver results.
+ * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
+ */
+ protected abstract void startScanInternal(Context context, List scanFilters, ScanSettings scanSettings, ScanCallback callback, EventHandler handler);
+
+ /**
+ * Stops an ongoing Bluetooth LE scan.
+ *
+ *
+ * @param callback The callback used to start scanning.
+ */
+ public final void stopScan(final ScanCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback is null");
+ }
+ stopScanInternal(callback);
+ }
+
+ /**
+ * Starts Bluetooth LE scan. Its implementation depends on the Android version.
+ *
+ * @param filters {@link ScanFilter}s for finding exact BLE devices.
+ * @param settings Settings for the scan.
+ * @param callback Callback used to deliver scan results.
+ * @param EventHandler Handler used to deliver results.
+ */
+ /**
+ * Stops an ongoing Bluetooth LE scan. Its implementation depends on the Android version.
+ *
+ * @param callback The callback used to start scanning.
+ */
+ /* package */
+ abstract void stopScanInternal(ScanCallback callback);
+
+ /**
+ * via the PendingIntent. On platforms before Oreo this will start {@link ScannerService}
+ * which will scan in background using given settings.
+ *
+ * This method of scanning is intended to work in background. Long running scanning may
+ * consume a lot of battery, so it is recommended to use low power mode in settings,
+ * offloaded filtering and batching. However, the library may emulate batching, filtering or
+ * callback types {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} and
+ * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST} if they are not supported.
+ *
+ * A {@link PendingIntentReceiver} and {@link ScannerService} will be added to AndroidManifest
+ * whether this feature is used or not.
+ *
+ * An app must hold
+ * in order to get results.
+ *
+ * When the PendingIntent is delivered, the Intent passed to the receiver or activity will
+ * contain one or more of the extras {@link #EXTRA_CALLBACK_TYPE}, {@link #EXTRA_ERROR_CODE} and
+ * {@link #EXTRA_LIST_SCAN_RESULT} to indicate the result of the scan.
+ *
+ * @param filters {@link ScanFilter}s for finding exact BLE devices.
+ * @param settings Optional settings for the scan.
+ * @param context Context used to start {@link ScannerService}.
+ * @param callbackIntent The PendingIntent to deliver the result to.
+ * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
+ */
+ public final void startScan(@Nullable final List filters,
+ @Nullable final ScanSettings settings,
+ final Context context,
+ final IntentAgent callbackIntent) {
+ if (callbackIntent == null) {
+ }
+ if (context == null) {
+ throw new IllegalArgumentException("context is null");
+ }
+ startScanInternal(filters != null ? filters : Collections.emptyList(),
+ settings != null ? settings : new ScanSettings.Builder().build(),
+ context, callbackIntent);
+ }
+
+ /**
+ * Stops an ongoing Bluetooth LE scan.
+ *
+ *
+ * @param context Context used to stop {@link ScannerService}.
+ * @param callbackIntent The PendingIntent that was used to start the scan.
+ */
+ public final void stopScan(final Context context,
+ final IntentAgent callbackIntent) {
+ if (callbackIntent == null) {
+ throw new IllegalArgumentException("callbackIntent is null");
+ }
+ if (context == null) {
+ throw new IllegalArgumentException("context is null");
+ }
+ stopScanInternal(context, callbackIntent);
+ }
+
+ /**
+ * Starts Bluetooth LE scan using PendingIntent.
+ * Its implementation depends on the Android version.
+ *
+ * @param filters {@link ScanFilter}s for finding exact BLE devices.
+ * @param settings Settings for the scan.
+ * @param context Context used to start {@link ScannerService}.
+ * @param callbackIntent The PendingIntent to deliver the result to.
+ */
+ /* package */
+ abstract void startScanInternal(List filters,
+ ScanSettings settings,
+ Context context,
+ IntentAgent callbackIntent);
+
+ /**
+ * Stops an ongoing Bluetooth LE scan.
+ *
+ * @param context Context used to stop {@link ScannerService}.
+ * @param callbackIntent The PendingIntent that was used to start the scan.
+ */
+ /* package */
+ abstract void stopScanInternal(Context context,
+ IntentAgent callbackIntent);
+
+ /**
+ * Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth
+ * LE scan results batched on Bluetooth controller. Returns immediately, batch scan results data
+ * will be delivered through the {@code callback}.
+ *
+ * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
+ * used to start scan.
+ */
+ @SuppressWarnings("unused")
+ public abstract void flushPendingScanResults(ScanCallback callback);
+
+ public abstract ArrayList flushPendingScanResults(List nativeScanResults);
+
+ /* package */ static class ScanCallbackWrapper {
+ private final Object LOCK = new Object();
+ private final boolean emulateFiltering;
+ private final boolean emulateBatching;
+ private boolean scanningStopped;
+
+ final List filters;
+ final ScanSettings scanSettings;
+ final ScanCallback scanCallback;
+ final EventHandler handler;
+ Context context;
+
+ private final List scanResults = new ArrayList<>();
+
+ private final Set devicesInBatch = new HashSet<>();
+
+ /**
+ * A collection of scan result of devices in range.
+ */
+ private final Map devicesInRange = new HashMap<>();
+
+ private final Runnable flushPendingScanResultsTask = new Runnable() {
+ @Override
+ public void run() {
+ if (!scanningStopped) {
+ flushPendingScanResults();
+ handler.postTask(this, scanSettings.getReportDelayMillis());
+ }
+ }
+ };
+
+ /**
+ * A task, called periodically, that notifies about match lost.
+ */
+ private final Runnable matchLostNotifierTask = new Runnable() {
+ @Override
+ public void run() {
+ Clock clock = new Clock(context);
+ long now = clock.getTime();
+ synchronized (LOCK) {
+ final Iterator iterator = devicesInRange.values().iterator();
+ while (iterator.hasNext()) {
+ final ScanResult result = iterator.next();
+ if (result.getTimestampNanos() < now - scanSettings.getMatchLostDeviceTimeout()) {
+ iterator.remove();
+ handler.postTask(new Runnable() {
+ @Override
+ public void run() {
+ scanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, result);
+ }
+ });
+ }
+ }
+ if (!devicesInRange.isEmpty()) {
+ handler.postTask(this, scanSettings.getMatchLostTaskInterval());
+ }
+ }
+ }
+ };
+
+ /* package */ ScanCallbackWrapper(final boolean offloadedBatchingSupported,
+ final boolean offloadedFilteringSupported,
+ final List filters,
+ final ScanSettings settings,
+ final ScanCallback callback,
+ final EventHandler handler) {
+ this.filters = Collections.unmodifiableList(filters);
+ this.scanSettings = settings;
+ this.scanCallback = callback;
+ this.handler = handler;
+ this.scanningStopped = false;
+ emulateFiltering = !filters.isEmpty() && (!offloadedFilteringSupported || !settings.getUseHardwareFilteringIfSupported());
+ final long delay = settings.getReportDelayMillis();
+ emulateBatching = delay > 0 && (!offloadedBatchingSupported || !settings.getUseHardwareBatchingIfSupported());
+ if (emulateBatching) {
+ handler.postTask(flushPendingScanResultsTask, delay);
+ }
+ }
+
+ /* package */ void close() {
+ scanningStopped = true;
+
+ synchronized (LOCK) {
+ devicesInRange.clear();
+ devicesInBatch.clear();
+ scanResults.clear();
+ }
+ }
+
+ /* package */ void flushPendingScanResults() {
+ if (emulateBatching && !scanningStopped) {
+ synchronized (LOCK) {
+ scanCallback.onBatchScanResults(new ArrayList<>(scanResults));
+ scanResults.clear();
+ devicesInBatch.clear();
+ }
+ }
+ }
+
+ /* package */ void handleScanResult(final int callbackType,
+ final ScanResult scanResult) {
+ if (scanningStopped || !filters.isEmpty() && !matches(scanResult))
+ return;
+ final String deviceAddress = (String) scanResult.getDevice();
+ ScanResult previousResult;
+ boolean firstResult;
+ synchronized (devicesInRange) {
+ firstResult = devicesInRange.isEmpty();
+ previousResult = devicesInRange.put(deviceAddress, scanResult);
+ if (previousResult == null) {
+ if ((scanSettings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) > 0) {
+ scanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, scanResult);
+ }
+ }
+ if (firstResult) {
+ if ((scanSettings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST) > 0) {
+ handler.removeTask(matchLostNotifierTask);
+ handler.postTask(matchLostNotifierTask, scanSettings.getMatchLostTaskInterval());
+ }
+ }
+ if (emulateBatching) {
+ synchronized (LOCK) {
+ if (!devicesInBatch.contains(deviceAddress)) {
+ scanResults.add(scanResult);
+ devicesInBatch.add(deviceAddress);
+ }
+ }
+ return;
+ }
+
+ scanCallback.onScanResult(callbackType, scanResult);
+ }
+ }
+
+ /* package */ void handleScanResults(final List results) {
+ if (scanningStopped)
+ return;
+
+ List filteredResults = results;
+
+ if (emulateFiltering) {
+ filteredResults = new ArrayList<>();
+ for (final ScanResult result : results)
+ if (matches(result))
+ filteredResults.add(result);
+ }
+
+ scanCallback.onBatchScanResults(filteredResults);
+ }
+
+ /* package */ void handleScanError(final int errorCode) {
+ scanCallback.onScanFailed(errorCode);
+ }
+
+ private boolean matches(final ScanResult result) {
+ for (final BleScanFilter filter : filters) {
+ return true;
+ }
+ return false;
+ }
+ }
}
diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeUtils.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeUtils.java
index 14a947d56c9c152bc7701d997db3fcd71e33ae97..35bff436fc86214e5242083700845178219a9a38 100644
--- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeUtils.java
+++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeUtils.java
@@ -1,129 +1,93 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
package no.nordicsemi.android.support.v18.scanner;
-import android.util.SparseArray;
-
+import ohos.utils.PlainArray;
+import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
-import androidx.annotation.Nullable;
-
/**
* Helper class for Bluetooth LE utils.
*/
/* package */
-@SuppressWarnings("unused")
class BluetoothLeUtils {
+ static String toString(@Nullable final PlainArray array) {
+ if (array == null) {
+ return "null";
+ }
+ if (array.size() == 0) {
+ return "{}";
+ }
+ final StringBuilder buffer = new StringBuilder();
+ buffer.append('{');
+ for (int Isiarry = 0; array.size() > Isiarry; Isiarry++) {
+ buffer.append(array.keyAt(Isiarry)).append("=").append(Arrays.toString(array.valueAt(Isiarry)));
+ }
+ buffer.append('}');
+ return buffer.toString();
+ }
+ static String toString(@Nullable final Map map) {
+ if (map == null) {
+ return "null";
+ }
+ if (map.isEmpty()) {
+ return "{}";
+ }
+ final StringBuilder buffer = new StringBuilder();
+ buffer.append('{');
+ final Iterator> it = map.entrySet().iterator();
+ while (it.hasNext()) {
+ final Map.Entry entry = it.next();
+ final Object key = entry.getKey();
+ buffer.append(key).append("=").append(Arrays.toString(map.get(key)));
+ if (it.hasNext()) {
+ buffer.append(", ");
+ }
+ }
+ buffer.append('}');
+ return buffer.toString();
+ }
- /**
- * Returns a string composed from a {@link SparseArray}.
- */
- static String toString(@Nullable final SparseArray array) {
- if (array == null) {
- return "null";
- }
- if (array.size() == 0) {
- return "{}";
- }
- final StringBuilder buffer = new StringBuilder();
- buffer.append('{');
- for (int i = 0; i < array.size(); ++i) {
- buffer.append(array.keyAt(i)).append("=").append(Arrays.toString(array.valueAt(i)));
- }
- buffer.append('}');
- return buffer.toString();
- }
-
- /**
- * Returns a string composed from a {@link Map}.
- */
- static String toString(@Nullable final Map map) {
- if (map == null) {
- return "null";
- }
- if (map.isEmpty()) {
- return "{}";
- }
- final StringBuilder buffer = new StringBuilder();
- buffer.append('{');
- final Iterator> it = map.entrySet().iterator();
- while (it.hasNext()) {
- final Map.Entry entry = it.next();
- final Object key = entry.getKey();
- buffer.append(key).append("=").append(Arrays.toString(map.get(key)));
- if (it.hasNext()) {
- buffer.append(", ");
- }
- }
- buffer.append('}');
- return buffer.toString();
- }
-
- /**
- * Check whether two {@link SparseArray} equal.
- */
- static boolean equals(@Nullable final SparseArray array,
- @Nullable final SparseArray otherArray) {
- if (array == otherArray) {
- return true;
- }
- if (array == null || otherArray == null) {
- return false;
- }
- if (array.size() != otherArray.size()) {
- return false;
- }
-
- // Keys are guaranteed in ascending order when indices are in ascending order.
- for (int i = 0; i < array.size(); ++i) {
- if (array.keyAt(i) != otherArray.keyAt(i) ||
- !Arrays.equals(array.valueAt(i), otherArray.valueAt(i))) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Check whether two {@link Map} equal.
- */
- static boolean equals(@Nullable final Map map, Map otherMap) {
- if (map == otherMap) {
- return true;
- }
- if (map == null || otherMap == null) {
- return false;
- }
- if (map.size() != otherMap.size()) {
- return false;
- }
- Set keys = map.keySet();
- if (!keys.equals(otherMap.keySet())) {
- return false;
- }
- for (T key : keys) {
- if (!Objects.deepEquals(map.get(key), otherMap.get(key))) {
- return false;
- }
- }
- return true;
- }
+ static boolean equals(@Nullable final PlainArray array,
+ @Nullable final PlainArray otherArray) {
+ if (array == otherArray) {
+ return true;
+ }
+ if (array == null || otherArray == null) {
+ return false;
+ }
+ if (array.size() != otherArray.size()) {
+ return false;
+ }
+ for (int Isiarry = 0; array.size() > Isiarry; Isiarry++) {
+ if (array.keyAt(Isiarry) != otherArray.keyAt(Isiarry)
+ || !Arrays.equals(array.valueAt(Isiarry), otherArray.valueAt(Isiarry))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ static boolean equals(@Nullable final Map map, Map otherMap) {
+ if (map == otherMap) {
+ return true;
+ }
+ if (map == null || otherMap == null) {
+ return false;
+ }
+ if (map.size() != otherMap.size()) {
+ return false;
+ }
+ Set keys = map.keySet();
+ if (!keys.equals(otherMap.keySet())) {
+ return false;
+ }
+ for (T key : keys) {
+ if (!Objects.deepEquals(map.get(key), otherMap.get(key))) {
+ return false;
+ }
+ }
+ return true;
+ }
}
\ No newline at end of file
diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothScannerImplV5.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothScannerImplV5.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f3f77af52c8279d932e504828b8e8172baa2d34
--- /dev/null
+++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothScannerImplV5.java
@@ -0,0 +1,220 @@
+package no.nordicsemi.android.support.v18.scanner;
+
+import ohos.aafwk.content.Intent;
+import ohos.aafwk.content.Operation;
+import ohos.app.Context;
+import ohos.bluetooth.ble.BleCentralManager;
+import ohos.bluetooth.ble.BleCentralManagerCallback;
+import ohos.bluetooth.ble.BleScanFilter;
+import ohos.bluetooth.ble.BleScanResult;
+import ohos.event.intentagent.IntentAgent;
+import ohos.eventhandler.EventHandler;
+import ohos.eventhandler.InnerEvent;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+import java.util.logging.Handler;
+
+public class BluetoothScannerImplV5 extends BluetoothLeScannerCompat {
+ private final Map wrappers = new HashMap<>();
+ private long powerSaveRestInterval;
+ private long powerSaveScanInterval;
+ @Nullable
+ private EventHandler handlerThread;
+ @Nullable
+ private Handler powerSaveHandler;
+ private BleCentralManager centralManager;
+ private List filters;
+
+ @Override
+ protected void startScanInternal(Context context, List scanFilters
+ ,ScanSettings scanSettings, ScanCallback callback, EventHandler handler) {
+ boolean shouldStart;
+ synchronized (wrappers) {
+ if (wrappers.containsKey(callback)) {
+ throw new IllegalArgumentException("scanner already started with given scanCallback");
+ }
+ final ScanCallbackWrapper wrapper = new ScanCallbackWrapper(
+ false, false,
+ scanFilters, scanSettings, callback, handler);
+ shouldStart = wrappers.isEmpty();
+ wrappers.put(callback, wrapper);
+ }
+ centralManager = new BleCentralManager(context, new ScanCallbackV5());
+ filters = new ArrayList();
+ centralManager.startScan(filters);
+ setPowerSaveSettings();
+ }
+
+ @Override
+ void stopScanInternal(ScanCallback callback) {
+ boolean shouldStop;
+ ScanCallbackWrapper wrapper;
+ synchronized (wrappers) {
+ wrapper = wrappers.remove(callback);
+ shouldStop = wrappers.isEmpty();
+ }
+ if (wrapper == null)
+ return;
+ wrapper.close();
+ setPowerSaveSettings();
+ if (shouldStop) {
+ if (powerSaveHandler != null) {
+ }
+ if (handlerThread != null) {
+ handlerThread = null;
+ }
+ }
+ setPowerSaveSettings();
+ }
+
+ @Override
+ void startScanInternal(Listfilters, ScanSettings settings
+ ,Context context, IntentAgent callbackIntent) {
+ Intent intent = new Intent();
+ Operation operation = new Intent.OperationBuilder()
+ .withDeviceId("")
+ .withBundleName(context.getBundleName())
+ .withAbilityName("no.nordicsemi.android.support.v18.scanner.ScannerService")
+ .build();
+ intent.setParam(ScannerService.EXTRA_SETTINGS, settings);
+ intent.setParam(ScannerService.EXTRA_PENDING_INTENT, callbackIntent);
+ intent.setParam(ScannerService.EXTRA_START, true);
+ intent.setOperation(operation);
+ context.startAbility(intent, 0);
+ }
+
+ @Override
+ void stopScanInternal(Context context, IntentAgent callbackIntent) {
+ final Intent service = new Intent();
+ }
+
+ @Override
+ public void flushPendingScanResults(ScanCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null!");
+ }
+ ScanCallbackWrapper wrapper;
+ synchronized (wrappers) {
+ wrapper = wrappers.get(callback);
+ }
+ if (wrapper == null) {
+ throw new IllegalArgumentException("callback not registered!");
+ }
+ wrapper.flushPendingScanResults();
+ }
+
+ @Override
+ public ArrayList flushPendingScanResults(List nativeScanResults) {
+ if (nativeScanResults == null) {
+ throw new IllegalArgumentException("callback cannot be null!");
+ }
+ ScanCallbackWrapper wrapper;
+ synchronized (wrappers) {
+ wrapper = wrappers.get(nativeScanResults);
+ }
+ if (wrapper == null) {
+ throw new IllegalArgumentException("callback not registered!");
+ }
+ wrapper.flushPendingScanResults();
+ return null;
+ }
+
+ public class ScanCallbackV5 implements BleCentralManagerCallback {
+ List results = new ArrayList();
+
+ @Override
+ public void scanResultEvent(BleScanResult resultCode) {
+ synchronized (wrappers) {
+ for (ScanResult scanResult : results) {
+ BleScanResult temp = (BleScanResult) scanResult.getDevice();
+ if (temp.getPeripheralDevice().getDeviceAddr()
+ .equals(resultCode.getPeripheralDevice().getDeviceAddr())) {
+ return;
+ }
+ }
+ ScanResult scanResult = fromNativeScanResult(resultCode);
+ results.add(scanResult);
+ final Collection scanCallbackWrappers = wrappers.keySet();
+ for (final ScanCallback wrapper : scanCallbackWrappers) {
+ wrapper.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, scanResult);
+ }
+ for (final ScanCallback wrapper : scanCallbackWrappers) {
+ wrapper.onBatchScanResults(results);
+ }
+ }
+ }
+
+ @Override
+ public void scanFailedEvent(int resultCode) {
+ synchronized (wrappers) {
+ final Collection scanCallbackWrappers = wrappers.keySet();
+ for (final ScanCallback wrapper : scanCallbackWrappers) {
+ wrapper.onScanFailed(resultCode);
+ }
+ }
+ }
+
+ @Override
+ public void groupScanResultsEvent(final List scanResults) {
+ synchronized (wrappers) {
+ }
+ }
+
+ ScanResult fromNativeScanResult(BleScanResult nativeScanResult) {
+ return new ScanResult(nativeScanResult,
+ ScanRecord.parseFromBytes(nativeScanResult.getRawData()), nativeScanResult.getRssi(), nativeScanResult.getTime());
+ }
+ }
+
+ private final Runnable powerSaveSleepTask = new Runnable() {
+ @Override
+ public void run() {
+ if (centralManager != null && powerSaveRestInterval > 0 && powerSaveScanInterval > 0) {
+ centralManager.stopScan();
+ handlerThread.sendEvent(InnerEvent.get(powerSaveScanTask), powerSaveRestInterval);
+ }
+ }
+ };
+ private final Runnable powerSaveScanTask = new Runnable() {
+ @Override
+ public void run() {
+ if (centralManager != null && powerSaveRestInterval > 0 && powerSaveScanInterval > 0) {
+ centralManager.startScan(filters);
+ handlerThread.sendEvent(InnerEvent.get(powerSaveSleepTask), powerSaveScanInterval);
+ }
+ }
+ };
+
+ private void setPowerSaveSettings() {
+ long minRest = Long.MAX_VALUE, minScan = Long.MAX_VALUE;
+ synchronized (wrappers) {
+ for (final ScanCallbackWrapper wrapper : wrappers.values()) {
+ final ScanSettings settings = wrapper.scanSettings;
+ if (settings.hasPowerSaveMode()) {
+ if (minRest > settings.getPowerSaveRest()) {
+ minRest = settings.getPowerSaveRest();
+ }
+ if (minScan > settings.getPowerSaveScan()) {
+ minScan = settings.getPowerSaveScan();
+ }
+ }
+ }
+ }
+ if (minRest < Long.MAX_VALUE && minScan < Long.MAX_VALUE) {
+ powerSaveRestInterval = minRest;
+ powerSaveScanInterval = minScan;
+ if (powerSaveHandler != null) {
+ handlerThread.removeTask(powerSaveScanTask);
+ handlerThread.removeTask(powerSaveSleepTask);
+ handlerThread.sendEvent(InnerEvent.get(powerSaveSleepTask), powerSaveScanInterval);
+ }
+ } else {
+ powerSaveRestInterval = powerSaveScanInterval = 0;
+ if (powerSaveHandler != null) {
+ handlerThread.removeTask(powerSaveScanTask);
+ handlerThread.removeTask(powerSaveSleepTask);
+ }
+ }
+ }
+}
diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothUuid.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothUuid.java
index 167a45bfd201e7c9b6d7b98cf5badaf2e5e20b2f..77d555e7012dc8a1f07148fc94a0a067ad10eb82 100644
--- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothUuid.java
+++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothUuid.java
@@ -1,23 +1,6 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
package no.nordicsemi.android.support.v18.scanner;
-import android.os.ParcelUuid;
-
+import ohos.utils.SequenceUuid;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.UUID;
@@ -25,59 +8,56 @@ import java.util.UUID;
/**
* Static helper methods and constants to decode the ParcelUuid of remote devices.
*/
-/* package */ final class BluetoothUuid {
-
- private static final ParcelUuid BASE_UUID =
- ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
-
- /** Length of bytes for 16 bit UUID */
- static final int UUID_BYTES_16_BIT = 2;
- /** Length of bytes for 32 bit UUID */
- static final int UUID_BYTES_32_BIT = 4;
- /** Length of bytes for 128 bit UUID */
- static final int UUID_BYTES_128_BIT = 16;
-
- /**
- * Parse UUID from bytes. The {@code uuidBytes} can represent a 16-bit, 32-bit or 128-bit UUID,
- * but the returned UUID is always in 128-bit format.
- * Note UUID is little endian in Bluetooth.
- *
- * @param uuidBytes Byte representation of uuid.
- * @return {@link ParcelUuid} parsed from bytes.
- * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed.
- */
- static ParcelUuid parseUuidFrom(final byte[] uuidBytes) {
- if (uuidBytes == null) {
- throw new IllegalArgumentException("uuidBytes cannot be null");
- }
- final int length = uuidBytes.length;
- if (length != UUID_BYTES_16_BIT && length != UUID_BYTES_32_BIT &&
- length != UUID_BYTES_128_BIT) {
- throw new IllegalArgumentException("uuidBytes length invalid - " + length);
- }
-
- // Construct a 128 bit UUID.
- if (length == UUID_BYTES_128_BIT) {
- final ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN);
- final long msb = buf.getLong(8);
- final long lsb = buf.getLong(0);
- return new ParcelUuid(new UUID(msb, lsb));
- }
-
- // For 16 bit and 32 bit UUID we need to convert them to 128 bit value.
- // 128_bit_value = uuid * 2^96 + BASE_UUID
- long shortUuid;
- if (length == UUID_BYTES_16_BIT) {
- shortUuid = uuidBytes[0] & 0xFF;
- shortUuid += (uuidBytes[1] & 0xFF) << 8;
- } else {
- shortUuid = uuidBytes[0] & 0xFF ;
- shortUuid += (uuidBytes[1] & 0xFF) << 8;
- shortUuid += (uuidBytes[2] & 0xFF) << 16;
- shortUuid += (uuidBytes[3] & 0xFF) << 24;
- }
- final long msb = BASE_UUID.getUuid().getMostSignificantBits() + (shortUuid << 32);
- final long lsb = BASE_UUID.getUuid().getLeastSignificantBits();
- return new ParcelUuid(new UUID(msb, lsb));
- }
+/* package */ public final class BluetoothUuid {
+ private static final SequenceUuid BASE_UUID =
+ SequenceUuid.uuidFromString("00000000-0000-1000-8000-00805F9B34FB");
+ /**
+ * Length of bytes for 16 bit UUID
+ */
+ static final int UUID_BYTES_16_BIT = 2;
+ /**
+ * Length of bytes for 32 bit UUID
+ */
+ static final int UUID_BYTES_32_BIT = 4;
+ /**
+ * Length of bytes for 128 bit UUID
+ */
+ static final int UUID_BYTES_128_BIT = 16;
+ /**
+ * Parse UUID from bytes. The {@code uuidBytes} can represent a 16-bit, 32-bit or 128-bit UUID,
+ * but the returned UUID is always in 128-bit format.
+ * Note UUID is little endian in Bluetooth.
+ * @param uuidBytes Byte representation of uuid.
+ * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed.
+ */
+
+ static SequenceUuid parseUuidFrom(final byte[] uuidBytes) {
+ if (uuidBytes == null) {
+ throw new IllegalArgumentException("uuidBytes cannot be null");
+ }
+ final int length = uuidBytes.length;
+ if (length != UUID_BYTES_16_BIT && length != UUID_BYTES_32_BIT
+ && length != UUID_BYTES_128_BIT) {
+ throw new IllegalArgumentException("uuidBytes length invalid - " + length);
+ }
+ if (length == UUID_BYTES_128_BIT) {
+ final ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN);
+ final long msb = buf.getLong(8);
+ final long lsb = buf.getLong(0);
+ return new SequenceUuid(new UUID(msb, lsb));
+ }
+ long shortUuid;
+ if (length == UUID_BYTES_16_BIT) {
+ shortUuid = uuidBytes[0] & 0xFF;
+ shortUuid += (uuidBytes[1] & 0xFF) << 8;
+ } else {
+ shortUuid = uuidBytes[0] & 0xFF;
+ shortUuid += (uuidBytes[1] & 0xFF) << 8;
+ shortUuid += (uuidBytes[2] & 0xFF) << 16;
+ shortUuid += (uuidBytes[3] & 0xFF) << 24;
+ }
+ final long msb = BASE_UUID.getUuid().getMostSignificantBits() + (shortUuid << 32);
+ final long lsb = BASE_UUID.getUuid().getLeastSignificantBits();
+ return new SequenceUuid(new UUID(msb, lsb));
+ }
}
diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/Objects.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/Objects.java
index d54c0c7172089f89cc64504d2c81e718e181d435..80828574fb131249f24fc5a2f53b751a56c66df4 100644
--- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/Objects.java
+++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/Objects.java
@@ -1,82 +1,41 @@
-/*
- * Copyright (c) 2018, Nordic Semiconductor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
package no.nordicsemi.android.support.v18.scanner;
+import org.jetbrains.annotations.Nullable;
+
import java.util.Arrays;
/* package */ class Objects {
-
- /**
- * Returns true if both arguments are null,
- * the result of {@link Arrays#equals} if both arguments are primitive arrays,
- * the result of {@link Arrays#deepEquals} if both arguments are arrays of reference types,
- * and the result of {@link #equals} otherwise.
- */
- static boolean deepEquals(final Object a, final Object b) {
- if (a == null || b == null) {
- return a == b;
- } else if (a instanceof Object[] && b instanceof Object[]) {
- return Arrays.deepEquals((Object[]) a, (Object[]) b);
- } else if (a instanceof boolean[] && b instanceof boolean[]) {
- return Arrays.equals((boolean[]) a, (boolean[]) b);
- } else if (a instanceof byte[] && b instanceof byte[]) {
- return Arrays.equals((byte[]) a, (byte[]) b);
- } else if (a instanceof char[] && b instanceof char[]) {
- return Arrays.equals((char[]) a, (char[]) b);
- } else if (a instanceof double[] && b instanceof double[]) {
- return Arrays.equals((double[]) a, (double[]) b);
- } else if (a instanceof float[] && b instanceof float[]) {
- return Arrays.equals((float[]) a, (float[]) b);
- } else if (a instanceof int[] && b instanceof int[]) {
- return Arrays.equals((int[]) a, (int[]) b);
- } else if (a instanceof long[] && b instanceof long[]) {
- return Arrays.equals((long[]) a, (long[]) b);
- } else if (a instanceof short[] && b instanceof short[]) {
- return Arrays.equals((short[]) a, (short[]) b);
- }
- return a.equals(b);
- }
-
- /**
- * Null-safe equivalent of {@code a.equals(b)}.
- */
- static boolean equals(final Object a, final Object b) {
- return (a == null) ? (b == null) : a.equals(b);
- }
-
- /**
- * Convenience wrapper for {@link Arrays#hashCode}, adding varargs.
- * This can be used to compute a hash code for an object's fields as follows:
- * {@code Objects.hash(a, b, c)}.
- */
- static int hash(final Object... values) {
- return Arrays.hashCode(values);
- }
-
- /**
- * Returns "null" for null or {@code o.toString()}.
- */
- static String toString(final Object o) {
- return (o == null) ? "null" : o.toString();
- }
+ static boolean deepEquals(final Object a, final Object b) {
+ if (a == null || b == null) {
+ return a == b;
+ } else if (a instanceof Object[] && b instanceof Object[]) {
+ return Arrays.deepEquals((Object[]) a, (Object[]) b);
+ } else if (a instanceof boolean[] && b instanceof boolean[]) {
+ return Arrays.equals((boolean[]) a, (boolean[]) b);
+ } else if (a instanceof byte[] && b instanceof byte[]) {
+ return Arrays.equals((byte[]) a, (byte[]) b);
+ } else if (a instanceof char[] && b instanceof char[]) {
+ return Arrays.equals((char[]) a, (char[]) b);
+ } else if (a instanceof double[] && b instanceof double[]) {
+ return Arrays.equals((double[]) a, (double[]) b);
+ } else if (a instanceof float[] && b instanceof float[]) {
+ return Arrays.equals((float[]) a, (float[]) b);
+ } else if (a instanceof int[] && b instanceof int[]) {
+ return Arrays.equals((int[]) a, (int[]) b);
+ } else if (a instanceof long[] && b instanceof long[]) {
+ return Arrays.equals((long[]) a, (long[]) b);
+ } else if (a instanceof short[] && b instanceof short[]) {
+ return Arrays.equals((short[]) a, (short[]) b);
+ }
+ return a.equals(b);
+ }
+ static boolean equals(final Object a, final Object b) {
+ return (a == null) ? (b == null) : a.equals(b);
+ }
+ static int hash(final Object... values) {
+ return Arrays.hashCode(values);
+ }
+ static String toString(final Object o) {
+ return (o == null) ? "null" : o.toString();
+ }
}
diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/PendingIntentExecutor.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/PendingIntentExecutor.java
index 06e93088bf5e50c74c0d8a6595b6fc7be3aa2954..44d81f08600fbabe98ddbf32acdcba0a1eaede5a 100644
--- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/PendingIntentExecutor.java
+++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/PendingIntentExecutor.java
@@ -1,129 +1,76 @@
package no.nordicsemi.android.support.v18.scanner;
-
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Parcelable;
-import android.os.SystemClock;
-
+import ohos.aafwk.content.Intent;
+import ohos.aafwk.content.IntentParams;
+import ohos.agp.components.Clock;
+import ohos.app.Context;
+import ohos.event.intentagent.IntentAgent;
+import org.jetbrains.annotations.Nullable;
+
+import java.security.Provider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * A ScanCallback that will send a {@link PendingIntent} when callback's methods are called.
- */
/* package */ class PendingIntentExecutor extends ScanCallback {
+ private final IntentAgent callbackIntent;
- @NonNull private final PendingIntent callbackIntent;
-
- /** A temporary context given to the {@link android.content.BroadcastReceiver}. */
- @Nullable private Context context;
- /** The service using this executor. */
- @Nullable private Context service;
+ @Nullable
+ private Context context;
+ @Nullable
+ private Context service;
private long lastBatchTimestamp;
private long reportDelay;
-
- /**
- * Creates the {@link PendingIntent} executor that will be used from a
- * {@link android.content.BroadcastReceiver}. The {@link Context} may change in every
- * {@link android.content.BroadcastReceiver#onReceive(Context, Intent)} call, so it is not
- * kept here. Instead, a temporary context must be set with {@link #setTemporaryContext(Context)}
- * each time before the received results are handled and released after that to
- * prevent from keeping a string reference to the context by a static object.
- *
- * @param callbackIntent User's {@link PendingIntent} used in
- * {@link BluetoothLeScannerCompat#startScan(List, ScanSettings, Context, PendingIntent)}.
- * @param settings Scan settings specified by the user.
- */
- PendingIntentExecutor(@NonNull final PendingIntent callbackIntent,
- @NonNull final ScanSettings settings) {
- this.callbackIntent = callbackIntent;
- this.reportDelay = settings.getReportDelayMillis();
- }
-
- /**
- * Creates the {@link PendingIntent} executor that will be used from a {@link Service}.
- * The service instance will be used as {@link Context} to send intents.
- *
- * @param callbackIntent User's {@link PendingIntent} used in
- * {@link BluetoothLeScannerCompat#startScan(List, ScanSettings, Context, PendingIntent)}.
- * @param settings Scan settings specified by the user.
- * @param service The service that will scan for Bluetooth LE devices in background.
- */
- PendingIntentExecutor(@NonNull final PendingIntent callbackIntent,
- @NonNull final ScanSettings settings,
- @NonNull final Service service) {
- this.callbackIntent = callbackIntent;
- this.reportDelay = settings.getReportDelayMillis();
- this.service = service;
- }
-
- /* package */ void setTemporaryContext(@Nullable final Context context) {
- this.context = context;
- }
-
- @Override
- public void onScanResult(final int callbackType, @NonNull final ScanResult result) {
- final Context context = this.context != null ? this.context : this.service;
- if (context == null)
- return;
-
- try {
- final Intent extrasIntent = new Intent();
- extrasIntent.putExtra(BluetoothLeScannerCompat.EXTRA_CALLBACK_TYPE, callbackType);
- extrasIntent.putParcelableArrayListExtra(BluetoothLeScannerCompat.EXTRA_LIST_SCAN_RESULT,
- new ArrayList<>(Collections.singletonList(result)));
- callbackIntent.send(context, 0, extrasIntent);
- } catch (final PendingIntent.CanceledException e) {
- // Ignore
- }
- }
-
- @Override
- public void onBatchScanResults(@NonNull final List results) {
- final Context context = this.context != null ? this.context : this.service;
- if (context == null)
- return;
-
- // On several phones the broadcast is sent twice for every batch.
- // Skip the second call if came to early.
- final long now = SystemClock.elapsedRealtime();
- if (lastBatchTimestamp > now - reportDelay + 5) {
+ PendingIntentExecutor(final IntentAgent callbackIntent,
+ final ScanSettings settings, ScannerService scannerService) {
+ this.callbackIntent = callbackIntent;
+ this.reportDelay = settings.getReportDelayMillis();
+ }
+ PendingIntentExecutor(final IntentAgent callbackIntent,
+ final ScanSettings settings,
+ final Provider.Service service) {
+ this.callbackIntent = callbackIntent;
+ this.reportDelay = settings.getReportDelayMillis();
+ this.context = context;
+ }
+ /* package */ void setTemporaryContext(@Nullable final Context context) {
+ this.context = context;
+ }
+ public void onScanResult(final int callbackType, final ScanResult result) {
+ final Context context = this.context != null ? this.context : this.service;
+ if (context == null)
+ return;
+ final Intent extrasIntent = new Intent();
+ extrasIntent.setParam(BluetoothLeScannerCompat.EXTRA_CALLBACK_TYPE, callbackType);
+ extrasIntent.setSequenceableArrayListParam(BluetoothLeScannerCompat.EXTRA_LIST_SCAN_RESULT, new ArrayList<>(Collections.singletonList(result)));
+ callbackIntent.equals(extrasIntent);
+ }
+
+ @Override
+ public void onBatchScanResults(final List results) {
+ final Context context = this.context != null ? this.context : this.service;
+ if (context == null)
+ return;
+ Clock clock = new Clock(context);
+ final long now = clock.getTime();
+ if (lastBatchTimestamp > now - reportDelay + 5) {
return;
}
lastBatchTimestamp = now;
-
- try {
- final Intent extrasIntent = new Intent();
- extrasIntent.putExtra(BluetoothLeScannerCompat.EXTRA_CALLBACK_TYPE,
- ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
- extrasIntent.putParcelableArrayListExtra(BluetoothLeScannerCompat.EXTRA_LIST_SCAN_RESULT,
- new ArrayList(results));
- extrasIntent.setExtrasClassLoader(ScanResult.class.getClassLoader());
- callbackIntent.send(context, 0, extrasIntent);
- } catch (final PendingIntent.CanceledException e) {
- // Ignore
- }
- }
-
- @Override
- public void onScanFailed(final int errorCode) {
- final Context context = this.context != null ? this.context : this.service;
- if (context == null)
- return;
-
- try {
- final Intent extrasIntent = new Intent();
- extrasIntent.putExtra(BluetoothLeScannerCompat.EXTRA_ERROR_CODE, errorCode);
- callbackIntent.send(context, 0, extrasIntent);
- } catch (final PendingIntent.CanceledException e) {
- // Ignore
- }
- }
+ final Intent extrasIntent = new Intent();
+ final IntentParams Intent = new IntentParams();
+ extrasIntent.setParam(BluetoothLeScannerCompat.EXTRA_CALLBACK_TYPE, ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
+ extrasIntent.setSequenceableArrayListParam(BluetoothLeScannerCompat.EXTRA_LIST_SCAN_RESULT, new ArrayList<>(results));
+ Intent.setClassLoader(ScanResult.class.getClassLoader());
+ callbackIntent.equals(extrasIntent);
+ }
+
+ @Override
+ public void onScanFailed(final int errorCode) {
+ final Context context = this.context != null ? this.context : this.service;
+ if (context == null)
+ return;
+ final Intent extrasIntent = new Intent();
+ extrasIntent.setParam(BluetoothLeScannerCompat.EXTRA_ERROR_CODE, errorCode);
+ callbackIntent.equals(extrasIntent);
+ }
}
\ No newline at end of file
diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/PendingIntentReceiver.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/PendingIntentReceiver.java
index e9ff998b6f96c98b81c489b39f701db86342bbac..b966816df77886b0893bb88f34601edf2c4bccd5 100644
--- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/PendingIntentReceiver.java
+++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/PendingIntentReceiver.java
@@ -1,134 +1,70 @@
package no.nordicsemi.android.support.v18.scanner;
-import android.app.PendingIntent;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.le.BluetoothLeScanner;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
+import ohos.aafwk.content.Intent;
+import ohos.app.Context;
+import ohos.app.GeneralReceiver;
+import ohos.bluetooth.ble.BleScanFilter;
+import ohos.event.intentagent.IntentAgent;
import java.util.ArrayList;
import java.util.List;
-import androidx.annotation.RequiresApi;
-
-/**
- * This receiver, registered in AndroidManifest, will translate received
- * {@link android.bluetooth.le.ScanResult}s into compat {@link ScanResult}s and will send
- * a {@link PendingIntent} registered by the user with those converted data. It will also apply
- * any filters, perform batching or emulate callback types
- * {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} and
- * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST} on devices that do not support it.
- */
-public class PendingIntentReceiver extends BroadcastReceiver {
-
- /* package */ static final String ACTION = "no.nordicsemi.android.support.v18.ACTION_FOUND";
- /* package */ static final String EXTRA_PENDING_INTENT = "no.nordicsemi.android.support.v18.EXTRA_PENDING_INTENT";
- /* package */ static final String EXTRA_FILTERS = "no.nordicsemi.android.support.v18.EXTRA_FILTERS";
- /* package */ static final String EXTRA_SETTINGS = "no.nordicsemi.android.support.v18.EXTRA_SETTINGS";
- /* package */ static final String EXTRA_USE_HARDWARE_BATCHING = "no.nordicsemi.android.support.v18.EXTRA_USE_HARDWARE_BATCHING";
- /* package */ static final String EXTRA_USE_HARDWARE_FILTERING = "no.nordicsemi.android.support.v18.EXTRA_USE_HARDWARE_FILTERING";
- /* package */ static final String EXTRA_USE_HARDWARE_CALLBACK_TYPES = "no.nordicsemi.android.support.v18.EXTRA_USE_HARDWARE_CALLBACK_TYPES";
- /* package */ static final String EXTRA_MATCH_LOST_TIMEOUT = "no.nordicsemi.android.support.v18.EXTRA_MATCH_LOST_TIMEOUT";
- /* package */ static final String EXTRA_MATCH_LOST_INTERVAL = "no.nordicsemi.android.support.v18.EXTRA_MATCH_LOST_INTERVAL";
- /* package */ static final String EXTRA_MATCH_MODE = "no.nordicsemi.android.support.v18.EXTRA_MATCH_MODE";
- /* package */ static final String EXTRA_NUM_OF_MATCHES = "no.nordicsemi.android.support.v18.EXTRA_NUM_OF_MATCHES";
-
- @RequiresApi(api = Build.VERSION_CODES.O)
- @Override
- public void onReceive(final Context context, final Intent intent) {
- // Ensure we are ok.
- if (context == null || intent == null)
- return;
-
- // Find the target pending intent.
- final PendingIntent callbackIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
- if (callbackIntent == null)
- return;
-
- // Filters and settings have been set as native objects, otherwise they could not be
- // serialized by the system scanner.
- final ArrayList nativeScanFilters =
- intent.getParcelableArrayListExtra(EXTRA_FILTERS);
- final android.bluetooth.le.ScanSettings nativeScanSettings = intent.getParcelableExtra(EXTRA_SETTINGS);
- if (nativeScanFilters == null || nativeScanSettings == null)
- return;
-
- // Some ScanSettings parameters are only on compat version and need to be sent separately.
- final boolean useHardwareBatchingIfSupported = intent.getBooleanExtra(EXTRA_USE_HARDWARE_BATCHING, true);
- final boolean useHardwareFilteringIfSupported = intent.getBooleanExtra(EXTRA_USE_HARDWARE_FILTERING, true);
- final boolean useHardwareCallbackTypesIfSupported = intent.getBooleanExtra(EXTRA_USE_HARDWARE_CALLBACK_TYPES, true);
- final long matchLostDeviceTimeout = intent.getLongExtra(EXTRA_MATCH_LOST_TIMEOUT, ScanSettings.MATCH_LOST_DEVICE_TIMEOUT_DEFAULT);
- final long matchLostTaskInterval = intent.getLongExtra(EXTRA_MATCH_LOST_INTERVAL, ScanSettings.MATCH_LOST_TASK_INTERVAL_DEFAULT);
- final int matchMode = intent.getIntExtra(EXTRA_MATCH_MODE, ScanSettings.MATCH_MODE_AGGRESSIVE);
- final int numOfMatches = intent.getIntExtra(EXTRA_NUM_OF_MATCHES, ScanSettings.MATCH_NUM_MAX_ADVERTISEMENT);
-
- // Convert native objects to compat versions.
- final BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
- final BluetoothLeScannerImplOreo scannerImpl = (BluetoothLeScannerImplOreo) scanner;
- final ArrayList filters = scannerImpl.fromNativeScanFilters(nativeScanFilters);
- final ScanSettings settings = scannerImpl.fromNativeScanSettings(nativeScanSettings,
- useHardwareBatchingIfSupported,
- useHardwareFilteringIfSupported,
- useHardwareCallbackTypesIfSupported,
- matchLostDeviceTimeout, matchLostTaskInterval,
- matchMode, numOfMatches);
-
- // Check device capabilities and create a wrapper that will send a PendingIntent.
- final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- final boolean offloadedBatchingSupported = adapter.isOffloadedScanBatchingSupported();
- final boolean offloadedFilteringSupported = adapter.isOffloadedFilteringSupported();
-
- // Obtain or create a PendingIntentExecutorWrapper. A static instance (obtained from a
- // static BluetoothLeScannerCompat singleton) is necessary as it allows to keeps
- // track of found devices and emulate batching and callback types if those are not
- // supported or a compat version was forced.
-
- BluetoothLeScannerImplOreo.PendingIntentExecutorWrapper wrapper;
- //noinspection SynchronizationOnLocalVariableOrMethodParameter
- synchronized (scanner) {
- try {
- wrapper = scannerImpl.getWrapper(callbackIntent);
- } catch (final IllegalStateException e) {
- // Scanning has been stopped.
- return;
- }
- if (wrapper == null) {
- // Wrapper has not been created, or was created, but the app was then killed
- // and must be created again. Some information will be lost (batched devices).
- wrapper = new BluetoothLeScannerImplOreo.PendingIntentExecutorWrapper(offloadedBatchingSupported,
- offloadedFilteringSupported, filters, settings, callbackIntent);
- scannerImpl.addWrapper(callbackIntent, wrapper);
- }
- }
-
- // The context may change each time. Set the one time temporary context that will be used
- // to send PendingIntent. It will be released after the results were handled.
- wrapper.executor.setTemporaryContext(context);
-
- // Check what results were received and send them to PendingIntent.
- final List nativeScanResults =
- intent.getParcelableArrayListExtra(BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT);
- if (nativeScanResults != null) {
- final ArrayList results = scannerImpl.fromNativeScanResults(nativeScanResults);
-
- if (settings.getReportDelayMillis() > 0) {
- wrapper.handleScanResults(results);
- } else if (!results.isEmpty()) {
- final int callbackType = intent.getIntExtra(BluetoothLeScanner.EXTRA_CALLBACK_TYPE,
- ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
- wrapper.handleScanResult(callbackType, results.get(0));
- }
- } else {
- final int errorCode = intent.getIntExtra(BluetoothLeScanner.EXTRA_ERROR_CODE, 0);
- if (errorCode != 0) {
- wrapper.handleScanError(errorCode);
- }
- }
-
- // Release the temporary context reference, so that static executor does not hold a
- // reference to a context.
- wrapper.executor.setTemporaryContext(null);
- }
+public class PendingIntentReceiver extends GeneralReceiver {
+ /* package */ static final String
+ ACTION = "no.nordicsemi.android.support.v18.ACTION_FOUND";
+ /* package */ static final String
+ EXTRA_PENDING_INTENT = "no.nordicsemi.android.support.v18.EXTRA_PENDING_INTENT";
+ /* package */ static final String
+ EXTRA_FILTERS = "no.nordicsemi.android.support.v18.EXTRA_FILTERS";
+ /* package */ static final String
+ EXTRA_SETTINGS = "no.nordicsemi.android.support.v18.EXTRA_SETTINGS";
+ /* package */ static final String
+ EXTRA_USE_HARDWARE_BATCHING = "no.nordicsemi.android.support.v18.EXTRA_USE_HARDWARE_BATCHING";
+ /* package */ static final String
+ EXTRA_USE_HARDWARE_FILTERING = "no.nordicsemi.android.support.v18.EXTRA_USE_HARDWARE_FILTERING";
+ /* package */ static final String
+ EXTRA_USE_HARDWARE_CALLBACK_TYPES = "no.nordicsemi.android.support.v18.EXTRA_USE_HARDWARE_CALLBACK_TYPES";
+ /* package */ static final String
+ EXTRA_MATCH_LOST_TIMEOUT = "no.nordicsemi.android.support.v18.EXTRA_MATCH_LOST_TIMEOUT";
+ /* package */ static final String
+ EXTRA_MATCH_LOST_INTERVAL = "no.nordicsemi.android.support.v18.EXTRA_MATCH_LOST_INTERVAL";
+ /* package */ static final String
+ EXTRA_MATCH_MODE = "no.nordicsemi.android.support.v18.EXTRA_MATCH_MODE";
+ /* package */ static final String
+ EXTRA_NUM_OF_MATCHES = "no.nordicsemi.android.support.v18.EXTRA_NUM_OF_MATCHES";
+
+ public void onReceive(final Context context, final Intent intent) {
+ if (context == null || intent == null)
+ return;
+ final IntentAgent callbackIntent = intent.getSequenceableParam(EXTRA_PENDING_INTENT);
+ if (callbackIntent == null)
+ return;
+ final ArrayList nativeScanFilters =
+ intent.getSequenceableArrayListParam(EXTRA_FILTERS);
+ final ScanSettings nativeScanSettings = intent.getSequenceableParam(EXTRA_SETTINGS);
+ if (nativeScanFilters == null || nativeScanSettings == null)
+ return;
+ final boolean useHardwareBatchingIfSupported
+ = intent.getBooleanParam(EXTRA_USE_HARDWARE_BATCHING, true);
+ final boolean useHardwareFilteringIfSupported
+ = intent.getBooleanParam(EXTRA_USE_HARDWARE_FILTERING, true);
+ final boolean useHardwareCallbackTypesIfSupported
+ = intent.getBooleanParam(EXTRA_USE_HARDWARE_CALLBACK_TYPES, true);
+ final long matchLostDeviceTimeout
+ = intent.getLongParam(EXTRA_MATCH_LOST_TIMEOUT, ScanSettings.MATCH_LOST_DEVICE_TIMEOUT_DEFAULT);
+ final long matchLostTaskInterval
+ = intent.getLongParam(EXTRA_MATCH_LOST_INTERVAL, ScanSettings.MATCH_LOST_TASK_INTERVAL_DEFAULT);
+ final int matchMode
+ = intent.getIntParam(EXTRA_MATCH_MODE, ScanSettings.MATCH_MODE_AGGRESSIVE);
+ final int numOfMatches
+ = intent.getIntParam(EXTRA_NUM_OF_MATCHES, ScanSettings.MATCH_NUM_MAX_ADVERTISEMENT);
+ final BluetoothLeScannerCompat scanner
+ = BluetoothLeScannerCompat.getScanner();
+ synchronized (scanner) {
+ }
+
+ final List nativeScanResults
+ = intent.getParcelableArrayListParam(BluetoothLeScannerCompat.EXTRA_LIST_SCAN_RESULT);
+ final ArrayList results = scanner.flushPendingScanResults(nativeScanResults);
+ }
}
diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanCallback.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanCallback.java
index 799e430f133be3c4b90a3f0b8ae5fb1e6dbb448e..34cb1c0dfeaa3559377a9e41a40007ce8f5e6163 100644
--- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanCallback.java
+++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanCallback.java
@@ -24,8 +24,6 @@ package no.nordicsemi.android.support.v18.scanner;
import java.util.List;
-import androidx.annotation.NonNull;
-
/**
* Bluetooth LE scan callbacks. Scan results are reported using these callbacks.
*
@@ -33,63 +31,63 @@ import androidx.annotation.NonNull;
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public abstract class ScanCallback {
- /**
- * Fails to start scan as BLE scan with the same settings is already started by the app.
- */
- public static final int SCAN_FAILED_ALREADY_STARTED = 1;
+ /**
+ * Fails to start scan as BLE scan with the same settings is already started by the app.
+ */
+ public static final int SCAN_FAILED_ALREADY_STARTED = 1;
- /**
- * Fails to start scan as app cannot be registered.
- */
- public static final int SCAN_FAILED_APPLICATION_REGISTRATION_FAILED = 2;
+ /**
+ * Fails to start scan as app cannot be registered.
+ */
+ public static final int SCAN_FAILED_APPLICATION_REGISTRATION_FAILED = 2;
- /**
- * Fails to start scan due an internal error
- */
- public static final int SCAN_FAILED_INTERNAL_ERROR = 3;
+ /**
+ * Fails to start scan due an internal error
+ */
+ public static final int SCAN_FAILED_INTERNAL_ERROR = 3;
- /**
- * Fails to start power optimized scan as this feature is not supported.
- */
- public static final int SCAN_FAILED_FEATURE_UNSUPPORTED = 4;
+ /**
+ * Fails to start power optimized scan as this feature is not supported.
+ */
+ public static final int SCAN_FAILED_FEATURE_UNSUPPORTED = 4;
- /**
- * Fails to start scan as it is out of hardware resources.
- */
- public static final int SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5;
+ /**
+ * Fails to start scan as it is out of hardware resources.
+ */
+ public static final int SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5;
- /**
- * Fails to start scan as application tries to scan too frequently.
- */
- public static final int SCAN_FAILED_SCANNING_TOO_FREQUENTLY = 6;
+ /**
+ * Fails to start scan as application tries to scan too frequently.
+ */
+ public static final int SCAN_FAILED_SCANNING_TOO_FREQUENTLY = 6;
- static final int NO_ERROR = 0;
+ static final int NO_ERROR = 0;
- /**
- * Callback when a BLE advertisement has been found.
- *
- * @param callbackType Determines how this callback was triggered. Could be one of
- * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES},
- * {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} or
- * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST}
- * @param result A Bluetooth LE scan result.
- */
- public void onScanResult(final int callbackType, @NonNull final ScanResult result) {
- }
+ /**
+ * Callback when a BLE advertisement has been found.
+ *
+ * @param callbackType Determines how this callback was triggered. Could be one of
+ * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES},
+ * {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} or
+ * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST}
+ * @param result A Bluetooth LE scan result.
+ */
+ public void onScanResult(final int callbackType,final ScanResult result) {
+ }
- /**
- * Callback when batch results are delivered.
- *
- * @param results List of scan results that are previously scanned.
- */
- public void onBatchScanResults(@NonNull final List results) {
- }
+ /**
+ * Callback when batch results are delivered.
+ *
+ * @param results List of scan results that are previously scanned.
+ */
+ public void onBatchScanResults(final List results) {
+ }
- /**
- * Callback when scan could not be started.
- *
- * @param errorCode Error code (one of SCAN_FAILED_*) for scan failure.
- */
- public void onScanFailed(final int errorCode) {
- }
+ /**
+ * Callback when scan could not be started.
+ *
+ * @param errorCode Error code (one of SCAN_FAILED_*) for scan failure.
+ */
+ public void onScanFailed(final int errorCode) {
+ }
}
diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanFilter.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanFilter.java
index f47b746b6709916078b6260c73b4c3d7a5b45f10..36b49591c8f8312d10fcb936ac3dc62d4533b2e8 100644
--- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanFilter.java
+++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanFilter.java
@@ -1,634 +1,463 @@
-/*
- * Copyright (c) 2018, Nordic Semiconductor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
package no.nordicsemi.android.support.v18.scanner;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
+import ohos.bluetooth.ble.BlePeripheralDevice;
+import ohos.interwork.utils.ParcelableEx;
+import ohos.utils.Parcel;
+import ohos.utils.SequenceUuid;
+import ohos.utils.Sequenceable;
+import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * Criteria for filtering result from Bluetooth LE scans. A {@link ScanFilter} allows clients to
- * restrict scan results to only those that are of interest to them.
- *
Current filtering on the following fields are supported:
- *
- *
Service UUIDs which identify the bluetooth gatt services running on the device.
- *
Name of remote Bluetooth LE device.
- *
Mac address of the remote device.
- *
Service data which is the data associated with a service.
- *
Manufacturer specific data which is the data associated with a particular manufacturer.
- *
- *
- * @see ScanResult
- * @see BluetoothLeScannerCompat
- */
-@SuppressWarnings("WeakerAccess")
-public final class ScanFilter implements Parcelable {
-
- @Nullable
- private final String deviceName;
- @Nullable
- private final String deviceAddress;
-
- @Nullable
- private final ParcelUuid serviceUuid;
- @Nullable
- private final ParcelUuid serviceUuidMask;
-
- @Nullable
- private final ParcelUuid serviceDataUuid;
- @Nullable
- private final byte[] serviceData;
- @Nullable
- private final byte[] serviceDataMask;
-
- private final int manufacturerId;
- @Nullable
- private final byte[] manufacturerData;
- @Nullable
- private final byte[] manufacturerDataMask;
-
- private static final ScanFilter EMPTY = new ScanFilter.Builder().build() ;
-
- private ScanFilter(@Nullable final String name, @Nullable final String deviceAddress,
- @Nullable final ParcelUuid uuid, @Nullable final ParcelUuid uuidMask,
- @Nullable final ParcelUuid serviceDataUuid, @Nullable final byte[] serviceData,
- @Nullable final byte[] serviceDataMask, final int manufacturerId,
- @Nullable final byte[] manufacturerData,
- @Nullable final byte[] manufacturerDataMask) {
- this.deviceName = name;
- this.serviceUuid = uuid;
- this.serviceUuidMask = uuidMask;
- this.deviceAddress = deviceAddress;
- this.serviceDataUuid = serviceDataUuid;
- this.serviceData = serviceData;
- this.serviceDataMask = serviceDataMask;
- this.manufacturerId = manufacturerId;
- this.manufacturerData = manufacturerData;
- this.manufacturerDataMask = manufacturerDataMask;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(final Parcel dest, final int flags) {
- dest.writeInt(deviceName == null ? 0 : 1);
- if (deviceName != null) {
- dest.writeString(deviceName);
- }
- dest.writeInt(deviceAddress == null ? 0 : 1);
- if (deviceAddress != null) {
- dest.writeString(deviceAddress);
- }
- dest.writeInt(serviceUuid == null ? 0 : 1);
- if (serviceUuid != null) {
- dest.writeParcelable(serviceUuid, flags);
- dest.writeInt(serviceUuidMask == null ? 0 : 1);
- if (serviceUuidMask != null) {
- dest.writeParcelable(serviceUuidMask, flags);
- }
- }
- dest.writeInt(serviceDataUuid == null ? 0 : 1);
- if (serviceDataUuid != null) {
- dest.writeParcelable(serviceDataUuid, flags);
- dest.writeInt(serviceData == null ? 0 : 1);
- if (serviceData != null) {
- dest.writeInt(serviceData.length);
- dest.writeByteArray(serviceData);
-
- dest.writeInt(serviceDataMask == null ? 0 : 1);
- if (serviceDataMask != null) {
- dest.writeInt(serviceDataMask.length);
- dest.writeByteArray(serviceDataMask);
- }
- }
- }
- dest.writeInt(manufacturerId);
- dest.writeInt(manufacturerData == null ? 0 : 1);
- if (manufacturerData != null) {
- dest.writeInt(manufacturerData.length);
- dest.writeByteArray(manufacturerData);
-
- dest.writeInt(manufacturerDataMask == null ? 0 : 1);
- if (manufacturerDataMask != null) {
- dest.writeInt(manufacturerDataMask.length);
- dest.writeByteArray(manufacturerDataMask);
- }
- }
- }
-
- /**
- * A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} from parcel.
- */
- public static final Creator CREATOR = new Creator() {
-
- @Override
- public ScanFilter[] newArray(final int size) {
- return new ScanFilter[size];
- }
-
- @Override
- public ScanFilter createFromParcel(final Parcel in) {
- final Builder builder = new Builder();
- if (in.readInt() == 1) {
- builder.setDeviceName(in.readString());
- }
- if (in.readInt() == 1) {
- builder.setDeviceAddress(in.readString());
- }
- if (in.readInt() == 1) {
- ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader());
- builder.setServiceUuid(uuid);
- if (in.readInt() == 1) {
- ParcelUuid uuidMask = in.readParcelable(
- ParcelUuid.class.getClassLoader());
- builder.setServiceUuid(uuid, uuidMask);
- }
- }
- if (in.readInt() == 1) {
- ParcelUuid serviceDataUuid = in.readParcelable(ParcelUuid.class.getClassLoader());
- if (in.readInt() == 1) {
- final int serviceDataLength = in.readInt();
- final byte[] serviceData = new byte[serviceDataLength];
- in.readByteArray(serviceData);
- if (in.readInt() == 0) {
- //noinspection ConstantConditions
- builder.setServiceData(serviceDataUuid, serviceData);
- } else {
- final int serviceDataMaskLength = in.readInt();
- final byte[] serviceDataMask = new byte[serviceDataMaskLength];
- in.readByteArray(serviceDataMask);
- //noinspection ConstantConditions
- builder.setServiceData(serviceDataUuid, serviceData, serviceDataMask);
- }
- }
- }
-
- final int manufacturerId = in.readInt();
- if (in.readInt() == 1) {
- final int manufacturerDataLength = in.readInt();
- final byte[] manufacturerData = new byte[manufacturerDataLength];
- in.readByteArray(manufacturerData);
- if (in.readInt() == 0) {
- builder.setManufacturerData(manufacturerId, manufacturerData);
- } else {
- final int manufacturerDataMaskLength = in.readInt();
- final byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength];
- in.readByteArray(manufacturerDataMask);
- builder.setManufacturerData(manufacturerId, manufacturerData,
- manufacturerDataMask);
- }
- }
-
- return builder.build();
- }
- };
-
- /**
- * Returns the filter set the device name field of Bluetooth advertisement data.
- */
- @Nullable
- public String getDeviceName() {
- return deviceName;
- }
-
- /**
- * Returns the filter set on the service uuid.
- */
- @Nullable
- public ParcelUuid getServiceUuid() {
- return serviceUuid;
- }
-
- @Nullable
- public ParcelUuid getServiceUuidMask() {
- return serviceUuidMask;
- }
-
- @Nullable
- public String getDeviceAddress() {
- return deviceAddress;
- }
-
- @Nullable
- public byte[] getServiceData() {
- return serviceData;
- }
-
- @Nullable
- public byte[] getServiceDataMask() {
- return serviceDataMask;
- }
-
- @Nullable
- public ParcelUuid getServiceDataUuid() {
- return serviceDataUuid;
- }
-
- /**
- * Returns the manufacturer id. -1 if the manufacturer filter is not set.
- */
- public int getManufacturerId() {
- return manufacturerId;
- }
-
- @Nullable
- public byte[] getManufacturerData() {
- return manufacturerData;
- }
-
- @Nullable
- public byte[] getManufacturerDataMask() {
- return manufacturerDataMask;
- }
-
- /**
- * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match
- * if it matches all the field filters.
- */
- public boolean matches(@Nullable final ScanResult scanResult) {
- if (scanResult == null) {
- return false;
- }
- final BluetoothDevice device = scanResult.getDevice();
- // Device match.
- if (deviceAddress != null && !deviceAddress.equals(device.getAddress())) {
- return false;
- }
-
- final ScanRecord scanRecord = scanResult.getScanRecord();
-
- // Scan record is null but there exist filters on it.
- if (scanRecord == null
- && (deviceName != null || serviceUuid != null || manufacturerData != null
- || serviceData != null)) {
- return false;
- }
-
- // Local name match.
- if (deviceName != null && !deviceName.equals(scanRecord.getDeviceName())) {
- return false;
- }
-
- // UUID match.
- if (serviceUuid != null && !matchesServiceUuids(serviceUuid, serviceUuidMask,
- scanRecord.getServiceUuids())) {
- return false;
- }
-
- // Service data match
- if (serviceDataUuid != null && scanRecord != null) {
- if (!matchesPartialData(serviceData, serviceDataMask,
- scanRecord.getServiceData(serviceDataUuid))) {
- return false;
- }
- }
-
- // Manufacturer data match.
- if (manufacturerId >= 0 && scanRecord != null) {
- //noinspection RedundantIfStatement
- if (!matchesPartialData(manufacturerData, manufacturerDataMask,
- scanRecord.getManufacturerSpecificData(manufacturerId))) {
- return false;
- }
- }
- // All filters match.
- return true;
- }
-
- /**
- * Check if the uuid pattern is contained in a list of parcel uuids.
- */
- private static boolean matchesServiceUuids(@Nullable final ParcelUuid uuid,
- @Nullable final ParcelUuid parcelUuidMask,
- @Nullable final List uuids) {
- if (uuid == null) {
- return true;
- }
- if (uuids == null) {
- return false;
- }
-
- for (final ParcelUuid parcelUuid : uuids) {
- final UUID uuidMask = parcelUuidMask == null ? null : parcelUuidMask.getUuid();
- if (matchesServiceUuid(uuid.getUuid(), uuidMask, parcelUuid.getUuid())) {
- return true;
- }
- }
- return false;
- }
-
- // Check if the uuid pattern matches the particular service uuid.
- private static boolean matchesServiceUuid(@NonNull final UUID uuid,
- @Nullable final UUID mask,
- @NonNull final UUID data) {
- if (mask == null) {
- return uuid.equals(data);
- }
- if ((uuid.getLeastSignificantBits() & mask.getLeastSignificantBits()) !=
- (data.getLeastSignificantBits() & mask.getLeastSignificantBits())) {
- return false;
- }
- return ((uuid.getMostSignificantBits() & mask.getMostSignificantBits()) ==
- (data.getMostSignificantBits() & mask.getMostSignificantBits()));
- }
-
- // Check whether the data pattern matches the parsed data.
- @SuppressWarnings("BooleanMethodIsAlwaysInverted")
- private boolean matchesPartialData(@Nullable final byte[] data,
- @Nullable final byte[] dataMask,
- @Nullable final byte[] parsedData) {
- if (data == null) {
- // If filter data is null it means it doesn't matter.
- // We return true if any data matching the manufacturerId were found.
- return parsedData != null;
- }
- if (parsedData == null || parsedData.length < data.length) {
- return false;
- }
- if (dataMask == null) {
- for (int i = 0; i < data.length; ++i) {
- if (parsedData[i] != data[i]) {
- return false;
- }
- }
- return true;
- }
- for (int i = 0; i < data.length; ++i) {
- if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public String toString() {
- return "BluetoothLeScanFilter [deviceName=" + deviceName + ", deviceAddress="
- + deviceAddress
- + ", mUuid=" + serviceUuid + ", uuidMask=" + serviceUuidMask
- + ", serviceDataUuid=" + Objects.toString(serviceDataUuid) + ", serviceData="
- + Arrays.toString(serviceData) + ", serviceDataMask="
- + Arrays.toString(serviceDataMask) + ", manufacturerId=" + manufacturerId
- + ", manufacturerData=" + Arrays.toString(manufacturerData)
- + ", manufacturerDataMask=" + Arrays.toString(manufacturerDataMask) + "]";
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(deviceName, deviceAddress, manufacturerId,
- Arrays.hashCode(manufacturerData),
- Arrays.hashCode(manufacturerDataMask),
- serviceDataUuid,
- Arrays.hashCode(serviceData),
- Arrays.hashCode(serviceDataMask),
- serviceUuid, serviceUuidMask);
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
- final ScanFilter other = (ScanFilter) obj;
- return Objects.equals(deviceName, other.deviceName) &&
- Objects.equals(deviceAddress, other.deviceAddress) &&
- manufacturerId == other.manufacturerId &&
- Objects.deepEquals(manufacturerData, other.manufacturerData) &&
- Objects.deepEquals(manufacturerDataMask, other.manufacturerDataMask) &&
- Objects.equals(serviceDataUuid, other.serviceDataUuid) &&
- Objects.deepEquals(serviceData, other.serviceData) &&
- Objects.deepEquals(serviceDataMask, other.serviceDataMask) &&
- Objects.equals(serviceUuid, other.serviceUuid) &&
- Objects.equals(serviceUuidMask, other.serviceUuidMask);
- }
-
- /**
- * Checks if the scan filter is empty.
- */
- @SuppressWarnings("unused")
- /* package */ boolean isAllFieldsEmpty() {
- return EMPTY.equals(this);
- }
-
- /**
- * Builder class for {@link ScanFilter}.
- */
- public static final class Builder {
-
- private String deviceName;
- private String deviceAddress;
-
- private ParcelUuid serviceUuid;
- private ParcelUuid uuidMask;
-
- private ParcelUuid serviceDataUuid;
- private byte[] serviceData;
- private byte[] serviceDataMask;
-
- private int manufacturerId = -1;
- private byte[] manufacturerData;
- private byte[] manufacturerDataMask;
-
- /**
- * Set filter on device name.
- */
- public Builder setDeviceName(@Nullable final String deviceName) {
- this.deviceName = deviceName;
- return this;
- }
-
- /**
- * Set filter on device address.
- *
- * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
- * format of "01:02:03:AB:CD:EF". The device address can be validated using
- * {@link BluetoothAdapter#checkBluetoothAddress}.
- * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
- */
- public Builder setDeviceAddress(@Nullable final String deviceAddress) {
- if (deviceAddress != null && !BluetoothAdapter.checkBluetoothAddress(deviceAddress)) {
- throw new IllegalArgumentException("invalid device address " + deviceAddress);
- }
- this.deviceAddress = deviceAddress;
- return this;
- }
-
- /**
- * Set filter on service uuid.
- */
- public Builder setServiceUuid(@Nullable final ParcelUuid serviceUuid) {
- this.serviceUuid = serviceUuid;
- this.uuidMask = null; // clear uuid mask
- return this;
- }
-
- /**
- * Set filter on partial service uuid. The {@code uuidMask} is the bit mask for the
- * {@code serviceUuid}. Set any bit in the mask to 1 to indicate a match is needed for the
- * bit in {@code serviceUuid}, and 0 to ignore that bit.
- *
- * @throws IllegalArgumentException If {@code serviceUuid} is {@code null} but
- * {@code uuidMask} is not {@code null}.
- */
- public Builder setServiceUuid(@Nullable final ParcelUuid serviceUuid,
- @Nullable final ParcelUuid uuidMask) {
- if (uuidMask != null && serviceUuid == null) {
- throw new IllegalArgumentException("uuid is null while uuidMask is not null!");
- }
- this.serviceUuid = serviceUuid;
- this.uuidMask = uuidMask;
- return this;
- }
-
- /**
- * Set filtering on service data.
- *
- * @throws IllegalArgumentException If {@code serviceDataUuid} is null.
- */
- public Builder setServiceData(@NonNull final ParcelUuid serviceDataUuid,
- @Nullable final byte[] serviceData) {
- //noinspection ConstantConditions
- if (serviceDataUuid == null) {
- throw new IllegalArgumentException("serviceDataUuid is null!");
- }
- this.serviceDataUuid = serviceDataUuid;
- this.serviceData = serviceData;
- this.serviceDataMask = null; // clear service data mask
- return this;
- }
-
- /**
- * Set partial filter on service data. For any bit in the mask, set it to 1 if it needs to
- * match the one in service data, otherwise set it to 0 to ignore that bit.
- *
- * The {@code serviceDataMask} must have the same length of the {@code serviceData}.
- *
- * @throws IllegalArgumentException If {@code serviceDataUuid} is null or
- * {@code serviceDataMask} is {@code null} while {@code serviceData} is not or
- * {@code serviceDataMask} and {@code serviceData} has different length.
- */
- public Builder setServiceData(@NonNull final ParcelUuid serviceDataUuid,
- @Nullable final byte[] serviceData,
- @Nullable final byte[] serviceDataMask) {
- //noinspection ConstantConditions
- if (serviceDataUuid == null) {
- throw new IllegalArgumentException("serviceDataUuid is null");
- }
- if (serviceDataMask != null) {
- if (serviceData == null) {
- throw new IllegalArgumentException(
- "serviceData is null while serviceDataMask is not null");
- }
- // Since the serviceDataMask is a bit mask for serviceData, the lengths of the two
- // byte array need to be the same.
- if (serviceData.length != serviceDataMask.length) {
- throw new IllegalArgumentException(
- "size mismatch for service data and service data mask");
- }
- }
- this.serviceDataUuid = serviceDataUuid;
- this.serviceData = serviceData;
- this.serviceDataMask = serviceDataMask;
- return this;
- }
-
- /**
- * Set filter on on manufacturerData. A negative manufacturerId is considered as invalid id.
- *
- * Note the first two bytes of the {@code manufacturerData} is the manufacturerId.
- *
- * @throws IllegalArgumentException If the {@code manufacturerId} is invalid.
- */
- public Builder setManufacturerData(final int manufacturerId,
- @Nullable final byte[] manufacturerData) {
- if (manufacturerData != null && manufacturerId < 0) {
- throw new IllegalArgumentException("invalid manufacture id");
- }
- this.manufacturerId = manufacturerId;
- this.manufacturerData = manufacturerData;
- this.manufacturerDataMask = null; // clear manufacturer data mask
- return this;
- }
-
- /**
- * Set filter on partial manufacture data. For any bit in the mask, set it the 1 if it needs
- * to match the one in manufacturer data, otherwise set it to 0.
- *
- * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData}.
- *
- * @throws IllegalArgumentException If the {@code manufacturerId} is invalid, or
- * {@code manufacturerData} is null while {@code manufacturerDataMask} is not,
- * or {@code manufacturerData} and {@code manufacturerDataMask} have different
- * length.
- */
- public Builder setManufacturerData(final int manufacturerId,
- @Nullable final byte[] manufacturerData,
- @Nullable final byte[] manufacturerDataMask) {
- if (manufacturerData != null && manufacturerId < 0) {
- throw new IllegalArgumentException("invalid manufacture id");
- }
- if (manufacturerDataMask != null) {
- if (manufacturerData == null) {
- throw new IllegalArgumentException(
- "manufacturerData is null while manufacturerDataMask is not null");
- }
- // Since the manufacturerDataMask is a bit mask for manufacturerData, the lengths
- // of the two byte array need to be the same.
- if (manufacturerData.length != manufacturerDataMask.length) {
- throw new IllegalArgumentException(
- "size mismatch for manufacturerData and manufacturerDataMask");
- }
- }
- this.manufacturerId = manufacturerId;
- this.manufacturerData = manufacturerData;
- this.manufacturerDataMask = manufacturerDataMask;
- return this;
- }
-
- /**
- * Build {@link ScanFilter}.
- *
- * @throws IllegalArgumentException If the filter cannot be built.
- */
- public ScanFilter build() {
- return new ScanFilter(deviceName, deviceAddress, serviceUuid, uuidMask,
- serviceDataUuid, serviceData, serviceDataMask,
- manufacturerId, manufacturerData, manufacturerDataMask);
- }
- }
+public final class ScanFilter implements Sequenceable {
+
+ @Nullable
+ private final String deviceName;
+
+ @Nullable
+ private final String deviceAddress;
+
+ private final Object serviceUuid;
+ private final Object serviceUuidMask;
+ private final Object serviceDataUuid;
+
+ @Nullable
+ private final byte[] serviceData;
+
+ @Nullable
+ private final byte[] serviceDataMask;
+ private final int manufacturerId;
+
+ @Nullable
+ private final byte[] manufacturerData;
+
+ @Nullable
+ private final byte[] manufacturerDataMask;
+ private static final ScanFilter EMPTY = new Builder().build();
+
+ private ScanFilter(@Nullable final String name, @Nullable final String deviceAddress,
+ final Object uuid, final Object uuidMask,
+ final Object serviceDataUuid, @Nullable final byte[] serviceData,
+ @Nullable final byte[] serviceDataMask, final int manufacturerId,
+ @Nullable final byte[] manufacturerData,
+ @Nullable final byte[] manufacturerDataMask) {
+ this.deviceName = name;
+ this.serviceUuid = uuid;
+ this.serviceUuidMask = uuidMask;
+ this.deviceAddress = deviceAddress;
+ this.serviceDataUuid = serviceDataUuid;
+ this.serviceData = serviceData;
+ this.serviceDataMask = serviceDataMask;
+ this.manufacturerId = manufacturerId;
+ this.manufacturerData = manufacturerData;
+ this.manufacturerDataMask = manufacturerDataMask;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(final Parcel dest, final int flags) {
+ dest.writeInt(deviceName == null ? 0 : 1);
+ if (deviceName != null) {
+ dest.writeString(deviceName);
+ }
+ dest.writeInt(deviceAddress == null ? 0 : 1);
+ if (deviceAddress != null) {
+ dest.writeString(deviceAddress);
+ }
+ dest.writeInt(serviceUuid == null ? 0 : 1);
+ if (serviceUuid != null) {
+ dest.writeParcelableEx((ParcelableEx) serviceUuid);
+ dest.writeInt(serviceUuidMask == null ? 0 : 1);
+ if (serviceUuidMask != null) {
+ dest.writeParcelableEx((ParcelableEx) serviceUuidMask);
+ }
+ }
+ dest.writeInt(serviceDataUuid == null ? 0 : 1);
+ if (serviceDataUuid != null) {
+ dest.writeParcelableEx((ParcelableEx) serviceUuid);
+ dest.writeInt(serviceData == null ? 0 : 1);
+ if (serviceData != null) {
+ dest.writeInt(serviceData.length);
+ dest.writeByteArray(serviceData);
+
+ dest.writeInt(serviceDataMask == null ? 0 : 1);
+ if (serviceDataMask != null) {
+ dest.writeInt(serviceDataMask.length);
+ dest.writeByteArray(serviceDataMask);
+ }
+ }
+ }
+ dest.writeInt(manufacturerId);
+ dest.writeInt(manufacturerData == null ? 0 : 1);
+ if (manufacturerData != null) {
+ dest.writeInt(manufacturerData.length);
+ dest.writeByteArray(manufacturerData);
+
+ dest.writeInt(manufacturerDataMask == null ? 0 : 1);
+ if (manufacturerDataMask != null) {
+ dest.writeInt(manufacturerDataMask.length);
+ dest.writeByteArray(manufacturerDataMask);
+ }
+ }
+ }
+
+
+ public static final Sequenceable.Producer CREATOR = new Producer() {
+ public ScanFilter[] newArray(final int size) {
+ return new ScanFilter[size];
+ }
+
+ public ScanFilter createFromParcel(final Parcel in) {
+ final Builder builder = new Builder();
+ if (in.readInt() == 1) {
+ builder.setDeviceName(in.readString());
+ }
+ if (in.readInt() == 1) {
+ builder.setDeviceAddress(in.readString());
+ }
+ if (in.readInt() == 1) {
+ ParcelableEx uuid = in.readParcelableEx(UUID.class.getClassLoader());
+ builder.setServiceUuid(uuid);
+ if (in.readInt() == 1) {
+ ParcelableEx uuidMask = in.readParcelableEx(SequenceUuid.class.getClassLoader());
+ builder.setServiceUuid(uuid, uuidMask);
+ }
+ }
+ if (in.readInt() == 1) {
+ ParcelableEx serviceDataUuid = in.readParcelableEx(SequenceUuid.class.getClassLoader());
+ if (in.readInt() == 1) {
+ final int serviceDataLength = in.readInt();
+ final byte[] serviceData = new byte[serviceDataLength];
+ in.readByteArray(serviceData);
+ if (in.readInt() == 0) {
+ builder.setServiceData(serviceDataUuid, serviceData);
+ } else {
+ final int serviceDataMaskLength = in.readInt();
+ final byte[] serviceDataMask = new byte[serviceDataMaskLength];
+ in.readByteArray(serviceDataMask);
+ builder.setServiceData(serviceDataUuid, serviceData, serviceDataMask);
+ }
+ }
+ }
+
+ final int manufacturerId = in.readInt();
+ if (in.readInt() == 1) {
+ final int manufacturerDataLength = in.readInt();
+ final byte[] manufacturerData = new byte[manufacturerDataLength];
+ in.readByteArray(manufacturerData);
+ if (in.readInt() == 0) {
+ builder.setManufacturerData(manufacturerId, manufacturerData);
+ } else {
+ final int manufacturerDataMaskLength = in.readInt();
+ final byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength];
+ in.readByteArray(manufacturerDataMask);
+ builder.setManufacturerData(manufacturerId, manufacturerData,
+ manufacturerDataMask);
+ }
+ }
+
+ return builder.build();
+ }
+ };
+
+ @Nullable
+ public String getDeviceName() {
+ return deviceName;
+ }
+
+ public Object getServiceUuid() {
+ return serviceUuid;
+ }
+
+ public Object getServiceUuidMask() {
+ return serviceUuidMask;
+ }
+
+ @Nullable
+ public String getDeviceAddress() {
+ return deviceAddress;
+ }
+
+ @Nullable
+ public byte[] getServiceData() {
+ return serviceData;
+ }
+
+ @Nullable
+ public byte[] getServiceDataMask() {
+ return serviceDataMask;
+ }
+
+ public Object getServiceDataUuid() {
+ return serviceDataUuid;
+ }
+
+ public int getManufacturerId() {
+ return manufacturerId;
+ }
+
+ @Nullable
+ public byte[] getManufacturerData() {
+ return manufacturerData;
+ }
+
+ @Nullable
+ public byte[] getManufacturerDataMask() {
+ return manufacturerDataMask;
+ }
+
+ public boolean matches(final ScanResult scanResult) {
+ if (scanResult == null) {
+ return false;
+ }
+ BlePeripheralDevice device = (BlePeripheralDevice) scanResult.getDevice();
+ if (deviceAddress != null && !deviceAddress.equals(device.getDeviceAddr())) {
+ return false;
+ }
+ final BlePeripheralDevice scanRecord = (BlePeripheralDevice) scanResult.getDevice();
+ if (scanRecord == null
+ && (deviceName != null || serviceUuid != null || manufacturerData != null
+ || serviceData != null)) {
+ return false;
+ }
+ if (deviceName != null && !deviceName.equals(scanRecord.getDeviceName())) {
+ return false;
+ }
+ if (serviceDataUuid != null && scanRecord != null) {
+ }
+ if (manufacturerId >= 0 && scanRecord != null) {
+ }
+ return true;
+ }
+
+ private static boolean matchesServiceUuids(final SequenceUuid uuid,final SequenceUuid parcelUuidMask,
+ @Nullable final List uuids) {
+ if (uuid == null) {
+ return true;
+ }
+ if (uuids == null) {
+ return false;
+ }
+
+ for (final SequenceUuid parcelUuid : uuids) {
+ final UUID uuidMask = parcelUuidMask == null ? null : parcelUuidMask.getUuid();
+ if (matchesServiceUuid(uuid.getUuid(), uuidMask, parcelUuid.getUuid())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ private
+ static boolean matchesServiceUuid(final UUID uuid,
+ @Nullable final UUID mask,
+ final UUID data) {
+ if (mask == null) {
+ return uuid.equals(data);
+ }
+ if ((uuid.getLeastSignificantBits() & mask.getLeastSignificantBits())
+ != (data.getLeastSignificantBits() & mask.getLeastSignificantBits())) {
+ return false;
+ }
+ return ((uuid.getMostSignificantBits() & mask.getMostSignificantBits())
+ == (data.getMostSignificantBits() & mask.getMostSignificantBits()));
+ }
+ private boolean matchesPartialData(@Nullable final byte[] data,
+ @Nullable final byte[] dataMask,
+ @Nullable final byte[] parsedData) {
+ if (data == null) {
+ return parsedData != null;
+ }
+ if (parsedData == null || parsedData.length < data.length) {
+ return false;
+ }
+ if (dataMask == null) {
+ for (int i = 0; i < data.length; ++i) {
+ if (parsedData[i] != data[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ for (int i = 0; i < data.length; ++i) {
+ if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "BluetoothLeScanFilter [deviceName=" + deviceName + ", deviceAddress="
+ + deviceAddress
+ + ", mUuid=" + serviceUuid + ", uuidMask=" + serviceUuidMask
+ + ", serviceDataUuid=" + Objects.toString(serviceDataUuid) + ", serviceData="
+ + Arrays.toString(serviceData) + ", serviceDataMask="
+ + Arrays.toString(serviceDataMask) + ", manufacturerId=" + manufacturerId
+ + ", manufacturerData=" + Arrays.toString(manufacturerData)
+ + ", manufacturerDataMask=" + Arrays.toString(manufacturerDataMask) + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(deviceName, deviceAddress, manufacturerId,
+ Arrays.hashCode(manufacturerData),
+ Arrays.hashCode(manufacturerDataMask),
+ serviceDataUuid,
+ Arrays.hashCode(serviceData),
+ Arrays.hashCode(serviceDataMask),
+ serviceUuid, serviceUuidMask);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final ScanFilter other = (ScanFilter) obj;
+ return Objects.equals(deviceName, other.deviceName)
+ && Objects.equals(deviceAddress, other.deviceAddress)
+ && manufacturerId == other.manufacturerId
+ && Objects.deepEquals(manufacturerData, other.manufacturerData)
+ && Objects.deepEquals(manufacturerDataMask, other.manufacturerDataMask)
+ && Objects.equals(serviceDataUuid, other.serviceDataUuid)
+ && Objects.deepEquals(serviceData, other.serviceData)
+ && Objects.deepEquals(serviceDataMask, other.serviceDataMask)
+ && Objects.equals(serviceUuid, other.serviceUuid)
+ && Objects.equals(serviceUuidMask, other.serviceUuidMask);
+ }
+ /* package */ boolean isAllFieldsEmpty() {
+ return EMPTY.equals(this);
+ }
+
+ @Override
+ public boolean hasFileDescriptor() {
+ return false;
+ }
+
+ @Override
+ public boolean marshalling(Parcel parcel) {
+ return false;
+ }
+
+ @Override
+ public boolean unmarshalling(Parcel parcel) {
+ return false;
+ }
+ public static final class Builder {
+ private String deviceName;
+ private String deviceAddress;
+ private Object serviceUuid;
+ private Object uuidMask;
+ private Object serviceDataUuid;
+ private byte[] serviceData;
+ private byte[] serviceDataMask;
+ private int manufacturerId = -1;
+ private byte[] manufacturerData;
+ private byte[] manufacturerDataMask;
+
+ public Builder setDeviceName(@Nullable final String deviceName) {
+ this.deviceName = deviceName;
+ return this;
+ }
+
+ public Builder setDeviceAddress(@Nullable final String deviceAddress) {
+ this.deviceAddress = deviceAddress;
+ return this;
+ }
+
+ public Builder setServiceUuid(final Object serviceUuid) {
+ this.serviceUuid = serviceUuid;
+ this.uuidMask = null; // clear uuid mask
+ return this;
+ }
+
+ public Builder setServiceUuid(final Object serviceUuid,
+ final Object uuidMask) {
+ if (uuidMask != null && serviceUuid == null) {
+ throw new IllegalArgumentException("uuid is null while uuidMask is not null!");
+ }
+ this.serviceUuid = serviceUuid;
+ this.uuidMask = uuidMask;
+ return this;
+ }
+ public Builder setServiceData(final Object serviceDataUuid,
+ @Nullable final byte[] serviceData) {
+ if (serviceDataUuid == null) {
+ throw new IllegalArgumentException("serviceDataUuid is null!");
+ }
+ this.serviceDataUuid = serviceDataUuid;
+ this.serviceData = serviceData;
+ this.serviceDataMask = null;
+ return this;
+ }
+
+ public Builder setServiceData(final Object serviceDataUuid,
+ @Nullable final byte[] serviceData,
+ @Nullable final byte[] serviceDataMask) {
+ if (serviceDataUuid == null) {
+ throw new IllegalArgumentException("serviceDataUuid is null");
+ }
+ if (serviceDataMask != null) {
+ if (serviceData == null) {
+ throw new IllegalArgumentException(
+ "serviceData is null while serviceDataMask is not null");
+ }
+ if (serviceData.length != serviceDataMask.length) {
+ throw new IllegalArgumentException(
+ "size mismatch for service data and service data mask");
+ }
+ }
+ this.serviceDataUuid = serviceDataUuid;
+ this.serviceData = serviceData;
+ this.serviceDataMask = serviceDataMask;
+ return this;
+ }
+
+ public Builder setManufacturerData(final int manufacturerId,
+ @Nullable final byte[] manufacturerData) {
+ if (manufacturerData != null && manufacturerId < 0) {
+ throw new IllegalArgumentException("invalid manufacture id");
+ }
+ this.manufacturerId = manufacturerId;
+ this.manufacturerData = manufacturerData;
+ this.manufacturerDataMask = null;
+ return this;
+ }
+
+ public Builder setManufacturerData(final int manufacturerId,
+ @Nullable final byte[] manufacturerData,
+ @Nullable final byte[] manufacturerDataMask) {
+ if (manufacturerData != null && manufacturerId < 0) {
+ throw new IllegalArgumentException("invalid manufacture id");
+ }
+ if (manufacturerDataMask != null) {
+ if (manufacturerData == null) {
+ throw new IllegalArgumentException(
+ "manufacturerData is null while manufacturerDataMask is not null");
+ }
+ if (manufacturerData.length != manufacturerDataMask.length) {
+ throw new IllegalArgumentException(
+ "size mismatch for manufacturerData and manufacturerDataMask");
+ }
+ }
+ this.manufacturerId = manufacturerId;
+ this.manufacturerData = manufacturerData;
+ this.manufacturerDataMask = manufacturerDataMask;
+ return this;
+ }
+
+ public ScanFilter build() {
+ return new ScanFilter(deviceName, deviceAddress, serviceUuid, uuidMask,
+ serviceDataUuid, serviceData, serviceDataMask,
+ manufacturerId, manufacturerData, manufacturerDataMask);
+ }
+ }
}
diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanRecord.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanRecord.java
index 3818756ee0306cce39fd6abd42223e4f35bc75db..3156e359780538ed17a82c144c380f7d35515e13 100644
--- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanRecord.java
+++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanRecord.java
@@ -1,51 +1,11 @@
-/*
- * Copyright (c) 2018, Nordic Semiconductor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
package no.nordicsemi.android.support.v18.scanner;
+import ohos.utils.PlainArray;
+import ohos.utils.SequenceUuid;
+import org.jetbrains.annotations.Nullable;
-import android.os.ParcelUuid;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * Represents a scan record from Bluetooth LE scan.
- */
-@SuppressWarnings("WeakerAccess")
+import java.util.*;
public final class ScanRecord {
-
private static final String TAG = "ScanRecord";
-
- // The following data type values are assigned by Bluetooth SIG.
- // For more details refer to Bluetooth 4.1 specification, Volume 3, Part C, Section 18.
private static final int DATA_TYPE_FLAGS = 0x01;
private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02;
private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03;
@@ -60,114 +20,60 @@ public final class ScanRecord {
private static final int DATA_TYPE_SERVICE_DATA_32_BIT = 0x20;
private static final int DATA_TYPE_SERVICE_DATA_128_BIT = 0x21;
private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
-
- // Flags of the advertising data.
+ private static final String LABEL_LOG ="ScanRecord" ;
private final int advertiseFlags;
-
- @Nullable private final List serviceUuids;
-
- @Nullable private final SparseArray manufacturerSpecificData;
-
- @Nullable private final Map serviceData;
-
- // Transmission power level(in dB).
+ @Nullable
+ private final List serviceUuids;
+ @Nullable private final PlainArray manufacturerSpecificData;
+ @Nullable private final Map serviceData;
private final int txPowerLevel;
-
- // Local name of the Bluetooth LE device.
private final String deviceName;
-
- // Raw bytes of scan record.
private final byte[] bytes;
-
- /**
- * Returns the advertising flags indicating the discoverable mode and capability of the device.
- * Returns -1 if the flag field is not set.
- */
public int getAdvertiseFlags() {
return advertiseFlags;
}
-
- /**
- * Returns a list of service UUIDs within the advertisement that are used to identify the
- * bluetooth GATT services.
- */
@Nullable
- public List getServiceUuids() {
+ public List getServiceUuids() {
return serviceUuids;
}
-
- /**
- * Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific
- * data.
- */
@Nullable
- public SparseArray getManufacturerSpecificData() {
+ public PlainArray getManufacturerSpecificData() {
return manufacturerSpecificData;
}
-
- /**
- * Returns the manufacturer specific data associated with the manufacturer id. Returns
- * {@code null} if the {@code manufacturerId} is not found.
- */
- @Nullable
- public byte[] getManufacturerSpecificData(final int manufacturerId) {
+ public Optional getManufacturerSpecificData(final int manufacturerId) {
if (manufacturerSpecificData == null) {
return null;
}
return manufacturerSpecificData.get(manufacturerId);
}
-
- /**
- * Returns a map of service UUID and its corresponding service data.
- */
@Nullable
- public Map getServiceData() {
+ public Map getServiceData() {
return serviceData;
}
-
- /**
- * Returns the service data byte array associated with the {@code serviceUuid}. Returns
- * {@code null} if the {@code serviceDataUuid} is not found.
- */
@Nullable
- public byte[] getServiceData(@NonNull final ParcelUuid serviceDataUuid) {
- //noinspection ConstantConditions
+ public byte[] getServiceData( final Object serviceDataUuid) {
if (serviceDataUuid == null || serviceData == null) {
return null;
}
return serviceData.get(serviceDataUuid);
}
- /**
- * Returns the transmission power level of the packet in dBm. Returns {@link Integer#MIN_VALUE}
- * if the field is not set. This value can be used to calculate the path loss of a received
- * packet using the following equation:
- *
- * pathloss = txPowerLevel - rssi
- */
public int getTxPowerLevel() {
return txPowerLevel;
}
- /**
- * Returns the local name of the BLE device. The is a UTF-8 encoded string.
- */
- @Nullable
public String getDeviceName() {
return deviceName;
}
- /**
- * Returns raw bytes of scan record.
- */
@Nullable
public byte[] getBytes() {
return bytes;
}
- private ScanRecord(@Nullable final List serviceUuids,
- @Nullable final SparseArray manufacturerData,
- @Nullable final Map serviceData,
+ private ScanRecord(@Nullable final List serviceUuids,
+ @Nullable final PlainArray manufacturerData,
+ @Nullable final Map serviceData,
final int advertiseFlags, final int txPowerLevel,
final String localName, final byte[] bytes) {
this.serviceUuids = serviceUuids;
@@ -179,16 +85,6 @@ public final class ScanRecord {
this.bytes = bytes;
}
- /**
- * Parse scan record bytes to {@link ScanRecord}.
- *
- * The format is defined in Bluetooth 4.1 specification, Volume 3, Part C, Section 11 and 18.
- *
- * All numerical multi-byte entities and values shall use little-endian byte
- * order.
- *
- * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.
- */
@Nullable
/* package */ static ScanRecord parseFromBytes(@Nullable final byte[] scanRecord) {
if (scanRecord == null) {
@@ -199,9 +95,9 @@ public final class ScanRecord {
int advertiseFlag = -1;
int txPowerLevel = Integer.MIN_VALUE;
String localName = null;
- List serviceUuids = null;
- SparseArray manufacturerData = null;
- Map serviceData = null;
+ List serviceUuids = null;
+ PlainArray manufacturerData = null;
+ Map serviceData = null;
try {
while (currentPos < scanRecord.length) {
@@ -210,9 +106,7 @@ public final class ScanRecord {
if (length == 0) {
break;
}
- // Note the length includes the length of the field type itself.
final int dataLength = length - 1;
- // fieldType is unsigned int.
final int fieldType = scanRecord[currentPos++] & 0xFF;
switch (fieldType) {
case DATA_TYPE_FLAGS:
@@ -259,7 +153,7 @@ public final class ScanRecord {
final byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
serviceUuidLength);
- final ParcelUuid serviceDataUuid = BluetoothUuid.parseUuidFrom(
+ final SequenceUuid serviceDataUuid = BluetoothUuid.parseUuidFrom(
serviceDataUuidBytes);
final byte[] serviceDataArray = extractBytes(scanRecord,
currentPos + serviceUuidLength, dataLength - serviceUuidLength);
@@ -268,29 +162,22 @@ public final class ScanRecord {
serviceData.put(serviceDataUuid, serviceDataArray);
break;
case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
- // The first two bytes of the manufacturer specific data are
- // manufacturer ids in little endian.
final int manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8) +
(scanRecord[currentPos] & 0xFF);
final byte[] manufacturerDataBytes = extractBytes(scanRecord, currentPos + 2,
dataLength - 2);
if (manufacturerData == null)
- manufacturerData = new SparseArray<>();
+ manufacturerData = new PlainArray<>();
manufacturerData.put(manufacturerId, manufacturerDataBytes);
break;
default:
- // Just ignore, we don't handle such data type.
break;
}
currentPos += dataLength;
}
-
return new ScanRecord(serviceUuids, manufacturerData, serviceData,
advertiseFlag, txPowerLevel, localName, scanRecord);
} catch (final Exception e) {
- Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord));
- // As the record is invalid, ignore all the parsed results for this packet
- // and return an empty record with raw scanRecord bytes in results
return new ScanRecord(null, null, null,
-1, Integer.MIN_VALUE, null, scanRecord);
}
@@ -316,12 +203,9 @@ public final class ScanRecord {
+ ", txPowerLevel=" + txPowerLevel + ", deviceName=" + deviceName + "]";
}
- // Parse service UUIDs.
- @SuppressWarnings("UnusedReturnValue")
- private static int parseServiceUuid(@NonNull final byte[] scanRecord,
- int currentPos, int dataLength,
- final int uuidLength,
- @NonNull final List serviceUuids) {
+ private static int parseServiceUuid( final byte[] scanRecord,int currentPos, int dataLength
+ ,final int uuidLength
+ ,final List serviceUuids) {
while (dataLength > 0) {
final byte[] uuidBytes = extractBytes(scanRecord, currentPos,
uuidLength);
@@ -332,8 +216,7 @@ public final class ScanRecord {
return currentPos;
}
- // Helper method to extract bytes from byte array.
- private static byte[] extractBytes(@NonNull final byte[] scanRecord,
+ private static byte[] extractBytes( final byte[] scanRecord,
final int start, final int length) {
byte[] bytes = new byte[length];
System.arraycopy(scanRecord, start, bytes, 0, length);
diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanResult.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanResult.java
index faf47fc850d8d1effc9e5b9f557059c9c5361d28..1fce438a3935aaf33a4d65ae51e5259fd0ba679e 100644
--- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanResult.java
+++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanResult.java
@@ -22,327 +22,345 @@
package no.nordicsemi.android.support.v18.scanner;
-import android.bluetooth.BluetoothDevice;
-import android.os.Parcel;
-import android.os.Parcelable;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
+import ohos.bluetooth.ble.BleScanResult;
+import ohos.utils.Parcel;
+import ohos.utils.Sequenceable;
+import org.jetbrains.annotations.Nullable;
/**
* ScanResult for Bluetooth LE scan.
*/
@SuppressWarnings({"WeakerAccess", "unused"})
-public final class ScanResult implements Parcelable {
-
- /**
- * For chained advertisements, indicates that the data contained in this
- * scan result is complete.
- */
- public static final int DATA_COMPLETE = 0x00;
-
- /**
- * For chained advertisements, indicates that the controller was
- * unable to receive all chained packets and the scan result contains
- * incomplete truncated data.
- */
- public static final int DATA_TRUNCATED = 0x02;
-
- /**
- * Indicates that the secondary physical layer was not used.
- */
- public static final int PHY_UNUSED = 0x00;
-
- /**
- * Advertising Set ID is not present in the packet.
- */
- public static final int SID_NOT_PRESENT = 0xFF;
-
- /**
- * TX power is not present in the packet.
- */
- public static final int TX_POWER_NOT_PRESENT = 0x7F;
-
- /**
- * Periodic advertising interval is not present in the packet.
- */
- public static final int PERIODIC_INTERVAL_NOT_PRESENT = 0x00;
-
- /**
- * Mask for checking whether event type represents legacy advertisement.
- */
- static final int ET_LEGACY_MASK = 0x10;
-
- /**
- * Mask for checking whether event type represents connectable advertisement.
- */
- static final int ET_CONNECTABLE_MASK = 0x01;
-
- // Remote Bluetooth device.
- @SuppressWarnings("NullableProblems")
- @NonNull
- private BluetoothDevice device;
-
- // Scan record, including advertising data and scan response data.
- @Nullable
- private ScanRecord scanRecord;
-
- // Received signal strength.
- private int rssi;
-
- // Device timestamp when the result was last seen.
- private long timestampNanos;
-
- private int eventType;
- private int primaryPhy;
- private int secondaryPhy;
- private int advertisingSid;
- private int txPower;
- private int periodicAdvertisingInterval;
-
- /**
- * Constructs a new ScanResult.
- *
- * @param device Remote Bluetooth device found.
- * @param scanRecord Scan record including both advertising data and scan response data.
- * @param rssi Received signal strength.
- * @param timestampNanos Timestamp at which the scan result was observed.
- * @deprecated use {@link #ScanResult(BluetoothDevice, int, int, int, int, int, int, int, ScanRecord, long)}
- */
- public ScanResult(@NonNull final BluetoothDevice device, @Nullable final ScanRecord scanRecord,
- int rssi, long timestampNanos) {
- this.device = device;
- this.scanRecord = scanRecord;
- this.rssi = rssi;
- this.timestampNanos = timestampNanos;
- this.eventType = (DATA_COMPLETE << 5) | ET_LEGACY_MASK | ET_CONNECTABLE_MASK;
- this.primaryPhy = 1; // BluetoothDevice.PHY_LE_1M;
- this.secondaryPhy = PHY_UNUSED;
- this.advertisingSid = SID_NOT_PRESENT;
- this.txPower = 127;
- this.periodicAdvertisingInterval = 0;
- }
-
- /**
- * Constructs a new ScanResult.
- *
- * @param device Remote Bluetooth device found.
- * @param eventType Event type.
- * @param primaryPhy Primary advertising phy.
- * @param secondaryPhy Secondary advertising phy.
- * @param advertisingSid Advertising set ID.
- * @param txPower Transmit power.
- * @param rssi Received signal strength.
- * @param periodicAdvertisingInterval Periodic advertising interval.
- * @param scanRecord Scan record including both advertising data and scan response data.
- * @param timestampNanos Timestamp at which the scan result was observed.
- */
- public ScanResult(@NonNull final BluetoothDevice device, final int eventType,
- final int primaryPhy, final int secondaryPhy,
- final int advertisingSid, final int txPower, final int rssi,
- final int periodicAdvertisingInterval,
- @Nullable final ScanRecord scanRecord, final long timestampNanos) {
- this.device = device;
- this.eventType = eventType;
- this.primaryPhy = primaryPhy;
- this.secondaryPhy = secondaryPhy;
- this.advertisingSid = advertisingSid;
- this.txPower = txPower;
- this.rssi = rssi;
- this.periodicAdvertisingInterval = periodicAdvertisingInterval;
- this.scanRecord = scanRecord;
- this.timestampNanos = timestampNanos;
- }
-
- private ScanResult(final Parcel in) {
- readFromParcel(in);
- }
-
- @Override
- public void writeToParcel(final Parcel dest, final int flags) {
- device.writeToParcel(dest, flags);
- if (scanRecord != null) {
- dest.writeInt(1);
- dest.writeByteArray(scanRecord.getBytes());
- } else {
- dest.writeInt(0);
- }
- dest.writeInt(rssi);
- dest.writeLong(timestampNanos);
- dest.writeInt(eventType);
- dest.writeInt(primaryPhy);
- dest.writeInt(secondaryPhy);
- dest.writeInt(advertisingSid);
- dest.writeInt(txPower);
- dest.writeInt(periodicAdvertisingInterval);
- }
-
- private void readFromParcel(final Parcel in) {
- device = BluetoothDevice.CREATOR.createFromParcel(in);
- if (in.readInt() == 1) {
- scanRecord = ScanRecord.parseFromBytes(in.createByteArray());
- }
- rssi = in.readInt();
- timestampNanos = in.readLong();
- eventType = in.readInt();
- primaryPhy = in.readInt();
- secondaryPhy = in.readInt();
- advertisingSid = in.readInt();
- txPower = in.readInt();
- periodicAdvertisingInterval = in.readInt();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- /**
- * Returns the remote Bluetooth device identified by the Bluetooth device address.
- */
- @NonNull
- public BluetoothDevice getDevice() {
- return device;
- }
-
- /**
- * Returns the scan record, which is a combination of advertisement and scan response.
- */
- @Nullable
- public ScanRecord getScanRecord() {
- return scanRecord;
- }
-
- /**
- * Returns the received signal strength in dBm. The valid range is [-127, 126].
- */
- public int getRssi() {
- return rssi;
- }
-
- /**
- * Returns timestamp since boot when the scan record was observed.
- */
- public long getTimestampNanos() {
- return timestampNanos;
- }
-
- /**
- * Returns true if this object represents legacy scan result.
- * Legacy scan results do not contain advanced advertising information
- * as specified in the Bluetooth Core Specification v5.
- */
- public boolean isLegacy() {
- return (eventType & ET_LEGACY_MASK) != 0;
- }
-
- /**
- * Returns true if this object represents connectable scan result.
- */
- public boolean isConnectable() {
- return (eventType & ET_CONNECTABLE_MASK) != 0;
- }
-
- /**
- * Returns the data status.
- * Can be one of {@link ScanResult#DATA_COMPLETE} or
- * {@link ScanResult#DATA_TRUNCATED}.
- */
- public int getDataStatus() {
- // return bit 5 and 6
- return (eventType >> 5) & 0x03;
- }
-
- /**
- * Returns the primary Physical Layer
- * on which this advertisement was received.
- * Can be one of {@link BluetoothDevice#PHY_LE_1M} or
- * {@link BluetoothDevice#PHY_LE_CODED}.
- */
- public int getPrimaryPhy() { return primaryPhy; }
-
- /**
- * Returns the secondary Physical Layer
- * on which this advertisement was received.
- * Can be one of {@link BluetoothDevice#PHY_LE_1M},
- * {@link BluetoothDevice#PHY_LE_2M}, {@link BluetoothDevice#PHY_LE_CODED}
- * or {@link ScanResult#PHY_UNUSED} - if the advertisement
- * was not received on a secondary physical channel.
- */
- public int getSecondaryPhy() { return secondaryPhy; }
-
- /**
- * Returns the advertising set id.
- * May return {@link ScanResult#SID_NOT_PRESENT} if
- * no set id was is present.
- */
- public int getAdvertisingSid() { return advertisingSid; }
-
- /**
- * Returns the transmit power in dBm.
- * Valid range is [-127, 126]. A value of {@link ScanResult#TX_POWER_NOT_PRESENT}
- * indicates that the TX power is not present.
- */
- public int getTxPower() { return txPower; }
-
- /**
- * Returns the periodic advertising interval in units of 1.25ms.
- * Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of
- * {@link ScanResult#PERIODIC_INTERVAL_NOT_PRESENT} means periodic
- * advertising interval is not present.
- */
- public int getPeriodicAdvertisingInterval() {
- return periodicAdvertisingInterval;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(device, rssi, scanRecord, timestampNanos,
- eventType, primaryPhy, secondaryPhy,
- advertisingSid, txPower,
- periodicAdvertisingInterval);
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
- final ScanResult other = (ScanResult) obj;
- return Objects.equals(device, other.device) && (rssi == other.rssi) &&
- Objects.equals(scanRecord, other.scanRecord) &&
- (timestampNanos == other.timestampNanos) &&
- eventType == other.eventType &&
- primaryPhy == other.primaryPhy &&
- secondaryPhy == other.secondaryPhy &&
- advertisingSid == other.advertisingSid &&
- txPower == other.txPower &&
- periodicAdvertisingInterval == other.periodicAdvertisingInterval;
- }
-
- @Override
- public String toString() {
- return "ScanResult{" + "device=" + device + ", scanRecord=" +
- Objects.toString(scanRecord) + ", rssi=" + rssi +
- ", timestampNanos=" + timestampNanos + ", eventType=" + eventType +
- ", primaryPhy=" + primaryPhy + ", secondaryPhy=" + secondaryPhy +
- ", advertisingSid=" + advertisingSid + ", txPower=" + txPower +
- ", periodicAdvertisingInterval=" + periodicAdvertisingInterval + '}';
- }
-
- public static final Parcelable.Creator CREATOR = new Creator() {
- @Override
- public ScanResult createFromParcel(final Parcel source) {
- return new ScanResult(source);
- }
-
- @Override
- public ScanResult[] newArray(final int size) {
- return new ScanResult[size];
- }
- };
-
+public final class ScanResult implements Sequenceable {
+
+ /**
+ * For chained advertisements, indicates that the data contained in this
+ * scan result is complete.
+ */
+ public static final int DATA_COMPLETE = 0x00;
+
+ /**
+ * For chained advertisements, indicates that the controller was
+ * unable to receive all chained packets and the scan result contains
+ * incomplete truncated data.
+ */
+ public static final int DATA_TRUNCATED = 0x02;
+
+ /**
+ * Indicates that the secondary physical layer was not used.
+ */
+ public static final int PHY_UNUSED = 0x00;
+
+ /**
+ * Advertising Set ID is not present in the packet.
+ */
+ public static final int SID_NOT_PRESENT = 0xFF;
+
+ /**
+ * TX power is not present in the packet.
+ */
+ public static final int TX_POWER_NOT_PRESENT = 0x7F;
+
+ /**
+ * Periodic advertising interval is not present in the packet.
+ */
+ public static final int PERIODIC_INTERVAL_NOT_PRESENT = 0x00;
+
+ /**
+ * Mask for checking whether event type represents legacy advertisement.
+ */
+ static final int ET_LEGACY_MASK = 0x10;
+
+ /**
+ * Mask for checking whether event type represents connectable advertisement.
+ */
+ static final int ET_CONNECTABLE_MASK = 0x01;
+
+ // Remote Bluetooth device.
+ @SuppressWarnings("NullableProblems")
+
+ private BleScanResult device;
+
+ // Scan record, including advertising data and scan response data.
+
+ private ScanRecord scanRecord;
+
+ // Received signal strength.
+ private int rssi;
+
+ // Device timestamp when the result was last seen.
+ private long timestampNanos;
+
+ private int eventType;
+ private int primaryPhy;
+ private int secondaryPhy;
+ private int advertisingSid;
+ private int txPower;
+ private int periodicAdvertisingInterval;
+
+ /**
+ * Constructs a new ScanResult.
+ *
+ * @param device Remote Bluetooth device found.
+ * @param scanRecord Scan record including both advertising data and scan response data.
+ * @param rssi Received signal strength.
+ * @param timestampNanos Timestamp at which the scan result was observed.*/
+ public ScanResult(final BleScanResult device, @Nullable final ScanRecord scanRecord,
+ int rssi, long timestampNanos) {
+ this.device = device;
+ this.scanRecord = scanRecord;
+ this.rssi = rssi;
+ this.timestampNanos = timestampNanos;
+ this.eventType = (DATA_COMPLETE << 5) | ET_LEGACY_MASK | ET_CONNECTABLE_MASK;
+ this.primaryPhy = 1; // BluetoothDevice.PHY_LE_1M;
+ this.secondaryPhy = PHY_UNUSED;
+ this.advertisingSid = SID_NOT_PRESENT;
+ this.txPower = 127;
+ this.periodicAdvertisingInterval = 0;
+ }
+
+ /**
+ * Constructs a new ScanResult.
+ *
+ * @param device Remote Bluetooth device found.
+ * @param eventType Event type.
+ * @param primaryPhy Primary advertising phy.
+ * @param secondaryPhy Secondary advertising phy.
+ * @param advertisingSid Advertising set ID.
+ * @param txPower Transmit power.
+ * @param rssi Received signal strength.
+ * @param periodicAdvertisingInterval Periodic advertising interval.
+ * @param scanRecord Scan record including both advertising data and scan response data.
+ * @param timestampNanos Timestamp at which the scan result was observed.
+ */
+ public ScanResult(final BleScanResult device, final int eventType,
+ final int primaryPhy, final int secondaryPhy,
+ final int advertisingSid, final int txPower, final int rssi,
+ final int periodicAdvertisingInterval,
+ @Nullable final ScanRecord scanRecord, final long timestampNanos) {
+ this.device = device;
+ this.eventType = eventType;
+ this.primaryPhy = primaryPhy;
+ this.secondaryPhy = secondaryPhy;
+ this.advertisingSid = advertisingSid;
+ this.txPower = txPower;
+ this.rssi = rssi;
+ this.periodicAdvertisingInterval = periodicAdvertisingInterval;
+ this.scanRecord = scanRecord;
+ this.timestampNanos = timestampNanos;
+ }
+
+ private ScanResult(final Parcel in) {
+ readFromParcel(in);
+ }
+
+// @Override
+ public void writeToParcel(final Parcel dest, final int flags) {
+ //TODO
+// device.writeToParcel(dest, flags);
+// if (scanRecord != null) {
+// dest.writeInt(1);
+// dest.writeByteArray(scanRecord.getBytes());
+// } else {
+// dest.writeInt(0);
+// }
+// dest.writeInt(rssi);
+// dest.writeLong(timestampNanos);
+// dest.writeInt(eventType);
+// dest.writeInt(primaryPhy);
+// dest.writeInt(secondaryPhy);
+// dest.writeInt(advertisingSid);
+// dest.writeInt(txPower);
+// dest.writeInt(periodicAdvertisingInterval);
+ }
+
+ private void readFromParcel(final Parcel in) {
+ //TODO
+// device = ScanResult.CREATOR.createFromParcel(in);
+// if (in.readInt() == 1) {
+//// scanRecord = ScanRecord.parseFromBytes(in.createByteArray());
+// scanRecord = ScanRecord.parseFromBytes(in.readByteArray());
+// }
+// rssi = in.readInt();
+// timestampNanos = in.readLong();
+// eventType = in.readInt();
+// primaryPhy = in.readInt();
+// secondaryPhy = in.readInt();
+// advertisingSid = in.readInt();
+// txPower = in.readInt();
+// periodicAdvertisingInterval = in.readInt();
+ }
+
+// @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns the remote Bluetooth device identified by the Bluetooth device address.
+ */
+
+ public Object getDevice() {
+ return device;
+ }
+
+ /**
+ * Returns the scan record, which is a combination of advertisement and scan response.
+ */
+ @Nullable
+ public ScanRecord getScanRecord() {
+ return scanRecord;
+ }
+
+ /**
+ * Returns the received signal strength in dBm. The valid range is [-127, 126].
+ */
+ public int getRssi() {
+ return rssi;
+ }
+
+ /**
+ * Returns timestamp since boot when the scan record was observed.
+ */
+ public long getTimestampNanos() {
+ return timestampNanos;
+ }
+
+ /**
+ * Returns true if this object represents legacy scan result.
+ * Legacy scan results do not contain advanced advertising information
+ * as specified in the Bluetooth Core Specification v5.
+ */
+ public boolean isLegacy() {
+ return (eventType & ET_LEGACY_MASK) != 0;
+ }
+
+ /**
+ * Returns true if this object represents connectable scan result.
+ */
+ public boolean isConnectable() {
+ return (eventType & ET_CONNECTABLE_MASK) != 0;
+ }
+
+ /**
+ * Returns the data status.
+ * Can be one of {@link ScanResult#DATA_COMPLETE} or
+ * {@link ScanResult#DATA_TRUNCATED}.
+ */
+ public int getDataStatus() {
+ // return bit 5 and 6
+ return (eventType >> 5) & 0x03;
+ }
+
+ /**
+ * Returns the primary Physical Layer
+ * on which this advertisement was received.
+ */
+ public int getPrimaryPhy() {
+ return primaryPhy;
+ }
+
+ /**
+ * Returns the secondary Physical Layer
+ * or {@link ScanResult#PHY_UNUSED} - if the advertisement
+ * was not received on a secondary physical channel.
+ */
+ public int getSecondaryPhy() {
+ return secondaryPhy;
+ }
+
+ /**
+ * Returns the advertising set id.
+ * May return {@link ScanResult#SID_NOT_PRESENT} if
+ * no set id was is present.
+ */
+ public int getAdvertisingSid() {
+ return advertisingSid;
+ }
+
+ /**
+ * Returns the transmit power in dBm.
+ * Valid range is [-127, 126]. A value of {@link ScanResult#TX_POWER_NOT_PRESENT}
+ * indicates that the TX power is not present.
+ */
+ public int getTxPower() {
+ return txPower;
+ }
+
+ /**
+ * Returns the periodic advertising interval in units of 1.25ms.
+ * Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of
+ * {@link ScanResult#PERIODIC_INTERVAL_NOT_PRESENT} means periodic
+ * advertising interval is not present.
+ */
+ public int getPeriodicAdvertisingInterval() {
+ return periodicAdvertisingInterval;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(device, rssi, scanRecord, timestampNanos,
+ eventType, primaryPhy, secondaryPhy,
+ advertisingSid, txPower,
+ periodicAdvertisingInterval);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final ScanResult other = (ScanResult) obj;
+ return Objects.equals(device, other.device) && (rssi == other.rssi) &&
+ Objects.equals(scanRecord, other.scanRecord) &&
+ (timestampNanos == other.timestampNanos) &&
+ eventType == other.eventType &&
+ primaryPhy == other.primaryPhy &&
+ secondaryPhy == other.secondaryPhy &&
+ advertisingSid == other.advertisingSid &&
+ txPower == other.txPower &&
+ periodicAdvertisingInterval == other.periodicAdvertisingInterval;
+ }
+
+ @Override
+ public String toString() {
+ return "ScanResult{" + "device=" + device + ", scanRecord=" +
+ Objects.toString(scanRecord) + ", rssi=" + rssi +
+ ", timestampNanos=" + timestampNanos + ", eventType=" + eventType +
+ ", primaryPhy=" + primaryPhy + ", secondaryPhy=" + secondaryPhy +
+ ", advertisingSid=" + advertisingSid + ", txPower=" + txPower +
+ ", periodicAdvertisingInterval=" + periodicAdvertisingInterval + '}';
+ }
+
+// public static final Parcelable.Creator CREATOR = new Creator() {
+ public static final Sequenceable.Producer CREATOR = new Producer() {
+ @Override
+ public ScanResult createFromParcel(final Parcel source) {
+ return new ScanResult(source);
+ }
+
+// @Override
+ public BleScanResult[] newArray(final int size) {
+ return new BleScanResult[size];
+ }
+ };
+
+ @Override
+ public boolean hasFileDescriptor() {
+ return false;
+ }
+
+ @Override
+ public boolean marshalling(Parcel parcel) {
+ return false;
+ }
+
+ @Override
+ public boolean unmarshalling(Parcel parcel) {
+ return false;
+ }
}
diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanSettings.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanSettings.java
index fbb157290498e1df19810ed010dccab8c7a075c3..b3e6225e447947e4974017c521becac9cdb0260b 100644
--- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanSettings.java
+++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanSettings.java
@@ -1,642 +1,450 @@
-/*
- * Copyright (c) 2018, Nordic Semiconductor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
package no.nordicsemi.android.support.v18.scanner;
-import android.bluetooth.BluetoothDevice;
-import android.os.Parcel;
-import android.os.Parcelable;
+import ohos.utils.Parcel;
+import ohos.utils.Sequenceable;
+import java.io.Serializable;
import java.util.List;
-
-import androidx.annotation.NonNull;
-
/**
* Bluetooth LE scan settings are passed to {@link BluetoothLeScannerCompat#startScan} to define the
* parameters for the scan.
*/
-@SuppressWarnings({"WeakerAccess", "unused"})
-public final class ScanSettings implements Parcelable {
-
- /**
- * The default value of the maximum time for the device not to be discoverable before it will be
- * assumed lost.
- */
- public static final long MATCH_LOST_DEVICE_TIMEOUT_DEFAULT = 10000L; // [ms]
-
- /**
- * The default interval of the task that calls match lost events.
- */
- public static final long MATCH_LOST_TASK_INTERVAL_DEFAULT = 10000L; // [ms]
-
- /**
- * A special Bluetooth LE scan mode. Applications using this scan mode will passively listen for
- * other scan results without starting BLE scans themselves.
- *
- * On Android Lollipop {@link #SCAN_MODE_LOW_POWER} will be used instead, as opportunistic
- * mode was not yet supported.
- *
- * On pre-Lollipop devices it is possible to override the default intervals
- * using {@link Builder#setPowerSave(long, long)}.
- */
- public static final int SCAN_MODE_OPPORTUNISTIC = -1;
-
- /**
- * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the
- * least power. This mode is enforced if the scanning application is not in foreground.
- *
- * On pre-Lollipop devices this mode will be emulated by scanning for 0.5 second followed
- * by 4.5 second of idle, which corresponds to the low power intervals on Lollipop or newer.
- */
- public static final int SCAN_MODE_LOW_POWER = 0;
-
- /**
- * Perform Bluetooth LE scan in balanced power mode. Scan results are returned at a rate that
- * provides a good trade-off between scan frequency and power consumption.
- *
- * On pre-Lollipop devices this mode will be emulated by scanning for 2 second followed
- * by 3 seconds of idle, which corresponds to the low power intervals on Lollipop or newer.
- */
- public static final int SCAN_MODE_BALANCED = 1;
-
- /**
- * Scan using highest duty cycle. It's recommended to only use this mode when the application is
- * running in the foreground.
- */
- public static final int SCAN_MODE_LOW_LATENCY = 2;
-
- /**
- * Trigger a callback for every Bluetooth advertisement found that matches the filter criteria.
- * If no filter is active, all advertisement packets are reported.
- */
- public static final int CALLBACK_TYPE_ALL_MATCHES = 1;
-
- /**
- * A result callback is only triggered for the first advertisement packet received that matches
- * the filter criteria.
- */
- public static final int CALLBACK_TYPE_FIRST_MATCH = 2;
-
- /**
- * Receive a callback when advertisements are no longer received from a device that has been
- * previously reported by a first match callback.
- */
- public static final int CALLBACK_TYPE_MATCH_LOST = 4;
-
-
- /*
- * Determines how many advertisements to match per filter, as this is scarce hw resource
- */
- /**
- * Match one advertisement per filter
- */
- public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1;
-
- /**
- * Match few advertisement per filter, depends on current capability and availability of
- * the resources in hw
- */
- public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2;
-
- /**
- * Match as many advertisement per filter as hw could allow, depends on current
- * capability and availability of the resources in hw
- */
- public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3;
-
- /**
- * In Aggressive mode, hw will determine a match sooner even with feeble signal strength
- * and few number of sightings/match in a duration.
- */
- public static final int MATCH_MODE_AGGRESSIVE = 1;
-
- /**
- * For sticky mode, higher threshold of signal strength and sightings is required
- * before reporting by hw
- */
- public static final int MATCH_MODE_STICKY = 2;
-
- /**
- * Use all supported PHYs for scanning.
- * This will check the controller capabilities, and start
- * the scan on 1Mbit and LE Coded PHYs if supported, or on
- * the 1Mbit PHY only.
- */
- public static final int PHY_LE_ALL_SUPPORTED = 255;
-
- /**
- * Pre-Lollipop scanning requires a wakelock and the CPU cannot go to sleep.
- * To conserve power we can optionally scan for a certain duration (scan interval)
- * and then rest for a time before starting scanning again.
- */
- private final long powerSaveScanInterval;
- private final long powerSaveRestInterval;
-
- // Bluetooth LE scan mode.
- private int scanMode;
-
- // Bluetooth LE scan callback type
- private int callbackType;
-
- // Time of delay for reporting the scan result
- private long reportDelayMillis;
-
- private int matchMode;
-
- private int numOfMatchesPerFilter;
-
- private boolean useHardwareFilteringIfSupported;
-
- private boolean useHardwareBatchingIfSupported;
-
- private boolean useHardwareCallbackTypesIfSupported;
-
- private long matchLostDeviceTimeout;
-
- private long matchLostTaskInterval;
-
- // Include only legacy advertising results
- private boolean legacy;
-
- private int phy;
-
- public int getScanMode() {
- return scanMode;
- }
-
- public int getCallbackType() {
- return callbackType;
- }
-
- public int getMatchMode() {
- return matchMode;
- }
-
- public int getNumOfMatches() {
- return numOfMatchesPerFilter;
- }
-
- public boolean getUseHardwareFilteringIfSupported() {
- return useHardwareFilteringIfSupported;
- }
-
- public boolean getUseHardwareBatchingIfSupported() {
- return useHardwareBatchingIfSupported;
- }
-
- public boolean getUseHardwareCallbackTypesIfSupported() {
- return useHardwareCallbackTypesIfSupported;
- }
-
- /**
- * Some devices with Android Marshmallow (Nexus 6) theoretically support other callback types,
- * but call {@link android.bluetooth.le.ScanCallback#onScanFailed(int)} with error = 5.
- * In that case the Scanner Compat will disable the hardware support and start using compat
- * mechanism.
- */
- /* package */ void disableUseHardwareCallbackTypes() {
- useHardwareCallbackTypesIfSupported = false;
- }
-
- public long getMatchLostDeviceTimeout() {
- return matchLostDeviceTimeout;
- }
-
- public long getMatchLostTaskInterval() {
- return matchLostTaskInterval;
- }
-
- /**
- * Returns whether only legacy advertisements will be returned.
- * Legacy advertisements include advertisements as specified
- * by the Bluetooth core specification 4.2 and below.
- */
- public boolean getLegacy() {
- return legacy;
- }
-
- /**
- * Returns the physical layer used during a scan.
- */
- public int getPhy() {
- return phy;
- }
-
- /**
- * Returns report delay timestamp based on the device clock.
- */
- public long getReportDelayMillis() {
- return reportDelayMillis;
- }
-
- private ScanSettings(final int scanMode, final int callbackType,
- final long reportDelayMillis, final int matchMode,
- final int numOfMatchesPerFilter, final boolean legacy, final int phy,
- final boolean hardwareFiltering, final boolean hardwareBatching,
- final boolean hardwareCallbackTypes, final long matchTimeout,
- final long taskInterval,
- final long powerSaveScanInterval, final long powerSaveRestInterval) {
- this.scanMode = scanMode;
- this.callbackType = callbackType;
- this.reportDelayMillis = reportDelayMillis;
- this.numOfMatchesPerFilter = numOfMatchesPerFilter;
- this.matchMode = matchMode;
- this.legacy = legacy;
- this.phy = phy;
- this.useHardwareFilteringIfSupported = hardwareFiltering;
- this.useHardwareBatchingIfSupported = hardwareBatching;
- this.useHardwareCallbackTypesIfSupported = hardwareCallbackTypes;
- this.matchLostDeviceTimeout = matchTimeout * 1000000L; // convert to nanos
- this.matchLostTaskInterval = taskInterval;
- this.powerSaveScanInterval = powerSaveScanInterval;
- this.powerSaveRestInterval = powerSaveRestInterval;
- }
-
- private ScanSettings(final Parcel in) {
- scanMode = in.readInt();
- callbackType = in.readInt();
- reportDelayMillis = in.readLong();
- matchMode = in.readInt();
- numOfMatchesPerFilter = in.readInt();
- legacy = in.readInt() != 0;
- phy = in.readInt();
- useHardwareFilteringIfSupported = in.readInt() == 1;
- useHardwareBatchingIfSupported = in.readInt() == 1;
- powerSaveScanInterval = in.readLong();
- powerSaveRestInterval = in.readLong();
- }
-
- @Override
- public void writeToParcel(final Parcel dest, final int flags) {
- dest.writeInt(scanMode);
- dest.writeInt(callbackType);
- dest.writeLong(reportDelayMillis);
- dest.writeInt(matchMode);
- dest.writeInt(numOfMatchesPerFilter);
- dest.writeInt(legacy ? 1 : 0);
- dest.writeInt(phy);
- dest.writeInt(useHardwareFilteringIfSupported ? 1 : 0);
- dest.writeInt(useHardwareBatchingIfSupported ? 1 : 0);
- dest.writeLong(powerSaveScanInterval);
- dest.writeLong(powerSaveRestInterval);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- public static final Parcelable.Creator CREATOR = new Creator() {
- @Override
- public ScanSettings[] newArray(final int size) {
- return new ScanSettings[size];
- }
-
- @Override
- public ScanSettings createFromParcel(final Parcel in) {
- return new ScanSettings(in);
- }
- };
-
- /**
- * Determine if we should do power-saving sleep on pre-Lollipop
- */
- public boolean hasPowerSaveMode() {
- return powerSaveRestInterval > 0 && powerSaveScanInterval > 0;
- }
-
- public long getPowerSaveRest() {
- return powerSaveRestInterval;
- }
-
- public long getPowerSaveScan() {
- return powerSaveScanInterval;
- }
-
- /**
- * Builder for {@link ScanSettings}.
- */
- @SuppressWarnings({"UnusedReturnValue", "unused"})
- public static final class Builder {
- private int scanMode = SCAN_MODE_LOW_POWER;
- private int callbackType = CALLBACK_TYPE_ALL_MATCHES;
- private long reportDelayMillis = 0;
- private int matchMode = MATCH_MODE_AGGRESSIVE;
- private int numOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT;
- private boolean legacy = true;
- private int phy = PHY_LE_ALL_SUPPORTED;
- private boolean useHardwareFilteringIfSupported = true;
- private boolean useHardwareBatchingIfSupported = true;
- private boolean useHardwareCallbackTypesIfSupported = true;
- private long matchLostDeviceTimeout = MATCH_LOST_DEVICE_TIMEOUT_DEFAULT;
- private long matchLostTaskInterval = MATCH_LOST_TASK_INTERVAL_DEFAULT;
- private long powerSaveRestInterval = 0;
- private long powerSaveScanInterval = 0;
-
- /**
- * Set scan mode for Bluetooth LE scan.
- *
- * {@link #SCAN_MODE_OPPORTUNISTIC} is supported on Android Marshmallow onwards.
- * On Lollipop this mode will fall back {@link #SCAN_MODE_LOW_POWER}, which actually means
- * that the library will start its own scan instead of relying on scans from other apps.
- * This may have significant impact on battery usage.
- *
- * On pre-Lollipop devices, the settings set by {@link #setPowerSave(long, long)}
- * will be used. By default, the intervals are the same as for {@link #SCAN_MODE_LOW_POWER}.
- *
- * @param scanMode The scan mode can be one of {@link ScanSettings#SCAN_MODE_LOW_POWER},
- * {@link #SCAN_MODE_BALANCED},
- * {@link #SCAN_MODE_LOW_LATENCY} or
- * {@link #SCAN_MODE_OPPORTUNISTIC}.
- * @throws IllegalArgumentException If the {@code scanMode} is invalid.
- */
- @NonNull
- public Builder setScanMode(final int scanMode) {
- if (scanMode < SCAN_MODE_OPPORTUNISTIC || scanMode > SCAN_MODE_LOW_LATENCY) {
- throw new IllegalArgumentException("invalid scan mode " + scanMode);
- }
- this.scanMode = scanMode;
- return this;
- }
-
- /**
- * Set callback type for Bluetooth LE scan.
- *
- * @param callbackType The callback type flags for the scan.
- * @throws IllegalArgumentException If the {@code callbackType} is invalid.
- */
- @NonNull
- public Builder setCallbackType(final int callbackType) {
- if (!isValidCallbackType(callbackType)) {
- throw new IllegalArgumentException("invalid callback type - " + callbackType);
- }
- this.callbackType = callbackType;
- return this;
- }
-
- // Returns true if the callbackType is valid.
- private boolean isValidCallbackType(final int callbackType) {
- if (callbackType == CALLBACK_TYPE_ALL_MATCHES ||
- callbackType == CALLBACK_TYPE_FIRST_MATCH ||
- callbackType == CALLBACK_TYPE_MATCH_LOST) {
- return true;
- }
- return callbackType == (CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST);
- }
-
- /**
- * Set report delay timestamp for Bluetooth LE scan.
- *
- * @param reportDelayMillis Delay of report in milliseconds. Set to 0 to be notified of
- * results immediately. Values > 0 causes the scan results
- * to be queued up and delivered after the requested delay or
- * when the internal buffers fill up.
- * For delays below 5000 ms (5 sec) the
- * {@link ScanCallback#onBatchScanResults(List)}
- * will be called in unreliable intervals, but starting from
- * around 5000 the intervals get even.
- * @throws IllegalArgumentException If {@code reportDelayMillis} < 0.
- */
- @NonNull
- public Builder setReportDelay(final long reportDelayMillis) {
- if (reportDelayMillis < 0) {
- throw new IllegalArgumentException("reportDelay must be > 0");
- }
- this.reportDelayMillis = reportDelayMillis;
- return this;
- }
-
- /**
- * Set the number of matches for Bluetooth LE scan filters hardware match.
- *
- * @param numOfMatches The num of matches can be one of
- * {@link ScanSettings#MATCH_NUM_ONE_ADVERTISEMENT} or
- * {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or
- * {@link ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT}
- * @throws IllegalArgumentException If the {@code matchMode} is invalid.
- */
- @NonNull
- public Builder setNumOfMatches(final int numOfMatches) {
- if (numOfMatches < MATCH_NUM_ONE_ADVERTISEMENT
- || numOfMatches > MATCH_NUM_MAX_ADVERTISEMENT) {
- throw new IllegalArgumentException("invalid numOfMatches " + numOfMatches);
- }
- numOfMatchesPerFilter = numOfMatches;
- return this;
- }
-
- /**
- * Set match mode for Bluetooth LE scan filters hardware match
- *
- * @param matchMode The match mode can be one of
- * {@link ScanSettings#MATCH_MODE_AGGRESSIVE} or
- * {@link ScanSettings#MATCH_MODE_STICKY}
- * @throws IllegalArgumentException If the {@code matchMode} is invalid.
- */
- @NonNull
- public Builder setMatchMode(final int matchMode) {
- if (matchMode < MATCH_MODE_AGGRESSIVE
- || matchMode > MATCH_MODE_STICKY) {
- throw new IllegalArgumentException("invalid matchMode " + matchMode);
- }
- this.matchMode = matchMode;
- return this;
- }
-
- /**
- * Set whether only legacy advertisements should be returned in scan results.
- * Legacy advertisements include advertisements as specified by the
- * Bluetooth core specification 4.2 and below. This is true by default
- * for compatibility with older apps.
- *
- * @param legacy true if only legacy advertisements will be returned
- */
- @NonNull
- public Builder setLegacy(final boolean legacy) {
- this.legacy = legacy;
- return this;
- }
-
- /**
- * Set the Physical Layer to use during this scan.
- * This is used only if {@link ScanSettings.Builder#setLegacy}
- * is set to false and only on Android 0reo or newer.
- * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}
- * may be used to check whether LE Coded phy is supported by calling
- * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}.
- * Selecting an unsupported phy will result in failure to start scan.
- *
- * @param phy Can be one of
- * {@link BluetoothDevice#PHY_LE_1M},
- * {@link BluetoothDevice#PHY_LE_CODED} or
- * {@link ScanSettings#PHY_LE_ALL_SUPPORTED}
- */
- @NonNull
- public Builder setPhy(final int phy) {
- this.phy = phy;
- return this;
- }
-
- /**
- * Several phones may have some issues when it comes to offloaded filtering.
- * Even if it should be supported, it may not work as expected.
- * It has been observed for example, that setting 2 filters with different devices
- * addresses on Nexus 6 with Lollipop gives no callbacks if one or both devices advertise.
- * See https://code.google.com/p/android/issues/detail?id=181561.
- *
- * @param use true to enable (default) hardware offload filtering.
- * If false a compat software filtering will be used
- * (uses much more resources).
- */
- @NonNull
- public Builder setUseHardwareFilteringIfSupported(final boolean use) {
- useHardwareFilteringIfSupported = use;
- return this;
- }
-
- /**
- * Some devices, for example Samsung S6 and S6 Edge with Lollipop, return always
- * the same RSSI value for all devices if offloaded batching is used.
- * Batching may also be emulated using a compat mechanism - a periodically called timer.
- * Timer approach requires more resources but reports devices in constant delays
- * and works on devices that does not support offloaded batching.
- * In comparison, when setReportDelay(..) is called with parameter 1000 the standard,
- * hardware triggered callback will be called every 1500ms +-200ms.
- *
- * @param use true to enable (default) hardware offloaded batching if they are supported.
- * False to always use compat mechanism.
- */
- @NonNull
- public Builder setUseHardwareBatchingIfSupported(final boolean use) {
- useHardwareBatchingIfSupported = use;
- return this;
- }
-
- /**
- * This method may be used when callback type is set to a value different than
- * {@link #CALLBACK_TYPE_ALL_MATCHES}. When disabled, the Scanner Compat itself will
- * take care of reporting first match and match lost. The compat behaviour may differ
- * from the one natively supported on Android Marshmallow or newer.
- *
- * Also, in compat mode values set by {@link #setMatchMode(int)} and
- * {@link #setNumOfMatches(int)} are ignored.
- * Instead use {@link #setMatchOptions(long, long)} to set timer options.
- *
- * @param use true to enable (default) the offloaded match reporting if hardware supports it,
- * false to enable compat implementation.
- */
- @NonNull
- public Builder setUseHardwareCallbackTypesIfSupported(final boolean use) {
- useHardwareCallbackTypesIfSupported = use;
- return this;
- }
-
- /**
- * The match options are used when the callback type has been set to
- * {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} or
- * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST} and hardware does not support those types.
- * In that case {@link BluetoothLeScannerCompat} starts a task that runs periodically
- * and calls {@link ScanCallback#onScanResult(int, ScanResult)} with type
- * {@link #CALLBACK_TYPE_MATCH_LOST} if a device has not been seen for at least given time.
- *
- * @param deviceTimeoutMillis the time required for the device to be recognized as lost
- * (default {@link #MATCH_LOST_DEVICE_TIMEOUT_DEFAULT}).
- * @param taskIntervalMillis the task interval (default {@link #MATCH_LOST_TASK_INTERVAL_DEFAULT}).
- */
- @NonNull
- public Builder setMatchOptions(final long deviceTimeoutMillis, final long taskIntervalMillis) {
- if (deviceTimeoutMillis <= 0 || taskIntervalMillis <= 0) {
- throw new IllegalArgumentException("maxDeviceAgeMillis and taskIntervalMillis must be > 0");
- }
- matchLostDeviceTimeout = deviceTimeoutMillis;
- matchLostTaskInterval = taskIntervalMillis;
- return this;
- }
-
- /**
- * Pre-Lollipop scanning requires a wakelock and the CPU cannot go to sleep.
- * To conserve power we can optionally scan for a certain duration (scan interval)
- * and then rest for a time before starting scanning again. Won't affect Lollipop
- * or later devices.
- *
- * @param scanInterval interval in ms to scan at a time.
- * @param restInterval interval to sleep for without scanning before scanning again for
- * scanInterval.
- */
- @NonNull
- public Builder setPowerSave(final long scanInterval, final long restInterval) {
- if (scanInterval <= 0 || restInterval <= 0) {
- throw new IllegalArgumentException("scanInterval and restInterval must be > 0");
- }
- powerSaveScanInterval = scanInterval;
- powerSaveRestInterval = restInterval;
- return this;
- }
-
- /**
- * Build {@link ScanSettings}.
- */
- @NonNull
- public ScanSettings build() {
- if (powerSaveRestInterval == 0 && powerSaveScanInterval == 0)
- updatePowerSaveSettings();
-
- return new ScanSettings(scanMode, callbackType, reportDelayMillis, matchMode,
- numOfMatchesPerFilter, legacy, phy, useHardwareFilteringIfSupported,
- useHardwareBatchingIfSupported, useHardwareCallbackTypesIfSupported,
- matchLostDeviceTimeout, matchLostTaskInterval,
- powerSaveScanInterval, powerSaveRestInterval);
- }
-
- /**
- * Sets power save settings based on the scan mode selected.
- */
- private void updatePowerSaveSettings() {
- switch (scanMode) {
- case SCAN_MODE_LOW_LATENCY:
- // Disable power save mode
- powerSaveScanInterval = 0;
- powerSaveRestInterval = 0;
- break;
- case SCAN_MODE_BALANCED:
- // Scan for 2 seconds every 5 seconds
- powerSaveScanInterval = 2000;
- powerSaveRestInterval = 3000;
- break;
- case SCAN_MODE_OPPORTUNISTIC:
- // It is not possible to emulate OPPORTUNISTIC scanning, but in theory
- // that should be even less battery consuming than LOW_POWER.
- // For pre-Lollipop devices intervals can be overwritten by
- // setPowerSave(long, long) if needed.
-
- // On Android Lollipop the native SCAN_MODE_LOW_POWER will be used instead
- // of power save values.
- case SCAN_MODE_LOW_POWER:
- default:
- // Scan for 0.5 second every 5 seconds
- powerSaveScanInterval = 500;
- powerSaveRestInterval = 4500;
- break;
- }
- }
- }
+public final class ScanSettings implements Serializable {
+ public static final long MATCH_LOST_DEVICE_TIMEOUT_DEFAULT = 10000L; // [ms]
+ public static final long MATCH_LOST_TASK_INTERVAL_DEFAULT = 10000L; // [ms]
+ public static final int SCAN_MODE_OPPORTUNISTIC = -1;
+ public static final int SCAN_MODE_LOW_POWER = 0;
+ public static final int SCAN_MODE_BALANCED = 1;
+ public static final int SCAN_MODE_LOW_LATENCY = 2;
+ public static final int CALLBACK_TYPE_ALL_MATCHES = 1;
+ public static final int CALLBACK_TYPE_FIRST_MATCH = 2;
+ public static final int CALLBACK_TYPE_MATCH_LOST = 4;
+ public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1;
+ public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2;
+ public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3;
+ public static final int MATCH_MODE_AGGRESSIVE = 1;
+ public static final int MATCH_MODE_STICKY = 2;
+ public static final int PHY_LE_ALL_SUPPORTED = 255;
+ private final long powerSaveScanInterval;
+ private final long powerSaveRestInterval;
+ private int scanMode;
+ private int callbackType;
+ private long reportDelayMillis;
+ private int matchMode;
+ private int numOfMatchesPerFilter;
+ private boolean isuseHardwareFilteringIfSupported;
+ private boolean isuseHardwareBatchingIfSupported;
+ private boolean isuseHardwareCallbackTypesIfSupported;
+ private long matchLostDeviceTimeout;
+ private long matchLostTaskInterval;
+ private boolean legacy;
+ private int phy;
+
+ public int getScanMode() {
+ return scanMode;
+ }
+
+ public int getCallbackType() {
+ return callbackType;
+ }
+
+ public int getMatchMode() {
+ return matchMode;
+ }
+
+ public int getNumOfMatches() {
+ return numOfMatchesPerFilter;
+ }
+
+ public boolean getUseHardwareFilteringIfSupported() {
+ return isuseHardwareFilteringIfSupported;
+ }
+
+ public boolean getUseHardwareBatchingIfSupported() {
+ return isuseHardwareBatchingIfSupported;
+ }
+
+ public boolean getUseHardwareCallbackTypesIfSupported() {
+ return isuseHardwareCallbackTypesIfSupported;
+ }
+ /**
+ * Some devices with Android Marshmallow (Nexus 6) theoretically support other callback types,
+ * but call {@link ScanCallback#onScanFailed(int)} with error = 5.
+ * In that case the Scanner Compat will disable the hardware support and start using compat
+ * mechanism.
+ */
+ /* package */
+ void disableUseHardwareCallbackTypes() {
+ isuseHardwareCallbackTypesIfSupported = false;
+ }
+
+ public long getMatchLostDeviceTimeout() {
+ return matchLostDeviceTimeout;
+ }
+
+ public long getMatchLostTaskInterval() {
+ return matchLostTaskInterval;
+ }
+ /**
+ * Returns whether only legacy advertisements will be returned.
+ * Legacy advertisements include advertisements as specified
+ * by the Bluetooth core specification 4.2 and below.
+ */
+ public boolean getLegacy() {
+ return legacy;
+ }
+ /**
+ * Returns the physical layer used during a scan.
+ */
+ public int getPhy() {
+ return phy;
+ }
+
+ /**
+ * Returns report delay timestamp based on the device clock.
+ */
+ public long getReportDelayMillis() {
+ return reportDelayMillis;
+ }
+
+ private ScanSettings(final int scanMode, final int callbackType,
+ final long reportDelayMillis, final int matchMode,
+ final int numOfMatchesPerFilter, final boolean legacy, final int phy,
+ final boolean hardwareFiltering, final boolean hardwareBatching,
+ final boolean hardwareCallbackTypes, final long matchTimeout,
+ final long taskInterval,
+ final long powerSaveScanInterval, final long powerSaveRestInterval) {
+ this.scanMode = scanMode;
+ this.callbackType = callbackType;
+ this.reportDelayMillis = reportDelayMillis;
+ this.numOfMatchesPerFilter = numOfMatchesPerFilter;
+ this.matchMode = matchMode;
+ this.legacy = legacy;
+ this.phy = phy;
+ this.isuseHardwareFilteringIfSupported = hardwareFiltering;
+ this.isuseHardwareCallbackTypesIfSupported = hardwareBatching;
+ this.isuseHardwareCallbackTypesIfSupported = hardwareCallbackTypes;
+ this.matchLostDeviceTimeout = matchTimeout * 1000000L; // convert to nanos
+ this.matchLostTaskInterval = taskInterval;
+ this.powerSaveScanInterval = powerSaveScanInterval;
+ this.powerSaveRestInterval = powerSaveRestInterval;
+ }
+
+ private ScanSettings(final Parcel in) {
+ scanMode = in.readInt();
+ callbackType = in.readInt();
+ reportDelayMillis = in.readLong();
+ matchMode = in.readInt();
+ numOfMatchesPerFilter = in.readInt();
+ legacy = in.readInt() != 0;
+ phy = in.readInt();
+ isuseHardwareFilteringIfSupported = in.readInt() == 1;
+ isuseHardwareBatchingIfSupported = in.readInt() == 1;
+ powerSaveScanInterval = in.readLong();
+ powerSaveRestInterval = in.readLong();
+ }
+
+
+ public void writeToParcel(final Parcel dest, final int flags) {
+ dest.writeInt(scanMode);
+ dest.writeInt(callbackType);
+ dest.writeLong(reportDelayMillis);
+ dest.writeInt(matchMode);
+ dest.writeInt(numOfMatchesPerFilter);
+ dest.writeInt(legacy ? 1 : 0);
+ dest.writeInt(phy);
+ dest.writeInt(isuseHardwareFilteringIfSupported ? 1 : 0);
+ dest.writeInt(isuseHardwareBatchingIfSupported ? 1 : 0);
+ dest.writeLong(powerSaveScanInterval);
+ dest.writeLong(powerSaveRestInterval);
+ }
+
+
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Determine if we should do power-saving sleep on pre-Lollipop
+ */
+ public boolean hasPowerSaveMode() {
+ return powerSaveRestInterval > 0 && powerSaveScanInterval > 0;
+ }
+
+ public long getPowerSaveRest() {
+ return powerSaveRestInterval;
+ }
+
+ public long getPowerSaveScan() {
+ return powerSaveScanInterval;
+ }
+ /**
+ * Builder for {@link ScanSettings}.
+ */
+ public static final class Builder {
+ private int scanMode = SCAN_MODE_LOW_POWER;
+ private int callbackType = CALLBACK_TYPE_ALL_MATCHES;
+ private long reportDelayMillis = 0;
+ private int matchMode = MATCH_MODE_AGGRESSIVE;
+ private int numOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT;
+ private boolean legacy = true;
+ private int phy = PHY_LE_ALL_SUPPORTED;
+ private boolean isuseHardwareFilteringIfSupported = true;
+ private boolean isuseHardwareBatchingIfSupported = true;
+ private boolean useHardwareCallbackTypesIfSupported = true;
+ private long matchLostDeviceTimeout = MATCH_LOST_DEVICE_TIMEOUT_DEFAULT;
+ private long matchLostTaskInterval = MATCH_LOST_TASK_INTERVAL_DEFAULT;
+ private long powerSaveRestInterval = 0;
+ private long powerSaveScanInterval = 0;
+
+ /**
+ * Set scan mode for Bluetooth LE scan.
+ *
+ * {@link #SCAN_MODE_OPPORTUNISTIC} is supported on Android Marshmallow onwards.
+ * On Lollipop this mode will fall back {@link #SCAN_MODE_LOW_POWER}, which actually means
+ * that the library will start its own scan instead of relying on scans from other apps.
+ * This may have significant impact on battery usage.
+ *
+ * On pre-Lollipop devices, the settings set by {@link #setPowerSave(long, long)}
+ * will be used. By default, the intervals are the same as for {@link #SCAN_MODE_LOW_POWER}.
+ *
+ * @param scanMode The scan mode can be one of {@link ScanSettings#SCAN_MODE_LOW_POWER},
+ * {@link #SCAN_MODE_BALANCED},
+ * {@link #SCAN_MODE_LOW_LATENCY} or
+ * {@link #SCAN_MODE_OPPORTUNISTIC}.
+ * @throws IllegalArgumentException If the {@code scanMode} is invalid.
+ */
+
+ public Builder setScanMode(final int scanMode) {
+ if (scanMode < SCAN_MODE_OPPORTUNISTIC || scanMode > SCAN_MODE_LOW_LATENCY) {
+ throw new IllegalArgumentException("invalid scan mode " + scanMode);
+ }
+ this.scanMode = scanMode;
+ return this;
+ }
+
+ /**
+ * Set callback type for Bluetooth LE scan.
+ *
+ * @param callbackType The callback type flags for the scan.
+ * @throws IllegalArgumentException If the {@code callbackType} is invalid.
+ */
+
+ public Builder setCallbackType(final int callbackType) {
+ if (!isValidCallbackType(callbackType)) {
+ throw new IllegalArgumentException("invalid callback type - " + callbackType);
+ }
+ this.callbackType = callbackType;
+ return this;
+ }
+ private boolean isValidCallbackType(final int callbackType) {
+ if (callbackType == CALLBACK_TYPE_ALL_MATCHES ||
+ callbackType == CALLBACK_TYPE_FIRST_MATCH ||
+ callbackType == CALLBACK_TYPE_MATCH_LOST) {
+ return true;
+ }
+ return callbackType == (CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST);
+ }
+
+ /**
+ * Set report delay timestamp for Bluetooth LE scan.
+ *
+ * @param reportDelayMillis Delay of report in milliseconds. Set to 0 to be notified of
+ * results immediately. Values > 0 causes the scan results
+ * to be queued up and delivered after the requested delay or
+ * when the internal buffers fill up.
+ * For delays below 5000 ms (5 sec) the
+ * {@link ScanCallback#onBatchScanResults(List)}
+ * will be called in unreliable intervals, but starting from
+ * around 5000 the intervals get even.
+ * @throws IllegalArgumentException If {@code reportDelayMillis} < 0.
+ */
+ public Builder setReportDelay(final long reportDelayMillis) {
+ if (reportDelayMillis < 0) {
+ throw new IllegalArgumentException("reportDelay must be > 0");
+ }
+ this.reportDelayMillis = reportDelayMillis;
+ return this;
+ }
+
+ /**
+ * Set the number of matches for Bluetooth LE scan filters hardware match.
+ *
+ * @param numOfMatches The num of matches can be one of
+ * {@link ScanSettings#MATCH_NUM_ONE_ADVERTISEMENT} or
+ * {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or
+ * {@link ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT}
+ * @throws IllegalArgumentException If the {@code matchMode} is invalid.
+ */
+
+ public Builder setNumOfMatches(final int numOfMatches) {
+ if (numOfMatches < MATCH_NUM_ONE_ADVERTISEMENT
+ || numOfMatches > MATCH_NUM_MAX_ADVERTISEMENT) {
+ throw new IllegalArgumentException("invalid numOfMatches " + numOfMatches);
+ }
+ numOfMatchesPerFilter = numOfMatches;
+ return this;
+ }
+
+ /**
+ * Set match mode for Bluetooth LE scan filters hardware match
+ *
+ * @param matchMode The match mode can be one of
+ * {@link ScanSettings#MATCH_MODE_AGGRESSIVE} or
+ * {@link ScanSettings#MATCH_MODE_STICKY}
+ * @throws IllegalArgumentException If the {@code matchMode} is invalid.
+ */
+
+ public Builder setMatchMode(final int matchMode) {
+ if (matchMode < MATCH_MODE_AGGRESSIVE
+ || matchMode > MATCH_MODE_STICKY) {
+ throw new IllegalArgumentException("invalid matchMode " + matchMode);
+ }
+ this.matchMode = matchMode;
+ return this;
+ }
+
+ /**
+ * Set whether only legacy advertisements should be returned in scan results.
+ * Legacy advertisements include advertisements as specified by the
+ * Bluetooth core specification 4.2 and below. This is true by default
+ * for compatibility with older apps.
+ *
+ * @param legacy true if only legacy advertisements will be returned
+ */
+ public Builder setLegacy(final boolean legacy) {
+ this.legacy = legacy;
+ return this;
+ }
+
+ /**
+ * Set the Physical Layer to use during this scan.
+ * This is used only if {@link Builder#setLegacy}
+ * is set to false and only on Android 0reo or newer.
+ * may be used to check whether LE Coded phy is supported by calling
+ * Selecting an unsupported phy will result in failure to start scan.
+ */
+
+ public Builder setPhy(final int phy) {
+ this.phy = phy;
+ return this;
+ }
+
+ /**
+ * Several phones may have some issues when it comes to offloaded filtering.
+ * Even if it should be supported, it may not work as expected.
+ * It has been observed for example, that setting 2 filters with different devices
+ * addresses on Nexus 6 with Lollipop gives no callbacks if one or both devices advertise.
+ * See https://code.google.com/p/android/issues/detail?id=181561.
+ *
+ * @param use true to enable (default) hardware offload filtering.
+ * If false a compat software filtering will be used
+ * (uses much more resources).
+ */
+
+ public Builder setUseHardwareFilteringIfSupported(final boolean use) {
+ isuseHardwareFilteringIfSupported = use;
+ return this;
+ }
+
+ /**
+ * Some devices, for example Samsung S6 and S6 Edge with Lollipop, return always
+ * the same RSSI value for all devices if offloaded batching is used.
+ * Batching may also be emulated using a compat mechanism - a periodically called timer.
+ * Timer approach requires more resources but reports devices in constant delays
+ * and works on devices that does not support offloaded batching.
+ * In comparison, when setReportDelay(..) is called with parameter 1000 the standard,
+ * hardware triggered callback will be called every 1500ms +-200ms.
+ *
+ * @param use true to enable (default) hardware offloaded batching if they are supported.
+ * False to always use compat mechanism.
+ */
+
+ public Builder setUseHardwareBatchingIfSupported(final boolean use) {
+ isuseHardwareBatchingIfSupported = use;
+ return this;
+ }
+
+ /**
+ * This method may be used when callback type is set to a value different than
+ * {@link #CALLBACK_TYPE_ALL_MATCHES}. When disabled, the Scanner Compat itself will
+ * take care of reporting first match and match lost. The compat behaviour may differ
+ * from the one natively supported on Android Marshmallow or newer.
+ *
+ * Also, in compat mode values set by {@link #setMatchMode(int)} and
+ * {@link #setNumOfMatches(int)} are ignored.
+ * Instead use {@link #setMatchOptions(long, long)} to set timer options.
+ *
+ * @param use true to enable (default) the offloaded match reporting if hardware supports it,
+ * false to enable compat implementation.
+ */
+
+ public Builder setUseHardwareCallbackTypesIfSupported(final boolean use) {
+ useHardwareCallbackTypesIfSupported = use;
+ return this;
+ }
+
+ /**
+ * The match options are used when the callback type has been set to
+ * {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} or
+ * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST} and hardware does not support those types.
+ * In that case {@link BluetoothLeScannerCompat} starts a task that runs periodically
+ * {@link #CALLBACK_TYPE_MATCH_LOST} if a device has not been seen for at least given time.
+ *
+ * @param deviceTimeoutMillis the time required for the device to be recognized as lost
+ * (default {@link #MATCH_LOST_DEVICE_TIMEOUT_DEFAULT}).
+ * @param taskIntervalMillis the task interval (default {@link #MATCH_LOST_TASK_INTERVAL_DEFAULT}).
+ */
+
+ public Builder setMatchOptions(final long deviceTimeoutMillis, final long taskIntervalMillis) {
+ if (deviceTimeoutMillis <= 0 || taskIntervalMillis <= 0) {
+ throw new IllegalArgumentException("maxDeviceAgeMillis and taskIntervalMillis must be > 0");
+ }
+ matchLostDeviceTimeout = deviceTimeoutMillis;
+ matchLostTaskInterval = taskIntervalMillis;
+ return this;
+ }
+
+ public Builder setPowerSave(final long scanInterval, final long restInterval) {
+ if (scanInterval <= 0 || restInterval <= 0) {
+ throw new IllegalArgumentException("scanInterval and restInterval must be > 0");
+ }
+ powerSaveScanInterval = scanInterval;
+ powerSaveRestInterval = restInterval;
+ return this;
+ }
+
+ public ScanSettings build() {
+ if (powerSaveRestInterval == 0 && powerSaveScanInterval == 0)
+ updatePowerSaveSettings();
+
+ return new ScanSettings(scanMode, callbackType, reportDelayMillis, matchMode,
+ numOfMatchesPerFilter, legacy, phy, isuseHardwareFilteringIfSupported,
+ isuseHardwareBatchingIfSupported, useHardwareCallbackTypesIfSupported,
+ matchLostDeviceTimeout, matchLostTaskInterval,
+ powerSaveScanInterval, powerSaveRestInterval);
+ }
+
+ /**
+ * Sets power save settings based on the scan mode selected.
+ */
+ private void updatePowerSaveSettings() {
+ switch (scanMode) {
+ case SCAN_MODE_LOW_LATENCY:
+ powerSaveScanInterval = 0;
+ powerSaveRestInterval = 0;
+ break;
+ case SCAN_MODE_BALANCED:
+ powerSaveScanInterval = 2000;
+ powerSaveRestInterval = 3000;
+ break;
+ case SCAN_MODE_OPPORTUNISTIC:
+ case SCAN_MODE_LOW_POWER:
+ default:
+ powerSaveScanInterval = 500;
+ powerSaveRestInterval = 4500;
+ break;
+ }
+ }
+ }
}
diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScannerService.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScannerService.java
index d33034fb27c45ea1e70d88a12fa63ae305be1868..d4997bb21f338a881178dc70c0852cff11b7c623 100644
--- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScannerService.java
+++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScannerService.java
@@ -1,149 +1,95 @@
package no.nordicsemi.android.support.v18.scanner;
-import android.Manifest;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.util.Log;
+import ohos.aafwk.ability.Ability;
+import ohos.aafwk.content.Intent;
+import ohos.app.Context;
+import ohos.bluetooth.ble.BleScanFilter;
+import ohos.event.intentagent.IntentAgent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresPermission;
+import static no.nordicsemi.android.support.v18.scanner.PendingIntentReceiver.EXTRA_PENDING_INTENT;
-/**
- * A service that will emulate
- * {@link android.bluetooth.le.BluetoothLeScanner#startScan(List, android.bluetooth.le.ScanSettings, PendingIntent)}
- * on Android versions before Oreo.
- *
- * To start the service call
- * {@link BluetoothLeScannerCompat#startScan(List, ScanSettings, Context, PendingIntent)}.
- * It will be stopped automatically when the last scan has been stopped using
- * {@link BluetoothLeScannerCompat#stopScan(Context, PendingIntent)}.
- *
- * As this service will run and scan in background it is recommended to use
- * {@link ScanSettings#SCAN_MODE_LOW_POWER} mode and set filter to lower power consumption.
- */
-public class ScannerService extends Service {
- private static final String TAG = "ScannerService";
+public class ScannerService extends Ability {
+ /* package */ static final String EXTRA_PENDING_INTENT = "ohos.permission.GET_NETWORK_INFO";
+ /* package */ static final String EXTRA_FILTERS = "EXTRA_FILTERS";
+ /* package */ static final String EXTRA_SETTINGS = "EXTRA_SETTINGS";
+ /* package */ static final String EXTRA_START = "EXTRA_START";
- /* package */ static final String EXTRA_PENDING_INTENT = "no.nordicsemi.android.support.v18.EXTRA_PENDING_INTENT";
- /* package */ static final String EXTRA_FILTERS = "no.nordicsemi.android.support.v18.EXTRA_FILTERS";
- /* package */ static final String EXTRA_SETTINGS = "no.nordicsemi.android.support.v18.EXTRA_SETTINGS";
- /* package */ static final String EXTRA_START = "no.nordicsemi.android.support.v18.EXTRA_START";
-
- @NonNull private final Object LOCK = new Object();
-
- private HashMap callbacks;
- private Handler handler;
+ private HashMap callbacks;
+ Handler handler;
+ private final Object LOCK = new Object();
+ Context context = getContext();
@Override
- public void onCreate() {
- super.onCreate();
+ protected void onStart(Intent intent) {
+ super.onStart(intent);
callbacks = new HashMap<>();
- handler = new Handler();
+ handler = new Handler() {
+
+ @Override
+ public void publish(LogRecord logRecord) {
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void close() throws SecurityException {
+ }
+ };
}
@Override
- @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH})
- public int onStartCommand(final Intent intent, final int flags, final int startId) {
- final PendingIntent callbackIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
- final boolean start = intent.getBooleanExtra(EXTRA_START, false);
+ public void onCommand(final Intent intent,final boolean restart,final int startId) {
+ super.onCommand(intent, restart, startId);
+ final IntentAgent callbackIntent = intent.getSequenceableParam(EXTRA_PENDING_INTENT);
+ final boolean start = intent.getBooleanParam(EXTRA_START, false);
final boolean stop = !start;
-
if (callbackIntent == null) {
boolean shouldStop;
synchronized (LOCK) {
shouldStop = callbacks.isEmpty();
}
if (shouldStop)
- stopSelf();
- return START_NOT_STICKY;
+ stopScan(callbackIntent);
}
-
boolean knownCallback;
synchronized (LOCK) {
knownCallback = callbacks.containsKey(callbackIntent);
}
if (start && !knownCallback) {
- final ArrayList filters = intent.getParcelableArrayListExtra(EXTRA_FILTERS);
- final ScanSettings settings = intent.getParcelableExtra(EXTRA_SETTINGS);
- startScan(filters != null ? filters : Collections.emptyList(),
- settings != null ? settings : new ScanSettings.Builder().build(),
+ final ArrayList filters = intent.getSequenceableArrayListParam(EXTRA_FILTERS);
+ final ScanSettings settings = intent.getSequenceableParam(EXTRA_SETTINGS);
+ startScan(filters != null ? filters : Collections.emptyList(),
+ settings != null ? settings:new ScanSettings.Builder().build(),
callbackIntent);
} else if (stop && knownCallback) {
stopScan(callbackIntent);
}
-
- return START_NOT_STICKY;
- }
-
- @Nullable
- @Override
- public IBinder onBind(final Intent intent) {
- // Forbid binding
- return null;
- }
-
- @Override
- public void onTaskRemoved(final Intent rootIntent) {
- super.onTaskRemoved(rootIntent);
- // Stopping self here would cause the service to be killed when user removes the task
- // from Recents. This is not the behavior found in Oreo+.
- // Related issue: https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library/issues/45
-
- // Even with this line removed, the service will stop receiving devices when the phone
- // enters Doze mode.
- // Find out more here: https://developer.android.com/training/monitoring-device-state/doze-standby
-
- // stopSelf();
- }
-
- @Override
- @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH})
- public void onDestroy() {
- final BluetoothLeScannerCompat scannerCompat = BluetoothLeScannerCompat.getScanner();
- for (final ScanCallback callback : callbacks.values()) {
- try {
- scannerCompat.stopScan(callback);
- } catch (final Exception e) {
- // Ignore
- }
- }
- callbacks.clear();
- callbacks = null;
- handler = null;
- super.onDestroy();
}
- @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH})
- private void startScan(@NonNull final List filters,
- @NonNull final ScanSettings settings,
- @NonNull final PendingIntent callbackIntent) {
+ private void startScan(List bleScanFilters, ScanSettings scanSettings, IntentAgent callbackIntent) {
final PendingIntentExecutor executor =
- new PendingIntentExecutor(callbackIntent, settings, this);
+ new PendingIntentExecutor(callbackIntent, scanSettings,this);
synchronized (LOCK) {
callbacks.put(callbackIntent, executor);
}
try {
final BluetoothLeScannerCompat scannerCompat = BluetoothLeScannerCompat.getScanner();
- scannerCompat.startScanInternal(filters, settings, executor, handler);
} catch (final Exception e) {
- Log.w(TAG, "Starting scanning failed", e);
}
}
- @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH})
- private void stopScan(@NonNull final PendingIntent callbackIntent) {
+ private void stopScan(final IntentAgent callbackIntent) {
ScanCallback callback;
boolean shouldStop;
synchronized (LOCK) {
@@ -152,15 +98,28 @@ public class ScannerService extends Service {
}
if (callback == null)
return;
-
try {
final BluetoothLeScannerCompat scannerCompat = BluetoothLeScannerCompat.getScanner();
scannerCompat.stopScan(callback);
} catch (final Exception e) {
- Log.w(TAG, "Stopping scanning failed", e);
}
-
if (shouldStop)
- stopSelf();
+ stopScan(callbackIntent);
+ }
+
+ @Override
+ protected void onStop() {
+ final BluetoothLeScannerCompat scannerCompat = BluetoothLeScannerCompat.getScanner();
+ for (final ScanCallback callback : callbacks.values()) {
+ try {
+ scannerCompat.stopScan(callback);
+ } catch (final Exception e) {
+ }
+ }
+ callbacks.clear();
+ callbacks = null;
+ handler = null;
+ super.onStop();
+ super.onStop();
}
}
diff --git a/scanner/src/main/resources/base/element/string.json b/scanner/src/main/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..e7b27905e9f2365c469bceecc7c8bb736c432186
--- /dev/null
+++ b/scanner/src/main/resources/base/element/string.json
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "app_name",
+ "value": "scanner"
+ }
+ ]
+}
diff --git a/settings.gradle b/settings.gradle
index ad5fbba4334aec0c2fa2db00e4561f6839c18828..fa0fffb0b00f6a153b568adda2fa0c8505b4cc0d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':scanner'
+include ':entry', ':scanner'