diff options
| -rw-r--r-- | core/java/com/android/internal/infra/AndroidFuture.java | 116 | ||||
| -rw-r--r-- | core/java/com/android/internal/infra/ServiceConnector.java | 28 |
2 files changed, 86 insertions, 58 deletions
diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java index 9f15d8991fa7..84391c169941 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 @@ 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; + } + + 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; } - p.writeInt(Parcel.getExceptionCode(t)); - p.writeString(t.getClass().getName()); - p.writeString(t.getMessage()); - p.writeStackTrace(t); - parcelException(p, t.getCause()); + 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 167d128a76f8..89869389cb59 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.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()); } /** |