[apps/code] Check that app can be exited before switching to DFU

This fixes the following crash: create a script which contains only
"input()". Execute it, then while in the input, plug in the calculator.
When un-plugging it, the device crashes.
This commit is contained in:
Léa Saviot
2019-01-04 16:00:28 +01:00
committed by EmilieNumworks
parent 1782326ed8
commit 58f94f5e5f
4 changed files with 31 additions and 8 deletions

View File

@@ -152,14 +152,25 @@ bool AppsContainer::dispatchEvent(Ion::Events::Event event) {
if (event == Ion::Events::USBEnumeration) {
if (Ion::USB::isPlugged()) {
App::Snapshot * activeSnapshot = (activeApp() == nullptr ? appSnapshotAtIndex(0) : activeApp()->snapshot());
/* Just after a software update, the battery timer does not have time to
* fire before the calculator enters DFU mode. As the DFU mode blocks the
* event loop, we update the battery state "manually" here. */
updateBatteryState();
switchTo(usbConnectedAppSnapshot());
Ion::USB::DFU();
switchTo(activeSnapshot);
didProcessEvent = true;
if (activeApp() == nullptr || activeApp()->prepareForExit()) {
/* Just after a software update, the battery timer does not have time to
* fire before the calculator enters DFU mode. As the DFU mode blocks the
* event loop, we update the battery state "manually" here. */
updateBatteryState();
switchTo(usbConnectedAppSnapshot());
Ion::USB::DFU();
switchTo(activeSnapshot);
didProcessEvent = true;
} else {
/* activeApp()->prepareForExit() returned false, which means that the
* app needs another event loop to prepare for being switched off.
* Discard the current enumeration interruption.
* The USB host tries a few times in a row to enumerate the device, so
* hopefully the device will get another enumeration event soon and this
* time the device will be ready to go in DFU mode. Otherwise, the user
* needs to re-plug the device to go into DFU mode. */
Ion::USB::clearEnumerationInterrupt();
}
} else {
/* Sometimes, the device gets an ENUMDNE interrupts when being unplugged
* from a non-USB communicating host (e.g. a USB charger). The interrupt
@@ -212,6 +223,7 @@ bool AppsContainer::processEvent(Ion::Events::Event event) {
}
void AppsContainer::switchTo(App::Snapshot * snapshot) {
assert(activeApp() == nullptr || activeApp()->prepareForExit());
if (activeApp() && snapshot != activeApp()->snapshot()) {
resetShiftAlphaStatus();
}

View File

@@ -88,6 +88,7 @@ App::App(Container * container, Snapshot * snapshot) :
}
App::~App() {
assert(!m_consoleController.inputRunLoopActive());
deinitPython();
}

View File

@@ -37,6 +37,13 @@ public:
ScriptStore m_scriptStore;
};
~App();
bool prepareForExit() override {
if (m_consoleController.inputRunLoopActive()) {
m_consoleController.terminateInputLoop();
return false;
}
return true;
}
StackViewController * stackViewController() { return &m_codeStackViewController; }
ConsoleController * consoleController() { return &m_consoleController; }

View File

@@ -50,6 +50,9 @@ public:
void setFirstResponder(Responder * responder);
Responder * firstResponder();
virtual bool processEvent(Ion::Events::Event event);
/* prepareForExit returns true if the app can be switched off in the current
* runloop step, else it prepares for a switch off and returns false. */
virtual bool prepareForExit() { return true; }
void displayModalViewController(ViewController * vc, float verticalAlignment, float horizontalAlignment,
KDCoordinate topMargin = 0, KDCoordinate leftMargin = 0, KDCoordinate bottomMargin = 0, KDCoordinate rightMargin = 0);
void dismissModalViewController();