Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c761c3a5 authored by Hai Zhang's avatar Hai Zhang Committed by Automerger Merge Worker
Browse files

DO NOT MERGE Re-implement reading/writing Throwables from/to Parcel, without am: 562f1bd9

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16406531

Change-Id: I8d0d29e2e6fd16a01a33022532ff59e2c4a0a584
parents 920d225e 562f1bd9
Loading
Loading
Loading
Loading
+76 −40
Original line number Diff line number Diff line
@@ -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 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable

    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 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable
            // Done
            if (in.readBoolean()) {
                // Failed
                completeExceptionally(unparcelException(in));
                completeExceptionally(readThrowable(in));
            } else {
                // Success
                complete((T) in.readValue(null));
@@ -108,6 +108,15 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable
        }
    }

    @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 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable
        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 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable
                } 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 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable
    /** @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 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable
                result = get();
            } catch (Throwable t) {
                dest.writeBoolean(true);
                parcelException(dest, unwrapExecutionException(t));
                writeThrowable(dest, unwrapExecutionException(t));
                return;
            }
            dest.writeBoolean(false);
@@ -545,45 +551,75 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable
     * 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
+10 −18
Original line number Diff line number Diff line
@@ -26,14 +26,11 @@ import android.content.ServiceConnection;
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.concurrent.TimeUnit;
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 @@ public interface ServiceConnector<I extends IInterface> {
        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 @@ public interface ServiceConnector<I extends IInterface> {
         *                          {@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 @@ public interface ServiceConnector<I extends IInterface> {
         * {@link Handler} on which {@link Job}s will be called
         */
        protected Handler getJobHandler() {
            return Handler.getMain();
            return mMainHandler;
        }

        /**
@@ -391,8 +387,7 @@ public interface ServiceConnector<I extends IInterface> {

        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 @@ public interface ServiceConnector<I extends IInterface> {
            if (DEBUG) {
                logTrace();
            }
            Handler.getMain().removeCallbacks(mTimeoutDisconnect);
            mMainHandler.removeCallbacks(mTimeoutDisconnect);
        }

        void completeExceptionally(@NonNull Job<?, ?> job, @NonNull Throwable ex) {
@@ -486,7 +481,7 @@ public interface ServiceConnector<I extends IInterface> {
            }
            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 @@ public interface ServiceConnector<I extends IInterface> {
                logTrace();
            }
            mUnbinding = true;
            getJobHandler().sendMessage(PooledLambda.obtainMessage(Impl::unbindJobThread, this));
            getJobHandler().post(this::unbindJobThread);
        }

        void unbindJobThread() {
@@ -659,10 +654,7 @@ public interface ServiceConnector<I extends IInterface> {
        }

        private void logTrace() {
            Log.i(LOG_TAG,
                    TextUtils.join(" -> ",
                            DebugUtils.callersWithin(ServiceConnector.class, /* offset= */ 1))
                    + "(" + this + ")");
            Log.i(LOG_TAG, "See stacktrace", new Throwable());
        }

        /**