diff --git a/app/build.gradle b/app/build.gradle index 8292c32a8..80bad0b4f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -31,8 +31,8 @@ android { applicationId "com.geeksville.mesh" minSdkVersion 21 // The oldest emulator image I have tried is 22 (though 21 probably works) targetSdkVersion 29 - versionCode 20133 // format is Mmmss (where M is 1+the numeric major number - versionName "1.1.33" + versionCode 20135 // format is Mmmss (where M is 1+the numeric major number + versionName "1.1.35" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index b5686f564..4f5a1fce3 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -606,34 +606,46 @@ class MainActivity : AppCompatActivity(), Logging, /// Called when we gain/lose a connection to our mesh radio private fun onMeshConnectionChanged(connected: MeshService.ConnectionState) { - model.isConnected.value = connected debug("connchange ${model.isConnected.value}") + if (connected == MeshService.ConnectionState.CONNECTED) { - model.meshService?.let { service -> + + val oldConnection = model.isConnected.value + model.isConnected.value = connected + debug("Getting latest radioconfig from service") - model.radioConfig.value = - MeshProtos.RadioConfig.parseFrom(service.radioConfig) + try { + model.radioConfig.value = + MeshProtos.RadioConfig.parseFrom(service.radioConfig) - val info = service.myNodeInfo - model.myNodeInfo.value = info + val info = service.myNodeInfo + model.myNodeInfo.value = info - val isOld = info.minAppVersion > BuildConfig.VERSION_CODE - if (isOld) - MaterialAlertDialogBuilder(this) - .setTitle(getString(R.string.app_too_old)) - .setMessage(getString(R.string.must_update)) - .setPositiveButton("Okay") { _, _ -> - info("User acknowledged app is old") - } - .show() + val isOld = info.minAppVersion > BuildConfig.VERSION_CODE + if (isOld) + MaterialAlertDialogBuilder(this) + .setTitle(getString(R.string.app_too_old)) + .setMessage(getString(R.string.must_update)) + .setPositiveButton("Okay") { _, _ -> + info("User acknowledged app is old") + } + .show() - updateNodesFromDevice() + updateNodesFromDevice() - // we have a connection to our device now, do the channel change - perhapsChangeChannel() + // we have a connection to our device now, do the channel change + perhapsChangeChannel() + } catch (ex: RemoteException) { + warn("Abandoning connect $ex, because we probably just lost device connection") + model.isConnected.value = oldConnection + } } } + else { + // For other connection states, just slam them in + model.isConnected.value = connected + } } private fun perhapsChangeChannel() { @@ -715,7 +727,7 @@ class MainActivity : AppCompatActivity(), Logging, model.messagesState.addMessage(payload) } else -> - warn("Ignored dataType ${payload.dataType}") + debug("activity only cares about text messages, ignoring dataType ${payload.dataType}") } } @@ -799,7 +811,8 @@ class MainActivity : AppCompatActivity(), Logging, registerMeshReceiver() // Init our messages table with the service's record of past text messages (ignore all other message types) - val msgs = service.oldMessages.filter { p -> p.dataType == Portnums.PortNum.TEXT_MESSAGE_APP_VALUE } + val msgs = + service.oldMessages.filter { p -> p.dataType == Portnums.PortNum.TEXT_MESSAGE_APP_VALUE } debug("Service provided ${msgs.size} messages") model.messagesState.setMessages(msgs) val connectionState = @@ -849,7 +862,11 @@ class MainActivity : AppCompatActivity(), Logging, } // ALSO bind so we can use the api - mesh.connect(this, MeshService.createIntent(), Context.BIND_AUTO_CREATE + Context.BIND_ABOVE_CLIENT) + mesh.connect( + this, + MeshService.createIntent(), + Context.BIND_AUTO_CREATE + Context.BIND_ABOVE_CLIENT + ) } private fun unbindMeshService() { diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt index bde6821fc..d8ef31baf 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -60,6 +60,7 @@ class MeshService : Service(), Logging { class IdNotFoundException(id: String) : Exception("ID not found $id") class NodeNumNotFoundException(id: Int) : Exception("NodeNum not found $id") + class IsUpdatingException() : Exception("Operation prohibited during firmware update") /** * Talk to our running service and try to set a new device address. And then immediately @@ -228,6 +229,9 @@ class MeshService : Service(), Logging { private fun sendToRadio(p: ToRadio.Builder) { val b = p.build().toByteArray() + if(SoftwareUpdateService.isUpdating) + throw IsUpdatingException() + connectedRadio.sendToRadio(b) } @@ -892,7 +896,7 @@ class MeshService : Service(), Logging { // Do our startup init try { connectTimeMsec = System.currentTimeMillis() - SoftwareUpdateService.sendProgress(this, ProgressNotStarted) // Kinda crufty way of reiniting software update + SoftwareUpdateService.sendProgress(this, ProgressNotStarted, true) // Kinda crufty way of reiniting software update startConfig() } catch (ex: InvalidProtocolBufferException) { diff --git a/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt b/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt index 84bcfbc1a..507ee007b 100644 --- a/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt @@ -194,8 +194,21 @@ class SoftwareUpdateService : JobIntentService(), Logging { } - fun sendProgress(context: Context, p: Int) { - if(progress != p) { + /** + * true if we are busy with an update right now + */ + val isUpdating get() = progress >= 0 + + /** + * Update our progress indication for GUIs + * + * @param isAppload if false, we don't report failure indications (because we consider spiffs non critical for now). But do report to analytics + */ + fun sendProgress(context: Context, p: Int, isAppload: Boolean) { + if(!isAppload && p < 0) + reportError("Error while writing spiffs $progress") // See if this is happening in the wild + + if(progress != p && (p >= 0 || isAppload)) { progress = p val intent = Intent(ACTION_UPDATE_PROGRESS).putExtra( @@ -280,7 +293,7 @@ class SoftwareUpdateService : JobIntentService(), Logging { errormsg("Ignoring failure to update spiffs on old appload") } assets.appLoad?.let { doUpdate(context, sync, it, FLASH_REGION_APPLOAD) } - sendProgress(context, ProgressSuccess) + sendProgress(context, ProgressSuccess, true) } // writable region codes in the ESP32 update code @@ -292,6 +305,8 @@ class SoftwareUpdateService : JobIntentService(), Logging { * you can use it for the software update. */ private fun doUpdate(context: Context, sync: SafeBluetooth, assetName: String, flashRegion: Int = FLASH_REGION_APPLOAD) { + val isAppload = flashRegion == FLASH_REGION_APPLOAD + try { val g = sync.gatt!! val service = g.services.find { it.uuid == SW_UPDATE_UUID } @@ -306,7 +321,7 @@ class SoftwareUpdateService : JobIntentService(), Logging { info("Starting firmware update for $assetName, flash region $flashRegion") - sendProgress(context,0) + sendProgress(context,0, isAppload) val totalSizeDesc = getCharacteristic(SW_UPDATE_TOTALSIZE_CHARACTER) val dataDesc = getCharacteristic(SW_UPDATE_DATA_CHARACTER) val crc32Desc = getCharacteristic(SW_UPDATE_CRC32_CHARACTER) @@ -354,7 +369,7 @@ class SoftwareUpdateService : JobIntentService(), Logging { // yet val maxProgress = if(flashRegion != FLASH_REGION_APPLOAD) 50 else 100 - sendProgress(context, firmwareNumSent * maxProgress / firmwareSize) + sendProgress(context, firmwareNumSent * maxProgress / firmwareSize, isAppload) debug("sending block ${progress}%") var blockSize = 512 - 3 // Max size MTU excluding framing @@ -384,7 +399,7 @@ class SoftwareUpdateService : JobIntentService(), Logging { sync.readCharacteristic(updateResultDesc) .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0) if (updateResult != 0) { - sendProgress(context, ProgressUpdateFailed) + sendProgress(context, ProgressUpdateFailed, isAppload) throw Exception("Device update failed, reason=$updateResult") } @@ -395,7 +410,7 @@ class SoftwareUpdateService : JobIntentService(), Logging { } } } catch (ex: BLEException) { - sendProgress(context, ProgressBleException) + sendProgress(context, ProgressBleException, isAppload) throw ex // Unexpected BLE exception } }