AmazeFileManager
AmazeFileManager copied to clipboard
TransactionTooLarge when pulling from SD Card
Issue explanation (write below this line)
I'm getting a considerable amount of app breaking errors stemming from SD card usage.
Exception
- App Name: Amaze File Manager
- Package: com.amaze.filemanager
- Version: 3.6.7
- User Action: UI Error
- Request: Application crash
- OS: Linux Android 11 - 30
- Device: kiev
- Model: motorola one 5G ace
- Product: kiev_retail
Crash log
java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 1318820 bytes
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1720)
at android.app.ContextImpl.startService(ContextImpl.java:1670)
at android.content.ContextWrapper.startService(ContextWrapper.java:720)
at android.content.ContextWrapper.startService(ContextWrapper.java:720)
at com.amaze.filemanager.asynchronous.management.ServiceWatcherUtil.runService(ServiceWatcherUtil.java:195)
at com.amaze.filemanager.asynchronous.asynctasks.PrepareCopyTask.startService(PrepareCopyTask.java:206)
at com.amaze.filemanager.asynchronous.asynctasks.PrepareCopyTask.finishCopying(PrepareCopyTask.java:345)
at com.amaze.filemanager.asynchronous.asynctasks.PrepareCopyTask.onEndDialog(PrepareCopyTask.java:283)
at com.amaze.filemanager.asynchronous.asynctasks.PrepareCopyTask.replaceFiles(PrepareCopyTask.java:319)
at com.amaze.filemanager.asynchronous.asynctasks.PrepareCopyTask.lambda$showDialog$2(PrepareCopyTask.java:241)
at com.amaze.filemanager.asynchronous.asynctasks.PrepareCopyTask.lambda$showDialog$2$PrepareCopyTask(Unknown Source:0)
at com.amaze.filemanager.asynchronous.asynctasks.-$$Lambda$PrepareCopyTask$75dSuI7LFbSJVQGrHk_P9Ju86wU.onClick(Unknown Source:12)
at com.afollestad.materialdialogs.MaterialDialog.onClick(MaterialDialog.java:433)
at android.view.View.performClick(View.java:7455)
at android.view.View.performClickInternal(View.java:7432)
at android.view.View.access$3600(View.java:810)
at android.view.View$PerformClick.run(View.java:28312)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: android.os.TransactionTooLargeException: data parcel size 1318820 bytes
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(BinderProxy.java:550)
at android.app.IActivityManager$Stub$Proxy.startService(IActivityManager.java:5877)
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1700)
... 23 more
I first got an exception like this one by trying to move (cut & paste) 31944 files from internal storage to the SD card.
Then I was able to move 31944 files from one directory in internal storage to another, empty one, also in the internal storage. So Amaze can move 31944 files in one shot.
After that I got an exception like this one trying to move those same 31944 files from the new internal storage directory to an empty directory in the SD card. I tried with two different target directories to see if there was something about file paths' length, but the data parcel
size in the exception was the same for both cases - so it doesn't seem to have to do with file names.
I can test any other scenarios you may come up with, if that's useful.
I've spent a while taking a look at this issue.
Problem
Android's Binder
defines a hard-coded limit of 1MB for IPC transaction buffers (or something like that). (source: https://developer.android.com/reference/android/os/TransactionTooLargeException)
We're trying to start an Intent
with the list of files to move/copy, and - with ~30k paths - it's easy to hit 1MB (1MB / 30k ~= 35 bytes per path object).
Alternatives
- Write the array of files to move/copy somewhere (are there tempfiles in Android? are they easy to use and share between processes?) and share in the
Intent
a pointer to that thing. This would be the preferred way, since we would completly avoid the issue with a not-that-big change. - Guesstimate the size of the
Parcelable
ArrayList
of paths (or the wholeIntent
) before starting the Intent and maybe abort the operation if it's not below 1MB.- Offer to remove files until the object is below 1MB?
- Paginate? (ie: create intent, guesstimate size, shrink until it fits 1MB, start intent, create new intent with previously discarded paths, repeat)
- Refactor intent so we only share source directory + selected files/dirs + target directory (we save string size in repeated base path - ie, instead of
[ "/path/to/file1.bin", "/path/to/file2.bin"]
, change it to/path/to
&["file1.bin", "file2.bin"]
). This would work on most cases, except onRecent
views and similar, in which user could select files on different directories at the same time.
Questions
- Any pointers on how to easily auto-create a bunch of files inside the Android emulator to be able to test this?
-
Why is this not an issue when moving in the same FS? I can't understand what's different then, since the
Intent
seems to be created anyways.
The byte size of an Intent
object can be obtained by this piece of code:
Intent intent = new Intent(context.get(), CopyService.class);
// ... set stuff on intent ...
Parcel parcel = Parcel.obtain();
parcel.writeParcelable(intent, 0);
Log.e("The intent lenght is " + parcel.dataSize() + " bytes");