| Summary: | Krita crashes and file corrupts after closing the application in a span of a few minutes | ||
|---|---|---|---|
| Product: | [Applications] krita | Reporter: | baylon09richard |
| Component: | General | Assignee: | Krita Bugs <krita-bugs-null> |
| Status: | RESOLVED FIXED | ||
| Severity: | major | CC: | dimula73, dra.w.p.il.e+bugskde |
| Priority: | NOR | Keywords: | triaged |
| Version First Reported In: | 5.2.12 | ||
| Target Milestone: | --- | ||
| Platform: | Android | ||
| OS: | Android 13.x | ||
| Latest Commit: | https://invent.kde.org/graphics/krita/-/commit/49af64b324526aa94b1a9d4b604dac9f79671b41 | Version Fixed/Implemented In: | |
| Sentry Crash Report: | |||
|
Description
baylon09richard
2025-01-04 19:00:01 UTC
This crash is one of the two backtraces: They are the two most frequent crashes on Android for Krita (28% of all the crashes in total). # Backtrace 1 Exception java.lang.RuntimeException: at android.app.ActivityThread.performPauseActivityIfNeeded (ActivityThread.java:5111) at android.app.ActivityThread.performPauseActivity (ActivityThread.java:5060) at android.app.ActivityThread.handlePauseActivity (ActivityThread.java:5012) at android.app.servertransaction.PauseActivityItem.execute (PauseActivityItem.java:47) at android.app.servertransaction.ActivityTransactionItem.execute (ActivityTransactionItem.java:45) at android.app.servertransaction.TransactionExecutor.executeLifecycleState (TransactionExecutor.java:177) at android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:98) at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2252) at android.os.Handler.dispatchMessage (Handler.java:106) at android.os.Looper.loopOnce (Looper.java:201) at android.os.Looper.loop (Looper.java:288) at android.app.ActivityThread.main (ActivityThread.java:7941) at java.lang.reflect.Method.invoke at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:553) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1003) Caused by android.app.BackgroundServiceStartNotAllowedException: at android.app.ContextImpl.startServiceCommon (ContextImpl.java:1862) at android.app.ContextImpl.startService (ContextImpl.java:1818) at android.content.ContextWrapper.startService (ContextWrapper.java:776) at org.krita.android.MainActivity.startForegroundServiceS (MainActivity.java:128) at org.krita.android.MainActivity.startServiceGeneric (MainActivity.java:111) at org.krita.android.MainActivity.onPause (MainActivity.java:102) at android.app.Activity.performPause (Activity.java:8244) at android.app.Instrumentation.callActivityOnPause (Instrumentation.java:1530) at android.app.ActivityThread.performPauseActivityIfNeeded (ActivityThread.java:5099) # Backtrace 2 Exception java.lang.UnsatisfiedLinkError: No implementation found for void org.krita.android.JNIWrappers.saveState() (tried Java_org_krita_android_JNIWrappers_saveState and Java_org_krita_android_JNIWrappers_saveState__) - is the library loaded, e.g. System.loadLibrary? at org.krita.android.JNIWrappers.saveState (JNIWrappers.java) at org.krita.android.DocumentSaverService$1.run (DocumentSaverService.java:120) at java.lang.Thread.run (Thread.java:1012) The errors Dmitry mentioned are fixed, but you can still pretty easily corrupt a file with the following steps to reproduce: 1. Save a file that takes a while to save. 2. Go into the application overview and swipe Krita out of the way so that it terminates. 3. Try to reopen the file, see that it won't work. Of course #2 is likely to happen in practice in the form of Android deciding that it would really like to reclaim memory while Krita is saving and the out-of-memory killer kills it dead. I assume the reason this happens is because Krita writes directly to the output file, given that Android has no support for QSaveFile, so it just truncates the file first and starts writing it in-place. That means there's an immensely long span of time where the OS getting impatient will guarantee total data loss, both of the old file and any changes made meanwhile. That could be solved by writing the file to the internal application data first and then once that has succeeded copy it over the original file. This isn't perfect, because Android can't do an atomic move of a file like a real operating system could, but just shoveling data over is so fast that it's pretty unlikely for the application to be terminated in that exact moment. Alternatively, Krita could first back up the old file to the internal application data and then start saving, then detect whether that got interrupted on the next startup and offer to recover the file. This would never leave you in the situation where neither the old nor the new data are persisted anywhere, but downside is that it's a whole lot more work to implement, to the point where it's questionable whether it makes sense to bother with it at all over implementing the save to sandbox instead, cf. https://invent.kde.org/graphics/krita/-/issues/20 In both cases, you'd still lose your current changes of course, since Krita is terminated before it can save that, but it would at least retain the old state. (In reply to dra.w.p.il.e+bugskde from comment #2) > The errors Dmitry mentioned are fixed, but you can still pretty easily > corrupt a file with the following steps to reproduce: > > 1. Save a file that takes a while to save. > 2. Go into the application overview and swipe Krita out of the way so that > it terminates. > 3. Try to reopen the file, see that it won't work. > > Of course #2 is likely to happen in practice in the form of Android deciding > that it would really like to reclaim memory while Krita is saving and the > out-of-memory killer kills it dead. > > I assume the reason this happens is because Krita writes directly to the > output file, given that Android has no support for QSaveFile, so it just > truncates the file first and starts writing it in-place. That means there's > an immensely long span of time where the OS getting impatient will guarantee > total data loss, both of the old file and any changes made meanwhile. > > That could be solved by writing the file to the internal application data > first and then once that has succeeded copy it over the original file. This > isn't perfect, because Android can't do an atomic move of a file like a real > operating system could, but just shoveling data over is so fast that it's > pretty unlikely for the application to be terminated in that exact moment. > > Alternatively, Krita could first back up the old file to the internal > application data and then start saving, then detect whether that got > interrupted on the next startup and offer to recover the file. This would > never leave you in the situation where neither the old nor the new data are > persisted anywhere, but downside is that it's a whole lot more work to > implement, to the point where it's questionable whether it makes sense to > bother with it at all over implementing the save to sandbox instead, cf. > https://invent.kde.org/graphics/krita/-/issues/20 > > In both cases, you'd still lose your current changes of course, since Krita > is terminated before it can save that, but it would at least retain the old > state. Thank you!(In reply to dra.w.p.il.e+bugskde from comment #2) > The errors Dmitry mentioned are fixed, but you can still pretty easily > corrupt a file with the following steps to reproduce: > > 1. Save a file that takes a while to save. > 2. Go into the application overview and swipe Krita out of the way so that > it terminates. > 3. Try to reopen the file, see that it won't work. > > Of course #2 is likely to happen in practice in the form of Android deciding > that it would really like to reclaim memory while Krita is saving and the > out-of-memory killer kills it dead. > > I assume the reason this happens is because Krita writes directly to the > output file, given that Android has no support for QSaveFile, so it just > truncates the file first and starts writing it in-place. That means there's > an immensely long span of time where the OS getting impatient will guarantee > total data loss, both of the old file and any changes made meanwhile. > > That could be solved by writing the file to the internal application data > first and then once that has succeeded copy it over the original file. This > isn't perfect, because Android can't do an atomic move of a file like a real > operating system could, but just shoveling data over is so fast that it's > pretty unlikely for the application to be terminated in that exact moment. > > Alternatively, Krita could first back up the old file to the internal > application data and then start saving, then detect whether that got > interrupted on the next startup and offer to recover the file. This would > never leave you in the situation where neither the old nor the new data are > persisted anywhere, but downside is that it's a whole lot more work to > implement, to the point where it's questionable whether it makes sense to > bother with it at all over implementing the save to sandbox instead, cf. > https://invent.kde.org/graphics/krita/-/issues/20 > > In both cases, you'd still lose your current changes of course, since Krita > is terminated before it can save that, but it would at least retain the old > state. Thank you! I think I made the comment a little long sorry A possibly relevant merge request was started @ https://invent.kde.org/graphics/krita/-/merge_requests/2556 Git commit 02e9966601ca99f07b9db31cf3edabdd8d40ee70 by Dmitry Kazakov, on behalf of Carsten Hartenfels. Committed on 08/12/2025 at 10:36. Pushed by dkazakov into branch 'master'. [android] Write to temporary file when saving Instead of using QSaveFile, because that doesn't work on Android due to sandboxing. Nor does the usual method of QFile::copy work on Android, we have to do that manually by opening the target file and shoveling bytes. M +90 -12 libs/ui/KisImportExportManager.cpp https://invent.kde.org/graphics/krita/-/commit/02e9966601ca99f07b9db31cf3edabdd8d40ee70 Git commit 49af64b324526aa94b1a9d4b604dac9f79671b41 by Dmitry Kazakov. Committed on 08/12/2025 at 10:36. Pushed by dkazakov into branch 'krita/5.2'. [android] Write to temporary file when saving Instead of using QSaveFile, because that doesn't work on Android due to sandboxing. Nor does the usual method of QFile::copy work on Android, we have to do that manually by opening the target file and shoveling bytes. (cherry picked from commit 02e9966601ca99f07b9db31cf3edabdd8d40ee70) Co-authored-by: Carsten Hartenfels <carsten.hartenfels@pm.me> M +90 -12 libs/ui/KisImportExportManager.cpp https://invent.kde.org/graphics/krita/-/commit/49af64b324526aa94b1a9d4b604dac9f79671b41 |