Skip to content

Commit c08a878

Browse files
committed
fix(android): Ensure MCUMgr errors always have a message
Frustratingly, some of the Nordic defined errors do not have messages. This causes react native to throw "Value is null, expected a String".
1 parent 7785d66 commit c08a878

File tree

3 files changed

+104
-22
lines changed

3 files changed

+104
-22
lines changed

android/src/main/java/uk/co/playerdata/reactnativemcumanager/DeviceUpgrade.kt

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,19 @@ import io.runtime.mcumgr.exception.McuMgrException
1414
import java.io.IOException
1515

1616
val UpgradeModes =
17-
mapOf(
18-
1 to FirmwareUpgradeManager.Mode.TEST_AND_CONFIRM,
19-
2 to FirmwareUpgradeManager.Mode.CONFIRM_ONLY,
20-
3 to FirmwareUpgradeManager.Mode.TEST_ONLY
21-
)
17+
mapOf(
18+
1 to FirmwareUpgradeManager.Mode.TEST_AND_CONFIRM,
19+
2 to FirmwareUpgradeManager.Mode.CONFIRM_ONLY,
20+
3 to FirmwareUpgradeManager.Mode.TEST_ONLY
21+
)
2222

2323
class DeviceUpgrade(
24-
private val id: String,
25-
device: BluetoothDevice,
26-
private val context: Context,
27-
private val updateFileUri: Uri,
28-
private val updateOptions: UpdateOptions,
29-
private val manager: ReactNativeMcuManagerModule
24+
private val id: String,
25+
device: BluetoothDevice,
26+
private val context: Context,
27+
private val updateFileUri: Uri,
28+
private val updateOptions: UpdateOptions,
29+
private val manager: ReactNativeMcuManagerModule
3030
) : FirmwareUpgradeCallback {
3131
private val TAG = "DeviceUpdate"
3232
private var lastNotification = -1
@@ -53,7 +53,15 @@ class DeviceUpgrade(
5353
dfuManager.cancel()
5454
disconnectDevice()
5555
Log.v(this.TAG, "Cancel")
56-
withSafePromise { promise -> promise.reject(CodedException("Update cancelled")) }
56+
withSafePromise { promise ->
57+
promise.reject(
58+
CodedException(
59+
"UPGRADE_CANCELLED",
60+
"Upgrade cancelled",
61+
null
62+
)
63+
)
64+
}
5765
}
5866

5967
private fun disconnectDevice() {
@@ -84,15 +92,21 @@ class DeviceUpgrade(
8492
e.printStackTrace()
8593
disconnectDevice()
8694
Log.v(this.TAG, "mcu exception")
87-
withSafePromise { promise -> promise.reject(CodedException(e)) }
95+
withSafePromise { promise ->
96+
promise.reject(
97+
ReactNativeMcuMgrException.fromMcuMgrException(
98+
e
99+
)
100+
)
101+
}
88102
}
89103
}
90104

91105
override fun onUpgradeStarted(controller: FirmwareUpgradeController) {}
92106

93107
override fun onStateChanged(
94-
prevState: FirmwareUpgradeManager.State,
95-
newState: FirmwareUpgradeManager.State
108+
prevState: FirmwareUpgradeManager.State,
109+
newState: FirmwareUpgradeManager.State
96110
) {
97111
val stateMap: Map<String, Any?> = mapOf("id" to id, "state" to newState.name)
98112
manager.upgradeStateCB(stateMap)
@@ -105,12 +119,26 @@ class DeviceUpgrade(
105119

106120
override fun onUpgradeFailed(state: FirmwareUpgradeManager.State, error: McuMgrException) {
107121
disconnectDevice()
108-
withSafePromise { promise -> promise.reject(CodedException(error)) }
122+
withSafePromise { promise ->
123+
promise.reject(
124+
ReactNativeMcuMgrException.fromMcuMgrException(
125+
error
126+
)
127+
)
128+
}
109129
}
110130

111131
override fun onUpgradeCanceled(state: FirmwareUpgradeManager.State) {
112132
disconnectDevice()
113-
withSafePromise { promise -> promise.reject(CodedException("Update cancelled")) }
133+
withSafePromise { promise ->
134+
promise.reject(
135+
CodedException(
136+
"UPGRADE_CANCELLED",
137+
"Upgrade cancelled",
138+
null
139+
)
140+
)
141+
}
114142
}
115143

116144
override fun onUploadProgressChanged(bytesSent: Int, imageSize: Int, timestamp: Long) {

android/src/main/java/uk/co/playerdata/reactnativemcumanager/ReactNativeMcuManagerModule.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import expo.modules.kotlin.modules.ModuleDefinition
1111
import expo.modules.kotlin.records.Field
1212
import expo.modules.kotlin.records.Record
1313
import io.runtime.mcumgr.ble.McuMgrBleTransport
14+
import io.runtime.mcumgr.exception.McuMgrException
1415
import io.runtime.mcumgr.managers.ImageManager
1516

1617
private const val MODULE_NAME = "ReactNativeMcuManager"
@@ -52,8 +53,8 @@ class ReactNativeMcuManagerModule : Module() {
5253
imageManager.erase()
5354

5455
promise.resolve(null)
55-
} catch (e: Throwable) {
56-
promise.reject(CodedException(e))
56+
} catch (e: McuMgrException) {
57+
promise.reject(ReactNativeMcuMgrException.fromMcuMgrException(e))
5758
}
5859
}
5960

@@ -87,15 +88,15 @@ class ReactNativeMcuManagerModule : Module() {
8788

8889
AsyncFunction("runUpgrade") { id: String, promise: Promise ->
8990
if (!upgrades.contains(id)) {
90-
promise.reject(CodedException("update ID not present"))
91+
promise.reject(CodedException("UPGRADE_ID_MISSING", "Upgrade ID $id not present", null))
9192
}
9293

9394
upgrades[id]!!.startUpgrade(promise)
9495
}
9596

9697
AsyncFunction("cancelUpgrade") { id: String, promise: Promise ->
9798
if (!upgrades.contains(id)) {
98-
Log.w(TAG, "can't cancel update ID ($id} not present")
99+
Log.w(TAG, "Can't cancel update ID ($id} not present")
99100
return@AsyncFunction
100101
}
101102

@@ -104,7 +105,7 @@ class ReactNativeMcuManagerModule : Module() {
104105

105106
Function("destroyUpgrade") { id: String ->
106107
if (!upgrades.contains(id)) {
107-
Log.w(TAG, "can't destroy update ID ($id} not present")
108+
Log.w(TAG, "Can't destroy update ID ($id} not present")
108109
return@Function
109110
}
110111

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package uk.co.playerdata.reactnativemcumanager
2+
3+
import expo.modules.kotlin.exception.CodedException
4+
import io.runtime.mcumgr.ble.exception.McuMgrBluetoothDisabledException
5+
import io.runtime.mcumgr.ble.exception.McuMgrDisconnectedException
6+
import io.runtime.mcumgr.ble.exception.McuMgrNotSupportedException
7+
import io.runtime.mcumgr.exception.InsufficientMtuException
8+
import io.runtime.mcumgr.exception.McuMgrCoapException
9+
import io.runtime.mcumgr.exception.McuMgrErrorException
10+
import io.runtime.mcumgr.exception.McuMgrException
11+
import io.runtime.mcumgr.exception.McuMgrTimeoutException
12+
13+
class ReactNativeMcuMgrException private constructor(
14+
code: String, message: String?, cause: Throwable?
15+
) : CodedException(code, message, cause) {
16+
17+
companion object {
18+
private fun getCode(e: McuMgrException): String {
19+
return when (e) {
20+
is McuMgrBluetoothDisabledException -> return "MCU_MGR_BLUETOOTH_DISABLED"
21+
is McuMgrDisconnectedException -> return "MCU_MGR_DISCONNECTED"
22+
is McuMgrNotSupportedException -> return "MCU_MGR_NOT_SUPPORTED"
23+
is InsufficientMtuException -> return "MCU_MGR_INSUFFICIENT_MTU"
24+
is McuMgrCoapException -> return "MCU_MGR_COAP"
25+
is McuMgrErrorException -> return "MCU_MGR_ERROR_${e.code}"
26+
is McuMgrTimeoutException -> return "MCU_MGR_TIMEOUT"
27+
else -> "UNEXPECTED_MCU_MGR_EXCEPTION"
28+
}
29+
}
30+
31+
private fun getMessage(e: McuMgrException): String {
32+
return when (e) {
33+
is McuMgrBluetoothDisabledException -> return "Bluetooth disabled"
34+
is McuMgrDisconnectedException -> return "Device disconnected"
35+
is McuMgrNotSupportedException -> return "Device not supported by MCUMgr"
36+
is McuMgrTimeoutException -> return "MCUMgr timeout"
37+
else -> {
38+
if (e.localizedMessage != null) {
39+
return e.localizedMessage
40+
} else if (e.message != null) {
41+
return e.message!!
42+
} else {
43+
return e.toString()
44+
}
45+
}
46+
}
47+
}
48+
49+
fun fromMcuMgrException(e: McuMgrException): ReactNativeMcuMgrException {
50+
return ReactNativeMcuMgrException(this.getCode(e), this.getMessage(e), e)
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)