Merge "Throw TransactionTooLargeException when Binder transaction fails. Bug: 5578022" into ics-mr1
diff --git a/api/current.txt b/api/current.txt
index d83659f..daf1036 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15132,6 +15132,7 @@
public class RemoteException extends android.util.AndroidException {
ctor public RemoteException();
+ ctor public RemoteException(java.lang.String);
}
public class ResultReceiver implements android.os.Parcelable {
@@ -15226,6 +15227,10 @@
method public abstract void released();
}
+ public class TransactionTooLargeException extends android.os.RemoteException {
+ ctor public TransactionTooLargeException();
+ }
+
public class Vibrator {
method public void cancel();
method public boolean hasVibrator();
diff --git a/core/java/android/os/RemoteException.java b/core/java/android/os/RemoteException.java
index 9d76156..e30d24f 100644
--- a/core/java/android/os/RemoteException.java
+++ b/core/java/android/os/RemoteException.java
@@ -24,4 +24,8 @@
public RemoteException() {
super();
}
+
+ public RemoteException(String message) {
+ super(message);
+ }
}
diff --git a/core/java/android/os/TransactionTooLargeException.java b/core/java/android/os/TransactionTooLargeException.java
new file mode 100644
index 0000000..25f09e8
--- /dev/null
+++ b/core/java/android/os/TransactionTooLargeException.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+import android.os.RemoteException;
+
+/**
+ * The Binder transaction failed because it was too large.
+ * <p>
+ * During a remote procedure call, the arguments and the return value of the call
+ * are transferred as {@link 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 {@link TransactionTooLargeException} will be thrown.
+ * </p><p>
+ * 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.
+ * </p><p>
+ * There are two possible outcomes when a remote procedure call throws
+ * {@link TransactionTooLargeException}. Either the client was unable to send
+ * its request to the service (most likely if the arguments were too large to fit in
+ * the transaction buffer), or the service was unable to send its response back
+ * to the client (most likely if the return value was too large to fit
+ * in the transaction buffer). It is not possible to tell which of these outcomes
+ * actually occurred. The client should assume that a partial failure occurred.
+ * </p><p>
+ * The key to avoiding {@link TransactionTooLargeException} is to keep all
+ * transactions relatively small. Try to minimize the amount of memory needed to create
+ * a {@link Parcel} for the arguments and the return value of the remote procedure call.
+ * Avoid transferring huge arrays of strings or large bitmaps.
+ * If possible, try to break up big requests into smaller pieces.
+ * </p><p>
+ * If you are implementing a service, it may help to impose size or complexity
+ * contraints on the queries that clients can perform. For example, if the result set
+ * could become large, then don't allow the client to request more than a few records
+ * at a time. Alternately, instead of returning all of the available data all at once,
+ * return the essential information first and make the client ask for additional information
+ * later as needed.
+ * </p>
+ */
+public class TransactionTooLargeException extends RemoteException {
+ public TransactionTooLargeException() {
+ super();
+ }
+}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 1718e74..2bd4fa0 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -38,6 +38,7 @@
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <utils/threads.h>
+#include <utils/String8.h>
#include <ScopedUtfChars.h>
#include <ScopedLocalRef.h>
@@ -651,7 +652,8 @@
gParcelFileDescriptorOffsets.mClass, gParcelFileDescriptorOffsets.mConstructor, fileDesc);
}
-void signalExceptionForError(JNIEnv* env, jobject obj, status_t err)
+static void signalExceptionForError(JNIEnv* env, jobject obj, status_t err,
+ bool canThrowRemoteException = false)
{
switch (err) {
case UNKNOWN_ERROR:
@@ -688,14 +690,25 @@
jniThrowException(env, "java/lang/RuntimeException", "Item already exists");
break;
case DEAD_OBJECT:
- jniThrowException(env, "android/os/DeadObjectException", NULL);
+ // DeadObjectException is a checked exception, only throw from certain methods.
+ jniThrowException(env, canThrowRemoteException
+ ? "android/os/DeadObjectException"
+ : "java/lang/RuntimeException", NULL);
break;
case UNKNOWN_TRANSACTION:
jniThrowException(env, "java/lang/RuntimeException", "Unknown transaction code");
break;
case FAILED_TRANSACTION:
LOGE("!!! FAILED BINDER TRANSACTION !!!");
- //jniThrowException(env, "java/lang/OutOfMemoryError", "Binder transaction too large");
+ // 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.
+ jniThrowException(env, canThrowRemoteException
+ ? "android/os/TransactionTooLargeException"
+ : "java/lang/RuntimeException", NULL);
break;
case FDS_NOT_ALLOWED:
jniThrowException(env, "java/lang/RuntimeException",
@@ -703,6 +716,12 @@
break;
default:
LOGE("Unknown binder error code. 0x%x", err);
+ String8 msg;
+ msg.appendFormat("Unknown binder error code. 0x%x", err);
+ // RemoteException is a checked exception, only throw from certain methods.
+ jniThrowException(env, canThrowRemoteException
+ ? "android/os/RemoteException" : "java/lang/RuntimeException", msg.string());
+ break;
}
}
@@ -1036,8 +1055,7 @@
}
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
- jint code, jobject dataObj,
- jobject replyObj, jint flags)
+ jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
if (dataObj == NULL) {
jniThrowNullPointerException(env, NULL);
@@ -1084,12 +1102,12 @@
return JNI_FALSE;
}
- signalExceptionForError(env, obj, err);
+ signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
return JNI_FALSE;
}
static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
- jobject recipient, jint flags)
+ jobject recipient, jint flags) // throws RemoteException
{
if (recipient == NULL) {
jniThrowNullPointerException(env, NULL);
@@ -1114,7 +1132,7 @@
// Failure adding the death recipient, so clear its reference
// now.
jdr->clearReference();
- signalExceptionForError(env, obj, err);
+ signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
}
}
}