From 56d43ddb1e5e75181f8b6031285fb165c47d66ec Mon Sep 17 00:00:00 2001 From: tajniak81 <13187254+tajniak81@users.noreply.github.com> Date: Wed, 21 Oct 2026 17:39:39 +0200 Subject: [PATCH] Stop the Fly App hanging forever on DJI registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DJI SDK can drop a registration attempt without ever calling onRegister (it logged "Exception when executing encrypt/decrypt functions" and nothing more), which left the app on "Registering…" with no way out but a restart. The bridge now gives up after 60 s and reports a failure that names the likely causes; a late answer from the SDK still replaces it. The failure was also only visible in the settings sheet, which the home screen's Connect aircraft flow never opens, so failures now show a snackbar too. Co-Authored-By: Claude Opus 5.5 --- .../com/pilotvault/flyapp/DjiSdkBridge.kt | 25 +++++++++++++++++++ Fly App/lib/main.dart | 3 +++ 2 files changed, 28 insertions(+) diff --git a/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/DjiSdkBridge.kt b/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/DjiSdkBridge.kt index ed524f4..10be3ff 100644 --- a/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/DjiSdkBridge.kt +++ b/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/DjiSdkBridge.kt @@ -49,6 +49,14 @@ class DjiSdkBridge( */ private const val IDENTITY_RETRY_MS = 2_000L private const val IDENTITY_MAX_ATTEMPTS = 30 + + /** + * The SDK can swallow a registration failure without ever calling + * [DJISDKManager.SDKManagerCallback.onRegister] — e.g. it logs "Exception when + * executing encrypt/decrypt functions" when DJI's server rejects the App Key — + * leaving the app on "Registering…" forever. Give up after this long. + */ + private const val REGISTRATION_TIMEOUT_MS = 60_000L } private val mainHandler = Handler(Looper.getMainLooper()) @@ -57,6 +65,19 @@ class DjiSdkBridge( private var eventSink: EventChannel.EventSink? = null + private val registrationTimeout = Runnable { + emit( + mapOf( + "type" to "registration", + "state" to "failed", + "error" to "DJI did not answer the registration within " + + "${REGISTRATION_TIMEOUT_MS / 1000} s. Check the internet connection, " + + "and that the DJI App Key is activated for package " + + "${appContext.packageName}.", + ) + ) + } + /** * The *flight controller's* serial number — not the aircraft's. * @@ -181,12 +202,16 @@ class DjiSdkBridge( private fun registerApp() { emit(mapOf("type" to "registration", "state" to "registering")) + mainHandler.removeCallbacks(registrationTimeout) + mainHandler.postDelayed(registrationTimeout, REGISTRATION_TIMEOUT_MS) DJISDKManager.getInstance().registerApp( appContext, object : DJISDKManager.SDKManagerCallback { override fun onRegister(error: DJIError?) { + // A late answer still wins: it replaces a timed-out "failed". + mainHandler.removeCallbacks(registrationTimeout) if (error == DJISDKError.REGISTRATION_SUCCESS) { emit(mapOf("type" to "registration", "state" to "success")) DJISDKManager.getInstance().startConnectionToProduct() diff --git a/Fly App/lib/main.dart b/Fly App/lib/main.dart index c2a273f..a427433 100644 --- a/Fly App/lib/main.dart +++ b/Fly App/lib/main.dart @@ -173,6 +173,9 @@ class _HomePageState extends State { case 'failed': _model.registration = RegistrationState.failed; _model.registrationError = event['error'] as String?; + // The error otherwise only shows in the settings sheet, which the + // home "Connect aircraft" flow never opens. + _snack('Registration failed: ${_model.registrationError ?? 'unknown error'}'); break; } _model.bump();