DO NOT MERGE Re-implement reading/writing Throwables from/to Parcel, without
Parcel private APIs.

Bug:197228210
Test: atest CtsSecurityTestCases:android.security.cts.AndroidFutureTest
(cherry picked from I577da5a3bc4ed537123b7eceaa5addf8f7bb0d92 and
Icc5ce702f0cd84e9136dee3c65f63619df697358)

Change-Id: I1d488c475f2f7af835a67496535cecdd6987c0cf
(cherry picked from commit 562f1bd91f2845788ab907d687de6800ee49c6f8)
Merged-In:I1d488c475f2f7af835a67496535cecdd6987c0cf
diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java
index 9f15d89..84391c1 100644
--- a/core/java/com/android/internal/infra/AndroidFuture.java
+++ b/core/java/com/android/internal/infra/AndroidFuture.java
@@ -16,23 +16,21 @@
 
 package com.android.internal.infra;
 
-import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Handler;
-import android.os.Message;
+import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
-import android.util.ExceptionUtils;
+import android.util.EventLog;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.pooled.PooledLambda;
 
+import java.lang.reflect.Constructor;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
@@ -75,14 +73,16 @@
 
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = AndroidFuture.class.getSimpleName();
+    private static final Executor DIRECT_EXECUTOR = Runnable::run;
     private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
+    private static @Nullable Handler sMainHandler;
 
     private final @NonNull Object mLock = new Object();
     @GuardedBy("mLock")
     private @Nullable BiConsumer<? super T, ? super Throwable> mListener;
     @GuardedBy("mLock")
     private @Nullable Executor mListenerExecutor = DIRECT_EXECUTOR;
-    private @NonNull Handler mTimeoutHandler = Handler.getMain();
+    private @NonNull Handler mTimeoutHandler = getMainHandler();
     private final @Nullable IAndroidFuture mRemoteOrigin;
 
     public AndroidFuture() {
@@ -96,7 +96,7 @@
             // Done
             if (in.readBoolean()) {
                 // Failed
-                completeExceptionally(unparcelException(in));
+                completeExceptionally(readThrowable(in));
             } else {
                 // Success
                 complete((T) in.readValue(null));
@@ -108,6 +108,15 @@
         }
     }
 
+    @NonNull
+    private static Handler getMainHandler() {
+        // This isn't thread-safe but we are okay with it.
+        if (sMainHandler == null) {
+            sMainHandler = new Handler(Looper.getMainLooper());
+        }
+        return sMainHandler;
+    }
+
     /**
      * Create a completed future with the given value.
      *
@@ -236,9 +245,7 @@
         if (mListenerExecutor == DIRECT_EXECUTOR) {
             callListener(listener, res, err);
         } else {
-            mListenerExecutor.execute(PooledLambda
-                    .obtainRunnable(AndroidFuture::callListener, listener, res, err)
-                    .recycleOnUse());
+            mListenerExecutor.execute(() -> callListener(listener, res, err));
         }
     }
 
@@ -260,7 +267,8 @@
                 } else {
                     // listener exception-case threw
                     // give up on listener but preserve the original exception when throwing up
-                    throw ExceptionUtils.appendCause(t, err);
+                    t.addSuppressed(err);
+                    throw t;
                 }
             }
         } catch (Throwable t2) {
@@ -272,9 +280,7 @@
     /** @inheritDoc */
     //@Override //TODO uncomment once java 9 APIs are exposed to frameworks
     public AndroidFuture<T> orTimeout(long timeout, @NonNull TimeUnit unit) {
-        Message msg = PooledLambda.obtainMessage(AndroidFuture::triggerTimeout, this);
-        msg.obj = this;
-        mTimeoutHandler.sendMessageDelayed(msg, unit.toMillis(timeout));
+        mTimeoutHandler.postDelayed(this::triggerTimeout, this, unit.toMillis(timeout));
         return this;
     }
 
@@ -507,7 +513,7 @@
                 result = get();
             } catch (Throwable t) {
                 dest.writeBoolean(true);
-                parcelException(dest, unwrapExecutionException(t));
+                writeThrowable(dest, unwrapExecutionException(t));
                 return;
             }
             dest.writeBoolean(false);
@@ -545,45 +551,75 @@
      * Alternative to {@link Parcel#writeException} that stores the stack trace, in a
      * way consistent with the binder IPC exception propagation behavior.
      */
-    private static void parcelException(Parcel p, @Nullable Throwable t) {
-        p.writeBoolean(t == null);
-        if (t == null) {
+    private static void writeThrowable(@NonNull Parcel parcel, @Nullable Throwable throwable) {
+        boolean hasThrowable = throwable != null;
+        parcel.writeBoolean(hasThrowable);
+        if (!hasThrowable) {
             return;
         }
 
-        p.writeInt(Parcel.getExceptionCode(t));
-        p.writeString(t.getClass().getName());
-        p.writeString(t.getMessage());
-        p.writeStackTrace(t);
-        parcelException(p, t.getCause());
+        boolean isFrameworkParcelable = throwable instanceof Parcelable
+                && throwable.getClass().getClassLoader() == Parcelable.class.getClassLoader();
+        parcel.writeBoolean(isFrameworkParcelable);
+        if (isFrameworkParcelable) {
+            parcel.writeParcelable((Parcelable) throwable,
+                    Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+            return;
+        }
+
+        parcel.writeString(throwable.getClass().getName());
+        parcel.writeString(throwable.getMessage());
+        StackTraceElement[] stackTrace = throwable.getStackTrace();
+        StringBuilder stackTraceBuilder = new StringBuilder();
+        int truncatedStackTraceLength = Math.min(stackTrace != null ? stackTrace.length : 0, 5);
+        for (int i = 0; i < truncatedStackTraceLength; i++) {
+            if (i > 0) {
+                stackTraceBuilder.append('\n');
+            }
+            stackTraceBuilder.append("\tat ").append(stackTrace[i]);
+        }
+        parcel.writeString(stackTraceBuilder.toString());
+        writeThrowable(parcel, throwable.getCause());
     }
 
     /**
-     * @see #parcelException
+     * @see #writeThrowable
      */
-    private static @Nullable Throwable unparcelException(Parcel p) {
-        if (p.readBoolean()) {
+    private static @Nullable Throwable readThrowable(@NonNull Parcel parcel) {
+        final boolean hasThrowable = parcel.readBoolean();
+        if (!hasThrowable) {
             return null;
         }
 
-        int exCode = p.readInt();
-        String cls = p.readString();
-        String msg = p.readString();
-        String stackTrace = p.readInt() > 0 ? p.readString() : "\t<stack trace unavailable>";
-        msg += "\n" + stackTrace;
-
-        Exception ex = p.createExceptionOrNull(exCode, msg);
-        if (ex == null) {
-            ex = new RuntimeException(cls + ": " + msg);
+        boolean isFrameworkParcelable = parcel.readBoolean();
+        if (isFrameworkParcelable) {
+            return parcel.readParcelable(Parcelable.class.getClassLoader());
         }
-        ex.setStackTrace(EMPTY_STACK_TRACE);
 
-        Throwable cause = unparcelException(p);
+        String className = parcel.readString();
+        String message = parcel.readString();
+        String stackTrace = parcel.readString();
+        String messageWithStackTrace = message + '\n' + stackTrace;
+        Throwable throwable;
+        try {
+            Class<?> clazz = Class.forName(className, true, Parcelable.class.getClassLoader());
+            if (Throwable.class.isAssignableFrom(clazz)) {
+                Constructor<?> constructor = clazz.getConstructor(String.class);
+                throwable = (Throwable) constructor.newInstance(messageWithStackTrace);
+            } else {
+                android.util.EventLog.writeEvent(0x534e4554, "186530450", -1, "");
+                throwable = new RuntimeException(className + ": " + messageWithStackTrace);
+            }
+        } catch (Throwable t) {
+            throwable = new RuntimeException(className + ": " + messageWithStackTrace);
+            throwable.addSuppressed(t);
+        }
+        throwable.setStackTrace(EMPTY_STACK_TRACE);
+        Throwable cause = readThrowable(parcel);
         if (cause != null) {
-            ex.initCause(ex);
+            throwable.initCause(cause);
         }
-
-        return ex;
+        return throwable;
     }
 
     @Override
diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java
index 167d128..8986938 100644
--- a/core/java/com/android/internal/infra/ServiceConnector.java
+++ b/core/java/com/android/internal/infra/ServiceConnector.java
@@ -26,14 +26,11 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IInterface;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.DebugUtils;
 import android.util.Log;
 
-import com.android.internal.util.function.pooled.PooledLambda;
-
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -47,7 +44,6 @@
 import java.util.function.BiConsumer;
 import java.util.function.Function;
 
-
 /**
  * Takes care of managing a {@link ServiceConnection} and auto-disconnecting from the service upon
  * a certain timeout.
@@ -220,6 +216,7 @@
         private final @NonNull Queue<Job<I, ?>> mQueue = this;
         private final @NonNull List<CompletionAwareJob<I, ?>> mUnfinishedJobs = new ArrayList<>();
 
+        private final @NonNull Handler mMainHandler = new Handler(Looper.getMainLooper());
         private final @NonNull ServiceConnection mServiceConnection = this;
         private final @NonNull Runnable mTimeoutDisconnect = this;
 
@@ -250,9 +247,8 @@
          *                          {@link IInterface}.
          *                          Typically this is {@code IMyInterface.Stub::asInterface}
          */
-        public Impl(@NonNull Context context, @NonNull Intent intent,
-                @Context.BindServiceFlags int bindingFlags, @UserIdInt int userId,
-                @Nullable Function<IBinder, I> binderAsInterface) {
+        public Impl(@NonNull Context context, @NonNull Intent intent, int bindingFlags,
+                @UserIdInt int userId, @Nullable Function<IBinder, I> binderAsInterface) {
             mContext = context;
             mIntent = intent;
             mBindingFlags = bindingFlags;
@@ -264,7 +260,7 @@
          * {@link Handler} on which {@link Job}s will be called
          */
         protected Handler getJobHandler() {
-            return Handler.getMain();
+            return mMainHandler;
         }
 
         /**
@@ -391,8 +387,7 @@
 
         private boolean enqueue(@NonNull Job<I, ?> job) {
             cancelTimeout();
-            return getJobHandler().sendMessage(PooledLambda.obtainMessage(
-                    ServiceConnector.Impl::enqueueJobThread, this, job));
+            return getJobHandler().post(() -> enqueueJobThread(job));
         }
 
         void enqueueJobThread(@NonNull Job<I, ?> job) {
@@ -422,7 +417,7 @@
             if (DEBUG) {
                 logTrace();
             }
-            Handler.getMain().removeCallbacks(mTimeoutDisconnect);
+            mMainHandler.removeCallbacks(mTimeoutDisconnect);
         }
 
         void completeExceptionally(@NonNull Job<?, ?> job, @NonNull Throwable ex) {
@@ -486,7 +481,7 @@
             }
             long timeout = getAutoDisconnectTimeoutMs();
             if (timeout > 0) {
-                Handler.getMain().postDelayed(mTimeoutDisconnect, timeout);
+                mMainHandler.postDelayed(mTimeoutDisconnect, timeout);
             } else if (DEBUG) {
                 Log.i(LOG_TAG, "Not scheduling unbind for permanently bound " + this);
             }
@@ -502,7 +497,7 @@
                 logTrace();
             }
             mUnbinding = true;
-            getJobHandler().sendMessage(PooledLambda.obtainMessage(Impl::unbindJobThread, this));
+            getJobHandler().post(this::unbindJobThread);
         }
 
         void unbindJobThread() {
@@ -659,10 +654,7 @@
         }
 
         private void logTrace() {
-            Log.i(LOG_TAG,
-                    TextUtils.join(" -> ",
-                            DebugUtils.callersWithin(ServiceConnector.class, /* offset= */ 1))
-                    + "(" + this + ")");
+            Log.i(LOG_TAG, "See stacktrace", new Throwable());
         }
 
         /**