Skip to content

Commit 0bde66a

Browse files
author
Jeff Brown
committed
Throw TransactionTooLargeException when Binder transaction fails.
Bug: 5578022 Previously, Binder transactions failed silently, which caused problems because apps would carry on assuming that the operation had succeeded. Often, the apps would crash soon due to a violated invariant, but sometimes they managed to do some damage first... Change-Id: Ia9cc98b3b761a8160e7c4e87507860b5912c0451
1 parent 650a3e8 commit 0bde66a

File tree

4 files changed

+94
-8
lines changed

4 files changed

+94
-8
lines changed

api/current.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15131,6 +15131,7 @@ package android.os {
1513115131

1513215132
public class RemoteException extends android.util.AndroidException {
1513315133
ctor public RemoteException();
15134+
ctor public RemoteException(java.lang.String);
1513415135
}
1513515136

1513615137
public class ResultReceiver implements android.os.Parcelable {
@@ -15225,6 +15226,10 @@ package android.os {
1522515226
method public abstract void released();
1522615227
}
1522715228

15229+
public class TransactionTooLargeException extends android.os.RemoteException {
15230+
ctor public TransactionTooLargeException();
15231+
}
15232+
1522815233
public class Vibrator {
1522915234
method public void cancel();
1523015235
method public boolean hasVibrator();

core/java/android/os/RemoteException.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,8 @@ public class RemoteException extends AndroidException {
2424
public RemoteException() {
2525
super();
2626
}
27+
28+
public RemoteException(String message) {
29+
super(message);
30+
}
2731
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (C) 2011 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package android.os;
18+
import android.os.RemoteException;
19+
20+
/**
21+
* The Binder transaction failed because it was too large.
22+
* <p>
23+
* During a remote procedure call, the arguments and the return value of the call
24+
* are transferred as {@link Parcel} objects stored in the Binder transaction buffer.
25+
* If the arguments or the return value are too large to fit in the transaction buffer,
26+
* then the call will fail and {@link TransactionTooLargeException} will be thrown.
27+
* </p><p>
28+
* The Binder transaction buffer has a limited fixed size, currently 1Mb, which
29+
* is shared by all transactions in progress for the process. Consequently this
30+
* exception can be thrown when there are many transactions in progress even when
31+
* most of the individual transactions are of moderate size.
32+
* </p><p>
33+
* There are two possible outcomes when a remote procedure call throws
34+
* {@link TransactionTooLargeException}. Either the client was unable to send
35+
* its request to the service (most likely if the arguments were too large to fit in
36+
* the transaction buffer), or the service was unable to send its response back
37+
* to the client (most likely if the return value was too large to fit
38+
* in the transaction buffer). It is not possible to tell which of these outcomes
39+
* actually occurred. The client should assume that a partial failure occurred.
40+
* </p><p>
41+
* The key to avoiding {@link TransactionTooLargeException} is to keep all
42+
* transactions relatively small. Try to minimize the amount of memory needed to create
43+
* a {@link Parcel} for the arguments and the return value of the remote procedure call.
44+
* Avoid transferring huge arrays of strings or large bitmaps.
45+
* If possible, try to break up big requests into smaller pieces.
46+
* </p><p>
47+
* If you are implementing a service, it may help to impose size or complexity
48+
* contraints on the queries that clients can perform. For example, if the result set
49+
* could become large, then don't allow the client to request more than a few records
50+
* at a time. Alternately, instead of returning all of the available data all at once,
51+
* return the essential information first and make the client ask for additional information
52+
* later as needed.
53+
* </p>
54+
*/
55+
public class TransactionTooLargeException extends RemoteException {
56+
public TransactionTooLargeException() {
57+
super();
58+
}
59+
}

core/jni/android_util_Binder.cpp

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <binder/ProcessState.h>
3939
#include <binder/IServiceManager.h>
4040
#include <utils/threads.h>
41+
#include <utils/String8.h>
4142

4243
#include <ScopedUtfChars.h>
4344
#include <ScopedLocalRef.h>
@@ -651,7 +652,8 @@ jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc)
651652
gParcelFileDescriptorOffsets.mClass, gParcelFileDescriptorOffsets.mConstructor, fileDesc);
652653
}
653654

654-
void signalExceptionForError(JNIEnv* env, jobject obj, status_t err)
655+
static void signalExceptionForError(JNIEnv* env, jobject obj, status_t err,
656+
bool canThrowRemoteException = false)
655657
{
656658
switch (err) {
657659
case UNKNOWN_ERROR:
@@ -688,21 +690,38 @@ void signalExceptionForError(JNIEnv* env, jobject obj, status_t err)
688690
jniThrowException(env, "java/lang/RuntimeException", "Item already exists");
689691
break;
690692
case DEAD_OBJECT:
691-
jniThrowException(env, "android/os/DeadObjectException", NULL);
693+
// DeadObjectException is a checked exception, only throw from certain methods.
694+
jniThrowException(env, canThrowRemoteException
695+
? "android/os/DeadObjectException"
696+
: "java/lang/RuntimeException", NULL);
692697
break;
693698
case UNKNOWN_TRANSACTION:
694699
jniThrowException(env, "java/lang/RuntimeException", "Unknown transaction code");
695700
break;
696701
case FAILED_TRANSACTION:
697702
LOGE("!!! FAILED BINDER TRANSACTION !!!");
698-
//jniThrowException(env, "java/lang/OutOfMemoryError", "Binder transaction too large");
703+
// TransactionTooLargeException is a checked exception, only throw from certain methods.
704+
// FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
705+
// but it is not the only one. The Binder driver can return BR_FAILED_REPLY
706+
// for other reasons also, such as if the transaction is malformed or
707+
// refers to an FD that has been closed. We should change the driver
708+
// to enable us to distinguish these cases in the future.
709+
jniThrowException(env, canThrowRemoteException
710+
? "android/os/TransactionTooLargeException"
711+
: "java/lang/RuntimeException", NULL);
699712
break;
700713
case FDS_NOT_ALLOWED:
701714
jniThrowException(env, "java/lang/RuntimeException",
702715
"Not allowed to write file descriptors here");
703716
break;
704717
default:
705718
LOGE("Unknown binder error code. 0x%x", err);
719+
String8 msg;
720+
msg.appendFormat("Unknown binder error code. 0x%x", err);
721+
// RemoteException is a checked exception, only throw from certain methods.
722+
jniThrowException(env, canThrowRemoteException
723+
? "android/os/RemoteException" : "java/lang/RuntimeException", msg.string());
724+
break;
706725
}
707726
}
708727

@@ -1036,8 +1055,7 @@ static bool should_time_binder_calls() {
10361055
}
10371056

10381057
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
1039-
jint code, jobject dataObj,
1040-
jobject replyObj, jint flags)
1058+
jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
10411059
{
10421060
if (dataObj == NULL) {
10431061
jniThrowNullPointerException(env, NULL);
@@ -1084,12 +1102,12 @@ static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
10841102
return JNI_FALSE;
10851103
}
10861104

1087-
signalExceptionForError(env, obj, err);
1105+
signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
10881106
return JNI_FALSE;
10891107
}
10901108

10911109
static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
1092-
jobject recipient, jint flags)
1110+
jobject recipient, jint flags) // throws RemoteException
10931111
{
10941112
if (recipient == NULL) {
10951113
jniThrowNullPointerException(env, NULL);
@@ -1114,7 +1132,7 @@ static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
11141132
// Failure adding the death recipient, so clear its reference
11151133
// now.
11161134
jdr->clearReference();
1117-
signalExceptionForError(env, obj, err);
1135+
signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
11181136
}
11191137
}
11201138
}

0 commit comments

Comments
 (0)