AmazeFileManager icon indicating copy to clipboard operation
AmazeFileManager copied to clipboard

TransactionTooLarge when pulling from SD Card

Open jus4ru opened this issue 3 years ago • 2 comments

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


jus4ru avatar Dec 23 '21 03:12 jus4ru

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.

mgarciaisaia avatar Feb 22 '22 17:02 mgarciaisaia

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 whole Intent) 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 on Recent 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");

mgarciaisaia avatar Feb 24 '22 04:02 mgarciaisaia