Why DeadObjectException during AIDL call using byte[] but not Bitmap? - android

Let's say there are two processes. I am interested in making an AIDL call (e.g. byte[] getBytes()) from an Activity (Process A) to a Service (Process B) which returns a byte[] of data.
However when this byte[] of data exceeds 1MB, it triggers the following expected exception. This also happens if the byte[] is wrapped inside of a custom Parcelable class.
W Error = android.os.DeadObjectException: Transaction failed on small parcel; remote process probably died
W at android.os.BinderProxy.transactNative(Native Method)
W at android.os.BinderProxy.transact(BinderProxy.java:571)
This appears to be caused by the data limits documented here: https://developer.android.com/guide/components/activities/parcelables-and-bundles
However, if the AIDL API returns a Bitmap (e.g. Bitmap getBitmap() ) containing data for an image that exceeds 1MB, this exception does not get triggered. Why does it work? In general, if I'd like to send data over 1MB, what would be the preferred method? In this test case, it's using an image file for the Bitmap but what if it is an audio file, video file or something else?

It depends. An implementation of Bitmap may not contain all of the image data in an array- it may contain information needed to get that data. For example, it may contain the filename of an image, and accessing the data would try to reopen the file. (whether that would work would depend on access rights of both apps). Or provide the URI of a content provider it can query for the data. Could that technique work with other data? Yes, if there's a reasonable way to do that. But there needs to be a way for the receiving app to reacquire the full data set.
However the 1MB limit to the size of a Bundle (the underlying class used to send data via AIDL) is hardcoded at the OS level. You can't get around it and put more in the bundle. You can only do a misdirection like this.

Related

Android - pass large amount of data to service running on a different process

is there any possible way to pass a large amount of data into an Android Service that runs on a different process from the main activity?
The data to pass is a byte array of around 5Mb.
As notes, I cannot save the data to file to do the transfer, needs to be transferred purely via memory or any other way which isn't to persist to file, databases, etc.
I've tried via AIDL, but seems in Android under AIDL the parcelable data is limited 1Mb. Also slicing the data and transferring it via chunks is not an option. Should be 1 single transaction.
I've also tried Intents, but again the same limitations of 500Kb to 1Mb.
So I am running out of ideas.
What about shared memory ashmem
http://www.androidenea.com/2010/03/share-memory-using-ashmem-and-binder-in.html
Memory file is an ashmem wrapper
http://developer.android.com/reference/android/os/MemoryFile.html
Or you could try opening a socket or ServerSocket in your service and connecting to it from your activity
http://developer.android.com/reference/java/net/ServerSocket.html

android Intents inline-data size limit

I have found that the android stock camera app, when passing an image back to a caller via a parcelable on an intent reduces the size to ~50k.
Search for the next text in the below source code link: Limit to 50k pixels so we can return it in the intent
Source Code Link: Android Stock Camera Source Code
My question is, why this limit, and what are the real size limits of data I can pass via an intent? I could find forums talking about this, but no real documentation from Google on the limits.
This is related to the binder transaction buffer:
During a remote procedure call, the arguments and the return value of the call are transferred as Parcel objects stored in the Binder transaction buffer. If the arguments or the return value are too large to fit in the transaction buffer, then the call will fail and TransactionTooLargeException will be thrown.
The Binder transaction buffer has a limited fixed size, currently 1Mb, which is shared by all transactions in progress for the process. Consequently this exception can be thrown when there are many transactions in progress even when most of the individual transactions are of moderate size. 1
If you exceed the Binder transaction buffer limit, you'll get a TransactionTooLargeException.
The limit is supposed to be 1MB but it varies by device from little less than 512KB up to almost a full 1MB.
Android Documentation

Find binder object in parcel

How can i find binder object in a android parcel? i want to marshall a parcel which has got a bitmap but i got Runtime exception says that the parcel has got binder object.
Sadly, you can't. A Parcel is unfortunately sometimes more than just a stream of bytes. Occasionally it contains an intelligent object, called a Binder. These objects can be passed around using IPC and have methods called by different processes in different parts of the Android system.
That's what's happening in this case. When you called Bitmap.writeToParcel() it's putting some intelligent object in there, which needs to be queried by other parts of the OS. That means that this Parcel simply can't be reduced to a stream of bytes.
(Specifically I think what's happening is this - but I could be wrong. I believe that this code:
http://androidxref.com/4.1.1/xref/frameworks/native/libs/binder/Parcel.cpp#736
is writing the bitmap data to an area of shared memory, and putting a reference to that shared memory area into the parcel. This means that the data doesn't need to be copied so often, which is great when you're passing the Parcel to another process using IPC, but not so good if you're just using it to serialize data.)
Using Parcel.marshall sometimes suggests bad design... as the comment says,
The data you retrieve here must not be placed in any kind of persistent storage (on local disk, across a network, etc). For that, you should use standard serialization or another kind of general serialization mechanism. The Parcel marshalled representation is highly optimized for local IPC, and as such does not attempt to maintain compatibility with data created in different versions of the platform.
If you are using it for local IPC, then you shouldn't need to call Parcel.marshall - because normally it would be part of an AIDL interface where these things are handled automatically.
Sorry there's no immediate solution! If you're using it for IPC, then use AIDL. If you're using it for something else, then don't use Parcel.marshall - instead you'll have to go to more effort to write the bitmap bytes to your own data format.

Is there any limit of bundle in Android?

I want to know whether android bundle's data size has upper limit. I try to post data by bundle which size >80k,and throw android fatal exception.The data is Serializable.
It depends on the purpose of the bundle. The bundle itself is only limited by the amount of memory.
The two main uses for bundles are to pass information between components using intents and to save the state of activities.
1. Intents / Binders
When used to pass information between Android components the bundle is serialized into a binder transaction. The total size for all binder transactions in a process is 1MB. If you exceed this limit you will receive this fatal error "!!! FAILED BINDER TRANSACTION !!!"
It's recommend that you keep the data in these bundles as small as possible because it's a shared buffer, anything more than a few kilobytes should be written to disk.
Reference: https://android.googlesource.com/platform/frameworks/base/+/jb-release/core/jni/android_util_Binder.cpp
ALOGE("!!! FAILED BINDER TRANSACTION !!!");
// TransactionTooLargeException is a checked exception, only throw from certain methods.
// FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
// but it is not the only one. The Binder driver can return BR_FAILED_REPLY
// for other reasons also, such as if the transaction is malformed or
// refers to an FD that has been closed. We should change the driver
// to enable us to distinguish these cases in the future.
Reference: http://developer.android.com/reference/android/os/TransactionTooLargeException.html
The Binder transaction buffer has a limited fixed size, currently 1Mb, which is shared by all transactions in progress for the process. Consequently this exception can be thrown when there are many transactions in progress even when most of the individual transactions are of moderate size.
2. Saved Instance State ( Activity onSaveInstanceState, onPause etc. )
I found no limit in the size I could store in the bundle used to preserve Activity state. I did some tests and could successfully store about 175mb before I received an out of memory exception trying to allocate the data I was attempting to save.
Update: This research was performed in 2014, newer versions of Android may crash with bundles over 500kb
I think the limit is 500kb.
You can save the passed object in a file and send the path of the file in the bundle instead.
You can check similar question asked by me at SO
The Binder transaction buffer has a limited fixed size, currently 1MB, which is shared by all transactions in progress for the process. Since this limit is at the process level rather than at the per activity level, these transactions include all binder transactions in the app such as onSaveInstanceState, startActivity and any interaction with the system. When the size limit is exceeded, a TransactionTooLargeException is thrown.
For the specific case of savedInstanceState, the amount of data should be kept small because the system process needs to hold on to the provided data for as long as the user can ever navigate back to that activity (even if the activity's process is killed). We recommend that you keep saved state to less than 50k of data.
Parcelable and Bundles
Yes it has, and now in android Nougat it will crash if you exceeded the limit roughly(500Kb).
android nougat issue
I think that the maximum bundle size is 1024 KiloBytes. In order to transfer large objects among activities, you should try other ways (memory cache, local storage, etc).
According to the Google Android API, the date should be less than 50K.
Yes it has 1MB limit.
You can use Singleton class to pass data.

Android remote method data limit

for my application I need to pass data between my activity and the the service both of which are in different processes. I know that Google recommends to keep the data passed while sending intent to a minimum (not full size bitmaps). Does a similar policy apply when you are communicating with the service over AIDL and want to pass the data via remote method calls?
http://developer.android.com/reference/android/os/TransactionTooLargeException.html
" During a remote procedure call, the arguments and the return value of the call are transferred as Parcel objects stored in the Binder transaction buffer. If the arguments or the return value are too large to fit in the transaction buffer, then the call will fail and TransactionTooLargeException will be thrown.
The Binder transaction buffer has a limited fixed size, currently 1Mb, which is shared by all transactions in progress for the process. "
So it seems, you should never send any arguments which are more than 1MB in size. Of course you could fail with lesser sized arguments too as explained on android site above.
I'm not sure about AIDL, but typically you DO want to keep Intent extras to a minimum. A better solution may be to implement your own ContentProvider and use that to provide data to your other process. This will allow for managed data transfer, and gives you all the extra protections that the ContentProvider API provides.

Categories

Resources