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

Commit 4d331c8e authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Make AndroidFuture and ServiceConnector ready for module usage."

parents a69fc7b2 a677335c
Loading
Loading
Loading
Loading
+70 −40
Original line number Diff line number Diff line
@@ -16,23 +16,20 @@

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.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 +72,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 +95,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 +107,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 +244,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 +266,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 +279,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 +512,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 +550,70 @@ 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);
            Constructor<?> constructor = clazz.getConstructor(String.class);
            throwable = (Throwable) constructor.newInstance(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());
        }

        /**