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*/);
         }
     }
 }