Skip to content

fix: make cash drawer failures visible and service binding recoverable - #34

Merged
FrenkyDema merged 1 commit into
mainfrom
fix/cash-drawer-and-service-binding
Sep 9, 2026
Merged

fix: make cash drawer failures visible and service binding recoverable#34
FrenkyDema merged 1 commit into
mainfrom
fix/cash-drawer-and-service-binding

Conversation

@FrenkyDema

@FrenkyDema FrenkyDema commented Sep 9, 2026

Copy link
Copy Markdown
Owner

Il problema

L'app di esempio non si avviava sui dispositivi Android 11+: BIND_SERVICE non si risolveva mai, quindi await bindingService() prima di runApp() restava appeso all'infinito e la UI non compariva.

Diagnosticato su un Sunmi D3 (Android 14, targetSdk 36). Due cause in cascata:

1. Package visibility. Senza una dichiarazione <queries> il servizio Sunmi è invisibile all'app, quindi bindService() ritorna false:

AppsFilter: ...sunmi_task_printer_example -> woyou.aidlservice.jiuiv5 BLOCKED
ActivityManager: Unable to start service Intent { act=woyou.aidlservice.jiuiv5.IWoyouService
                 pkg=woyou.aidlservice.jiuiv5 } U=0: not found

Il servizio è installato e funzionante sul device — semplicemente l'app non poteva vederlo. La dichiarazione ora sta nel manifest del plugin, così le app host la ereditano dal manifest merger senza doversene ricordare.

2. Il Result pendente non veniva mai completato. Era risolto solo da onServiceConnected, che non viene mai invocato se il bind è rifiutato. Ora ogni via d'uscita lo completa: ritorno false di bindService(), SecurityException, onNullBinding, onBindingDied.

Perché tocca il cassetto

Lo stesso stato di binding morto è esattamente ciò che si osserva quando il cassetto non si apre, e il plugin lo mascherava:

  • openDrawer() risolveva success(true) appena la chiamata AIDL era stata dispatchata, passando un callback dummy che scartava onRunResult e onRaiseException. Un kick rifiutato dal firmware veniva riportato come riuscito.
  • drawerStatus() e drawerTimesOpen() ritornavano false / 0 quando il servizio era irraggiungibile — indistinguibile da un cassetto semplicemente non collegato, il che sposta la diagnosi sull'hardware invece che sul binding.

Modifiche

  • Breaking: openDrawer() si completa solo quando il firmware conferma il kick; in caso contrario lancia PlatformException con codice OPERATION_FAILED, PRINTER_EXCEPTION, UNAVAILABLE o TIMEOUT.
  • Breaking: drawerStatus() e drawerTimesOpen() lanciano PlatformException con codice UNAVAILABLE invece di riportare hardware assente.
  • Aggiunte le <queries> per woyou.aidlservice.jiuiv5 al manifest del plugin.
  • BIND_SERVICE si risolve sempre, su ogni percorso.
  • Riconnessione dopo onBindingDied, e i comandi che trovano il servizio mancante schedulano un reconnect. Prima un riavvio del servizio Sunmi lasciava il plugin rotto in modo permanente fino al riavvio dell'app host.
  • unbindService() rilascia anche un bind richiesto ma mai completato (era un leak), e il plugin fa cleanup sul detach dell'engine.
  • I callback AIDL in volo sono tenuti con una reference forte, così non vengono garbage collected prima della risposta della stampante, con timeout di 5s.
  • L'esempio non blocca più runApp() sul binding.

Verifica

Sul D3 (D402P5CCJ1651), dopo il fix:

[SunmiDebug] Service binding returned: true
[SunmiDebug] Metadata fetched successfully. Size: 80, Version: 1.04, Serial: B97E3BCA...
  • Apertura fisica del cassetto verificata sull'hardware.
  • flutter test 11/11 verdi, inclusi due nuovi test sui percorsi di errore del cassetto.
  • flutter analyze pulito a parte un unnecessary_library_name preesistente in lib/enums.dart.

Note

  • Versione portata a 0.2.0 per il breaking change.
  • Resta un percorso silenzioso non toccato in questa PR: getPrinterStatus() ritorna PrinterStatus.ERROR quando il servizio è irraggiungibile, senza distinguerlo da un errore reale della stampante.

@github-actions github-actions Bot added documentation Improvements or additions to documentation lib test labels Sep 9, 2026
@FrenkyDema FrenkyDema self-assigned this Sep 9, 2026
@FrenkyDema FrenkyDema added the release Update release version label Sep 9, 2026
@FrenkyDema FrenkyDema added this to the v0.2 milestone Sep 9, 2026
@FrenkyDema
FrenkyDema force-pushed the fix/cash-drawer-and-service-binding branch from 524baf9 to 0dd4e06 Compare September 9, 2026 15:54
The example app could not start on Android 11+ devices: BIND_SERVICE never
resolved, so `await bindingService()` before `runApp()` hung forever.

Two causes, verified on a Sunmi D3 (Android 14, targetSdk 36):

1. Package visibility. Without a <queries> entry the Sunmi service is
   invisible to the app, so bindService() returns false:
     AppsFilter: ...example -> woyou.aidlservice.jiuiv5 BLOCKED
     Unable to start service Intent { act=...IWoyouService } U=0: not found
   The declaration now lives in the plugin manifest, so host apps inherit it.

2. The pending Result was only completed from onServiceConnected, which
   never fires when the bind is refused. Every exit path now settles it.

The same dead-binding state is what a stuck cash drawer looks like, and the
plugin hid it: openDrawer() reported success as soon as the AIDL call was
dispatched while discarding the firmware's callback, and drawerStatus() /
drawerTimesOpen() returned false / 0 when the service was unreachable,
indistinguishable from a drawer that is simply not attached.

BREAKING CHANGE: openDrawer(), drawerStatus() and drawerTimesOpen() now throw
a PlatformException instead of silently reporting success or absent hardware.

Also reconnects after onBindingDied, releases a bind that never completed,
cleans up on engine detach, and holds in-flight AIDL callbacks with a strong
reference so they are not collected before the printer replies.
@FrenkyDema
FrenkyDema force-pushed the fix/cash-drawer-and-service-binding branch from 0dd4e06 to e01d454 Compare September 9, 2026 15:56
@FrenkyDema
FrenkyDema merged commit 712ba75 into main Sep 9, 2026
2 checks passed
@FrenkyDema
FrenkyDema deleted the fix/cash-drawer-and-service-binding branch September 9, 2026 15:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation lib release Update release version test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant