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/.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..94a25f7f4cb416c083d265558da75d457237d671
--- /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..d057e0a2faa0501e0cc7f343de8e7124151bdaa4
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,2 @@
+## 0.0.1-SNAPSHOT
+* 鍙戝竷beta鐗堟湰
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index dc4ed99c92792afc10c4d61ff43b3c5fc226a50f..fa3ac9af016a9a02ebf08f741a0dc6a12d9c2195 100644
--- a/LICENSE
+++ b/LICENSE
@@ -11,7 +11,7 @@ modification, are permitted provided that the following conditions are met:
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
-* Neither the name of Android-Scanner-Compat-Library nor the names of its
+* Neither the name of Scanner-Compat-Library nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
diff --git a/README.OPENSOURCE b/README.OPENSOURCE
new file mode 100644
index 0000000000000000000000000000000000000000..79684771198ab0de37d0cc873c40ed0d455a15ea
--- /dev/null
+++ b/README.OPENSOURCE
@@ -0,0 +1,10 @@
+[
+ {
+ "Name": " Scanner-Compat-Library ",
+ "License": " Apache License ",
+ "License File": " LICENSE ",
+ "Version Number": " 1.4.3 ",
+ "Upstream URL": " https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library ",
+ "Description": " 钃濈墮鐨勬搷浣 "
+ }
+]
\ No newline at end of file
diff --git a/README.md b/README.md
index ef3839a6e65472d346ee99e20ef53abf541f1691..47854df52eb36e88bb82fec29cdc2274f46a5ffb 100644
--- a/README.md
+++ b/README.md
@@ -1,61 +1,49 @@
-# Android BLE Scanner Compat library
+# Scanner-Compat-Library
-[  ](https://bintray.com/nordic/android/no.nordicsemi.android.support.v18%3Ascanner/_latestVersion)
-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
+- 鍩虹嚎鐗堟湰锛歍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 beta4涓嬮」鐩彲鐩存帴杩愯
+濡傛棤娉曡繍琛岋紝鍒犻櫎椤圭洰.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 +55,21 @@ 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:
-
-```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.
+CodeCheck浠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);
-```
+CloudTest浠g爜娴嬭瘯鏃犲紓甯
-**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
+褰撳墠鐗堟湰demo鍔熻兘涓庡畨鍗撳師缁勪欢鍩烘湰鏃犲樊寮
-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).
+#### 鐗堟湰杩唬
-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.
+- v0.0.1_SNAPSHOT
-## License
+#### License
-The Scanner Compat library is available under BSD 3-Clause license. See the LICENSE file for more info.
\ No newline at end of file
+ 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..4a1be10f8ded255b4c6cd7581cc1e45b8dc5abfc
--- /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": [
+ "phone"
+ ],
+ "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.support.v18.scanner.ScannerService",
+ "type": "service",
+ "visible": true
+ }
+ ],
+ "reqPermissions": [
+ {
+ "name": "ohos.permission.USE_BLUETOOTH",
+ "reason": "浣跨敤钃濈墮鏉冮檺",
+ "usedScene": {
+ "ability": [
+ "com.example.myapplication.slice.MainAbility",
+ "no.nordicsemi.support.v18.scanner.ScannerService"
+ ],
+ "when": "always"
+ }
+ },
+ {
+ "name": "ohos.permission.DISCOVER_BLUETOOTH",
+ "reason": "鎼滅储钃濈墮鏉冮檺",
+ "usedScene": {
+ "ability": [
+ "com.example.myapplication.slice.MainAbility",
+ "no.nordicsemi.support.v18.scanner.ScannerService"
+ ],
+ "when": "always"
+ }
+ },
+ {
+ "name": "ohos.permission.LOCATION",
+ "reason": "鍏佽鍓嶅彴鑾峰彇鏉冮檺",
+ "usedScene": {
+ "ability": [
+ "com.example.myapplication.slice.MainAbility",
+ "no.nordicsemi.support.v18.scanner.ScannerService"
+ ],
+ "when": "always"
+ }
+ },
+ {
+ "name": "ohos.permission.LOCATION_IN_BACKGROUND",
+ "reason": "鍏佽鍚庡彴鑾峰彇鏉冮檺",
+ "usedScene": {
+ "ability": [
+ "com.example.myapplication.slice.MainAbility",
+ "no.nordicsemi.support.v18.scanner.ScannerService"
+ ],
+ "when": "always"
+ }
+ },
+ {
+ "name": "ohos.permission.GET_NETWORK_INFO",
+ "reason": "鍏佽搴旂敤鑾峰彇鏁版嵁缃戠粶淇℃伅",
+ "usedScene": {
+ "ability": [
+ "com.example.myapplication.slice.MainAbility",
+ "no.nordicsemi.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..16611ff9740853937a3e7e37918c678e6459f1a1
--- /dev/null
+++ b/entry/src/main/java/com/example/myapplication/HistoryItemProvider.java
@@ -0,0 +1,71 @@
+package com.example.myapplication;
+
+import no.nordicsemi.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/slice/MainAbilitySlice.java b/entry/src/main/java/com/example/myapplication/slice/MainAbilitySlice.java
new file mode 100644
index 0000000000000000000000000000000000000000..69fd21170488d9173f33321e658e29ca748db336
--- /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.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..ef07208038e52047aba17828a0c494a292f4fe99
--- /dev/null
+++ b/scanner/src/main/config.json
@@ -0,0 +1,27 @@
+{
+ "app": {
+ "bundleName": "no.nordicsemi.support.v18.scanner",
+ "vendor": "example",
+ "version": {
+ "code": 1,
+ "name": "1.0"
+ },
+ "apiVersion": {
+ "compatible": 5,
+ "target": 5
+ }
+ },
+ "deviceConfig": {},
+ "module": {
+ "package": "no.nordicsemi.support.v18.scanner",
+ "abilities": [],
+ "deviceType": [
+ "phone"
+ ],
+ "distro": {
+ "deliveryWithInstall": true,
+ "moduleName": "scanner",
+ "moduleType": "har"
+ }
+ }
+}
\ No newline at end of file
diff --git a/scanner/src/main/java/no/nordicsemi/support/v18/scanner/BluetoothLeScannerCompat.java b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/BluetoothLeScannerCompat.java
new file mode 100644
index 0000000000000000000000000000000000000000..b9a75acf24c78269e72b25274f4263ff4b6d86ef
--- /dev/null
+++ b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/BluetoothLeScannerCompat.java
@@ -0,0 +1,402 @@
+package no.nordicsemi.support.v18.scanner;
+
+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
+ * application can scan for a particular type of Bluetooth LE devices using {@link ScanFilter}. It
+ * can also request different types of callbacks for delivering the result.
+ *
+ * Use {@link BluetoothLeScannerCompat#getScanner()} to get an instance of the scanner.
+ *
+ * Since version 1.3.0 of the Scanner Compar Library library,
+ * 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 ScanResult} to compat {@link ScanResult} and apply additional
+ * filtering.
+ *
+ * Note: Most of the scan methods here require
+ *
+ * @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 =
+ "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 = "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 = "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);
+ }
+
+ /**
+ * @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.
+ */
+ /*
+ * @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.
+ *
+ * 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.
+ * @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/support/v18/scanner/BluetoothLeUtils.java b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/BluetoothLeUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..10ed90112454b21a3e3165819cc7bca884906913
--- /dev/null
+++ b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/BluetoothLeUtils.java
@@ -0,0 +1,93 @@
+package no.nordicsemi.support.v18.scanner;
+
+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;
+
+/**
+ * Helper class for Bluetooth LE utils.
+ */
+/* package */
+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();
+ }
+
+ 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/support/v18/scanner/BluetoothScannerImplV5.java b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/BluetoothScannerImplV5.java
new file mode 100644
index 0000000000000000000000000000000000000000..5259470aff5f8d9a47982f9ec76cf450cf244787
--- /dev/null
+++ b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/BluetoothScannerImplV5.java
@@ -0,0 +1,220 @@
+package no.nordicsemi.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.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/support/v18/scanner/BluetoothUuid.java b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/BluetoothUuid.java
new file mode 100644
index 0000000000000000000000000000000000000000..be0669ebc296f2c030ad04d8b74f5dc009fff838
--- /dev/null
+++ b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/BluetoothUuid.java
@@ -0,0 +1,63 @@
+package no.nordicsemi.support.v18.scanner;
+
+import ohos.utils.SequenceUuid;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.UUID;
+
+/**
+ * Static helper methods and constants to decode the ParcelUuid of remote devices.
+ */
+/* 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/support/v18/scanner/Objects.java b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/Objects.java
new file mode 100644
index 0000000000000000000000000000000000000000..9eb1b37295a3b02f08d99ed29be225e6a3ba570f
--- /dev/null
+++ b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/Objects.java
@@ -0,0 +1,41 @@
+package no.nordicsemi.support.v18.scanner;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Arrays;
+
+/* package */ class Objects {
+ 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/support/v18/scanner/PendingIntentExecutor.java b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/PendingIntentExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b23bc48ed2ac158ed0dfc4ddff7a24efeefb5cc
--- /dev/null
+++ b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/PendingIntentExecutor.java
@@ -0,0 +1,76 @@
+package no.nordicsemi.support.v18.scanner;
+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;
+/* package */ class PendingIntentExecutor extends ScanCallback {
+ private final IntentAgent callbackIntent;
+
+ @Nullable
+ private Context context;
+
+ @Nullable
+ private Context service;
+ private long lastBatchTimestamp;
+ private long reportDelay;
+ 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;
+ 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/support/v18/scanner/PendingIntentReceiver.java b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/PendingIntentReceiver.java
new file mode 100644
index 0000000000000000000000000000000000000000..156ac88f7b5cec973b46aab6f4114ed6ff15a07d
--- /dev/null
+++ b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/PendingIntentReceiver.java
@@ -0,0 +1,70 @@
+package no.nordicsemi.support.v18.scanner;
+
+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;
+
+public class PendingIntentReceiver extends GeneralReceiver {
+ /* package */ static final String
+ ACTION = "no.nordicsemi.support.v18.ACTION_FOUND";
+ /* package */ static final String
+ EXTRA_PENDING_INTENT = "no.nordicsemi.support.v18.EXTRA_PENDING_INTENT";
+ /* package */ static final String
+ EXTRA_FILTERS = "no.nordicsemi.support.v18.EXTRA_FILTERS";
+ /* package */ static final String
+ EXTRA_SETTINGS = "no.nordicsemi.support.v18.EXTRA_SETTINGS";
+ /* package */ static final String
+ EXTRA_USE_HARDWARE_BATCHING = "no.nordicsemi.support.v18.EXTRA_USE_HARDWARE_BATCHING";
+ /* package */ static final String
+ EXTRA_USE_HARDWARE_FILTERING = "no.nordicsemi.support.v18.EXTRA_USE_HARDWARE_FILTERING";
+ /* package */ static final String
+ EXTRA_USE_HARDWARE_CALLBACK_TYPES = "no.nordicsemi.support.v18.EXTRA_USE_HARDWARE_CALLBACK_TYPES";
+ /* package */ static final String
+ EXTRA_MATCH_LOST_TIMEOUT = "no.nordicsemi.support.v18.EXTRA_MATCH_LOST_TIMEOUT";
+ /* package */ static final String
+ EXTRA_MATCH_LOST_INTERVAL = "no.nordicsemi.support.v18.EXTRA_MATCH_LOST_INTERVAL";
+ /* package */ static final String
+ EXTRA_MATCH_MODE = "no.nordicsemi.support.v18.EXTRA_MATCH_MODE";
+ /* package */ static final String
+ EXTRA_NUM_OF_MATCHES = "no.nordicsemi.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/support/v18/scanner/ScanCallback.java b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/ScanCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ec81f07b7ab190ac8635514812a800b6b5e22e9
--- /dev/null
+++ b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/ScanCallback.java
@@ -0,0 +1,93 @@
+/*
+ * 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.support.v18.scanner;
+
+import java.util.List;
+
+/**
+ * Bluetooth LE scan callbacks. Scan results are reported using these callbacks.
+ *
+ * @see BluetoothLeScannerCompat#startScan
+ */
+@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 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 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 application tries to scan too frequently.
+ */
+ public static final int SCAN_FAILED_SCANNING_TOO_FREQUENTLY = 6;
+
+ 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,final ScanResult result) {
+ }
+
+ /**
+ * 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) {
+ }
+}
diff --git a/scanner/src/main/java/no/nordicsemi/support/v18/scanner/ScanFilter.java b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/ScanFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..dbffbec1640aa4520f0d715671151ae5be057e7f
--- /dev/null
+++ b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/ScanFilter.java
@@ -0,0 +1,463 @@
+package no.nordicsemi.support.v18.scanner;
+
+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;
+
+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/support/v18/scanner/ScanRecord.java b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/ScanRecord.java
new file mode 100644
index 0000000000000000000000000000000000000000..245fd3ca3ceb4cb48680ba552b54824c54dac8c7
--- /dev/null
+++ b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/ScanRecord.java
@@ -0,0 +1,225 @@
+package no.nordicsemi.support.v18.scanner;
+import ohos.utils.PlainArray;
+import ohos.utils.SequenceUuid;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+public final class ScanRecord {
+ private static final String TAG = "ScanRecord";
+ 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;
+ private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04;
+ private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05;
+ private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06;
+ private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07;
+ private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08;
+ private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09;
+ private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A;
+ private static final int DATA_TYPE_SERVICE_DATA_16_BIT = 0x16;
+ 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;
+ private static final String LABEL_LOG ="ScanRecord" ;
+ private final int advertiseFlags;
+ @Nullable
+ private final List serviceUuids;
+ @Nullable private final PlainArray manufacturerSpecificData;
+ @Nullable private final Map serviceData;
+ private final int txPowerLevel;
+ private final String deviceName;
+ private final byte[] bytes;
+ public int getAdvertiseFlags() {
+ return advertiseFlags;
+ }
+ @Nullable
+ public List getServiceUuids() {
+ return serviceUuids;
+ }
+ @Nullable
+ public PlainArray getManufacturerSpecificData() {
+ return manufacturerSpecificData;
+ }
+ public Optional getManufacturerSpecificData(final int manufacturerId) {
+ if (manufacturerSpecificData == null) {
+ return null;
+ }
+ return manufacturerSpecificData.get(manufacturerId);
+ }
+ @Nullable
+ public Map getServiceData() {
+ return serviceData;
+ }
+ @Nullable
+ public byte[] getServiceData( final Object serviceDataUuid) {
+ if (serviceDataUuid == null || serviceData == null) {
+ return null;
+ }
+ return serviceData.get(serviceDataUuid);
+ }
+
+ public int getTxPowerLevel() {
+ return txPowerLevel;
+ }
+
+ public String getDeviceName() {
+ return deviceName;
+ }
+
+ @Nullable
+ public byte[] getBytes() {
+ return bytes;
+ }
+
+ 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;
+ this.manufacturerSpecificData = manufacturerData;
+ this.serviceData = serviceData;
+ this.deviceName = localName;
+ this.advertiseFlags = advertiseFlags;
+ this.txPowerLevel = txPowerLevel;
+ this.bytes = bytes;
+ }
+
+ @Nullable
+ /* package */ static ScanRecord parseFromBytes(@Nullable final byte[] scanRecord) {
+ if (scanRecord == null) {
+ return null;
+ }
+
+ int currentPos = 0;
+ int advertiseFlag = -1;
+ int txPowerLevel = Integer.MIN_VALUE;
+ String localName = null;
+ List serviceUuids = null;
+ PlainArray manufacturerData = null;
+ Map serviceData = null;
+
+ try {
+ while (currentPos < scanRecord.length) {
+ // length is unsigned int.
+ final int length = scanRecord[currentPos++] & 0xFF;
+ if (length == 0) {
+ break;
+ }
+ final int dataLength = length - 1;
+ final int fieldType = scanRecord[currentPos++] & 0xFF;
+ switch (fieldType) {
+ case DATA_TYPE_FLAGS:
+ advertiseFlag = scanRecord[currentPos] & 0xFF;
+ break;
+ case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL:
+ case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE:
+ if (serviceUuids == null)
+ serviceUuids = new ArrayList<>();
+ parseServiceUuid(scanRecord, currentPos,
+ dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids);
+ break;
+ case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL:
+ case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE:
+ if (serviceUuids == null)
+ serviceUuids = new ArrayList<>();
+ parseServiceUuid(scanRecord, currentPos, dataLength,
+ BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids);
+ break;
+ case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL:
+ case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE:
+ if (serviceUuids == null)
+ serviceUuids = new ArrayList<>();
+ parseServiceUuid(scanRecord, currentPos, dataLength,
+ BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids);
+ break;
+ case DATA_TYPE_LOCAL_NAME_SHORT:
+ case DATA_TYPE_LOCAL_NAME_COMPLETE:
+ localName = new String(
+ extractBytes(scanRecord, currentPos, dataLength));
+ break;
+ case DATA_TYPE_TX_POWER_LEVEL:
+ txPowerLevel = scanRecord[currentPos];
+ break;
+ case DATA_TYPE_SERVICE_DATA_16_BIT:
+ case DATA_TYPE_SERVICE_DATA_32_BIT:
+ case DATA_TYPE_SERVICE_DATA_128_BIT:
+ int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT;
+ if (fieldType == DATA_TYPE_SERVICE_DATA_32_BIT) {
+ serviceUuidLength = BluetoothUuid.UUID_BYTES_32_BIT;
+ } else if (fieldType == DATA_TYPE_SERVICE_DATA_128_BIT) {
+ serviceUuidLength = BluetoothUuid.UUID_BYTES_128_BIT;
+ }
+
+ final byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
+ serviceUuidLength);
+ final SequenceUuid serviceDataUuid = BluetoothUuid.parseUuidFrom(
+ serviceDataUuidBytes);
+ final byte[] serviceDataArray = extractBytes(scanRecord,
+ currentPos + serviceUuidLength, dataLength - serviceUuidLength);
+ if (serviceData == null)
+ serviceData = new HashMap<>();
+ serviceData.put(serviceDataUuid, serviceDataArray);
+ break;
+ case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
+ 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 PlainArray<>();
+ manufacturerData.put(manufacturerId, manufacturerDataBytes);
+ break;
+ default:
+ break;
+ }
+ currentPos += dataLength;
+ }
+ return new ScanRecord(serviceUuids, manufacturerData, serviceData,
+ advertiseFlag, txPowerLevel, localName, scanRecord);
+ } catch (final Exception e) {
+ return new ScanRecord(null, null, null,
+ -1, Integer.MIN_VALUE, null, scanRecord);
+ }
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ ScanRecord other = (ScanRecord) obj;
+ return Arrays.equals(bytes, other.bytes);
+ }
+
+ @Override
+ public String toString() {
+ return "ScanRecord [advertiseFlags=" + advertiseFlags + ", serviceUuids=" + serviceUuids
+ + ", manufacturerSpecificData=" + BluetoothLeUtils.toString(manufacturerSpecificData)
+ + ", serviceData=" + BluetoothLeUtils.toString(serviceData)
+ + ", txPowerLevel=" + txPowerLevel + ", deviceName=" + deviceName + "]";
+ }
+
+ 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);
+ serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
+ dataLength -= uuidLength;
+ currentPos += uuidLength;
+ }
+ return currentPos;
+ }
+
+ 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);
+ return bytes;
+ }
+}
diff --git a/scanner/src/main/java/no/nordicsemi/support/v18/scanner/ScanResult.java b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/ScanResult.java
new file mode 100644
index 0000000000000000000000000000000000000000..6dc06d8508d7fb8b26c0e5467f857d261fb8a21a
--- /dev/null
+++ b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/ScanResult.java
@@ -0,0 +1,366 @@
+/*
+ * 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.support.v18.scanner;
+
+
+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 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/support/v18/scanner/ScanSettings.java b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/ScanSettings.java
new file mode 100644
index 0000000000000000000000000000000000000000..14fd70fa46d93f363d9f706329981b059ab0a294
--- /dev/null
+++ b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/ScanSettings.java
@@ -0,0 +1,445 @@
+package no.nordicsemi.support.v18.scanner;
+
+import ohos.utils.Parcel;
+import ohos.utils.Sequenceable;
+
+import java.io.Serializable;
+import java.util.List;
+/**
+ * Bluetooth LE scan settings are passed to {@link BluetoothLeScannerCompat#startScan} to define the
+ * parameters for the scan.
+ */
+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;
+ }
+ /**
+ * 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.
+ *
+ * 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}
+ * 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.
+ *
+ * @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
+ *
+ * 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/support/v18/scanner/ScannerService.java b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/ScannerService.java
new file mode 100644
index 0000000000000000000000000000000000000000..b5d0bcef93bcc63e053b0b519637699a9bc9f048
--- /dev/null
+++ b/scanner/src/main/java/no/nordicsemi/support/v18/scanner/ScannerService.java
@@ -0,0 +1,123 @@
+package no.nordicsemi.support.v18.scanner;
+
+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 = "EXTRA_FILTERS";
+ /* package */ static final String EXTRA_SETTINGS = "EXTRA_SETTINGS";
+ /* package */ static final String EXTRA_START = "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);
+ }
+ 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);
+ }
+ }
+
+ 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) {
+ }
+ }
+ 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'