From a4ccb656140de1cc15826f0b05c3ceb7ed0c1046 Mon Sep 17 00:00:00 2001 From: sakumar3 Date: Mon, 3 Jun 2024 15:53:58 +0530 Subject: [PATCH] Release-v2.9.0 package --- README.md | 1 + mobile/build.gradle.kts | 22 +- mobile/src/main/AndroidManifest.xml | 10 +- .../Bluetooth/Services/BluetoothService.kt | 17 +- .../siliconlabs/bledemo/Utils/GattQueue.kt | 4 +- .../adapters/MatterScannedResultAdapter.kt | 3 +- .../activities/RangeTestActivity.kt | 42 +- .../demo/range_test/models/RangeTestValues.kt | 19 +- .../base/models/ThunderBoardDevice.kt | 10 +- .../activities/BlinkyThunderboardActivity.kt | 156 +- .../demos/motion/adapters/GdxAdapter.kt | 3 +- .../demo/wifi_ota_update/AlertErrorDialog.kt | 40 + .../wifi_ota_update/WiFiOtaFileManager.kt | 53 + .../WiFiOtaFileSelectionDialog.kt | 77 + .../wifi_ota_update/WiFiOtaProgressDialog.kt | 65 + .../iop_test/activities/IOPTestActivity.kt | 1380 ++++++++++++++--- .../iop_test/models/DemoItemProvider.kt | 2 +- .../iop_test/models/SiliconLabsTestInfo.kt | 15 +- .../scan/browser/services/ShareLogServices.kt | 2 +- .../home_screen/activities/SplashActivity.kt | 2 +- .../home_screen/dialogs/SelectDeviceDialog.kt | 5 +- .../home_screen/fragments/DemoFragment.kt | 399 +++++ .../home_screen/fragments/SettingsFragment.kt | 8 +- .../bledemo/home_screen/menu_items/OTADemo.kt | 13 + .../main/res/drawable/background_grey_box.xml | 11 + .../res/drawable/redesign_ic_demo_ota.xml | 18 + mobile/src/main/res/drawable/si_connect.xml | 70 + .../si_connect_app_screen_background.jpg | Bin 0 -> 108594 bytes mobile/src/main/res/drawable/silabs_logo.xml | 46 + mobile/src/main/res/drawable/wi_fi_symbol.png | Bin 0 -> 78881 bytes .../main/res/layout/dialog_alert_error.xml | 55 + .../res/layout/dialog_device_info_layout.xml | 54 +- .../layout/dialog_wifi_ota_file_update.xml | 142 ++ .../res/layout/dialog_wifi_ota_progress.xml | 268 ++++ .../res/layout/si_connect_splash_screen.xml | 38 + .../mipmap-anydpi/efr_redesign_launcher.xml | 5 - .../efr_redesign_launcher_round.xml | 5 - .../src/main/res/mipmap-hdpi/si_launcher.png | Bin 0 -> 3212 bytes .../mipmap-hdpi/si_launcher_foreground.png | Bin 0 -> 6530 bytes .../res/mipmap-hdpi/si_launcher_round.png | Bin 0 -> 5298 bytes .../src/main/res/mipmap-mdpi/si_launcher.png | Bin 0 -> 1874 bytes .../mipmap-mdpi/si_launcher_foreground.png | Bin 0 -> 3631 bytes .../res/mipmap-mdpi/si_launcher_round.png | Bin 0 -> 3109 bytes .../src/main/res/mipmap-xhdpi/si_launcher.png | Bin 0 -> 3377 bytes .../mipmap-xhdpi/si_launcher_foreground.png | Bin 0 -> 7476 bytes .../res/mipmap-xhdpi/si_launcher_round.png | Bin 0 -> 6130 bytes .../main/res/mipmap-xxhdpi/si_launcher.png | Bin 0 -> 7208 bytes .../mipmap-xxhdpi/si_launcher_foreground.png | Bin 0 -> 29393 bytes .../res/mipmap-xxhdpi/si_launcher_round.png | Bin 0 -> 11789 bytes .../main/res/mipmap-xxxhdpi/si_launcher.png | Bin 0 -> 8132 bytes .../mipmap-xxxhdpi/si_launcher_foreground.png | Bin 0 -> 40551 bytes .../res/mipmap-xxxhdpi/si_launcher_round.png | Bin 0 -> 14136 bytes mobile/src/main/res/values-v31/styles.xml | 2 +- mobile/src/main/res/values/colors.xml | 1 + mobile/src/main/res/values/dimens.xml | 5 + mobile/src/main/res/values/strings.xml | 31 +- mobile/src/main/res/values/styles.xml | 11 +- 57 files changed, 2721 insertions(+), 389 deletions(-) create mode 100644 mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/AlertErrorDialog.kt create mode 100644 mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaFileManager.kt create mode 100644 mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaFileSelectionDialog.kt create mode 100644 mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaProgressDialog.kt create mode 100644 mobile/src/main/java/com/siliconlabs/bledemo/home_screen/menu_items/OTADemo.kt create mode 100644 mobile/src/main/res/drawable/background_grey_box.xml create mode 100644 mobile/src/main/res/drawable/redesign_ic_demo_ota.xml create mode 100644 mobile/src/main/res/drawable/si_connect.xml create mode 100644 mobile/src/main/res/drawable/si_connect_app_screen_background.jpg create mode 100644 mobile/src/main/res/drawable/silabs_logo.xml create mode 100644 mobile/src/main/res/drawable/wi_fi_symbol.png create mode 100644 mobile/src/main/res/layout/dialog_alert_error.xml create mode 100644 mobile/src/main/res/layout/dialog_wifi_ota_file_update.xml create mode 100644 mobile/src/main/res/layout/dialog_wifi_ota_progress.xml create mode 100644 mobile/src/main/res/layout/si_connect_splash_screen.xml delete mode 100644 mobile/src/main/res/mipmap-anydpi/efr_redesign_launcher.xml delete mode 100644 mobile/src/main/res/mipmap-anydpi/efr_redesign_launcher_round.xml create mode 100644 mobile/src/main/res/mipmap-hdpi/si_launcher.png create mode 100644 mobile/src/main/res/mipmap-hdpi/si_launcher_foreground.png create mode 100644 mobile/src/main/res/mipmap-hdpi/si_launcher_round.png create mode 100644 mobile/src/main/res/mipmap-mdpi/si_launcher.png create mode 100644 mobile/src/main/res/mipmap-mdpi/si_launcher_foreground.png create mode 100644 mobile/src/main/res/mipmap-mdpi/si_launcher_round.png create mode 100644 mobile/src/main/res/mipmap-xhdpi/si_launcher.png create mode 100644 mobile/src/main/res/mipmap-xhdpi/si_launcher_foreground.png create mode 100644 mobile/src/main/res/mipmap-xhdpi/si_launcher_round.png create mode 100644 mobile/src/main/res/mipmap-xxhdpi/si_launcher.png create mode 100644 mobile/src/main/res/mipmap-xxhdpi/si_launcher_foreground.png create mode 100644 mobile/src/main/res/mipmap-xxhdpi/si_launcher_round.png create mode 100644 mobile/src/main/res/mipmap-xxxhdpi/si_launcher.png create mode 100644 mobile/src/main/res/mipmap-xxxhdpi/si_launcher_foreground.png create mode 100644 mobile/src/main/res/mipmap-xxxhdpi/si_launcher_round.png diff --git a/README.md b/README.md index 5cb9b48b..9030c60e 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ EFR Connect includes many demos to test sample apps in the Silicon Labs GSDK qui - **Wi-Fi Commissioning**: Commission a Wi-Fi device over BLE. - **Bluetooth Electronic Shelf Labels (ESL)**: Adds and commissions ESL tags to the system network by scanning the tag's QR code with the mobile device's camera and provides the user a UI to view the list commissioned tags and control them. - **Matter**: Commission and control of the Matter devices over Thread and Wi-Fi. +- **Wi-Fi OTA Firmware Update**: The Wi-Fi OTA firmware update demo demonstrates how to update the SiWx91x user application firmware over Wi-Fi connection, by downloading the image from the mobile phone. ## Development Features EFR Connect helps developers create and troubleshoot Bluetooth applications running on Silicon Labs’ BLE hardware. Here’s a rundown of some example functionalities. diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index 5ddba351..f738a9f0 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -16,7 +16,6 @@ android { compileSdk = 33 namespace = "com.siliconlabs.bledemo" - defaultConfig { minSdk = 29 targetSdk = 33 @@ -24,6 +23,8 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } + packagingOptions { jniLibs { useLegacyPackaging = true } } + buildTypes { release { isMinifyEnabled = false @@ -57,8 +58,8 @@ android { create("blueGecko") { dimension = versionDim applicationId = "com.siliconlabs.bledemo" - versionCode = 48 - versionName = "2.8.2" + versionCode = 49 + versionName = "2.9.0" } } @@ -73,14 +74,7 @@ android { buildFeatures { viewBinding = true - } - -// packagingOptions { -// jniLibs { -// useLegacyPackaging = true -// } -// } } dependencies { @@ -108,7 +102,8 @@ dependencies { implementation("io.github.g00fy2.quickie:quickie-bundled:1.7.0") implementation("com.google.android.flexbox:flexbox:3.0.0") - // implementation("com.github.PhilJay:MPAndroidChart:v3.0.3") + //MPAndroidChart is added as jar library file + //implementation("com.github.PhilJay:MPAndroidChart:v3.0.3") // Navigation implementation("androidx.navigation:navigation-fragment-ktx:2.5.3") @@ -117,7 +112,6 @@ dependencies { // Dependency injection implementation("com.google.dagger:hilt-android:2.45") - implementation(files("libs/MPAndroidChart-v3.0.1.jar")) kapt("com.google.dagger:hilt-android-compiler:2.45") // View binding @@ -129,7 +123,7 @@ dependencies { // Parsing implementation("com.google.code.gson:gson:2.10.1") implementation("com.opencsv:opencsv:5.6") - implementation ("androidx.activity:activity:1.6.0-alpha05") + implementation("androidx.activity:activity:1.6.0-alpha05") // Only used for Int.pow() method in a couple of places implementation("com.google.guava:guava:29.0-android") @@ -152,5 +146,5 @@ dependencies { implementation("androidx.camera:camera-lifecycle:1.1.0") implementation("androidx.camera:camera-view:1.1.0") implementation("com.google.mlkit:barcode-scanning:17.0.2") - implementation ("com.daimajia.swipelayout:library:1.2.0@aar") + implementation("com.daimajia.swipelayout:library:1.2.0@aar") } diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index 2b411b12..bae93deb 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -18,7 +18,10 @@ - + + + + @@ -30,8 +33,9 @@ () { ENVIRONMENT, IOP_TEST, ESL_DEMO, - MATTER_DEMO + MATTER_DEMO, + WIFI_OTA_UPDATE } interface ScanListener { @@ -280,6 +281,7 @@ class BluetoothService : LocalService() { addAction(ACTION_GATT_SERVER_REMOVE_NOTIFICATION) } registerReceiver(gattServerBroadcastReceiver, filter) + } override fun onDestroy() { @@ -659,10 +661,13 @@ class BluetoothService : LocalService() { override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) { super.onMtuChanged(gatt, mtu, status) - Timber.d("onMtuChanged(): gatt device =${gatt.device.address}, mtu = $mtu") - addDeviceLog(GattOperationWithParameterLog(gatt, GattOperationLog.Type.MTU_CHANGED, + if (status == BluetoothGatt.GATT_SUCCESS) { + Timber.d("onMtuChanged(): gatt device =${gatt.device.address}, mtu = $mtu") + addDeviceLog(GattOperationWithParameterLog(gatt, GattOperationLog.Type.MTU_CHANGED, status, "mtu = $mtu")) - extraGattCallback?.onMtuChanged(gatt, mtu, status) + extraGattCallback?.onMtuChanged(gatt, mtu, status) + } + } override fun onPhyUpdate(gatt: BluetoothGatt, txPhy: Int, rxPhy: Int, status: Int) { @@ -843,7 +848,7 @@ class BluetoothService : LocalService() { createNotificationChannel() val notification = Notification.Builder(this, CHANNEL_ID) - .setSmallIcon(R.mipmap.efr_redesign_launcher) + .setSmallIcon(R.mipmap.si_launcher) .setContentTitle(getString(R.string.notification_title_device_has_connected, deviceName)) .setContentText(getString(R.string.notification_note_debug_connection)) .addAction(buildAction(getString(R.string.button_yes), getYesPendingIntent(device))) @@ -869,7 +874,7 @@ class BluetoothService : LocalService() { private fun buildAction(actionText: String, actionIntent: PendingIntent) : Notification.Action { return Notification.Action.Builder( - R.mipmap.efr_redesign_launcher, + R.mipmap.si_launcher, actionText, actionIntent ).build() diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/Utils/GattQueue.kt b/mobile/src/main/java/com/siliconlabs/bledemo/Utils/GattQueue.kt index 47f03170..8ea08ea5 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/Utils/GattQueue.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/Utils/GattQueue.kt @@ -3,6 +3,7 @@ package com.siliconlabs.bledemo.utils import android.annotation.SuppressLint import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCharacteristic +import android.util.Log import java.util.* import java.util.concurrent.locks.Lock import java.util.concurrent.locks.ReentrantLock @@ -62,12 +63,13 @@ class GattQueue(private val gatt: BluetoothGatt?) { private fun processNextCommand() { var success = false + Log.i("GattQueue","processNextCommand") val command = commands.poll() if (command?.gatt != null && command.characteristic != null) { val gatt = command.gatt val characteristic = command.characteristic - + Log.i("GattQueue","command.type "+command.type) success = when (command.type) { GattCommand.Type.READ -> gatt.readCharacteristic(characteristic) GattCommand.Type.WRITE -> gatt.writeCharacteristic(characteristic) diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/adapters/MatterScannedResultAdapter.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/adapters/MatterScannedResultAdapter.kt index bc73b515..687dba52 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/adapters/MatterScannedResultAdapter.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/matter_demo/adapters/MatterScannedResultAdapter.kt @@ -1,7 +1,6 @@ package com.siliconlabs.bledemo.features.demo.matter_demo.adapters import android.content.Context -import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup import androidx.core.content.ContextCompat @@ -65,7 +64,7 @@ class MatterScannedResultAdapter( - holder.itemView.setOnClickListener { + holder.binding.textViewHeader.setOnClickListener { runBlocking { if (onClickListener != null) { onClickListener!!.onClick(position, matterInfo) diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/activities/RangeTestActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/activities/RangeTestActivity.kt index 547468d4..c0293ef7 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/activities/RangeTestActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/activities/RangeTestActivity.kt @@ -39,6 +39,10 @@ import kotlin.math.abs @SuppressLint("MissingPermission") class RangeTestActivity : BaseDemoActivity(), Controller { + private var lowerLimit: Int = 0 + private var upperLimit: Int = 0 + private var descriptor2906: BluetoothGattDescriptor? = null + private var activeDeviceId = 1 private var advertisementHandler: RangeTestAdvertisementHandler? = null @@ -188,8 +192,8 @@ class RangeTestActivity : BaseDemoActivity(), Controller { } }) }.also { - it.show(supportFragmentManager, "select_device_dialog") - } + it.show(supportFragmentManager, "select_device_dialog") + } } else { showRangeTestFragment(presenter.getMode()) } @@ -781,6 +785,10 @@ class RangeTestActivity : BaseDemoActivity(), Controller { status: Int ) { super.onCharacteristicRead(gatt, characteristic, status) + val descriptorUuid = UUID.fromString("00002906-0000-1000-8000-00805f9b34fb") + if (characteristic != null) { + descriptor2906 = characteristic.getDescriptor(descriptorUuid) + } if (status != BluetoothGatt.GATT_SUCCESS) { handleConnectionError() @@ -794,11 +802,22 @@ class RangeTestActivity : BaseDemoActivity(), Controller { if (gattCharacteristic != null) { updatePresenter(gatt, characteristic, gattCharacteristic) - if (gattCharacteristic === GattCharacteristic.RangeTestTxPower || gattCharacteristic === GattCharacteristic.RangeTestPayload || gattCharacteristic === GattCharacteristic.RangeTestMaSize) { + if (gattCharacteristic === GattCharacteristic.RangeTestTxPower + || gattCharacteristic === GattCharacteristic.RangeTestPayload + || gattCharacteristic === GattCharacteristic.RangeTestMaSize + ) { val descriptors = characteristic.descriptors if (descriptors.size > 1) { queueReadDescriptor(gatt, characteristic, descriptors[descriptors.size - 1]) } + } else { + if (gattCharacteristic === GattCharacteristic.RangeTestChannel) { + val descriptors = characteristic.descriptors + if (descriptors.size > 1) { + descriptor2906?.let { queueReadDescriptor(gatt, characteristic, it) } + } + + } } } } @@ -809,7 +828,22 @@ class RangeTestActivity : BaseDemoActivity(), Controller { status: Int ) { super.onDescriptorRead(gatt, descriptor, status) + if (descriptor?.uuid == UUID.fromString("00002906-0000-1000-8000-00805f9b34fb")) { + try { + val validRangeValue = descriptor.value + lowerLimit = (validRangeValue[1].toInt() and 0xFF) shl 8 or (validRangeValue[0].toInt() and 0xFF) + upperLimit = (validRangeValue[3].toInt() and 0xFF) shl 8 or (validRangeValue[2].toInt() and 0xFF) + RangeTestValues.setChannelsMinMax(lowerLimit, upperLimit) + val gattCharacteristic1 = GattCharacteristic.fromUuid(descriptor.characteristic.uuid) + gattCharacteristic1?.let { + updatePresenter(gatt, descriptor, it) + } + }catch (e:Exception) + { + e.stackTrace + } + } if (status != BluetoothGatt.GATT_SUCCESS) { handleConnectionError() return @@ -1213,7 +1247,7 @@ class RangeTestActivity : BaseDemoActivity(), Controller { android.R.id.home -> { service?.disconnectAllGatts() gatt?.disconnect() - Handler(Looper.getMainLooper()).postDelayed({ onBackPressed() },500) + Handler(Looper.getMainLooper()).postDelayed({ onBackPressed() }, 500) true } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/models/RangeTestValues.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/models/RangeTestValues.kt index d05eb3e7..46b79532 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/models/RangeTestValues.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/range_test/models/RangeTestValues.kt @@ -13,8 +13,8 @@ class RangeTestValues { var PACKET_COUNT_LOOKUP: List var ID_LOOKUP: List - private const val CHANNELS_MIN = 0 - private const val CHANNELS_MAX = 19 + private var CHANNELS_MIN = 0 + private var CHANNELS_MAX = 0 private val PACKET_COUNTS = arrayOf(500, 1000, 2500, 5000, 10000, 25000, 50000) @@ -39,5 +39,20 @@ class RangeTestValues { ID_LOOKUP = Collections.unmodifiableList(idLookup) } + + fun setChannelsMinMax(min: Int, max: Int) { + CHANNELS_MIN = min + CHANNELS_MAX = max + updateChannelLookup() + } + + private fun updateChannelLookup() { + val channelsLookupSize = CHANNELS_MAX - CHANNELS_MIN + 1 + val channelsLookup: ArrayList = ArrayList(channelsLookupSize) + for (i in 0 until channelsLookupSize) { + channelsLookup.add(CHANNELS_MIN + i) + } + CHANNEL_LOOKUP = Collections.unmodifiableList(channelsLookup) + } } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/models/ThunderBoardDevice.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/models/ThunderBoardDevice.kt index b8cc294f..9b6fdee1 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/models/ThunderBoardDevice.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/base/models/ThunderBoardDevice.kt @@ -19,8 +19,11 @@ class ThunderBoardDevice(device: BluetoothDevice) { THUNDERBOARD_MODEL_SENSE -> Type.THUNDERBOARD_SENSE THUNDERBOARD_MODEL_BLUE_V1, THUNDERBOARD_MODEL_BLUE_V2 -> Type.THUNDERBOARD_BLUE + THUNDERBOARD_MODEL_DEV_KIT_V1, - THUNDERBOARD_MODEL_DEV_KIT_V2 -> Type.THUNDERBOARD_DEV_KIT + THUNDERBOARD_MODEL_DEV_KIT_V2, + THUNDERBOARD_MODEL_DEV_KIT_V3 -> Type.THUNDERBOARD_DEV_KIT + else -> Type.UNKNOWN } @@ -30,9 +33,9 @@ class ThunderBoardDevice(device: BluetoothDevice) { UNKNOWN(0); companion object { - fun fromInt(code: Int) : PowerSource { + fun fromInt(code: Int): PowerSource { for (source in values()) { - if (source.value == code ) return source + if (source.value == code) return source } return UNKNOWN } @@ -52,6 +55,7 @@ class ThunderBoardDevice(device: BluetoothDevice) { const val THUNDERBOARD_MODEL_BLUE_V2 = "BRD4184B" const val THUNDERBOARD_MODEL_DEV_KIT_V1 = "BRD2601A" const val THUNDERBOARD_MODEL_DEV_KIT_V2 = "BRD2601B" + const val THUNDERBOARD_MODEL_DEV_KIT_V3 = "BRD2608A" } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/activities/BlinkyThunderboardActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/activities/BlinkyThunderboardActivity.kt index 6c18dd5a..5d83a02c 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/activities/BlinkyThunderboardActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/blinky_thunderboard/activities/BlinkyThunderboardActivity.kt @@ -34,12 +34,17 @@ class BlinkyThunderboardActivity : ThunderboardActivity(), ColorLEDControlListen override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val view = LayoutInflater.from(this).inflate(R.layout.activity_blinky_thunderboard, null, false) - colorLEDControl = view.findViewById(R.id.color_led_control) // make the view gone (if necessary) before it can be showed + val view = + LayoutInflater.from(this).inflate(R.layout.activity_blinky_thunderboard, null, false) + colorLEDControl = + view.findViewById(R.id.color_led_control) // make the view gone (if necessary) before it can be showed ledsControl = view.findViewById(R.id.leds_control) val powerSourceIntent = intent.getIntExtra(SelectDeviceDialog.POWER_SOURCE_EXTRA, 0) val modelNumberIntent = intent.getStringExtra(SelectDeviceDialog.MODEL_TYPE_EXTRA) - setControlsVisibility(ThunderBoardDevice.PowerSource.fromInt(powerSourceIntent), modelNumberIntent) + setControlsVisibility( + ThunderBoardDevice.PowerSource.fromInt(powerSourceIntent), + modelNumberIntent + ) mainSection?.addView(view) @@ -77,14 +82,17 @@ class BlinkyThunderboardActivity : ThunderboardActivity(), ColorLEDControlListen viewModel.button0.observe(this, Observer { switch_0.setChecked(it) }) viewModel.button1.observe(this, Observer { switch_1.setChecked(it) }) viewModel.led0.observe(this, Observer { - if (it != led_0.isChecked) led_0.isChecked = it }) + if (it != led_0.isChecked) led_0.isChecked = it + }) viewModel.led1.observe(this, Observer { - if (it != led_1.isChecked) led_1.isChecked = it }) + if (it != led_1.isChecked) led_1.isChecked = it + }) when (modelNumber) { ThunderBoardDevice.THUNDERBOARD_MODEL_SENSE, ThunderBoardDevice.THUNDERBOARD_MODEL_DEV_KIT_V1, - ThunderBoardDevice.THUNDERBOARD_MODEL_DEV_KIT_V2 -> { + ThunderBoardDevice.THUNDERBOARD_MODEL_DEV_KIT_V2, + ThunderBoardDevice.THUNDERBOARD_MODEL_DEV_KIT_V3 -> { viewModel.colorLed.observe(this, Observer { colorLEDControl.setColorLEDsUI(it) }) @@ -99,10 +107,10 @@ class BlinkyThunderboardActivity : ThunderboardActivity(), ColorLEDControlListen private fun sendColorLedCommand(rgbState: LedRGBState) { val data = byteArrayOf( - (if (rgbState.on) viewModel.rgbLedMask.value!! else 0x00).toByte(), - (rgbState.red and 0xff).toByte(), - (rgbState.green and 0xff).toByte(), - (rgbState.blue and 0xff).toByte() + (if (rgbState.on) viewModel.rgbLedMask.value!! else 0x00).toByte(), + (rgbState.red and 0xff).toByte(), + (rgbState.green and 0xff).toByte(), + (rgbState.blue and 0xff).toByte() ) getRgbLedCharacteristic()?.apply { @@ -111,24 +119,28 @@ class BlinkyThunderboardActivity : ThunderboardActivity(), ColorLEDControlListen } } - private fun setControlsVisibility(powerSource: ThunderBoardDevice.PowerSource, modelNumber: String?) { + private fun setControlsVisibility( + powerSource: ThunderBoardDevice.PowerSource, + modelNumber: String? + ) { if (modelNumber == ThunderBoardDevice.THUNDERBOARD_MODEL_DEV_KIT_V1 || - modelNumber == ThunderBoardDevice.THUNDERBOARD_MODEL_DEV_KIT_V2) { + modelNumber == ThunderBoardDevice.THUNDERBOARD_MODEL_DEV_KIT_V2 + ) { ledsControl.visibility = View.GONE } if (modelNumber == ThunderBoardDevice.THUNDERBOARD_MODEL_SENSE && - powerSource == ThunderBoardDevice.PowerSource.COIN_CELL) { + powerSource == ThunderBoardDevice.PowerSource.COIN_CELL + ) { colorLEDControl.visibility = View.GONE } } @SuppressLint("MissingPermission") private fun initControls() { - if (statusFragment.viewModel.thunderboardDevice.value?. - boardType != ThunderBoardDevice.Type.THUNDERBOARD_SENSE && - statusFragment.viewModel.thunderboardDevice.value?. - boardType != ThunderBoardDevice.Type.THUNDERBOARD_DEV_KIT) { + if (statusFragment.viewModel.thunderboardDevice.value?.boardType != ThunderBoardDevice.Type.THUNDERBOARD_SENSE && + statusFragment.viewModel.thunderboardDevice.value?.boardType != ThunderBoardDevice.Type.THUNDERBOARD_DEV_KIT + ) { runOnUiThread { colorLEDControl.visibility = View.GONE } } @@ -146,30 +158,31 @@ class BlinkyThunderboardActivity : ThunderboardActivity(), ColorLEDControlListen gattQueue.clearAllButLast() // prevent from queueing more gatt commands } - private fun getDigitalWriteCharacteristic() : BluetoothGattCharacteristic? { + private fun getDigitalWriteCharacteristic(): BluetoothGattCharacteristic? { return gatt?.getService( - GattService.AutomationIo.number)?.characteristics - ?.filter { it.uuid == GattCharacteristic.Digital.uuid } // there are two - ?.first { it.properties and BluetoothGattCharacteristic.PROPERTY_WRITE != 0 } + GattService.AutomationIo.number + )?.characteristics + ?.filter { it.uuid == GattCharacteristic.Digital.uuid } // there are two + ?.first { it.properties and BluetoothGattCharacteristic.PROPERTY_WRITE != 0 } } - private fun getDigitalNotifyCharacteristic() : BluetoothGattCharacteristic? { + private fun getDigitalNotifyCharacteristic(): BluetoothGattCharacteristic? { return gatt?.getService( - GattService.AutomationIo.number)?.characteristics - ?.filter { it.uuid == GattCharacteristic.Digital.uuid } // there are two - ?.first { it.properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0 } + GattService.AutomationIo.number + )?.characteristics + ?.filter { it.uuid == GattCharacteristic.Digital.uuid } // there are two + ?.first { it.properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0 } } - private fun getRgbLedCharacteristic() : BluetoothGattCharacteristic? { - return gatt?.getService(GattService.UserInterface.number)?. - getCharacteristic(GattCharacteristic.RgbLeds.uuid) + private fun getRgbLedCharacteristic(): BluetoothGattCharacteristic? { + return gatt?.getService(GattService.UserInterface.number) + ?.getCharacteristic(GattCharacteristic.RgbLeds.uuid) } - private fun getRgbLedMaskDescriptor() : BluetoothGattDescriptor? { - return gatt?.getService(GattService.UserInterface.number)?. - getCharacteristic(GattCharacteristic.RgbLeds.uuid)?. - getDescriptor(LED_MASK_DESCRIPTOR) + private fun getRgbLedMaskDescriptor(): BluetoothGattDescriptor? { + return gatt?.getService(GattService.UserInterface.number) + ?.getCharacteristic(GattCharacteristic.RgbLeds.uuid)?.getDescriptor(LED_MASK_DESCRIPTOR) } override val gattCallback: TimeoutGattCallback = object : TimeoutGattCallback() { @@ -187,9 +200,11 @@ class BlinkyThunderboardActivity : ThunderboardActivity(), ColorLEDControlListen queueReadingDeviceCharacteristics() } - override fun onCharacteristicRead(gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - status: Int) { + override fun onCharacteristicRead( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + status: Int + ) { super.onCharacteristicRead(gatt, characteristic, status) gattQueue.handleCommandProcessed() if (status != BluetoothGatt.GATT_SUCCESS) return @@ -201,7 +216,9 @@ class BlinkyThunderboardActivity : ThunderboardActivity(), ColorLEDControlListen GattCharacteristic.ModelNumberString, GattCharacteristic.BatteryLevel, GattCharacteristic.PowerSource, - GattCharacteristic.FirmwareRevision -> statusFragment.handleBaseCharacteristic(characteristic) + GattCharacteristic.FirmwareRevision -> statusFragment.handleBaseCharacteristic( + characteristic + ) GattCharacteristic.RgbLeds -> { val on = characteristic.getIntValue(gattCharacteristic.format, 0) @@ -209,21 +226,24 @@ class BlinkyThunderboardActivity : ThunderboardActivity(), ColorLEDControlListen val green = characteristic.getIntValue(gattCharacteristic.format, 2) val blue = characteristic.getIntValue(gattCharacteristic.format, 3) val ledState = LedRGBState( - on != null && on != 0, - red ?: 0, - green ?: 0, - blue ?: 0 + on != null && on != 0, + red ?: 0, + green ?: 0, + blue ?: 0 ) viewModel.colorLed.postValue(ledState) } - else -> { } + + else -> {} } } - override fun onCharacteristicWrite(gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - status: Int) { + override fun onCharacteristicWrite( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + status: Int + ) { super.onCharacteristicWrite(gatt, characteristic, status) gattQueue.handleCommandProcessed() if (status != BluetoothGatt.GATT_SUCCESS) return @@ -233,54 +253,63 @@ class BlinkyThunderboardActivity : ThunderboardActivity(), ColorLEDControlListen when (gattCharacteristic) { GattCharacteristic.Digital -> { val led0State = - (characteristic.value[0].toInt() and BlinkyThunderboardViewModel.LED_0_ON) != 0 + (characteristic.value[0].toInt() and BlinkyThunderboardViewModel.LED_0_ON) != 0 val led1State = - (characteristic.value[0].toInt() and BlinkyThunderboardViewModel.LED_1_ON) != 0 + (characteristic.value[0].toInt() and BlinkyThunderboardViewModel.LED_1_ON) != 0 viewModel.led0.postValue(led0State) viewModel.led1.postValue(led1State) } + GattCharacteristic.RgbLeds -> { val on = characteristic.getIntValue(gattCharacteristic.format, 0) val red = characteristic.getIntValue(gattCharacteristic.format, 1) val green = characteristic.getIntValue(gattCharacteristic.format, 2) val blue = characteristic.getIntValue(gattCharacteristic.format, 3) val ledState = LedRGBState( - on != null && on != 0, - red ?: 0, - green ?: 0, - blue ?: 0 + on != null && on != 0, + red ?: 0, + green ?: 0, + blue ?: 0 ) viewModel.colorLed.postValue(ledState) } + else -> { } } } - override fun onCharacteristicChanged(gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic) { + override fun onCharacteristicChanged( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic + ) { super.onCharacteristicChanged(gatt, characteristic) val gattCharacteristic = GattCharacteristic.fromUuid(characteristic.uuid) when (gattCharacteristic) { GattCharacteristic.BatteryLevel, - GattCharacteristic.PowerSource -> statusFragment.handleBaseCharacteristic(characteristic) + GattCharacteristic.PowerSource -> statusFragment.handleBaseCharacteristic( + characteristic + ) GattCharacteristic.Digital -> { val button0State = - (characteristic.value[0].toInt() and BlinkyThunderboardViewModel.BUTTON_0_ON) != 0 + (characteristic.value[0].toInt() and BlinkyThunderboardViewModel.BUTTON_0_ON) != 0 val button1State = - (characteristic.value[0].toInt() and BlinkyThunderboardViewModel.BUTTON_1_ON) != 0 + (characteristic.value[0].toInt() and BlinkyThunderboardViewModel.BUTTON_1_ON) != 0 viewModel.button0.postValue(button0State) viewModel.button1.postValue(button1State) } - else -> { } + + else -> {} } } - override fun onDescriptorRead(gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, - status: Int) { + override fun onDescriptorRead( + gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, + status: Int + ) { super.onDescriptorRead(gatt, descriptor, status) if (descriptor?.uuid == LED_MASK_DESCRIPTOR) { @@ -289,20 +318,23 @@ class BlinkyThunderboardActivity : ThunderboardActivity(), ColorLEDControlListen } - override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, - status: Int) { + override fun onDescriptorWrite( + gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, + status: Int + ) { super.onDescriptorWrite(gatt, descriptor, status) gattQueue.handleCommandProcessed() if (status != BluetoothGatt.GATT_SUCCESS) return when (descriptor.characteristic.uuid) { GattCharacteristic.BatteryLevel.uuid -> initControls() - else -> { } + else -> {} } } } companion object { - private val LED_MASK_DESCRIPTOR: UUID? = UUID.fromString("1c694489-8825-45cc-8720-28b54b1fbf00") + private val LED_MASK_DESCRIPTOR: UUID? = + UUID.fromString("1c694489-8825-45cc-8720-28b54b1fbf00") } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/motion/adapters/GdxAdapter.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/motion/adapters/GdxAdapter.kt index 2ee2518b..9b89b0ec 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/motion/adapters/GdxAdapter.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/thunderboard_demos/demos/motion/adapters/GdxAdapter.kt @@ -263,7 +263,8 @@ class GdxAdapter(private val backgroundColor: Int, modelType: String?) : ThunderBoardDevice.THUNDERBOARD_MODEL_BLUE_V2 -> this.modelType = ModelType.BLUE ThunderBoardDevice.THUNDERBOARD_MODEL_SENSE, ThunderBoardDevice.THUNDERBOARD_MODEL_DEV_KIT_V1, - ThunderBoardDevice.THUNDERBOARD_MODEL_DEV_KIT_V2 -> this.modelType = ModelType.SENSE + ThunderBoardDevice.THUNDERBOARD_MODEL_DEV_KIT_V2, + ThunderBoardDevice.THUNDERBOARD_MODEL_DEV_KIT_V3 -> this.modelType = ModelType.SENSE else -> this.modelType = ModelType.SENSE } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/AlertErrorDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/AlertErrorDialog.kt new file mode 100644 index 00000000..49e69cb6 --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/AlertErrorDialog.kt @@ -0,0 +1,40 @@ +package com.siliconlabs.bledemo.features.demo.wifi_ota_update + +import android.content.DialogInterface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment +import com.siliconlabs.bledemo.R +import kotlinx.android.synthetic.main.dialog_error.* + +class AlertErrorDialog( + private val otaErrorCallback: OtaErrorCallback +) : BaseDialogFragment( + hasCustomWidth = true, + isCanceledOnTouchOutside = false +) { + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.dialog_alert_error, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + btn_ok.setOnClickListener { + dismiss() + otaErrorCallback.onDismiss() + } + } + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + otaErrorCallback.onDismiss() + } + + interface OtaErrorCallback { + fun onDismiss() + } +} \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaFileManager.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaFileManager.kt new file mode 100644 index 00000000..5b5b7424 --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaFileManager.kt @@ -0,0 +1,53 @@ +package com.siliconlabs.bledemo.features.demo.wifi_ota_update + +import android.content.Context +import android.net.Uri +import android.provider.OpenableColumns +import android.widget.Toast +import com.siliconlabs.bledemo.R +import java.util.* + +class WiFiOtaFileManager(private val context: Context) { + + var otaFilename: String? = null + var otaFile: ByteArray? = null + + fun hasCorrectFileExtensionRPS(): Boolean { + return otaFilename?.toUpperCase(Locale.ROOT)?.contains(".RPS")!! + } + + fun readFilename(uri: Uri) { + var result: String? = null + if (uri.scheme == "content") { + val cursor = context.contentResolver.query(uri, null, null, null, null) + cursor?.use { c -> + if (c.moveToFirst()) { + result = c.getString(c.getColumnIndex(OpenableColumns.DISPLAY_NAME)) + } + c.close() + } + } + if (result == null) { + result = uri.path + val cut = result?.lastIndexOf('/') + if (cut != -1) { + result = result?.substring(cut!! + 1) + } + } + otaFilename = result + } + + fun readFile(uri: Uri) { + context.contentResolver.openInputStream(uri)?.let { + otaFile = ByteArray(it.available()) + it.read(otaFile) + } ?: Toast.makeText(context, context.getString(R.string.problem_while_preparing_the_file), + Toast.LENGTH_LONG).show() + } + + enum class UploadMode { + AUTO, + USER + } + +} \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaFileSelectionDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaFileSelectionDialog.kt new file mode 100644 index 00000000..648b5f07 --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaFileSelectionDialog.kt @@ -0,0 +1,77 @@ +package com.siliconlabs.bledemo.features.demo.wifi_ota_update + +import android.content.DialogInterface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.siliconlabs.bledemo.base.fragments.BaseDialogFragment +import com.siliconlabs.bledemo.databinding.DialogWifiOtaFileUpdateBinding +import kotlinx.android.synthetic.main.dialog_ota_file_selection_iop.ota_cancel + +class WiFiOtaFileSelectionDialog(private val cancelCallback: CancelCallback, + private val listener: FileSelectionListener, private val ipAddress: String?) : BaseDialogFragment( + hasCustomWidth = true, + isCanceledOnTouchOutside = false +) { + + private lateinit var _binding: DialogWifiOtaFileUpdateBinding + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + _binding = DialogWifiOtaFileUpdateBinding.inflate(inflater) + _binding.wifiIpAddress.text = ipAddress + return _binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupUiListeners() + ota_cancel.setOnClickListener { + dismiss() + cancelCallback.onDismiss() + } + } + + fun setupUiListeners() { + _binding.apply { + selectAppFileBtn.setOnClickListener { listener.onSelectFileButtonClicked() } + otaCancel.setOnClickListener { listener.onCancelButtonClicked() } + otaProceed.setOnClickListener { listener.onOtaButtonClicked() } + } + } + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + cancelCallback.onDismiss() + } + + fun checkPortNumberValid(): Boolean { + return _binding.portId.text.length >= 4 + } + fun changeFileName(newName: String?) { + _binding.selectAppFileBtn.text = newName + } + + fun enableUploadButton() { + _binding.otaProceed.isEnabled = true + } + + fun disableUploadButton() { + _binding.otaProceed.isEnabled = false + } + + fun getPortId() : String { + return _binding.portId.text.toString() + } + + interface FileSelectionListener { + fun onSelectFileButtonClicked() + fun onOtaButtonClicked() + fun onCancelButtonClicked() + } + + interface CancelCallback { + fun onDismiss() + } +} \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaProgressDialog.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaProgressDialog.kt new file mode 100644 index 00000000..03ea0677 --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/demo/wifi_ota_update/WiFiOtaProgressDialog.kt @@ -0,0 +1,65 @@ +package com.siliconlabs.bledemo.features.demo.wifi_ota_update + +import android.app.Dialog +import android.content.Context +import android.view.View +import android.widget.* +import com.siliconlabs.bledemo.R + +class WiFiOtaProgressDialog(context: Context) : Dialog(context) { + + var progressBar: ProgressBar + var dataRate: TextView + var dataSize: TextView + var filename: TextView + var steps: TextView + var chrono: Chronometer + var btnOtaEnd: Button + var btnCancel: Button + var sizename: TextView + var uploadImage: ProgressBar + var firmwareStatus: TextView + var wifiIpAddress: TextView + var wifiPort: TextView + + + init { + setContentView(R.layout.dialog_wifi_ota_progress) + window?.apply { + setLayout( + (context.resources.displayMetrics.widthPixels * 0.8f).toInt(), + LinearLayout.LayoutParams.WRAP_CONTENT + ) + window?.setBackgroundDrawableResource(android.R.color.transparent) + } + + progressBar = findViewById(R.id.ota_progress_bar) + dataRate = findViewById(R.id.data_rate) + dataSize = findViewById(R.id.ota_progress_percent) + filename = findViewById(R.id.file_name) + steps = findViewById(R.id.ota_number_of_steps) + chrono = findViewById(R.id.ota_chronometer) + btnOtaEnd = findViewById(R.id.otabutton) + btnCancel = findViewById(R.id.cancel) + sizename = findViewById(R.id.file_size) + uploadImage = findViewById(R.id.connecting_spinner) + firmwareStatus = findViewById(R.id.text_firmware_status) + wifiIpAddress = findViewById(R.id.wifi_ip_address) + wifiPort = findViewById(R.id.wifi_port) + } + + override fun show() { + super.show() + setCanceledOnTouchOutside(false) + } + + fun setProgressInfo(fileName: String?, fileSize: Int?, ipAddress: String?, port: String?) { + filename.text = fileName + steps.text = context.getString(R.string.iop_test_label_1_of_1) + sizename.text = context.getString(R.string.iop_test_n_bytes, fileSize) + uploadImage.visibility = View.VISIBLE + wifiIpAddress.text = ipAddress + wifiPort.text = port + } + +} \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/activities/IOPTestActivity.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/activities/IOPTestActivity.kt index 5f307038..76c8b342 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/activities/IOPTestActivity.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/activities/IOPTestActivity.kt @@ -94,14 +94,18 @@ class IOPTestActivity : AppCompatActivity() { private var mIndexRunning = -1 private var countReTest = 0 private var iopPhase3IndexStartChildrenTest = -1 - + private var iopPhase3BondingStep = 2 + private var iopPhase3ExtraDescriptor: BluetoothGattDescriptor? = null + private var read_CCCD_value = ByteArray(1) + var isCCCDPass = true private var testParametersService: BluetoothGattService? = null private var characteristicIOPPhase3Control: BluetoothGattCharacteristic? = null private var characteristicIOPPhase3Throughput: BluetoothGattCharacteristic? = null private var characteristicIOPPhase3ClientSupportedFeatures: BluetoothGattCharacteristic? = null private var characteristicIOPPhase3DatabaseHash: BluetoothGattCharacteristic? = null private var characteristicIOPPhase3IOPTestCaching: BluetoothGattCharacteristic? = null - private var characteristicIOPPhase3IOPTestServiceChangedIndication: BluetoothGattCharacteristic? = null + private var characteristicIOPPhase3IOPTestServiceChangedIndication: BluetoothGattCharacteristic? = + null private var characteristicIOPPhase3ServiceChanged: BluetoothGattCharacteristic? = null private var characteristicIOPPhase3DeviceName: BluetoothGattCharacteristic? = null @@ -113,7 +117,8 @@ class IOPTestActivity : AppCompatActivity() { private var mBluetoothEnableDialog: Dialog? = null private val mListCharacteristics: MutableList = ArrayList() - private val characteristicsPhase3Security: MutableList = ArrayList() + private val characteristicsPhase3Security: MutableList = + ArrayList() private var isDisabled = false private val mtuValue = 255 @@ -166,13 +171,17 @@ class IOPTestActivity : AppCompatActivity() { override fun onReceive(context: Context, intent: Intent) { val state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1) val prevState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1) - val msg = "Bond state change: state " + printBondState(state) + ", previous state " + printBondState(prevState) + val msg = + "Bond state change: state " + printBondState(state) + ", previous state " + printBondState( + prevState + ) Log.d(TAG, msg) if (state == BluetoothDevice.BOND_BONDED && prevState == BluetoothDevice.BOND_BONDING) { handler?.postDelayed({ if (getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_SECURITY].getStatusTest() == Common.IOP3_TC_STATUS_PROCESSING - /* || getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_CACHING] - .getStatusTest() == Common.IOP3_TC_STATUS_PROCESSING*/) { + || getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_LE_PRIVACY].getStatusTest() == Common.IOP3_TC_STATUS_PROCESSING + /* || getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_CACHING] + .getStatusTest() == Common.IOP3_TC_STATUS_PROCESSING*/) { if (mBluetoothGatt != null) { mListCharacteristics.clear() characteristicsPhase3Security.clear() @@ -188,7 +197,11 @@ class IOPTestActivity : AppCompatActivity() { } }, 1600) } else if (state == BluetoothDevice.BOND_BONDING) { - Toast.makeText(this@IOPTestActivity, R.string.iop_test_toast_bonding, Toast.LENGTH_LONG).show() + Toast.makeText( + this@IOPTestActivity, + R.string.iop_test_toast_bonding, + Toast.LENGTH_LONG + ).show() } } @@ -210,28 +223,36 @@ class IOPTestActivity : AppCompatActivity() { private fun printVariant(variant: Int): String { if (variant == BluetoothDevice.PAIRING_VARIANT_PIN) { - Toast.makeText(this@IOPTestActivity, R.string.iop_test_toast_press_passkey, Toast.LENGTH_LONG).show() + Toast.makeText( + this@IOPTestActivity, + R.string.iop_test_toast_press_passkey, + Toast.LENGTH_LONG + ).show() return "PAIRING_VARIANT_PIN" } return variant.toString() } } - private val bluetoothAdapterStateChangeListener: BroadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val action = intent.action - val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR) - if (action == BluetoothAdapter.ACTION_STATE_CHANGED) { - Log.d(TAG, "BluetoothAdapter.ACTION_STATE_CHANGED mIndexRunning: $mIndexRunning,state: $state") - when (state) { - BluetoothAdapter.STATE_OFF -> { - disconnectGatt(mBluetoothGatt) - handler?.removeCallbacksAndMessages(null) - finish() + private val bluetoothAdapterStateChangeListener: BroadcastReceiver = + object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val action = intent.action + val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR) + if (action == BluetoothAdapter.ACTION_STATE_CHANGED) { + Log.d( + TAG, + "BluetoothAdapter.ACTION_STATE_CHANGED mIndexRunning: $mIndexRunning,state: $state" + ) + when (state) { + BluetoothAdapter.STATE_OFF -> { + disconnectGatt(mBluetoothGatt) + handler?.removeCallbacksAndMessages(null) + finish() + } } } } } - } /** * Update data test failed by item @@ -243,13 +264,20 @@ class IOPTestActivity : AppCompatActivity() { scanLeDevice(false) getSiliconLabsTestInfo().listItemTest[index].setStatusTest(Common.IOP3_TC_STATUS_FAILED) } - POSITION_TEST_CONNECTION, POSITION_TEST_DISCOVER_SERVICE, POSITION_TEST_SERVICE -> disconnectGatt(mBluetoothGatt) + + POSITION_TEST_CONNECTION, POSITION_TEST_DISCOVER_SERVICE, POSITION_TEST_SERVICE -> disconnectGatt( + mBluetoothGatt + ) + else -> { } } + Log.d(TAG, "listItemTest.size " + getSiliconLabsTestInfo().listItemTest.size) + for (i in index until getSiliconLabsTestInfo().listItemTest.size) { getSiliconLabsTestInfo().listItemTest[i].setStatusTest(Common.IOP3_TC_STATUS_FAILED) } + Log.d(TAG, "POSITION_TEST_SCANNER " + POSITION_TEST_SCANNER) if (index >= POSITION_TEST_SCANNER) { isTestRunning = false isTestFinished = true @@ -281,6 +309,7 @@ class IOPTestActivity : AppCompatActivity() { Log.d(TAG, "scanLeDevice at POSITION_TEST_SCANNER") scanLeDevice(true) } + POSITION_TEST_CONNECTION -> connectToDevice(mBluetoothDevice) POSITION_TEST_DISCOVER_SERVICE -> mBluetoothGatt?.requestMtu(247) POSITION_TEST_SERVICE -> runChildrenTestCase(mIndexStartChildrenTest) @@ -288,11 +317,19 @@ class IOPTestActivity : AppCompatActivity() { Log.d(TAG, "POSITION_TEST_IOP3_THROUGHPUT") iopPhase3RunTestCaseThroughput(1) } + + POSITION_TEST_IOP3_LE_PRIVACY -> { + Log.d(TAG, "POSITION_TEST_IOP3_LE_PRIVACY") + iopPhase3RunTestCaseLEPrivacy(1) + } + POSITION_TEST_IOP3_SECURITY -> { Log.d(TAG, "POSITION_TEST_IOP3_SECURITY") iopPhase3IndexStartChildrenTest = 0 iopPhase3RunTestCaseSecurity(iopPhase3IndexStartChildrenTest, 1) } + + /* POSITION_TEST_IOP3_CACHING -> { isServiceChangedIndication = 1 @@ -303,10 +340,12 @@ class IOPTestActivity : AppCompatActivity() { iopPhase3IndexStartChildrenTest = 0 startOtaTestCase(iopPhase3IndexStartChildrenTest) } + POSITION_TEST_IOP3_OTA_WITHOUT_ACK -> { iopPhase3IndexStartChildrenTest = 1 startOtaTestCase(iopPhase3IndexStartChildrenTest) } + else -> { } } @@ -325,10 +364,13 @@ class IOPTestActivity : AppCompatActivity() { POSITION_TEST_SCANNER -> { itemTestCaseInfo.timeStart = mStartTimeScanner itemTestCaseInfo.setTimeEnd(System.currentTimeMillis()) - Log.d(TAG, "finishItemTest: scanLeDevice startTime: " + itemTestCaseInfo.getTimeEnd()) + Log.d( + TAG, + "finishItemTest: scanLeDevice startTime: " + itemTestCaseInfo.getTimeEnd() + ) if (itemTestCaseInfo.getStatusTest() == Common.IOP3_TC_STATUS_FAILED) { countReTest++ - if (countReTest < 6) { + if (countReTest < 7) { itemTestCaseInfo.setStatusTest(-1) startItemTest(item) return @@ -337,13 +379,17 @@ class IOPTestActivity : AppCompatActivity() { countReTest = 0 } } + POSITION_TEST_CONNECTION -> { itemTestCaseInfo.timeStart = mStartTimeConnection itemTestCaseInfo.setTimeEnd(System.currentTimeMillis()) - Log.d(TAG, "finishItemTest: POSITION_TEST_CONNECTION time " + (System.currentTimeMillis() - mStartTimeConnection)) + Log.d( + TAG, + "finishItemTest: POSITION_TEST_CONNECTION time " + (System.currentTimeMillis() - mStartTimeConnection) + ) if (itemTestCaseInfo.getStatusTest() == Common.IOP3_TC_STATUS_FAILED) { countReTest++ - if (countReTest < 6) { + if (countReTest < 7) { if (isConnected) { isConnected = false mBluetoothGatt?.disconnect() @@ -357,31 +403,51 @@ class IOPTestActivity : AppCompatActivity() { countReTest = 0 } } + POSITION_TEST_DISCOVER_SERVICE -> { itemTestCaseInfo.timeStart = mStartTimeDiscover itemTestCaseInfo.setTimeEnd(mEndTimeDiscover) - Log.d(TAG, "finishItemTest: POSITION_TEST_DISCOVER_SERVICE " + (mEndTimeDiscover - mStartTimeDiscover) + "ms") + Log.d( + TAG, + "finishItemTest: POSITION_TEST_DISCOVER_SERVICE " + (mEndTimeDiscover - mStartTimeDiscover) + "ms" + ) countReTest = 0 } + POSITION_TEST_SERVICE -> { getItemTestCaseInfo(POSITION_TEST_SERVICE).checkStatusItemService() Log.d(TAG, "finishItemTest: POSITION_TEST_SERVICE") countReTest = 0 } + POSITION_TEST_IOP3_THROUGHPUT -> { val throughputAcceptable = calculateAcceptableThroughput() itemTestCaseInfo.setThroughputBytePerSec(mByteSpeed, throughputAcceptable) - Log.d(TAG, "finishItemTest: POSITION_TEST_IOP3_THROUGHPUT mByteSpeed $mByteSpeed throughputAcceptable $throughputAcceptable") + Log.d( + TAG, + "finishItemTest: POSITION_TEST_IOP3_THROUGHPUT mByteSpeed $mByteSpeed throughputAcceptable $throughputAcceptable" + ) countReTest = 0 } + + POSITION_TEST_IOP3_LE_PRIVACY -> { + getItemTestCaseInfo(POSITION_TEST_IOP3_LE_PRIVACY).checkStatusItemService() + Log.d(TAG, "finishItemTest: POSITION_TEST_IOP3_LE_PRIVACY") + countReTest = 0 + isTestRunning = false + isTestFinished = true + } + POSITION_TEST_IOP3_SECURITY -> { getItemTestCaseInfo(POSITION_TEST_IOP3_SECURITY).checkStatusItemService() Log.d(TAG, "finishItemTest: POSITION_TEST_IOP3_SECURITY") countReTest = 0 - isTestRunning = false - isTestFinished = true + // isTestRunning = false + // isTestFinished = true mBluetoothService?.isNotificationEnabled = true } + + /* POSITION_TEST_IOP3_CACHING -> { isTestRunning = false @@ -392,39 +458,62 @@ class IOPTestActivity : AppCompatActivity() { else -> { } } - if (item != POSITION_TEST_IOP3_SECURITY && countReTest == 0) { + if (item != POSITION_TEST_IOP3_LE_PRIVACY && countReTest == 0) { startItemTest(item + 1) } + /*if (item != POSITION_TEST_IOP3_SECURITY && countReTest == 0) { + startItemTest(item + 1) + }*/ + + + runOnUiThread { updateUIFooter(isTestRunning) } mListener?.updateUi() } private fun calculateAcceptableThroughput(): Int { - val notLegacyFw = getSiliconLabsTestInfo().firmwareVersion.split('.')[0].let { it != "" && it.toInt() >= 6 } + val notLegacyFw = + getSiliconLabsTestInfo().firmwareVersion.split('.')[0].let { it != "" && it.toInt() >= 6 } val knownPhy = currentRxPhy in arrayOf(BluetoothDevice.PHY_LE_1M, BluetoothDevice.PHY_LE_2M) - return if(notLegacyFw && knownPhy) { + + + return if (notLegacyFw && knownPhy) { try { - Log.d(TAG, "finishItemTest: POSITION_TEST_IOP3_THROUGHPUT calculating acceptable throughput (new method)") + Log.d( + TAG, + "finishItemTest: POSITION_TEST_IOP3_THROUGHPUT calculating acceptable throughput (new method)" + ) val connectionParameters = getSiliconLabsTestInfo().connectionParameters!! val pdu = connectionParameters.pdu - val tPacketMicroseconds: Int = when(currentRxPhy){ + Log.d(TAG, "pdu : " + pdu) + val tPacketMicroseconds: Int = when (currentRxPhy) { BluetoothDevice.PHY_LE_1M -> (8 * pdu) + 492 else -> (4 * pdu) + 396 } - val connectionIntervalMs: Double = getSiliconLabsTestInfo().connectionParameters!!.interval + val connectionIntervalMs: Double = + getSiliconLabsTestInfo().connectionParameters!!.interval val connectionIntervalMicroseconds = connectionIntervalMs * 1000 val connectionIntervalSeconds = connectionIntervalMs / 1000 + Log.d(TAG, "connectionIntervalMs " + connectionIntervalMs) val numPacket = floor(connectionIntervalMicroseconds / tPacketMicroseconds) val sizeEffective = mtu - 3 val fragmentationCount = ceil(mtu.toFloat() / (pdu - 4)) - val velExpected = numPacket * sizeEffective / fragmentationCount / connectionIntervalSeconds + val velExpected = + numPacket * sizeEffective / fragmentationCount / connectionIntervalSeconds (0.5 * velExpected).toInt() } catch (e: NullPointerException) { - Log.d(TAG, "finishItemTest: POSITION_TEST_IOP3_THROUGHPUT Failed to calculate acceptable throughput") + Log.d( + TAG, + "finishItemTest: POSITION_TEST_IOP3_THROUGHPUT Failed to calculate acceptable throughput" + ) 0 } } else { - Log.d(TAG, "finishItemTest: POSITION_TEST_IOP3_THROUGHPUT calculating acceptable throughput (legacy method)") + + Log.d( + TAG, + "finishItemTest: POSITION_TEST_IOP3_THROUGHPUT calculating acceptable throughput (legacy method)" + ) (mPDULength) * 4 * 1000 * 65 / 1500 } } @@ -438,9 +527,15 @@ class IOPTestActivity : AppCompatActivity() { /** * Update data test after listening bluetooth gatt callback */ - private fun updateDataTest(characteristic: BluetoothGattCharacteristic?, type: Int, status: Int) { + private fun updateDataTest( + characteristic: BluetoothGattCharacteristic?, + type: Int, + status: Int + ) { if (characteristic != null) { - for (itemChildrenTest: ChildrenItemTestInfo in getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!) { + for (itemChildrenTest: ChildrenItemTestInfo in getListChildrenItemTestCase( + POSITION_TEST_SERVICE + )!!) { if (itemChildrenTest.characteristic?.uuid.toString() == characteristic.uuid.toString()) { itemChildrenTest.statusRunTest = 1 // 0 running, 1 finish test itemChildrenTest.valueMtu = mtu @@ -456,7 +551,9 @@ class IOPTestActivity : AppCompatActivity() { itemChildrenTest.setDataAndCompareResult(characteristic) } } - for (itemChildrenTest: ChildrenItemTestInfo in getListChildrenItemTestCase(POSITION_TEST_IOP3_SECURITY)!!) { + for (itemChildrenTest: ChildrenItemTestInfo in getListChildrenItemTestCase( + POSITION_TEST_IOP3_SECURITY + )!!) { if (itemChildrenTest.characteristic?.uuid.toString() == characteristic.uuid.toString()) { itemChildrenTest.statusRunTest = 1 // 0 running, 1 finish test when (type) { @@ -468,6 +565,9 @@ class IOPTestActivity : AppCompatActivity() { itemChildrenTest.setDataAndCompareResult(characteristic) } } + + + if (mIndexRunning == POSITION_TEST_IOP3_OTA_ACK || mIndexRunning == POSITION_TEST_IOP3_OTA_WITHOUT_ACK) { if (!otaProcess) { testParametersService?.let { readCharacteristic(it.characteristics[0]) } @@ -477,15 +577,27 @@ class IOPTestActivity : AppCompatActivity() { } else { checkIOP3OTA(mIndexRunning, Common.IOP3_TC_STATUS_FAILED) } - handler?.postDelayed( { - finishItemTest(mIndexRunning, getSiliconLabsTestInfo().listItemTest[mIndexRunning]) - }, 100) /* Read testParametersService characteristic before starting next test case. */ + handler?.postDelayed( + { + finishItemTest( + mIndexRunning, + getSiliconLabsTestInfo().listItemTest[mIndexRunning] + ) + }, + 100 + ) /* Read testParametersService characteristic before starting next test case. */ } } } else if (characteristicIOPPhase3Control?.uuid.toString() == characteristic.uuid.toString()) { if (getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_THROUGHPUT].getStatusTest() == Common.IOP3_TC_STATUS_PROCESSING) { iopPhase3RunTestCaseThroughput(0) - } /* else if (getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_CACHING] + } /*else if (getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_LE_PRIVACY].getStatusTest() == Common.IOP3_TC_STATUS_PROCESSING) { + iopPhase3RunTestCaseLEPrivacy(0) + finishItemTest( + POSITION_TEST_IOP3_LE_PRIVACY, + getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_LE_PRIVACY] + ) + }*/ /* else if (getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_CACHING] .getStatusTest() == Common.IOP3_TC_STATUS_PROCESSING) { if (isServiceChangedIndication == 0) { handler?.postDelayed({ @@ -501,7 +613,8 @@ class IOPTestActivity : AppCompatActivity() { if (isServiceChangedIndication == 1) { if (characteristicIOPPhase3IOPTestServiceChangedIndication != null) { if (characteristicIOPPhase3IOPTestServiceChangedIndication?.uuid.toString() == characteristic.uuid.toString()) { - val itemChildrenTest = getListChildrenItemTestCase(POSITION_TEST_IOP3_CACHING)!![isServiceChangedIndication] + val itemChildrenTest = + getListChildrenItemTestCase(POSITION_TEST_IOP3_CACHING)!![isServiceChangedIndication] itemChildrenTest.statusRead = status itemChildrenTest.statusRunTest = 1 itemChildrenTest.setDataAndCompareResult((itemChildrenTest.characteristic)!!) @@ -510,15 +623,23 @@ class IOPTestActivity : AppCompatActivity() { } else { if (characteristicIOPPhase3DatabaseHash?.uuid.toString() == characteristic.uuid.toString()) { if (iopPhase3DatabaseHash != null) { - val itemTestInfo = getListChildrenItemTestCase(POSITION_TEST_IOP3_CACHING)!![0] - if (!itemTestInfo.compareValueCharacteristic(iopPhase3DatabaseHash!!, Converters.getHexValue(characteristic.value))) { + val itemTestInfo = + getListChildrenItemTestCase(POSITION_TEST_IOP3_CACHING)!![0] + if (!itemTestInfo.compareValueCharacteristic( + iopPhase3DatabaseHash!!, + Converters.getHexValue(characteristic.value) + ) + ) { Log.d(TAG, "iopPhase3DatabaseHash has changed") mListCharacteristics.clear() characteristicsPhase3Security.clear() refreshServices() } } else { - setNotificationForCharacteristic(characteristicIOPPhase3ServiceChanged, Notifications.DISABLED) + setNotificationForCharacteristic( + characteristicIOPPhase3ServiceChanged, + Notifications.DISABLED + ) } iopPhase3DatabaseHash = Converters.getHexValue(characteristic.value) Log.d(TAG, "characteristicIOPPhase3DatabaseHash: $iopPhase3DatabaseHash") @@ -526,7 +647,8 @@ class IOPTestActivity : AppCompatActivity() { readCharacteristic(characteristicIOPPhase3DatabaseHash) } else if (characteristicIOPPhase3IOPTestCaching != null) { if (characteristicIOPPhase3IOPTestCaching?.uuid.toString() == characteristic.uuid.toString()) { - val itemChildrenTest = getListChildrenItemTestCase(POSITION_TEST_IOP3_CACHING)!![isServiceChangedIndication] + val itemChildrenTest = + getListChildrenItemTestCase(POSITION_TEST_IOP3_CACHING)!![isServiceChangedIndication] itemChildrenTest.statusRead = status itemChildrenTest.statusRunTest = 1 itemChildrenTest.setDataAndCompareResult((itemChildrenTest.characteristic)!!) @@ -534,11 +656,15 @@ class IOPTestActivity : AppCompatActivity() { if (itemChildrenTest.statusChildrenTest) { getItemTestCaseInfo(POSITION_TEST_IOP3_CACHING).setStatusTest(Common.IOP3_TC_STATUS_PASS) } - finishItemTest(POSITION_TEST_IOP3_CACHING, getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_CACHING]) + finishItemTest( + POSITION_TEST_IOP3_CACHING, + getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_CACHING] + ) } } } } + } } @@ -565,7 +691,12 @@ class IOPTestActivity : AppCompatActivity() { mBluetoothBinding = object : BluetoothService.Binding(applicationContext) { override fun onBound(service: BluetoothService?) { service?.isNotificationEnabled = false - mBluetoothGatt = mBluetoothDevice?.connectGatt(applicationContext, false, gattCallback, BluetoothDevice.TRANSPORT_LE) + mBluetoothGatt = mBluetoothDevice?.connectGatt( + applicationContext, + false, + gattCallback, + BluetoothDevice.TRANSPORT_LE + ) } } mBluetoothBinding?.bind() @@ -623,6 +754,38 @@ class IOPTestActivity : AppCompatActivity() { }, 1000) } + private var onConnectionSuccess: (() -> Unit)? = null + private var onConnectionFailure: (() -> Unit)? = null + + + private fun connectToDevice( + bluetoothDevice: BluetoothDevice?, + onConnectionSuccess: (() -> Unit)? = null, + onConnectionFailure: (() -> Unit)? = null + ) { + Log.d(TAG, "connectToDevice() with param called with: bluetoothDevice = $bluetoothDevice") + mStartTimeConnection = System.currentTimeMillis() + + // Save the callback functions + this.onConnectionSuccess = onConnectionSuccess + this.onConnectionFailure = onConnectionFailure + + Log.d(TAG, "connectToDevice() with param, postDelayed connectionRunnable") + handler?.postDelayed({ + // Execute the connection logic after the delay + mBluetoothBinding = object : BluetoothService.Binding(applicationContext) { + override fun onBound(service: BluetoothService?) { + mBluetoothService = service + service?.isNotificationEnabled = false + service?.connectGatt(bluetoothDevice!!, false, gattCallback) + mBluetoothGatt = service?.connectedGatt!! + } + } + mBluetoothBinding?.bind() + }, 15000) // 15 seconds delay + } + + private fun connectToDevice(bluetoothDevice: BluetoothDevice?) { Log.d(TAG, "connectToDevice() called with: bluetoothDevice = $bluetoothDevice") mStartTimeConnection = System.currentTimeMillis() @@ -667,21 +830,41 @@ class IOPTestActivity : AppCompatActivity() { btn_start_and_stop_test.apply { text = getString(R.string.button_run_test) isEnabled = true - backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this@IOPTestActivity, R.color.silabs_blue)) + backgroundTintList = ColorStateList.valueOf( + ContextCompat.getColor( + this@IOPTestActivity, + R.color.silabs_blue + ) + ) } if (isTestFinished) { shareMenuItem?.isVisible = true + mBluetoothDevice?.let { removeBond(it) } } } else { btn_start_and_stop_test?.apply { text = getString(R.string.button_waiting) - backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this@IOPTestActivity, R.color.silabs_inactive_light)) + backgroundTintList = ColorStateList.valueOf( + ContextCompat.getColor( + this@IOPTestActivity, + R.color.silabs_inactive_light + ) + ) isEnabled = false } shareMenuItem?.isVisible = false } } + private fun removeBond(device: BluetoothDevice): Boolean { + return try { + return device::class.java.getMethod("removeBond").invoke(device) as Boolean + } catch (e: Exception) { + false + } + } + + /** * Show information device test and progress test item */ @@ -689,15 +872,21 @@ class IOPTestActivity : AppCompatActivity() { val siliconlabsTestInfo = getSiliconLabsTestInfo() runOnUiThread { if (isInformation || item == POSITION_TEST_CONNECTION) { - tv_fw_name.text = getString(R.string.iop_test_label_fw_name, siliconlabsTestInfo.fwName) - tv_device_name.text = getString(R.string.iop_test_label_device_name, siliconlabsTestInfo.phoneName) + tv_fw_name.text = + getString(R.string.iop_test_label_fw_name, siliconlabsTestInfo.fwName) + tv_device_name.text = + getString(R.string.iop_test_label_device_name, siliconlabsTestInfo.phoneName) } val total = siliconlabsTestInfo.totalTestCase.toString() if (item == POSITION_TEST_SERVICE) { testCaseCount = item + mIndexStartChildrenTest + 1 } Log.d(TAG, "The number of test case $testCaseCount") - tv_progress.text = getString(R.string.iop_test_label_progress_count_test, testCaseCount.toString(), total) + tv_progress.text = getString( + R.string.iop_test_label_progress_count_test, + testCaseCount.toString(), + total + ) } } @@ -783,7 +972,11 @@ class IOPTestActivity : AppCompatActivity() { private fun checkIfBluetoothIsSupported() { if (BluetoothAdapter.getDefaultAdapter() == null) { - Toast.makeText(this, R.string.iop_test_toast_bluetooth_not_supported, Toast.LENGTH_SHORT).show() + Toast.makeText( + this, + R.string.iop_test_toast_bluetooth_not_supported, + Toast.LENGTH_SHORT + ).show() finish() } } @@ -802,10 +995,12 @@ class IOPTestActivity : AppCompatActivity() { saveLogFile() true } + android.R.id.home -> { onBackPressed() true } + else -> false } } @@ -822,9 +1017,19 @@ class IOPTestActivity : AppCompatActivity() { } private fun registerBroadcastReceivers() { - registerReceiver(mBondStateReceiver, IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) - registerReceiver(mPairRequestReceiver, IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST)) - registerReceiver(bluetoothAdapterStateChangeListener, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)) + registerReceiver( + mBondStateReceiver, IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED), + RECEIVER_EXPORTED + ) + registerReceiver( + mPairRequestReceiver, IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST), + RECEIVER_EXPORTED + ) + registerReceiver( + bluetoothAdapterStateChangeListener, + IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED), + RECEIVER_EXPORTED + ) } private fun handleClickEvents() { @@ -887,15 +1092,15 @@ class IOPTestActivity : AppCompatActivity() { */ private fun showDialogConfirmStopTest() { AlertDialog.Builder(this) - .setMessage(getString(R.string.iop_test_message_stop_test)) - .setPositiveButton(getString(R.string.button_ok)) { dialog, _ -> - dialog.dismiss() - mBluetoothService?.isNotificationEnabled = true - disconnectGatt(mBluetoothGatt) - finish() - }.setNegativeButton(getString(R.string.button_cancel)) { dialog, _ -> - dialog.dismiss() - }.show() + .setMessage(getString(R.string.iop_test_message_stop_test)) + .setPositiveButton(getString(R.string.button_ok)) { dialog, _ -> + dialog.dismiss() + mBluetoothService?.isNotificationEnabled = true + disconnectGatt(mBluetoothGatt) + finish() + }.setNegativeButton(getString(R.string.button_cancel)) { dialog, _ -> + dialog.dismiss() + }.show() } /** @@ -916,6 +1121,7 @@ class IOPTestActivity : AppCompatActivity() { mBluetoothEnableDialog?.show() } } + GBL_FILE_CHOICE_REQUEST_CODE -> { intent?.data?.let { otaFileManager?.readFilename(it) @@ -924,9 +1130,14 @@ class IOPTestActivity : AppCompatActivity() { otaFileManager?.readFile(it) otaFileSelectionDialog?.enableUploadButton() } else { - Toast.makeText(this, getString(R.string.incorrect_file), Toast.LENGTH_SHORT).show() + Toast.makeText(this, getString(R.string.incorrect_file), Toast.LENGTH_SHORT) + .show() } - } ?: Toast.makeText(this, getString(R.string.chosen_file_not_found), Toast.LENGTH_SHORT).show() + } ?: Toast.makeText( + this, + getString(R.string.chosen_file_not_found), + Toast.LENGTH_SHORT + ).show() } } } @@ -934,7 +1145,10 @@ class IOPTestActivity : AppCompatActivity() { /** * Enable indicate by characteristics */ - private fun setIndicationProperty(characteristic: BluetoothGattCharacteristic?, indicate: Notifications) { + private fun setIndicationProperty( + characteristic: BluetoothGattCharacteristic?, + indicate: Notifications + ) { setNotificationForCharacteristic( mBluetoothGatt!!, characteristic, @@ -946,7 +1160,10 @@ class IOPTestActivity : AppCompatActivity() { /** * Enable notification by characteristics */ - private fun setNotificationForCharacteristic(characteristic: BluetoothGattCharacteristic?, notifications: Notifications) { + private fun setNotificationForCharacteristic( + characteristic: BluetoothGattCharacteristic?, + notifications: Notifications + ) { setNotificationForCharacteristic( mBluetoothGatt!!, characteristic, @@ -955,10 +1172,49 @@ class IOPTestActivity : AppCompatActivity() { ) } + + /** + * Write down the value in characteristics and wait for response + */ + private fun writeValueToCharacteristic( + characteristic: BluetoothGattCharacteristic?, + value: String?, + byteValues: ByteArray?, + callback: (Boolean) -> Unit // Callback parameter + ) { + var newValue: ByteArray? = null + if (byteValues != null) { + newValue = byteValues + } else if (!TextUtils.isEmpty(value)) { + newValue = Converters.hexToByteArray(value!!) + } + if (isSetProperty(Common.PropertyType.WRITE, characteristic!!.properties)) { + characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + } else if (isSetProperty( + Common.PropertyType.WRITE_NO_RESPONSE, + characteristic.properties + ) + ) { + characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE + } + characteristic.value = newValue + Log.d(TAG, "writeValueToCharacteristic " + characteristic.uuid.toString()) + + // Perform the write operation asynchronously + val success = mBluetoothGatt?.writeCharacteristic(characteristic) ?: false + + // Invoke the callback with the write operation result + callback(success) + } + /** * Write down the value in characteristics */ - private fun writeValueToCharacteristic(characteristic: BluetoothGattCharacteristic?, value: String?, byteValues: ByteArray?) { + private fun writeValueToCharacteristic( + characteristic: BluetoothGattCharacteristic?, + value: String?, + byteValues: ByteArray? + ) { var newValue: ByteArray? = null if (byteValues != null) { newValue = byteValues @@ -967,13 +1223,23 @@ class IOPTestActivity : AppCompatActivity() { } if (isSetProperty(Common.PropertyType.WRITE, characteristic!!.properties)) { characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - } else if (isSetProperty(Common.PropertyType.WRITE_NO_RESPONSE, characteristic.properties)) { + } else if (isSetProperty( + Common.PropertyType.WRITE_NO_RESPONSE, + characteristic.properties + ) + ) { characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE } characteristic.value = newValue Log.d(TAG, "writeValueToCharacteristic " + characteristic.uuid.toString()) if (!mBluetoothGatt!!.writeCharacteristic(characteristic)) { - Log.e(TAG, String.format("ERROR: writeCharacteristic failed for characteristic: %s", characteristic.uuid)) + Log.e( + TAG, + String.format( + "ERROR: writeCharacteristic failed for characteristic: %s", + characteristic.uuid + ) + ) } } @@ -1016,15 +1282,18 @@ class IOPTestActivity : AppCompatActivity() { CommonUUID.Characteristic.IOP_TEST_PHASE3_SERVICE_CHANGED.toString() == item.uuid.toString() -> { characteristicIOPPhase3ServiceChanged = item } + CommonUUID.Characteristic.IOP_TEST_PHASE3_CLIENT_SUPPORT_FEATURES.toString() == item.uuid.toString() -> { characteristicIOPPhase3ClientSupportedFeatures = item } + CommonUUID.Characteristic.IOP_TEST_PHASE3_DATABASE_HASH.toString() == item.uuid.toString() -> { characteristicIOPPhase3DatabaseHash = item } } } } + CommonUUID.Service.UUID_GENERIC_ACCESS.toString() -> { val gattCharacteristics = gattService.characteristics for (item: BluetoothGattCharacteristic in gattCharacteristics) { @@ -1034,10 +1303,12 @@ class IOPTestActivity : AppCompatActivity() { } } } + CommonUUID.Service.TEST_PARAMETERS.toString() -> { count++ testParametersService = gattService } + CommonUUID.Service.UUID_PROPERTIES_SERVICE.toString(), CommonUUID.Service.UUID_CHARACTERISTICS_SERVICE.toString() -> { count++ @@ -1046,6 +1317,7 @@ class IOPTestActivity : AppCompatActivity() { mListCharacteristics.add(item) } } + CommonUUID.Service.UUID_PHASE3_SERVICE.toString() -> { Log.d(TAG, "mBluetoothGattServicePhase3 " + gattService.uuid.toString()) val gattCharacteristicsIOPPhase3 = gattService.characteristics @@ -1055,17 +1327,27 @@ class IOPTestActivity : AppCompatActivity() { CommonUUID.Characteristic.IOP_TEST_PHASE3_CONTROL.toString() -> { characteristicIOPPhase3Control = item } + CommonUUID.Characteristic.IOP_TEST_THROUGHPUT.toString() -> { characteristicIOPPhase3Throughput = item } + CommonUUID.Characteristic.IOP_TEST_GATT_CATCHING.toString() -> { characteristicIOPPhase3IOPTestCaching = item - Log.d(TAG, "characteristicIOPPhase3IOPTestCaching " + characteristicIOPPhase3IOPTestCaching?.uuid.toString()) + Log.d( + TAG, + "characteristicIOPPhase3IOPTestCaching " + characteristicIOPPhase3IOPTestCaching?.uuid.toString() + ) } + CommonUUID.Characteristic.IOP_TEST_SERVICE_CHANGED_INDICATION.toString() -> { characteristicIOPPhase3IOPTestServiceChangedIndication = item - Log.d(TAG, "characteristicIOPPhase3IOPTestServiceChangedIndication " + characteristicIOPPhase3IOPTestServiceChangedIndication?.uuid.toString()) + Log.d( + TAG, + "characteristicIOPPhase3IOPTestServiceChangedIndication " + characteristicIOPPhase3IOPTestServiceChangedIndication?.uuid.toString() + ) } + CommonUUID.Characteristic.IOP_TEST_SECURITY_PAIRING.toString(), CommonUUID.Characteristic.IOP_TEST_SECURITY_AUTHENTICATION.toString(), CommonUUID.Characteristic.IOP_TEST_SECURITY_BONDING.toString() -> { @@ -1075,14 +1357,27 @@ class IOPTestActivity : AppCompatActivity() { } } } + CommonUUID.Service.UUID_BLE_OTA.toString() -> { val gattCharacteristics = gattService.characteristics for (gattCharacteristic: BluetoothGattCharacteristic in gattCharacteristics) { val characteristicUUID = gattCharacteristic.uuid.toString() - Log.i(TAG, "onServicesDiscovered Characteristic UUID " + characteristicUUID + " - Properties: " + gattCharacteristic.properties) + Log.i( + TAG, + "onServicesDiscovered Characteristic UUID " + characteristicUUID + " - Properties: " + gattCharacteristic.properties + ) if (gattCharacteristic.uuid.toString() == ota_control.toString()) { - if (gattCharacteristics.contains(mBluetoothGatt?.getService(ota_service)?.getCharacteristic(ota_data))) { - if (!gattServices.contains(mBluetoothGatt?.getService(homekit_service))) { + if (gattCharacteristics.contains( + mBluetoothGatt?.getService(ota_service) + ?.getCharacteristic(ota_data) + ) + ) { + if (!gattServices.contains( + mBluetoothGatt?.getService( + homekit_service + ) + ) + ) { Log.i(TAG, "onServicesDiscovered Device in DFU Mode") mBluetoothGatt?.requestMtu(247) } else { @@ -1107,23 +1402,28 @@ class IOPTestActivity : AppCompatActivity() { } for (i in getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indices) { for (j in i until mListCharacteristics.size) { - getListChildrenItemTestCase(POSITION_TEST_SERVICE)!![i].characteristic = mListCharacteristics[j] + getListChildrenItemTestCase(POSITION_TEST_SERVICE)!![i].characteristic = + mListCharacteristics[j] break } } for (i in getListChildrenItemTestCase(POSITION_TEST_IOP3_SECURITY)!!.indices) { for (j in i until characteristicsPhase3Security.size) { - getListChildrenItemTestCase(POSITION_TEST_IOP3_SECURITY)!![i].characteristic = characteristicsPhase3Security[j] + getListChildrenItemTestCase(POSITION_TEST_IOP3_SECURITY)!![i].characteristic = + characteristicsPhase3Security[j] break } } + + when (mIndexRunning) { POSITION_TEST_DISCOVER_SERVICE -> { testParametersService?.let { handler?.postDelayed({ readCharacteristic(it.characteristics[0]) }, 5000) } } + POSITION_TEST_IOP3_OTA_WITHOUT_ACK, POSITION_TEST_IOP3_OTA_ACK -> { if (!otaProcess) { @@ -1133,19 +1433,40 @@ class IOPTestActivity : AppCompatActivity() { }, 5000) } } + POSITION_TEST_IOP3_THROUGHPUT -> { mEndThroughputNotification = false - finishItemTest(POSITION_TEST_IOP3_THROUGHPUT, getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_THROUGHPUT]) + finishItemTest( + POSITION_TEST_IOP3_THROUGHPUT, + getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_THROUGHPUT] + ) } + + POSITION_TEST_IOP3_LE_PRIVACY -> { + return + /* finishItemTest( + POSITION_TEST_IOP3_LE_PRIVACY, + getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_LE_PRIVACY] + )*/ + } + POSITION_TEST_IOP3_SECURITY -> { - iopPhase3RunTestCaseSecurity(iopPhase3IndexStartChildrenTest, 0) + if (iopPhase3BondingStep == 2) { + iopPhase3RunTestCaseSecurity(iopPhase3IndexStartChildrenTest, 0) + } else { + iopPhase3RunTestCaseBonding(6) + } + // iopPhase3RunTestCaseSecurity(iopPhase3IndexStartChildrenTest, 0) } + POSITION_TEST_IOP3_CACHING -> { if (characteristicIOPPhase3IOPTestCaching != null) { - getListChildrenItemTestCase(POSITION_TEST_IOP3_CACHING)!![0].characteristic = characteristicIOPPhase3IOPTestCaching + getListChildrenItemTestCase(POSITION_TEST_IOP3_CACHING)!![0].characteristic = + characteristicIOPPhase3IOPTestCaching readCharacteristic(characteristicIOPPhase3IOPTestCaching) } else if (characteristicIOPPhase3IOPTestServiceChangedIndication != null) { - getListChildrenItemTestCase(POSITION_TEST_IOP3_CACHING)!![1].characteristic = characteristicIOPPhase3IOPTestServiceChangedIndication + getListChildrenItemTestCase(POSITION_TEST_IOP3_CACHING)!![1].characteristic = + characteristicIOPPhase3IOPTestServiceChangedIndication readCharacteristic(characteristicIOPPhase3IOPTestServiceChangedIndication) } } @@ -1162,20 +1483,28 @@ class IOPTestActivity : AppCompatActivity() { POSITION_TEST_DISCOVER_SERVICE -> { getSiliconLabsTestInfo().firmwareVersion = parseFirmwareVersion(payload) } + POSITION_TEST_IOP3_OTA_ACK -> { getSiliconLabsTestInfo().firmwareAckVersion = parseFirmwareVersion(payload) } + POSITION_TEST_IOP3_OTA_WITHOUT_ACK -> { getSiliconLabsTestInfo().firmwareUnackVersion = parseFirmwareVersion(payload) } - else -> { } + + else -> {} } readCharacteristic(testParametersService?.characteristics?.get(1)) } private fun getFirmwareVersion(): String { - for (fwString in listOf(getSiliconLabsTestInfo().firmwareUnackVersion, getSiliconLabsTestInfo().firmwareAckVersion)) { - if (fwString != "") { return fwString } + for (fwString in listOf( + getSiliconLabsTestInfo().firmwareUnackVersion, + getSiliconLabsTestInfo().firmwareAckVersion + )) { + if (fwString != "") { + return fwString + } } return getSiliconLabsTestInfo().firmwareVersion } @@ -1187,31 +1516,40 @@ class IOPTestActivity : AppCompatActivity() { getSiliconLabsTestInfo().iopBoard = IopBoard.fromBoardCode(payload[0]) payloadIndex = 2 } + else -> readCharacteristic(getModelNumberStringCharacteristic()) } getSiliconLabsTestInfo().connectionParameters = ConnectionParameters( - mtu = Converters.calculateDecimalValue( - payload.copyOfRange(payloadIndex, payloadIndex+2), isBigEndian = false), - pdu = Converters.calculateDecimalValue( - payload.copyOfRange(payloadIndex+2, payloadIndex+4), isBigEndian = false), - interval = Converters.calculateDecimalValue( - payload.copyOfRange(payloadIndex+4, payloadIndex+6), isBigEndian = false). - toDouble().times(1.25), // Conversion of sent int representation to actual double value in ms - slaveLatency = Converters.calculateDecimalValue( - payload.copyOfRange(payloadIndex+6, payloadIndex+8), isBigEndian = false), - supervisionTimeout = Converters.calculateDecimalValue( - payload.copyOfRange(payloadIndex+8, payloadIndex+10), isBigEndian = false) - .times(10), // Conversion of sent int representation to actual value in ms + mtu = Converters.calculateDecimalValue( + payload.copyOfRange(payloadIndex, payloadIndex + 2), isBigEndian = false + ), + pdu = Converters.calculateDecimalValue( + payload.copyOfRange(payloadIndex + 2, payloadIndex + 4), isBigEndian = false + ), + interval = Converters.calculateDecimalValue( + payload.copyOfRange(payloadIndex + 4, payloadIndex + 6), isBigEndian = false + ).toDouble() + .times(1.25), // Conversion of sent int representation to actual double value in ms + slaveLatency = Converters.calculateDecimalValue( + payload.copyOfRange(payloadIndex + 6, payloadIndex + 8), isBigEndian = false + ), + supervisionTimeout = Converters.calculateDecimalValue( + payload.copyOfRange(payloadIndex + 8, payloadIndex + 10), isBigEndian = false + ) + .times(10), // Conversion of sent int representation to actual value in ms ) - if(mIndexRunning == POSITION_TEST_DISCOVER_SERVICE) { + if (mIndexRunning == POSITION_TEST_DISCOVER_SERVICE) { mIndexStartChildrenTest = 0 - finishItemTest(POSITION_TEST_DISCOVER_SERVICE, getSiliconLabsTestInfo().listItemTest[POSITION_TEST_DISCOVER_SERVICE]) + finishItemTest( + POSITION_TEST_DISCOVER_SERVICE, + getSiliconLabsTestInfo().listItemTest[POSITION_TEST_DISCOVER_SERVICE] + ) Log.d(TAG, "convertValuesParameters(), finishItemTest(POSITION_TEST_DISCOVER_SERVICE)") } } - private fun parseFirmwareVersion(payload: ByteArray) : String { + private fun parseFirmwareVersion(payload: ByteArray): String { return if (payload.size < 8) "3.2.1" /* Old, incompatible method for numbering versions. */ else StringBuilder().apply { append(Converters.calculateDecimalValue(payload.copyOfRange(0, 2), isBigEndian = false)) @@ -1273,7 +1611,10 @@ class IOPTestActivity : AppCompatActivity() { myOutWriter = OutputStreamWriter(fOut) myOutWriter.write(getDataLog()) } catch (e: Exception) { - Log.e(TAG, e.localizedMessage ?: "saveDataTestToFile(), OutputStreamWriter exception") + Log.e( + TAG, + e.localizedMessage ?: "saveDataTestToFile(), OutputStreamWriter exception" + ) } finally { myOutWriter?.close() } @@ -1305,7 +1646,11 @@ class IOPTestActivity : AppCompatActivity() { private fun shareLogDataTestByEmail(fileLocation: String) { try { - val uri = FileProvider.getUriForFile(this, applicationContext.packageName + ".provider", File(fileLocation)) + val uri = FileProvider.getUriForFile( + this, + applicationContext.packageName + ".provider", + File(fileLocation) + ) val message = "Please check the attachment to get the log file." val intent = Intent(Intent.ACTION_SEND).apply { type = "text/plain" @@ -1343,56 +1688,107 @@ class IOPTestActivity : AppCompatActivity() { for (i in uuids.indices) { if (cUuid.equals(uuids[i].toString(), ignoreCase = true)) { matchChar = uuids[i].id - Log.d(TAG, "Run TestCase [" + (matchChar + 3) + "] at characteristic " + uuids[i].toString()) + Log.d( + TAG, + "Run TestCase [" + (matchChar + 3) + "] at characteristic " + uuids[i].toString() + ) break } } when (matchChar) { - CommonUUID.ID_READ_ONLY_LENGTH_1, CommonUUID.ID_READ_ONLY_LENGTH_255 -> readCharacteristic(childrenItem.characteristic) - CommonUUID.ID_WRITE_ONLY_LENGTH_1, CommonUUID.ID_WRITE_WITHOUT_RESPONSE_LENGTH_1 -> writeValueToCharacteristic(childrenItem.characteristic, writeHexForMaxLengthByte(Common.WRITE_VALUE_0, Common.WRITE_LENGTH_1), null) - CommonUUID.ID_WRITE_ONLY_LENGTH_255, CommonUUID.ID_WRITE_WITHOUT_RESPONSE_LENGTH_255 -> writeValueToCharacteristic(childrenItem.characteristic, writeHexForMaxLengthByte(Common.WRITE_VALUE_0, Common.WRITE_LENGTH_255), null) + CommonUUID.ID_READ_ONLY_LENGTH_1, CommonUUID.ID_READ_ONLY_LENGTH_255 -> readCharacteristic( + childrenItem.characteristic + ) + + CommonUUID.ID_WRITE_ONLY_LENGTH_1, CommonUUID.ID_WRITE_WITHOUT_RESPONSE_LENGTH_1 -> writeValueToCharacteristic( + childrenItem.characteristic, + writeHexForMaxLengthByte(Common.WRITE_VALUE_0, Common.WRITE_LENGTH_1), + null + ) + + CommonUUID.ID_WRITE_ONLY_LENGTH_255, CommonUUID.ID_WRITE_WITHOUT_RESPONSE_LENGTH_255 -> writeValueToCharacteristic( + childrenItem.characteristic, + writeHexForMaxLengthByte(Common.WRITE_VALUE_0, Common.WRITE_LENGTH_255), + null + ) + CommonUUID.ID_NOTIFICATION_LENGTH_1, CommonUUID.ID_NOTIFICATION_LENGTH_MTU_3 -> { setTimeStart(childrenItem.characteristic) setNotificationForCharacteristic(childrenItem.characteristic, Notifications.NOTIFY) } + CommonUUID.ID_INDICATE_LENGTH_1, CommonUUID.ID_INDICATE_LENGTH_MTU_3 -> { setTimeStart(childrenItem.characteristic) setIndicationProperty(childrenItem.characteristic, Notifications.INDICATE) } + CommonUUID.ID_IOP_TEST_LENGTH_1, CommonUUID.ID_IOP_TEST_USER_LEN_1 -> if (!childrenItem.isWriteCharacteristic) { - writeValueToCharacteristic(childrenItem.characteristic, writeHexForMaxLengthByte(Common.WRITE_VALUE_55, Common.WRITE_LENGTH_1), null) + writeValueToCharacteristic( + childrenItem.characteristic, + writeHexForMaxLengthByte(Common.WRITE_VALUE_55, Common.WRITE_LENGTH_1), + null + ) } else { readCharacteristic(childrenItem.characteristic) } + CommonUUID.ID_IOP_TEST_LENGTH_255, CommonUUID.ID_IOP_TEST_USER_LEN_255 -> if (!childrenItem.isWriteCharacteristic) { - writeValueToCharacteristic(childrenItem.characteristic, null, Converters.decToByteArray(createDataTestCaseLength255(mtuValue))) + writeValueToCharacteristic( + childrenItem.characteristic, + null, + Converters.decToByteArray(createDataTestCaseLength255(mtuValue)) + ) } else { readCharacteristic(childrenItem.characteristic) } + CommonUUID.ID_IOP_TEST_LENGTH_VARIABLE_4, CommonUUID.ID_IOP_TEST_USER_LEN_VARIABLE_4 -> // write length 1 - if (childrenItem.getLstValueItemTest().isEmpty() && !childrenItem.isWriteCharacteristic) { - writeValueToCharacteristic(childrenItem.characteristic, writeHexForMaxLengthByte(Common.WRITE_VALUE_55, Common.WRITE_LENGTH_1), null) - } else if (childrenItem.getLstValueItemTest().isEmpty() && childrenItem.isWriteCharacteristic) { + if (childrenItem.getLstValueItemTest() + .isEmpty() && !childrenItem.isWriteCharacteristic + ) { + writeValueToCharacteristic( + childrenItem.characteristic, + writeHexForMaxLengthByte(Common.WRITE_VALUE_55, Common.WRITE_LENGTH_1), + null + ) + } else if (childrenItem.getLstValueItemTest() + .isEmpty() && childrenItem.isWriteCharacteristic + ) { childrenItem.isWriteCharacteristic = false childrenItem.statusWrite = -1 readCharacteristic(childrenItem.characteristic) } else if (childrenItem.getLstValueItemTest().size == 1 && !childrenItem.isWriteCharacteristic) { childrenItem.statusRead = -1 childrenItem.isReadCharacteristic = false - writeValueToCharacteristic(childrenItem.characteristic, writeHexForMaxLengthByte(Common.WRITE_VALUE_66, Common.WRITE_LENGTH_4), null) + writeValueToCharacteristic( + childrenItem.characteristic, + writeHexForMaxLengthByte(Common.WRITE_VALUE_66, Common.WRITE_LENGTH_4), + null + ) } else if (childrenItem.getLstValueItemTest().size == 1 && childrenItem.isWriteCharacteristic) { readCharacteristic(childrenItem.characteristic) } + CommonUUID.ID_IOP_TEST_CONST_LENGTH_1 -> if (!childrenItem.isReadCharacteristic) { readCharacteristic(childrenItem.characteristic) } else { - writeValueToCharacteristic(childrenItem.characteristic, writeHexForMaxLengthByte(Common.WRITE_VALUE_55, Common.WRITE_LENGTH_1), null) + writeValueToCharacteristic( + childrenItem.characteristic, + writeHexForMaxLengthByte(Common.WRITE_VALUE_55, Common.WRITE_LENGTH_1), + null + ) } + CommonUUID.ID_IOP_TEST_CONST_LENGTH_255 -> if (!childrenItem.isReadCharacteristic) { readCharacteristic(childrenItem.characteristic) } else { - writeValueToCharacteristic(childrenItem.characteristic, null, Converters.decToByteArray(createDataTestCaseLength255(mtuValue))) + writeValueToCharacteristic( + childrenItem.characteristic, + null, + Converters.decToByteArray(createDataTestCaseLength255(mtuValue)) + ) } + else -> { } } @@ -1414,7 +1810,9 @@ class IOPTestActivity : AppCompatActivity() { */ private fun checkNextTestCase(characteristic: BluetoothGattCharacteristic?, type: Int) { if (characteristic != null) { - for (itemChildrenTest: ChildrenItemTestInfo in getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!) { + for (itemChildrenTest: ChildrenItemTestInfo in getListChildrenItemTestCase( + POSITION_TEST_SERVICE + )!!) { if (itemChildrenTest.characteristic?.uuid.toString() == characteristic.uuid.toString()) { if (type == 1) { itemChildrenTest.isReadCharacteristic = true @@ -1422,47 +1820,73 @@ class IOPTestActivity : AppCompatActivity() { itemChildrenTest.isWriteCharacteristic = true } if (CommonUUID.Characteristic.IOP_TEST_LENGTH_1.toString() == characteristic.uuid.toString() || CommonUUID.Characteristic.IOP_TEST_LENGTH_255.toString() == characteristic.uuid.toString() || CommonUUID.Characteristic.IOP_TEST_CONST_LENGTH_1.toString() == characteristic.uuid.toString() || CommonUUID.Characteristic.IOP_TEST_CONST_LENGTH_255.toString() == characteristic.uuid.toString() || CommonUUID.Characteristic.IOP_TEST_USER_LEN_1.toString() == characteristic.uuid.toString() || CommonUUID.Characteristic.IOP_TEST_USER_LEN_255.toString() == characteristic.uuid.toString()) { - mIndexStartChildrenTest = if (itemChildrenTest.isReadCharacteristic && itemChildrenTest.isWriteCharacteristic) { - getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf(itemChildrenTest) + 1 - } else { - getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf(itemChildrenTest) - } + mIndexStartChildrenTest = + if (itemChildrenTest.isReadCharacteristic && itemChildrenTest.isWriteCharacteristic) { + getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf( + itemChildrenTest + ) + 1 + } else { + getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf( + itemChildrenTest + ) + } } else if (CommonUUID.Characteristic.NOTIFICATION_LENGTH_1.toString() == characteristic.uuid.toString()) { if (isDisabled) { - mIndexStartChildrenTest = getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf(itemChildrenTest) + mIndexStartChildrenTest = + getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf( + itemChildrenTest + ) isDisabled = false } else { if (itemChildrenTest.statusChildrenTest) { countReTest = 0 - mIndexStartChildrenTest = getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf(itemChildrenTest) + 1 + mIndexStartChildrenTest = + getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf( + itemChildrenTest + ) + 1 isDisabled = false } else { countReTest++ if (countReTest > 5) { - mIndexStartChildrenTest = getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf(itemChildrenTest) + 1 + mIndexStartChildrenTest = + getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf( + itemChildrenTest + ) + 1 isDisabled = false countReTest = 0 } else { isDisabled = true - setNotificationForCharacteristic(characteristic, Notifications.DISABLED) + setNotificationForCharacteristic( + characteristic, + Notifications.DISABLED + ) return } } } } else if (CommonUUID.Characteristic.INDICATE_LENGTH_1.toString() == characteristic.uuid.toString()) { if (isDisabled) { - mIndexStartChildrenTest = getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf(itemChildrenTest) + mIndexStartChildrenTest = + getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf( + itemChildrenTest + ) isDisabled = false } else { if (itemChildrenTest.statusChildrenTest) { countReTest = 0 - mIndexStartChildrenTest = getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf(itemChildrenTest) + 1 + mIndexStartChildrenTest = + getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf( + itemChildrenTest + ) + 1 isDisabled = false } else { countReTest++ if (countReTest > 5) { countReTest = 0 - mIndexStartChildrenTest = getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf(itemChildrenTest) + 1 + mIndexStartChildrenTest = + getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf( + itemChildrenTest + ) + 1 isDisabled = false } else { isDisabled = true @@ -1473,21 +1897,35 @@ class IOPTestActivity : AppCompatActivity() { } } else if (CommonUUID.Characteristic.IOP_TEST_LENGTH_VARIABLE_4.toString() == characteristic.uuid.toString() || CommonUUID.Characteristic.IOP_TEST_USER_LEN_VARIABLE_4.toString() == characteristic.uuid.toString()) { if (itemChildrenTest.getLstValueItemTest().size > 1 && itemChildrenTest.isReadCharacteristic) { - mIndexStartChildrenTest = getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf(itemChildrenTest) + 1 + mIndexStartChildrenTest = + getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf( + itemChildrenTest + ) + 1 if ((CommonUUID.Characteristic.IOP_TEST_USER_LEN_VARIABLE_4.toString() == characteristic.uuid.toString())) { - finishItemTest(POSITION_TEST_SERVICE, getItemTestCaseInfo(POSITION_TEST_SERVICE)) + finishItemTest( + POSITION_TEST_SERVICE, + getItemTestCaseInfo(POSITION_TEST_SERVICE) + ) } } else { - mIndexStartChildrenTest = getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf(itemChildrenTest) + mIndexStartChildrenTest = + getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf( + itemChildrenTest + ) } } else { - mIndexStartChildrenTest = getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf(itemChildrenTest) + 1 + mIndexStartChildrenTest = + getListChildrenItemTestCase(POSITION_TEST_SERVICE)!!.indexOf( + itemChildrenTest + ) + 1 } break } } if (mIndexRunning == POSITION_TEST_IOP3_SECURITY) { - for (itemChildrenTest: ChildrenItemTestInfo in getListChildrenItemTestCase(POSITION_TEST_IOP3_SECURITY)!!) { + for (itemChildrenTest: ChildrenItemTestInfo in getListChildrenItemTestCase( + POSITION_TEST_IOP3_SECURITY + )!!) { if (itemChildrenTest.characteristic?.uuid.toString() == characteristic.uuid.toString()) { handler?.removeCallbacks(iopSecurityRunnable) if (type == 1) { @@ -1495,11 +1933,22 @@ class IOPTestActivity : AppCompatActivity() { } if (CommonUUID.Characteristic.IOP_TEST_SECURITY_BONDING.toString() != (characteristic.uuid.toString())) { iopPhase3IndexStartChildrenTest++ - Log.d(TAG, "iopPhase3IndexStartChildrenTest $iopPhase3IndexStartChildrenTest") + Log.d( + TAG, + "iopPhase3IndexStartChildrenTest $iopPhase3IndexStartChildrenTest" + ) showDetailInformationTest(testCaseCount++, false) iopPhase3RunTestCaseSecurity(iopPhase3IndexStartChildrenTest, 1) - } else { + } /*else { finishItemTest(POSITION_TEST_IOP3_SECURITY, getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_SECURITY]) + }*/ + else { + Log.d("checkNextTestCase", "iopPhase3RunTestCaseBonding") + if (iopPhase3BondingStep < 8) { + iopPhase3RunTestCaseBonding(iopPhase3BondingStep + 1) + } + + showDetailInformationTest(testCaseCount++, false) } } } @@ -1514,6 +1963,7 @@ class IOPTestActivity : AppCompatActivity() { } } } + } if (mIndexStartChildrenTest <= 17) { runChildrenTestCase(mIndexStartChildrenTest) @@ -1534,7 +1984,11 @@ class IOPTestActivity : AppCompatActivity() { /** * Create values for control characteristic */ - private fun iopPhase3WriteControlBytes(throughput: Int, security: Int, caching: Int): ByteArray { + private fun iopPhase3WriteControlBytes( + throughput: Int, + security: Int, + caching: Int + ): ByteArray { val hex = ByteArray(3) hex[0] = throughput.toByte() hex[1] = security.toByte() @@ -1542,6 +1996,85 @@ class IOPTestActivity : AppCompatActivity() { return hex } + private fun iopPhase3RunTestCaseBonding(step: Int) { + Log.d("iopPhase3RunTestCaseBonding", "step $step") + iopPhase3BondingStep = step + val CCCD_value = ByteArray(1) + val securityItem = getListChildrenItemTestCase(POSITION_TEST_IOP3_SECURITY)!![2] + Log.d( + "iopPhase3RunTestCaseBonding", + "uuid " + UUID.fromString(securityItem.characteristic!!.uuid.toString()) + ) + iopPhase3ExtraDescriptor = + securityItem.characteristic!!.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")) + when (step) { + // write CCCD to different value from the default + 3 -> { + CCCD_value[0] = 1 + iopPhase3ExtraDescriptor?.setValue(CCCD_value) + mBluetoothGatt?.writeDescriptor(iopPhase3ExtraDescriptor) + + mBluetoothGatt?.readDescriptor(iopPhase3ExtraDescriptor) + read_CCCD_value = iopPhase3ExtraDescriptor?.getValue()!!.copyOf() + } + + // disconnect + 4 -> { + + disconnectGatt(mBluetoothGatt) + // reconnect(4000) + //connectToDevice(mBluetoothDevice) + /* writeValueToCharacteristic( + characteristicIOPPhase3Control, + null, + iopPhase3WriteControlBytes(0, 4, 0)) + Log.d("iopPhase3RunTestCaseBonding", "Set Control Characteristic for Security")*/ + } + + 6 -> { + mBluetoothGatt?.readDescriptor(iopPhase3ExtraDescriptor) + Log.d("CASE 6: iopPhase3RunTestCaseBonding", "read CCCD:" + read_CCCD_value[0]) + // Mobile read CCCD. (Pass: if the CCCD value is the same as the value written before bond ). + if (read_CCCD_value[0].toInt() == 1) { + } else { + updateDataTestFailed(POSITION_TEST_IOP3_SECURITY) + isCCCDPass = false + finishItemTest( + POSITION_TEST_IOP3_SECURITY, + getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_SECURITY] + ) + } + } + + // Mobile write value 0xBB (0xBB or 0x00) to CCCD. (Pass: if the CCCD value was written in last step). + 7 -> { + Log.d("CASE 7: iopPhase3RunTestCaseBonding", "Write value 0x00 to CCCD") + CCCD_value[0] = 0 + iopPhase3ExtraDescriptor?.setValue(CCCD_value) + mBluetoothGatt?.writeDescriptor(iopPhase3ExtraDescriptor) + } + + 8 -> { + mBluetoothGatt?.readDescriptor(iopPhase3ExtraDescriptor) + read_CCCD_value = iopPhase3ExtraDescriptor?.getValue()!!.copyOf() + Log.d("CASE 8: iopPhase3RunTestCaseBonding", "read CCCD:" + read_CCCD_value[0]) + // Mobile read CCCD. (Pass: if the CCCD value is the same as the value written before bond ). + if (read_CCCD_value[0].toInt() == 0) { + } else { + isCCCDPass = false + updateDataTestFailed(POSITION_TEST_IOP3_SECURITY) + } + finishItemTest( + POSITION_TEST_IOP3_SECURITY, + getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_SECURITY] + ) + } + + else -> { + } + } + } + private fun iopPhase3RunTestCaseSecurity(index: Int, isControl: Int) { iopPhase3IndexStartChildrenTest = index isConnecting = false @@ -1552,7 +2085,10 @@ class IOPTestActivity : AppCompatActivity() { for (i in uuids.indices) { if (cUuid.equals(uuids[i].toString(), ignoreCase = true)) { matchChar = uuids[i].id - Log.d(TAG, "Run TestCase [7." + (matchChar - CommonUUID.ID_IOP_TEST_PHASE3_THROUGHPUT) + "] at Characteristic " + uuids[i].toString()) + Log.d( + TAG, + "Run TestCase [7." + (matchChar - CommonUUID.ID_IOP_TEST_PHASE3_THROUGHPUT) + "] at Characteristic " + uuids[i].toString() + ) break } } @@ -1561,42 +2097,65 @@ class IOPTestActivity : AppCompatActivity() { CommonUUID.ID_IOP_TEST_PHASE3_SECURITY_AUTHENTICATION, CommonUUID.ID_IOP_TEST_PHASE3_SECURITY_BONDING -> if (isControl == 1) { - countReTest = 0 - handler?.postDelayed(iopSecurityRunnable, 60000) - writeValueToCharacteristic( + if (iopPhase3BondingStep == 2) { + countReTest = 0 + handler?.postDelayed(iopSecurityRunnable, 60000) + writeValueToCharacteristic( characteristicIOPPhase3Control, null, - iopPhase3WriteControlBytes(0, matchChar - CommonUUID.ID_IOP_TEST_PHASE3_CONTROL - 1, 0)) - Log.d(TAG, "Set Control Characteristic for Security") + iopPhase3WriteControlBytes( + 0, + matchChar - CommonUUID.ID_IOP_TEST_PHASE3_CONTROL - 1, + 0 + ) + ) + Log.d(TAG, "Set Control Characteristic for Security") + } else { + //Mobile read CCCD + iopPhase3RunTestCaseBonding(6) + } } else if (!securityItem.isReadCharacteristic) { Log.d(TAG, "Read Security Characteristic $cUuid") readCharacteristic(securityItem.characteristic) } - else -> { - } + + else -> { + } } } + private fun iopPhase3RunTestCaseThroughput(isControl: Int) { - Log.d(TAG, "Run TestCase [6] at Characteristic " + characteristicIOPPhase3Throughput?.uuid.toString()) + Log.d( + TAG, + "Run TestCase [6] at Characteristic " + characteristicIOPPhase3Throughput?.uuid.toString() + ) if (isControl == 1) { writeValueToCharacteristic( - characteristicIOPPhase3Control, - null, - iopPhase3WriteControlBytes(1, 0, 0)) + characteristicIOPPhase3Control, + null, + iopPhase3WriteControlBytes(1, 0, 0) + ) Log.d(TAG, "Set Control Characteristic for Throughput") } else { Log.d(TAG, "set Notification enable for Throughput") mEndThroughputNotification = false - setNotificationForCharacteristic(characteristicIOPPhase3Throughput, Notifications.NOTIFY) + setNotificationForCharacteristic( + characteristicIOPPhase3Throughput, + Notifications.NOTIFY + ) mStartTimeThroughput = System.currentTimeMillis() handler?.postDelayed({ mEndTimeThroughput = System.currentTimeMillis() - mByteSpeed = ((mByteNumReceived * 1000) / (mEndTimeThroughput - mStartTimeThroughput)).toInt() + mByteSpeed = + ((mByteNumReceived * 1000) / (mEndTimeThroughput - mStartTimeThroughput)).toInt() Log.d(TAG, "set Notification disable for throughput") Log.d(TAG, "Throughput is $mByteSpeed Bytes/sec") try { - setNotificationForCharacteristic(characteristicIOPPhase3Throughput, Notifications.DISABLED) + setNotificationForCharacteristic( + characteristicIOPPhase3Throughput, + Notifications.DISABLED + ) } catch (e: Exception) { Log.e(TAG, "Error Notification disable for throughput") } @@ -1605,6 +2164,118 @@ class IOPTestActivity : AppCompatActivity() { } } + private fun iopPhase3RunTestCaseLEPrivacy(isControl: Int) { + Log.d( + TAG, + "Run TestCase [8] at Characteristic " + characteristicIOPPhase3Throughput?.uuid.toString() + ) + + if (isControl == 1) { + connectToDevice(mBluetoothDevice) + + writeValueToCharacteristic( + characteristicIOPPhase3Control, + null, + iopPhase3WriteControlBytes(0, 4, 0) + ) { success -> + if (success) { + handler?.postDelayed({ + Log.d(TAG, "Write operation successful") + + connectToDevice(mBluetoothDevice, + onConnectionSuccess = { + // Code to execute after successful connection + handler?.postDelayed({ + if (isConnected) { + Log.d(TAG, "Connection successful") + // Perform further actions here + + Log.d(TAG, "Set Control Characteristic for LE Privacy") + if (isServiceChangedIndication == 1) { + try { + val connectionState = mBluetoothDevice?.bondState + Log.d(TAG, "" + connectionState) + + + when (connectionState) { + BluetoothDevice.BOND_BONDED -> { + // Device is bonded (connected) + Log.d(TAG, "Device is bonded (connected)") + setNotificationForCharacteristic( + characteristicIOPPhase3ServiceChanged, + Notifications.INDICATE + ) + getItemTestCaseInfo( + POSITION_TEST_IOP3_LE_PRIVACY + ).setStatusTest(Common.IOP3_TC_STATUS_PASS) + + } + + BluetoothDevice.BOND_BONDING -> { + // Device is in the process of bonding (connecting) + // Handle this state if needed + Log.d( + TAG, + "Device is in the process of bonding (connecting)" + ) + } + + BluetoothDevice.BOND_NONE -> { + // Device is not bonded (not connected) + // Handle this state if needed + Log.d( + TAG, + "Device is not bonded (not connected)" + ) + Log.d( + TAG, + "Connection is not trusted, not bonded" + ) + updateDataTestFailed( + POSITION_TEST_IOP3_LE_PRIVACY + ) + // handler?.removeCallbacks(iopLEPrivacyRunnable) + } + } + + } catch (e: Exception) { + Log.d(TAG, "exception $e") + updateDataTestFailed(POSITION_TEST_IOP3_LE_PRIVACY) + } + + } else { + Log.d( + TAG, + "isServiceChangedIndication $isServiceChangedIndication" + ) + updateDataTestFailed(POSITION_TEST_IOP3_LE_PRIVACY) + } + } + }, 15000) + + + }, + onConnectionFailure = { + // Code to handle connection failure + Log.e(TAG, "Connection failed") + updateDataTestFailed(POSITION_TEST_IOP3_LE_PRIVACY) + // Perform actions to handle the failure + } + ) + }, CONNECTION_PERIOD) + + + } else { + // Write operation failed, handle accordingly + Log.e(TAG, "Write operation failed") + } + } + + } else { + Log.d(TAG, "is control $isControl") + } + } + private fun iopPhase3RunTestCaseCaching(isControl: Int) { Log.d(TAG, "Run TestCase [8]") val itemChildrenTest: ChildrenItemTestInfo @@ -1612,19 +2283,22 @@ class IOPTestActivity : AppCompatActivity() { if (isControl == 1) { if (isServiceChangedIndication == 1) { writeValueToCharacteristic( - characteristicIOPPhase3Control, - null, - iopPhase3WriteControlBytes(0, 0, 1)) + characteristicIOPPhase3Control, + null, + iopPhase3WriteControlBytes(0, 0, 1) + ) Log.d(TAG, "Set Control Characteristic for Service Changed Indications") } else { writeValueToCharacteristic( - characteristicIOPPhase3Control, - null, - iopPhase3WriteControlBytes(0, 0, 2)) + characteristicIOPPhase3Control, + null, + iopPhase3WriteControlBytes(0, 0, 2) + ) Log.d(TAG, "Set Control Characteristic for GATT Caching") } } else { - itemChildrenTest = getListChildrenItemTestCase(POSITION_TEST_IOP3_CACHING)!![isServiceChangedIndication] + itemChildrenTest = + getListChildrenItemTestCase(POSITION_TEST_IOP3_CACHING)!![isServiceChangedIndication] itemChildrenTest.statusRunTest = 0 itemChildrenTest.startTimeTest = System.currentTimeMillis() handler?.postDelayed(iopCachingRunnable, 90000) @@ -1632,7 +2306,10 @@ class IOPTestActivity : AppCompatActivity() { val bonded = mBluetoothGatt?.device?.bondState == BluetoothDevice.BOND_BONDED if (bonded) { Log.d(TAG, "Subscribe Indication for Service Changed") - setNotificationForCharacteristic(characteristicIOPPhase3ServiceChanged, Notifications.INDICATE) + setNotificationForCharacteristic( + characteristicIOPPhase3ServiceChanged, + Notifications.INDICATE + ) } else { Log.d(TAG, "Connection is not trusted, not bonded") iopCachingFailed() @@ -1640,23 +2317,32 @@ class IOPTestActivity : AppCompatActivity() { } } else { Log.d(TAG, "Set on Client Supported Features") - writeValueToCharacteristic(characteristicIOPPhase3ClientSupportedFeatures, "01", null) + writeValueToCharacteristic( + characteristicIOPPhase3ClientSupportedFeatures, + "01", + null + ) } } } + private val iopCachingRunnable = Runnable { Log.d(TAG, "iopCachingRunnable") iopCachingFailed() } private fun iopCachingFailed() { - val itemChildrenTest = getListChildrenItemTestCase(POSITION_TEST_IOP3_CACHING)!![isServiceChangedIndication] + val itemChildrenTest = + getListChildrenItemTestCase(POSITION_TEST_IOP3_CACHING)!![isServiceChangedIndication] itemChildrenTest.statusRunTest = 1 itemChildrenTest.statusChildrenTest = false itemChildrenTest.endTimeTest = System.currentTimeMillis() getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_CACHING].setStatusTest(Common.IOP3_TC_STATUS_FAILED) - finishItemTest(POSITION_TEST_IOP3_CACHING, getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_CACHING]) + finishItemTest( + POSITION_TEST_IOP3_CACHING, + getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_CACHING] + ) } private fun retryIOP3Failed(index: Int, mCountReTest: Int) { @@ -1664,9 +2350,14 @@ class IOPTestActivity : AppCompatActivity() { updateDataTestFailed(index) } else if (index == POSITION_TEST_CONNECTION) { Log.d(TAG, "retryIOP3Failed index $index") - handler?.postDelayed({ finishItemTest(POSITION_TEST_CONNECTION, getSiliconLabsTestInfo().listItemTest[POSITION_TEST_CONNECTION]) }, 1000) + handler?.postDelayed({ + finishItemTest( + POSITION_TEST_CONNECTION, + getSiliconLabsTestInfo().listItemTest[POSITION_TEST_CONNECTION] + ) + }, 1000) } else { - if (mCountReTest < 6) { + if (mCountReTest < 7) { isConnecting = false reconnect(2000) } else { @@ -1688,9 +2379,13 @@ class IOPTestActivity : AppCompatActivity() { private val iopSecurityRunnable: Runnable = Runnable { Log.d(TAG, "iopSecurityRunnable $iopPhase3IndexStartChildrenTest") - val itemChildrenTest = getListChildrenItemTestCase(POSITION_TEST_IOP3_SECURITY)!![iopPhase3IndexStartChildrenTest] + val itemChildrenTest = + getListChildrenItemTestCase(POSITION_TEST_IOP3_SECURITY)!![iopPhase3IndexStartChildrenTest] itemChildrenTest.statusRunTest = 1 // 0 running, 1 finish test - Log.d(TAG, "iopSecurityRunnable $iopPhase3IndexStartChildrenTest, setStatusRunTest finish test") + Log.d( + TAG, + "iopSecurityRunnable $iopPhase3IndexStartChildrenTest, setStatusRunTest finish test" + ) itemChildrenTest.statusChildrenTest = false Log.d(TAG, "setStatusChildrenTest false") val itemTestCaseInfo = getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_SECURITY] @@ -1711,19 +2406,23 @@ class IOPTestActivity : AppCompatActivity() { when (getSiliconLabsTestInfo().firmwareVersion) { "3.2.1", "3.2.2", "3.2.3", "3.2.4" -> { otaFileManager - ?.apply { uploadMode = OtaFileManager.UploadMode.AUTO } - ?.also { it.findGblFile( - getSiliconLabsTestInfo().firmwareVersion, - getSiliconLabsTestInfo().iopBoard, - reliable - ) } + ?.apply { uploadMode = OtaFileManager.UploadMode.AUTO } + ?.also { + it.findGblFile( + getSiliconLabsTestInfo().firmwareVersion, + getSiliconLabsTestInfo().iopBoard, + reliable + ) + } startOtaProcess() } + else -> { otaFileManager?.uploadMode = OtaFileManager.UploadMode.USER - otaFileSelectionDialog = OtaFileSelectionDialog(listener = fileSelectionListener).also { - it.show(supportFragmentManager, "ota_file_selection_dialog") - } + otaFileSelectionDialog = + OtaFileSelectionDialog(listener = fileSelectionListener).also { + it.show(supportFragmentManager, "ota_file_selection_dialog") + } } } @@ -1733,8 +2432,14 @@ class IOPTestActivity : AppCompatActivity() { override fun onSelectFileButtonClicked() { Intent(Intent.ACTION_GET_CONTENT) .apply { type = "*/*" } - .also { startActivityForResult(Intent.createChooser(it, - getString(R.string.ota_choose_file)), GBL_FILE_CHOICE_REQUEST_CODE) } + .also { + startActivityForResult( + Intent.createChooser( + it, + getString(R.string.ota_choose_file) + ), GBL_FILE_CHOICE_REQUEST_CODE + ) + } } override fun onOtaButtonClicked() { @@ -1742,9 +2447,17 @@ class IOPTestActivity : AppCompatActivity() { otaFileSelectionDialog?.dismiss() startOtaProcess() } ?: if (otaFileManager?.otaFilename != null) { - Toast.makeText(this@IOPTestActivity, getString(R.string.incorrect_file), Toast.LENGTH_SHORT).show() + Toast.makeText( + this@IOPTestActivity, + getString(R.string.incorrect_file), + Toast.LENGTH_SHORT + ).show() } else { - Toast.makeText(this@IOPTestActivity, getString(R.string.no_file_chosen), Toast.LENGTH_SHORT).show() + Toast.makeText( + this@IOPTestActivity, + getString(R.string.no_file_chosen), + Toast.LENGTH_SHORT + ).show() } } @@ -1769,20 +2482,24 @@ class IOPTestActivity : AppCompatActivity() { } } - private fun isOtaNameCorrect(deviceName: String) : Boolean { + private fun isOtaNameCorrect(deviceName: String): Boolean { return when { otaFileManager?.uploadMode == OtaFileManager.UploadMode.AUTO && mIndexRunning == POSITION_TEST_IOP3_OTA_ACK -> { deviceName.equals(OTA_DEVICE_NAME_AUTO_ACK, ignoreCase = true) } + otaFileManager?.uploadMode == OtaFileManager.UploadMode.AUTO && mIndexRunning == POSITION_TEST_IOP3_OTA_WITHOUT_ACK -> { deviceName.equals(OTA_DEVICE_NAME_AUTO_UNACK, ignoreCase = true) } + otaFileManager?.uploadMode == OtaFileManager.UploadMode.USER && mIndexRunning == POSITION_TEST_IOP3_OTA_ACK -> { deviceName.equals(OTA_DEVICE_NAME_USER_ACK, ignoreCase = true) } + otaFileManager?.uploadMode == OtaFileManager.UploadMode.USER && mIndexRunning == POSITION_TEST_IOP3_OTA_WITHOUT_ACK -> { deviceName.equals(OTA_DEVICE_NAME_USER_UNACK, ignoreCase = true) } + else -> false } @@ -1808,12 +2525,14 @@ class IOPTestActivity : AppCompatActivity() { handler?.postDelayed(WRITE_OTA_CONTROL_ZERO, 200) } } + "OTAUPLOAD" -> { Log.d(OTA_UPLOAD, "Called") //Check Services val mBluetoothGattService = mBluetoothGatt?.getService(ota_service) if (mBluetoothGattService != null) { - val charac = mBluetoothGatt?.getService(ota_service)?.getCharacteristic(ota_data) + val charac = + mBluetoothGatt?.getService(ota_service)?.getCharacteristic(ota_data) if (charac != null) { charac.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE Log.d("Instance ID", "" + charac.instanceId) @@ -1822,7 +2541,10 @@ class IOPTestActivity : AppCompatActivity() { //Set info into UI OTA Progress runOnUiThread { - otaProgressDialog?.setProgressInfo(otaFileManager?.otaFilename, otaFileManager?.otaFile?.size) + otaProgressDialog?.setProgressInfo( + otaFileManager?.otaFilename, + otaFileManager?.otaFile?.size + ) animateLoading() } //Start OTA_data Upload in another thread @@ -1837,15 +2559,18 @@ class IOPTestActivity : AppCompatActivity() { } } } + "OTAEND" -> { Log.d(TAG, "OTAEND Called") handler?.postDelayed({ writeOtaControl(0x03.toByte()) }, 1000) } + "DISCONNECTION" -> { otaProcess = false boolOTAbegin = false disconnectGatt(mBluetoothGatt) } + else -> { } } @@ -1999,7 +2724,10 @@ class IOPTestActivity : AppCompatActivity() { j++ } pgss = ((pack + last).toFloat() / (otaFileManager?.otaFile!!.size - 1)) * 100 - Log.d("characte", "last: " + pack + " / " + (pack + last) + " : " + Converters.getHexValue(writearray)) + Log.d( + "characte", + "last: " + pack + " / " + (pack + last) + " : " + Converters.getHexValue(writearray) + ) } else { var j = 0 writearray = ByteArray(mtuDivisible) @@ -2008,7 +2736,12 @@ class IOPTestActivity : AppCompatActivity() { j++ } pgss = ((pack + mtuDivisible).toFloat() / (otaFileManager?.otaFile!!.size - 1)) * 100 - Log.d("characte", "pack: " + pack + " / " + (pack + mtuDivisible) + " : " + Converters.getHexValue(writearray)) + Log.d( + "characte", + "pack: " + pack + " / " + (pack + mtuDivisible) + " : " + Converters.getHexValue( + writearray + ) + ) } val charac = mBluetoothGatt?.getService(ota_service)?.getCharacteristic(ota_data) charac?.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT @@ -2022,7 +2755,8 @@ class IOPTestActivity : AppCompatActivity() { otaProgressDialog?.progressBar?.progress = pgss.toInt() val datarate = String.format(Locale.US, kBits, bitrate) otaProgressDialog?.dataRate?.text = datarate - otaProgressDialog?.dataSize?.text = getString(R.string.iop_test_n_percent, pgss.toInt()) + otaProgressDialog?.dataSize?.text = + getString(R.string.iop_test_n_percent, pgss.toInt()) } } } else { @@ -2044,24 +2778,46 @@ class IOPTestActivity : AppCompatActivity() { j++ if (j >= mtu - 3 || i >= (dataThread.size - 1)) { var wait = System.nanoTime() - val charac = mBluetoothGatt?.getService(ota_service)?.getCharacteristic(ota_data) + val charac = + mBluetoothGatt?.getService(ota_service)?.getCharacteristic(ota_data) charac?.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE val progress = ((i + 1).toFloat() / dataThread.size) * 100 - val bitrate = (((i + 1) * (8.0)).toFloat() / (((wait - start) / 1000000.0).toFloat())) + val bitrate = + (((i + 1) * (8.0)).toFloat() / (((wait - start) / 1000000.0).toFloat())) if (j < mtu - 3) { val end = ByteArray(j) System.arraycopy(value, 0, end, 0, j) - Log.d("Progress", "sent " + (i + 1) + " / " + dataThread.size + " - " + String.format("%.1f", progress) + " % - " + String.format(kBits, bitrate) + " - " + Converters.getHexValue(end)) + Log.d( + "Progress", + "sent " + (i + 1) + " / " + dataThread.size + " - " + String.format( + "%.1f", + progress + ) + " % - " + String.format( + kBits, + bitrate + ) + " - " + Converters.getHexValue(end) + ) runOnUiThread { - otaProgressDialog?.dataSize?.text = getString(R.string.iop_test_n_percent, progress.toInt()) + otaProgressDialog?.dataSize?.text = + getString(R.string.iop_test_n_percent, progress.toInt()) otaProgressDialog?.progressBar?.progress = progress.toInt() } charac?.value = end } else { j = 0 - Log.d("Progress", "sent " + (i + 1) + " / " + dataThread.size + " - " + String.format("%.1f", progress) + " % - " + String.format(kBits, bitrate) + " - " + Converters.getHexValue(value)) + Log.d( + "Progress", + "sent " + (i + 1) + " / " + dataThread.size + " - " + String.format( + "%.1f", + progress + ) + " % - " + String.format( + kBits, + bitrate + ) + " - " + Converters.getHexValue(value) + ) runOnUiThread { - otaProgressDialog?.dataSize?.text = getString(R.string.iop_test_n_percent, progress.toInt()) + otaProgressDialog?.dataSize?.text = + getString(R.string.iop_test_n_percent, progress.toInt()) otaProgressDialog?.progressBar?.progress = progress.toInt() } charac?.value = value @@ -2107,7 +2863,18 @@ class IOPTestActivity : AppCompatActivity() { */ private fun homeKitOTAControl(instanceID: ByteArray) { //WRITE CHARACTERISTIC FOR HOMEKIT - val value = byteArrayOf(0x00, 0x02, 0xee.toByte(), instanceID[0], instanceID[1], 0x03, 0x00, 0x01, 0x01, 0x01) + val value = byteArrayOf( + 0x00, + 0x02, + 0xee.toByte(), + instanceID[0], + instanceID[1], + 0x03, + 0x00, + 0x01, + 0x01, + 0x01 + ) writeGenericCharacteristic(ota_service, ota_control, value) Log.d(TAG, "characteristic writting: " + Converters.getHexValue(value)) } @@ -2115,13 +2882,19 @@ class IOPTestActivity : AppCompatActivity() { /** * WRITES BYTE ARRAY TO A GENERIC CHARACTERISTIC */ - private fun writeGenericCharacteristic(service: UUID?, characteristic: UUID?, value: ByteArray?): Boolean { + private fun writeGenericCharacteristic( + service: UUID?, + characteristic: UUID?, + value: ByteArray? + ): Boolean { if (mBluetoothGatt != null) { - val bluetoothGattCharacteristic = mBluetoothGatt?.getService(service)?.getCharacteristic(characteristic) + val bluetoothGattCharacteristic = + mBluetoothGatt?.getService(service)?.getCharacteristic(characteristic) Log.d(TAG, "characteristic exists") if (bluetoothGattCharacteristic != null) { bluetoothGattCharacteristic.value = value - bluetoothGattCharacteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + bluetoothGattCharacteristic.writeType = + BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT mBluetoothGatt?.writeCharacteristic(bluetoothGattCharacteristic) Log.d(TAG, "characteristic written") } else { @@ -2152,24 +2925,52 @@ class IOPTestActivity : AppCompatActivity() { Log.d(TAG, "onConnectionStateChange status $status - newState $newState") when (newState) { BluetoothGatt.STATE_CONNECTED -> { + Log.d(TAG, "onConnectionStateChange connected") handler?.removeCallbacks(connectionRunnable) isConnected = true isTestFinished = false if (getSiliconLabsTestInfo().listItemTest[POSITION_TEST_CONNECTION].getStatusTest() == Common.IOP3_TC_STATUS_PROCESSING) { Log.d(TAG, "onConnectionStateChange POSITION_TEST_CONNECTION") - finishItemTest(POSITION_TEST_CONNECTION, getSiliconLabsTestInfo().listItemTest[POSITION_TEST_CONNECTION]) + finishItemTest( + POSITION_TEST_CONNECTION, + getSiliconLabsTestInfo().listItemTest[POSITION_TEST_CONNECTION] + ) } else { if (!otaProcess) { - Log.d(TAG, "onConnectionStateChange connected mIndexRunning $mIndexRunning") + Log.d( + TAG, + "onConnectionStateChange connected mIndexRunning $mIndexRunning" + ) mBluetoothGatt?.requestMtu(247) + handler?.postDelayed({ + if (isConnected && mIndexRunning == 8) { + Log.d( + TAG, + "onConnectionStateChange connected mIndexRunningis 8" + ) + isTestRunning = false + isTestFinished = true + + getItemTestCaseInfo(POSITION_TEST_IOP3_LE_PRIVACY).setStatusTest( + Common.IOP3_TC_STATUS_PASS + ) + runOnUiThread { updateUIFooter(isTestRunning) } + mListener?.updateUi() + } + }, CONNECTION_PERIOD) + } else { //After OTA process started //get information Log.d(TAG, "Device: " + gatt.device) Log.d(TAG, "Device Name: " + gatt.device.name) if (gatt.services.isEmpty()) { handler?.postDelayed({ - mBluetoothGatt = null //It's going to be equal gatt in Discover Services Callback... - Log.d(TAG, "onConnected, start Services Discovery: " + gatt.discoverServices()) + mBluetoothGatt = + null //It's going to be equal gatt in Discover Services Callback... + Log.d( + TAG, + "onConnected, start Services Discovery: " + gatt.discoverServices() + ) }, 250) discoverTimeout = true @@ -2178,7 +2979,11 @@ class IOPTestActivity : AppCompatActivity() { if (discoverTimeout) { disconnectGatt(gatt) runOnUiThread { - Toast.makeText(baseContext, "DISCOVER SERVICES TIMEOUT", Toast.LENGTH_LONG).show() + Toast.makeText( + baseContext, + "DISCOVER SERVICES TIMEOUT", + Toast.LENGTH_LONG + ).show() } } }, 25000) @@ -2188,6 +2993,7 @@ class IOPTestActivity : AppCompatActivity() { } } } + BluetoothGatt.STATE_DISCONNECTED -> { Log.d(TAG, "Disconnected IOP Test device: " + System.currentTimeMillis()) isConnected = false @@ -2203,7 +3009,10 @@ class IOPTestActivity : AppCompatActivity() { } } else { if (!otaProcess && !isTestFinished) { - Log.d(TAG, "onConnectionStateChange ota_process $otaProcess,isConnecting $isConnecting") + Log.d( + TAG, + "onConnectionStateChange ota_process $otaProcess,isConnecting $isConnecting" + ) if (mIndexRunning > POSITION_TEST_IOP3_OTA_WITHOUT_ACK || mIndexRunning < POSITION_TEST_IOP3_OTA_ACK) { if (!isConnecting) { isConnecting = true @@ -2242,7 +3051,12 @@ class IOPTestActivity : AppCompatActivity() { } } } - BluetoothGatt.STATE_CONNECTING -> Log.d(TAG, "onConnectionStateChange Connecting...") + + BluetoothGatt.STATE_CONNECTING -> Log.d( + TAG, + "onConnectionStateChange Connecting..." + ) + else -> { } } @@ -2261,7 +3075,11 @@ class IOPTestActivity : AppCompatActivity() { discoverTimeout = false if (status != 0) { runOnUiThread { - Toast.makeText(baseContext, ErrorCodes.getErrorName(status), Toast.LENGTH_LONG).show() + Toast.makeText( + baseContext, + ErrorCodes.getErrorName(status), + Toast.LENGTH_LONG + ).show() updateDataTestFailed(mIndexRunning) } handler?.postDelayed({ @@ -2272,7 +3090,8 @@ class IOPTestActivity : AppCompatActivity() { val otaServiceCheck = gatt.getService(ota_service) != null if (otaServiceCheck) { - val otaDataCheck = gatt.getService(ota_service).getCharacteristic(ota_data) != null + val otaDataCheck = + gatt.getService(ota_service).getCharacteristic(ota_data) != null if (otaDataCheck) { val homeKitCheck = gatt.getService(homekit_service) != null if (!homeKitCheck) { @@ -2299,12 +3118,20 @@ class IOPTestActivity : AppCompatActivity() { } } - override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { + override fun onCharacteristicRead( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + status: Int + ) { super.onCharacteristicRead(gatt, characteristic, status) - Log.d(TAG, "onCharacteristicRead: " + characteristic.uuid.toString() + " status " + status) + Log.d( + TAG, + "onCharacteristicRead: " + characteristic.uuid.toString() + " status " + status + ) if (characteristic.uuid == GattCharacteristic.ModelNumberString.uuid) { - getSiliconLabsTestInfo().iopBoard = IopBoard.fromBoardString(characteristic.getStringValue(0)) + getSiliconLabsTestInfo().iopBoard = + IopBoard.fromBoardString(characteristic.getStringValue(0)) } if (characteristic.service.uuid.toString() == CommonUUID.Service.TEST_PARAMETERS.toString()) { @@ -2312,9 +3139,11 @@ class IOPTestActivity : AppCompatActivity() { when (characteristic.uuid.toString()) { CommonUUID.Characteristic.FIRMWARE_VERSION.toString() -> readFirmwareVersion(characteristic.value) + CommonUUID.Characteristic.CONNECTION_PARAMETERS.toString() -> readConnectionParameters(characteristic.value) - else -> { } + + else -> {} } } else { nrTries = 0 @@ -2333,12 +3162,18 @@ class IOPTestActivity : AppCompatActivity() { checkNextTestCase(characteristic, 1) // type 1: CharacteristicRead, 2:CharacteristicWrite if (mBluetoothGatt?.getService(ota_service) != null) { - if (characteristic === mBluetoothGatt?.getService(ota_service)?.getCharacteristic(ota_control)) { + if (characteristic === mBluetoothGatt?.getService(ota_service) + ?.getCharacteristic(ota_control) + ) { val value = characteristic.value if (value[2] == 0x05.toByte()) { Log.e("homekit_descriptor", "Insecure Connection") runOnUiThread { - Toast.makeText(this@IOPTestActivity, "Error: Not a Homekit Secure Connection", Toast.LENGTH_SHORT).show() + Toast.makeText( + this@IOPTestActivity, + "Error: Not a Homekit Secure Connection", + Toast.LENGTH_SHORT + ).show() } } else if (value[2] == 0x04.toByte()) { Log.e("homekit_descriptor", "Wrong Address") @@ -2360,7 +3195,11 @@ class IOPTestActivity : AppCompatActivity() { } } - override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { + override fun onCharacteristicWrite( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + status: Int + ) { super.onCharacteristicWrite(gatt, characteristic, status) Log.d(TAG, "onCharacteristicWrite: " + characteristic.uuid.toString()) Log.d(TAG, "onCharacteristicWrite: $status") @@ -2371,7 +3210,10 @@ class IOPTestActivity : AppCompatActivity() { if (characteristic.uuid == ota_control) { //OTA Control Callback Handling if (characteristic.value.size == 1) { if (characteristic.value[0] == 0x00.toByte()) { - Log.e("Callback", "Control " + Converters.getHexValue(characteristic.value) + "status: " + status) + Log.e( + "Callback", + "Control " + Converters.getHexValue(characteristic.value) + "status: " + status + ) if (ota_mode && otaProcess) { Log.e(OTA_UPLOAD, "Sent") runOnUiThread(checkBeginRunnable) @@ -2386,13 +3228,19 @@ class IOPTestActivity : AppCompatActivity() { } if (characteristic.value[0] == 0x03.toByte()) { if (otaProcess) { - Log.e("Callback", "Control " + Converters.getHexValue(characteristic.value) + "status: " + status) + Log.e( + "Callback", + "Control " + Converters.getHexValue(characteristic.value) + "status: " + status + ) runOnUiThread { otaProgressDialog?.btnOtaEnd?.isEnabled = true } boolOTAbegin = false } } } else { - Log.i("OTA_Control", "Received: " + Converters.getHexValue(characteristic.value)) + Log.i( + "OTA_Control", + "Received: " + Converters.getHexValue(characteristic.value) + ) if (characteristic.value[0].toInt() == 0x00 && characteristic.value[1].toInt() == 0x02) { Log.i("HomeKit", "Reading OTA_Control...") mBluetoothGatt?.readCharacteristic(characteristic) @@ -2421,15 +3269,25 @@ class IOPTestActivity : AppCompatActivity() { } } - override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { + override fun onCharacteristicChanged( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic + ) { super.onCharacteristicChanged(gatt, characteristic) - Log.d(TAG, "onCharacteristicChanged: " + characteristic.uuid.toString() + " len:" + characteristic.value.size) + Log.d( + TAG, + "onCharacteristicChanged: " + characteristic.uuid.toString() + " len:" + characteristic.value.size + ) updateDataTest(characteristic, -1, -1) checkNextTestCase(characteristic, 0) // type 1: CharacteristicRead, 2:CharacteristicWrite, 0:Notify } - override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) { + override fun onDescriptorRead( + gatt: BluetoothGatt, + descriptor: BluetoothGattDescriptor, + status: Int + ) { super.onDescriptorRead(gatt, descriptor, status) Log.d(TAG, "onDescriptorRead mIndexStartChildrenTest: $mIndexStartChildrenTest") if (descriptor.uuid.toString() == homekit_descriptor.toString()) { @@ -2440,9 +3298,24 @@ class IOPTestActivity : AppCompatActivity() { Log.i("descriptor", "getValue " + Converters.getHexValue(descriptor.value)) } } + if (iopPhase3ExtraDescriptor != null) { + if (descriptor.uuid.toString() == iopPhase3ExtraDescriptor!!.uuid.toString()) { + Log.d( + "iopPhase3RunTestCaseBonding", + "Descriptor getValue " + Converters.getHexValue(descriptor.value) + ) + if (iopPhase3BondingStep < 8) { + iopPhase3RunTestCaseBonding(iopPhase3BondingStep + 1) + } + } + } } - override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) { + override fun onDescriptorWrite( + gatt: BluetoothGatt, + descriptor: BluetoothGattDescriptor, + status: Int + ) { super.onDescriptorWrite(gatt, descriptor, status) Log.d(TAG, "onDescriptorWrite status $status") if (mIndexStartChildrenTest <= 17 && isDisabled) { @@ -2451,11 +3324,28 @@ class IOPTestActivity : AppCompatActivity() { } else if (mEndThroughputNotification) { mEndThroughputNotification = false if (status == 0) { - finishItemTest(POSITION_TEST_IOP3_THROUGHPUT, getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_THROUGHPUT]) + finishItemTest( + POSITION_TEST_IOP3_THROUGHPUT, + getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_THROUGHPUT] + ) + } else if (getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_LE_PRIVACY] + .getStatusTest() == Common.IOP3_TC_STATUS_PROCESSING + ) { + iopPhase3RunTestCaseLEPrivacy(1) + } + /*} else if (getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_CACHING] + .getStatusTest() == Common.IOP3_TC_STATUS_PROCESSING) { + iopPhase3RunTestCaseCaching(1) */ + } else if (getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_LE_PRIVACY] + .getStatusTest() == Common.IOP3_TC_STATUS_PROCESSING + ) { + iopPhase3RunTestCaseLEPrivacy(1) + } else if (iopPhase3ExtraDescriptor != null) { + if (descriptor.uuid.toString() == iopPhase3ExtraDescriptor!!.uuid.toString()) { + if (iopPhase3BondingStep < 8) { + iopPhase3RunTestCaseBonding(iopPhase3BondingStep + 1) + } } - /*} else if (getSiliconLabsTestInfo().listItemTest[POSITION_TEST_IOP3_CACHING] - .getStatusTest() == Common.IOP3_TC_STATUS_PROCESSING) { - iopPhase3RunTestCaseCaching(1) */ } else { Log.d(TAG, "do nothing") } @@ -2473,7 +3363,9 @@ class IOPTestActivity : AppCompatActivity() { override fun onPhyRead(gatt: BluetoothGatt?, txPhy: Int, rxPhy: Int, status: Int) { super.onPhyRead(gatt, txPhy, rxPhy, status) // TODO read PHY before starting throughput? + Log.d(TAG, "read PHY before starting throughput") currentRxPhy = rxPhy + Log.d(TAG, "phy : " + currentRxPhy.toString()) } override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) { @@ -2482,15 +3374,20 @@ class IOPTestActivity : AppCompatActivity() { this@IOPTestActivity.mtu = mtu mStartTimeDiscover = System.currentTimeMillis() Log.d(TAG, "setPreferredPhy 1M") - mBluetoothGatt?.setPreferredPhy(BluetoothDevice.PHY_LE_1M_MASK, BluetoothDevice.PHY_LE_1M_MASK, BluetoothDevice.PHY_OPTION_NO_PREFERRED) - if (mIndexRunning == POSITION_TEST_DISCOVER_SERVICE || mIndexRunning == POSITION_TEST_IOP3_THROUGHPUT) { + mBluetoothGatt?.setPreferredPhy( + BluetoothDevice.PHY_LE_1M_MASK, + BluetoothDevice.PHY_LE_1M_MASK, + BluetoothDevice.PHY_OPTION_NO_PREFERRED + ) + if (mIndexRunning == POSITION_TEST_DISCOVER_SERVICE || mIndexRunning == POSITION_TEST_IOP3_THROUGHPUT || mIndexRunning == POSITION_TEST_IOP3_LE_PRIVACY || (mIndexRunning == POSITION_TEST_IOP3_SECURITY && iopPhase3BondingStep > 2)) { mListCharacteristics.clear() characteristicsPhase3Security.clear() characteristicIOPPhase3Control = null refreshServices() } else if ((mIndexRunning == POSITION_TEST_IOP3_OTA_WITHOUT_ACK - || mIndexRunning == POSITION_TEST_IOP3_OTA_ACK) - && !otaProcess) { + || mIndexRunning == POSITION_TEST_IOP3_OTA_ACK) + && !otaProcess + ) { Log.d(TAG, "rediscover OTA") handler?.postDelayed({ mListCharacteristics.clear() @@ -2523,13 +3420,17 @@ class IOPTestActivity : AppCompatActivity() { when (mIndexRunning) { POSITION_TEST_SCANNER -> { - if (device.name.equals(getSiliconLabsTestInfo().fwName, ignoreCase = true)) { - mBluetoothDevice = device - mDeviceAddress = device.address - scanLeDevice(false) - finishItemTest(POSITION_TEST_SCANNER, getSiliconLabsTestInfo().listItemTest[POSITION_TEST_SCANNER]) - } else return + if (device.name.equals(getSiliconLabsTestInfo().fwName, ignoreCase = true)) { + mBluetoothDevice = device + mDeviceAddress = device.address + scanLeDevice(false) + finishItemTest( + POSITION_TEST_SCANNER, + getSiliconLabsTestInfo().listItemTest[POSITION_TEST_SCANNER] + ) + } else return } + POSITION_TEST_IOP3_OTA_ACK, POSITION_TEST_IOP3_OTA_WITHOUT_ACK -> { if (device.address == mDeviceAddress) { @@ -2540,6 +3441,8 @@ class IOPTestActivity : AppCompatActivity() { }, 5000) } else return } + + POSITION_TEST_IOP3_LE_PRIVACY, POSITION_TEST_IOP3_SECURITY, POSITION_TEST_IOP3_CACHING -> { if (device.address == mDeviceAddress) { @@ -2586,7 +3489,8 @@ class IOPTestActivity : AppCompatActivity() { /* Add IoP Test for P3 */ private const val POSITION_TEST_IOP3_THROUGHPUT = 6 private const val POSITION_TEST_IOP3_SECURITY = 7 - private const val POSITION_TEST_IOP3_CACHING = 8 + private const val POSITION_TEST_IOP3_CACHING = 9 + private const val POSITION_TEST_IOP3_LE_PRIVACY = 8 private const val POSITION_TEST_IOP3_OTA_ACK = 4 private const val POSITION_TEST_IOP3_OTA_WITHOUT_ACK = 5 diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/models/DemoItemProvider.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/models/DemoItemProvider.kt index c1a73571..bb604e32 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/models/DemoItemProvider.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/models/DemoItemProvider.kt @@ -15,7 +15,7 @@ class DemoItemProvider { add(ItemTestCaseInfo(6, "IOP Test OTA update without ACK", "Update user application via OTA without ACK.", null, Common.IOP3_TC_STATUS_WAITING)) add(ItemTestCaseInfo(7, "Throughput", "Throughput-GATT Notification.", null, Common.IOP3_TC_STATUS_WAITING)) add(ItemTestCaseInfo(8, "Security and Encryption", "Security and Encryption.", dataChildrenSecurity(), Common.IOP3_TC_STATUS_WAITING)) - + add(ItemTestCaseInfo(9, "LE Privacy", "LE Privacy.",null, Common.IOP3_TC_STATUS_WAITING)) /* This test should remain hidden for now due to investigation being underway. It may return later. add(ItemTestCaseInfo(9, "GATT Caching", "GATT Caching.", dataChildrenCaching(), Common.IOP3_TC_STATUS_WAITING)) */ diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/models/SiliconLabsTestInfo.kt b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/models/SiliconLabsTestInfo.kt index 5327ece3..299bc1e7 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/models/SiliconLabsTestInfo.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/features/iop_test/models/SiliconLabsTestInfo.kt @@ -1,5 +1,7 @@ package com.siliconlabs.bledemo.features.iop_test.models +import android.util.Log +import com.siliconlabs.bledemo.features.iop_test.activities.IOPTestActivity import com.siliconlabs.bledemo.features.iop_test.utils.Utils import timber.log.Timber import java.text.SimpleDateFormat @@ -14,7 +16,7 @@ class SiliconLabsTestInfo(var fwName: String, val listItemTest: ArrayList 0) { @@ -85,6 +88,7 @@ class SiliconLabsTestInfo(var fwName: String, val listItemTest: ArrayList append("7.1") itemTest.idTest == 5 -> append("6.1") itemTest.idTest == 6 -> append("6.2") + itemTest.idTest == 9 -> append("7.6") else -> append(itemTest.idTest + 1) } append(",") @@ -108,6 +112,13 @@ class SiliconLabsTestInfo(var fwName: String, val listItemTest: ArrayList { + ThunderBoardDevice.THUNDERBOARD_MODEL_DEV_KIT_V2, + ThunderBoardDevice.THUNDERBOARD_MODEL_DEV_KIT_V3 -> { connectType = GattConnectType.BLINKY_THUNDERBOARD cachedBoardType = characteristic.getStringValue(0) gatt?.readCharacteristic(getPowerSourceCharacteristic(gatt)) @@ -125,6 +126,8 @@ class SelectDeviceDialog( } else -> { + showMessage(R.string.unknown_model) + (activity as MainActivity).dismissModalDialog() Timber.d("Unknown model") } } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/DemoFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/DemoFragment.kt index a465f107..f3b3546d 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/DemoFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/DemoFragment.kt @@ -1,11 +1,19 @@ package com.siliconlabs.bledemo.home_screen.fragments +import android.content.Context import android.content.DialogInterface import android.content.Intent +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.net.Uri +import android.net.wifi.WifiManager +import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast +import androidx.core.content.ContextCompat.getColor import androidx.recyclerview.widget.GridLayoutManager import by.kirich1409.viewbindingdelegate.viewBinding import com.siliconlabs.bledemo.R @@ -13,6 +21,10 @@ import com.siliconlabs.bledemo.bluetooth.services.BluetoothService import com.siliconlabs.bledemo.databinding.FragmentDemoBinding import com.siliconlabs.bledemo.features.demo.matter_demo.activities.MatterDemoActivity import com.siliconlabs.bledemo.features.demo.range_test.activities.RangeTestActivity +import com.siliconlabs.bledemo.features.demo.wifi_ota_update.AlertErrorDialog +import com.siliconlabs.bledemo.features.demo.wifi_ota_update.WiFiOtaFileManager +import com.siliconlabs.bledemo.features.demo.wifi_ota_update.WiFiOtaFileSelectionDialog +import com.siliconlabs.bledemo.features.demo.wifi_ota_update.WiFiOtaProgressDialog import com.siliconlabs.bledemo.home_screen.adapters.DemoAdapter import com.siliconlabs.bledemo.home_screen.base.BaseServiceDependentMainMenuFragment import com.siliconlabs.bledemo.home_screen.base.BluetoothDependent @@ -26,9 +38,25 @@ import com.siliconlabs.bledemo.home_screen.menu_items.EslDemo import com.siliconlabs.bledemo.home_screen.menu_items.HealthThermometer import com.siliconlabs.bledemo.home_screen.menu_items.MatterDemo import com.siliconlabs.bledemo.home_screen.menu_items.Motion +import com.siliconlabs.bledemo.home_screen.menu_items.OTADemo import com.siliconlabs.bledemo.home_screen.menu_items.RangeTest import com.siliconlabs.bledemo.home_screen.menu_items.Throughput import com.siliconlabs.bledemo.home_screen.menu_items.WifiCommissioning +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStream +import java.net.InetAddress +import java.net.ServerSocket +import java.net.Socket +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import java.nio.ByteBuffer +import java.nio.ByteOrder +import java.util.Locale +import java.util.Timer +import java.util.TimerTask class DemoFragment : BaseServiceDependentMainMenuFragment(), DemoAdapter.OnDemoItemClickListener, DialogInterface.OnDismissListener { @@ -38,8 +66,22 @@ class DemoFragment : BaseServiceDependentMainMenuFragment(), DemoAdapter.OnDemoI private val list: ArrayList = ArrayList() private var selectDeviceDialog: SelectDeviceDialog? = null + private var otaProgressDialog: WiFiOtaProgressDialog? = null + private var otaFileSelectionDialog: WiFiOtaFileSelectionDialog? = null + private var otaFileManager: WiFiOtaFileManager? = null + private var otaFilePath: Uri? = null + private var socket : Socket? = null + private var serverSocket : ServerSocket? = null + private var portId = 8080 + private var isClientConnected = false + private val kBits = "%.2f Kbit/s" + private var alertErrorDialog: AlertErrorDialog? = null + private var packetCount = 0 + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + otaFileManager = WiFiOtaFileManager(requireContext()) + initOtaProgressDialog() list.apply { add( @@ -112,6 +154,13 @@ class DemoFragment : BaseServiceDependentMainMenuFragment(), DemoAdapter.OnDemoI getString(R.string.matter_demo_description) ) ) + add( + OTADemo( + R.drawable.redesign_ic_demo_ota, + getString(R.string.ota_demo_title), + getString(R.string.ota_demo_desc) + ) + ) } } @@ -185,6 +234,24 @@ class DemoFragment : BaseServiceDependentMainMenuFragment(), DemoAdapter.OnDemoI requireContext().startActivity(Intent(requireContext(), MatterDemoActivity::class.java)) } else if (demoItem.connectType == BluetoothService.GattConnectType.RANGE_TEST) { startActivity(Intent(requireContext(), RangeTestActivity::class.java)) + } else if (demoItem.connectType == BluetoothService.GattConnectType.WIFI_OTA_UPDATE) { + if (isNetworkAvailable(context)) { + otaFileSelectionDialog = WiFiOtaFileSelectionDialog(object : WiFiOtaFileSelectionDialog.CancelCallback { + override fun onDismiss() { + otaFileSelectionDialog?.dismiss() + }}, + listener = fileSelectionListener, + getLocalIpAddress() + ).also { + it.show(childFragmentManager, "ota_file_selection_dialog") + } + } else { + Toast.makeText( + requireContext(), + getString(R.string.turn_on_wifi), + Toast.LENGTH_SHORT + ).show() + } } else { selectDeviceDialog = SelectDeviceDialog.newDialog(demoItem.connectType) selectDeviceDialog?.show(childFragmentManager, "select_device_dialog") @@ -194,4 +261,336 @@ class DemoFragment : BaseServiceDependentMainMenuFragment(), DemoAdapter.OnDemoI override fun onDismiss(dialog: DialogInterface) { selectDeviceDialog = null } + + @Throws(UnknownHostException::class) + private fun getLocalIpAddress(): String? { + val wifiManager = + (requireContext().getSystemService(Context.WIFI_SERVICE) as WifiManager) + val wifiInfo = wifiManager.connectionInfo + val ipInt = wifiInfo.ipAddress + return InetAddress.getByAddress( + ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ipInt).array() + ).hostAddress + } + + private fun isNetworkAvailable(context: Context?): Boolean { + if (context == null) return false + val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) + if (capabilities != null) { + when { + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> { + return true + } + } + } + } else { + val activeNetworkInfo = connectivityManager.activeNetworkInfo + if (activeNetworkInfo != null && activeNetworkInfo.isConnected) { + return true + } + } + return false + } + private val fileSelectionListener = object : WiFiOtaFileSelectionDialog.FileSelectionListener { + override fun onSelectFileButtonClicked() { + otaFileSelectionDialog?.disableUploadButton() + Intent(Intent.ACTION_GET_CONTENT) + .apply { type = "*/*" } + .also { startActivityForResult(Intent.createChooser(it, + getString(R.string.ota_choose_file)), + RPS_FILE_CHOICE_REQUEST_CODE + ) } + } + + override fun onOtaButtonClicked() { + if(otaFileSelectionDialog?.checkPortNumberValid() == true){ + otaFileManager?.otaFile?.let { + startOtaProcess() + } ?: if (otaFileManager?.otaFilename != null) { + Toast.makeText(requireContext(), getString(R.string.incorrect_file), Toast.LENGTH_SHORT).show() + } else { + Toast.makeText(requireContext(), getString(R.string.no_file_chosen), Toast.LENGTH_SHORT).show() + } + } else { + Toast.makeText(requireContext(), getString(R.string.port_id_validation), Toast.LENGTH_SHORT).show() + } + } + + override fun onCancelButtonClicked() { + otaFileSelectionDialog?.dismiss() + otaFileSelectionDialog = null + } + } + + private fun initOtaProgressDialog() { + otaProgressDialog = WiFiOtaProgressDialog(requireContext()) + initializeToDefaultValues() + otaProgressDialog?.btnOtaEnd?.setOnClickListener { + initializeToDefaultValues() + otaProgressDialog?.dismiss() + socket?.close() + serverSocket?.close() + } + otaProgressDialog?.btnCancel?.setOnClickListener { + initializeToDefaultValues() + otaProgressDialog?.dismiss() + socket?.close() + serverSocket?.close() + } + } + + private fun initializeToDefaultValues() { + isClientConnected = false + otaProgressDialog?.progressBar?.progress = 0 + otaProgressDialog?.dataRate?.visibility = View.INVISIBLE + otaProgressDialog?.dataSize?.text = getString(R.string.iop_test_n_percent, 0) + otaProgressDialog?.steps?.visibility = View.INVISIBLE + otaProgressDialog?.uploadImage?.visibility = View.VISIBLE +// otaProgressDialog?.btnOtaEnd?.isEnabled = false + otaProgressDialog?.btnOtaEnd?.setBackgroundColor(getColor(requireContext(),R.color.silabs_red)) + otaProgressDialog?.btnOtaEnd?.text = getString(R.string.button_cancel) + otaProgressDialog?.firmwareStatus?.text = getString(R.string.waiting_for_client_to_connect) + otaProgressDialog?.firmwareStatus?.setTextColor(getColor(requireContext(),R.color.silabs_dark_blue)) + } + + private fun showOtaProgressDialog() { + otaProgressDialog?.show() + } + + private fun startOtaProcess() { + activity?.runOnUiThread { + showOtaProgressDialog() + portId = Integer.parseInt(otaFileSelectionDialog?.getPortId()) + otaProgressDialog?.setProgressInfo(otaFileManager?.otaFilename, otaFileManager?.otaFile?.size, getLocalIpAddress() ,portId.toString()) + animateLoading() + + } + //Start OTA_data Upload in another thread + val otaUpload = Thread { + + try { + serverSocket = ServerSocket(portId) + serverSocket?.receiveBufferSize = RECEIVER_BUFFER_SIZE +// serverSocket.soTimeout = 60000 + while (true) { + println("Waiting for client to connect") + socket = serverSocket?.accept() + println("Accept success") + + Thread { + otaFilePath?.let { + getInputStreamFromUri(requireContext(), it)?.let { + processRequest(it) + } + } + socket?.close() //close socket + }.start() + } + } catch (e: SocketTimeoutException) { + showTimeOutError() + e.printStackTrace() + } catch (e: IOException) { + socket?.close() + serverSocket?.close() + e.printStackTrace() + } + + } + otaUpload.start() + } + + private fun showTimeOutAlertDialog() { + activity?.runOnUiThread { + alertErrorDialog = AlertErrorDialog( object : AlertErrorDialog.OtaErrorCallback { + override fun onDismiss() { + initializeToDefaultValues() + otaProgressDialog?.dismiss() + socket?.close() + serverSocket?.close() + } + }) + + alertErrorDialog?.show(childFragmentManager, "error_dialog") + } + } + + private fun showTimeOutError() { + activity?.runOnUiThread { + if (!isClientConnected) { + otaProgressDialog?.firmwareStatus?.text = getString(R.string.server_timeout) + otaProgressDialog?.firmwareStatus?.setTextColor( + getColor( + requireContext(), + R.color.silabs_red + ) + ) +// otaProgressDialog?.btnOtaEnd?.isEnabled = true + otaProgressDialog?.uploadImage?.visibility = View.GONE + } + } + } + + private fun getInputStreamFromUri(context: Context, uri: Uri): FileInputStream? { + try { + val inputStream = context.contentResolver.openInputStream(uri) + if (inputStream != null) { + // Optionally, you may want to copy the content to a temporary file + // if you need a FileInputStream specifically + val tempFile = createTempFileFromInputStream(inputStream, context.cacheDir) + return FileInputStream(tempFile) + } + } catch (e: Exception) { + e.printStackTrace() + } + return null + } + + private fun createTempFileFromInputStream(inputStream: InputStream, cacheDir: File): File { + val tempFile = File.createTempFile("temp", null, cacheDir) + tempFile.deleteOnExit() + FileOutputStream(tempFile).use { fileOutputStream -> + inputStream.copyTo(fileOutputStream) + } + return tempFile + } + + private fun processRequest(fp: FileInputStream): Int { + try { + println("processRequest") + var ctr = 0 + var retLen: Int + val txLen = 0 + val data = ByteArray(3) + val data1 = ByteArray(1503) // 1500 + 3 + var cmdType: Byte + var length = 0 + val outputStream = socket!!.getOutputStream() + val inputStream = socket!!.getInputStream() + val totalPackets = Integer.parseInt(otaFileManager?.otaFile?.size?.div(1024).toString())+1 + while (true) { + retLen = inputStream.read(data, 0, 3) + if (retLen > 0) { + cmdType = data[0] + if (cmdType == RPS_HEADER) { + length = fp.read(data1, 3, 64) + data1[0] = RPS_HEADER + data1[1] = (length and 0xff).toByte() + data1[2] = (length shr 8 and 0xff).toByte() + fp.channel.position(0) + } else if (cmdType == RPS_DATA) { + length = fp.read(data1, 3, 1024) + data1[0] = RPS_DATA + data1[1] = (length and 0x00ff).toByte() + data1[2] = (length shr 8 and 0x00ff).toByte() + if (length == -1) { + fp.close() + outputStream.write(data1, 0, length + 3) + length = 0 + data1[0] = RPS_DATA + data1[1] = (length and 0x00ff).toByte() + data1[2] = (length shr 8 and 0x00ff).toByte() + outputStream.write(data1, 0, length + 3) + outputStream.flush() + outputStream.close() + return ctr + } + } + } else if (length == 0) { + fp.close() + socket!!.close() + outputStream.write(data1, 0, length + 3) + return ctr + } + println("Size of data: $length") + outputStream.write(data1, 0, length + 3) + println("Send returns: $txLen") + if (txLen != 0) { + println("Error while sending") + return 0 + } + //Update the Progress Dialog UI + updateProgressDialog(ctr++, totalPackets) + packetCount = ctr + if(ctr { + intent?.data?.let { + otaFilePath = it + otaFileManager?.readFilename(it) + otaFileSelectionDialog?.changeFileName(otaFileManager?.otaFilename) + if (otaFileManager?.hasCorrectFileExtensionRPS() == true) { + otaFileManager?.readFile(it).toString() + otaFileSelectionDialog?.enableUploadButton() + } else { + Toast.makeText(requireContext(), getString(R.string.incorrect_file), Toast.LENGTH_SHORT).show() + } + } ?: Toast.makeText(requireContext(), getString(R.string.chosen_file_not_found), Toast.LENGTH_SHORT).show() + } + } + } + + companion object { + private const val RECEIVER_BUFFER_SIZE = 2920 + private const val RPS_FILE_CHOICE_REQUEST_CODE = 202 + private const val RPS_HEADER: Byte = 0x01 + private const val RPS_DATA: Byte = 0x00 + } } \ No newline at end of file diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/SettingsFragment.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/SettingsFragment.kt index f02bfbc2..3f31e029 100644 --- a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/SettingsFragment.kt +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/fragments/SettingsFragment.kt @@ -104,12 +104,12 @@ class SettingsFragment : Fragment() { } companion object { - private const val LINK_REPORT_ISSUE = "github.com/SiliconLabs/EFRConnect-android/issues" + private const val LINK_REPORT_ISSUE = "github.com/SiliconLabs/SimplicityConnect-android/issues" private const val LINK_MORE_INFO = "silabs.com/products/wireless" - private const val LINK_SOURCECODE = "github.com/SiliconLabs/EFRConnect-android" - private const val LINK_USERS_GUIDE = "docs.silabs.com/bluetooth/latest/miscellaneous/mobile/efr-connect-mobile-app" + private const val LINK_SOURCECODE = "github.com/SiliconLabs/SimplicityConnect-android" + private const val LINK_USERS_GUIDE = "docs.silabs.com/mobile-apps/latest/mobile-apps-start/" private const val LINK_SUPPORT = "silabs.com/support" - private const val LINK_RELEASE_NOTES = "silabs.com/documents/public/release-notes/efr-connect-release-notes.pdf" + private const val LINK_RELEASE_NOTES = "docs.silabs.com/mobile-apps/latest/mobile-apps-release-notes/" private const val LINK_DOCUMENTATION = "docs.silabs.com/bluetooth/latest" private const val LINK_GOOGLE_PLAY_STORE = "play.google.com/store/apps/developer?id=Silicon+Laboratories" } diff --git a/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/menu_items/OTADemo.kt b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/menu_items/OTADemo.kt new file mode 100644 index 00000000..ea3207d4 --- /dev/null +++ b/mobile/src/main/java/com/siliconlabs/bledemo/home_screen/menu_items/OTADemo.kt @@ -0,0 +1,13 @@ +package com.siliconlabs.bledemo.home_screen.menu_items + +import androidx.annotation.DrawableRes +import com.siliconlabs.bledemo.bluetooth.services.BluetoothService + +class OTADemo( @DrawableRes imageResId: Int, +title: String, +description: String +) : DemoMenuItem(imageResId, title, description +) { + + override val connectType: BluetoothService.GattConnectType = BluetoothService.GattConnectType.WIFI_OTA_UPDATE +} \ No newline at end of file diff --git a/mobile/src/main/res/drawable/background_grey_box.xml b/mobile/src/main/res/drawable/background_grey_box.xml new file mode 100644 index 00000000..181e6dd7 --- /dev/null +++ b/mobile/src/main/res/drawable/background_grey_box.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/mobile/src/main/res/drawable/redesign_ic_demo_ota.xml b/mobile/src/main/res/drawable/redesign_ic_demo_ota.xml new file mode 100644 index 00000000..9f86c388 --- /dev/null +++ b/mobile/src/main/res/drawable/redesign_ic_demo_ota.xml @@ -0,0 +1,18 @@ + + + + + diff --git a/mobile/src/main/res/drawable/si_connect.xml b/mobile/src/main/res/drawable/si_connect.xml new file mode 100644 index 00000000..9e6c9751 --- /dev/null +++ b/mobile/src/main/res/drawable/si_connect.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/src/main/res/drawable/si_connect_app_screen_background.jpg b/mobile/src/main/res/drawable/si_connect_app_screen_background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5e5372db3721f992029291e110277913b30f08b9 GIT binary patch literal 108594 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 S?)@`}U)}oi3Y^qgfB)G)pPViL literal 0 HcmV?d00001 diff --git a/mobile/src/main/res/drawable/silabs_logo.xml b/mobile/src/main/res/drawable/silabs_logo.xml new file mode 100644 index 00000000..aaece70d --- /dev/null +++ b/mobile/src/main/res/drawable/silabs_logo.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + diff --git a/mobile/src/main/res/drawable/wi_fi_symbol.png b/mobile/src/main/res/drawable/wi_fi_symbol.png new file mode 100644 index 0000000000000000000000000000000000000000..c9e32789faf94013711b48510bba1a8f7a055dce GIT binary patch literal 78881 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 literal 0 HcmV?d00001 diff --git a/mobile/src/main/res/layout/dialog_alert_error.xml b/mobile/src/main/res/layout/dialog_alert_error.xml new file mode 100644 index 00000000..d3f929dd --- /dev/null +++ b/mobile/src/main/res/layout/dialog_alert_error.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + +