1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.infra;
18 
19 import android.annotation.CallSuper;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.os.Handler;
23 import android.os.Looper;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.os.RemoteException;
27 import android.util.Log;
28 
29 import com.android.internal.annotations.GuardedBy;
30 import com.android.internal.util.Preconditions;
31 
32 import java.lang.reflect.Constructor;
33 import java.util.concurrent.CancellationException;
34 import java.util.concurrent.CompletableFuture;
35 import java.util.concurrent.CompletionStage;
36 import java.util.concurrent.ExecutionException;
37 import java.util.concurrent.Executor;
38 import java.util.concurrent.TimeUnit;
39 import java.util.concurrent.TimeoutException;
40 import java.util.function.BiConsumer;
41 import java.util.function.BiFunction;
42 import java.util.function.Function;
43 import java.util.function.Supplier;
44 
45 /**
46  * A customized {@link CompletableFuture} with focus on reducing the number of allocations involved
47  * in a typical future usage scenario for Android.
48  *
49  * <p>
50  * In particular this involves allocations optimizations in:
51  * <ul>
52  *     <li>{@link #thenCompose(Function)}</li>
53  *     <li>{@link #thenApply(Function)}</li>
54  *     <li>{@link #thenCombine(CompletionStage, BiFunction)}</li>
55  *     <li>{@link #orTimeout(long, TimeUnit)}</li>
56  *     <li>{@link #whenComplete(BiConsumer)}</li>
57  * </ul>
58  * As well as their *Async versions.
59  *
60  * <p>
61  * You can pass {@link AndroidFuture} across an IPC.
62  * When doing so, completing the future on the other side will propagate the completion back,
63  * effectively acting as an error-aware remote callback.
64  *
65  * <p>
66  * {@link AndroidFuture} is {@link Parcelable} iff its wrapped type {@code T} is
67  * effectively parcelable, i.e. is supported by {@link Parcel#readValue}/{@link Parcel#writeValue}.
68  *
69  * @param <T> see {@link CompletableFuture}
70  */
71 public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable {
72 
73     private static final boolean DEBUG = false;
74     private static final String LOG_TAG = AndroidFuture.class.getSimpleName();
75     private static final Executor DIRECT_EXECUTOR = Runnable::run;
76     private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
77     private static @Nullable Handler sMainHandler;
78 
79     private final @NonNull Object mLock = new Object();
80     @GuardedBy("mLock")
81     private @Nullable BiConsumer<? super T, ? super Throwable> mListener;
82     @GuardedBy("mLock")
83     private @Nullable Executor mListenerExecutor = DIRECT_EXECUTOR;
84     private @NonNull Handler mTimeoutHandler = getMainHandler();
85     private final @Nullable IAndroidFuture mRemoteOrigin;
86 
AndroidFuture()87     public AndroidFuture() {
88         super();
89         mRemoteOrigin = null;
90     }
91 
AndroidFuture(Parcel in)92     AndroidFuture(Parcel in) {
93         super();
94         if (in.readBoolean()) {
95             // Done
96             if (in.readBoolean()) {
97                 // Failed
98                 completeExceptionally(readThrowable(in));
99             } else {
100                 // Success
101                 complete((T) in.readValue(null));
102             }
103             mRemoteOrigin = null;
104         } else {
105             // Not done
106             mRemoteOrigin = IAndroidFuture.Stub.asInterface(in.readStrongBinder());
107         }
108     }
109 
110     @NonNull
getMainHandler()111     private static Handler getMainHandler() {
112         // This isn't thread-safe but we are okay with it.
113         if (sMainHandler == null) {
114             sMainHandler = new Handler(Looper.getMainLooper());
115         }
116         return sMainHandler;
117     }
118 
119     /**
120      * Create a completed future with the given value.
121      *
122      * @param value the value for the completed future
123      * @param <U> the type of the value
124      * @return the completed future
125      */
126     @NonNull
completedFuture(U value)127     public static <U> AndroidFuture<U> completedFuture(U value) {
128         AndroidFuture<U> future = new AndroidFuture<>();
129         future.complete(value);
130         return future;
131     }
132 
133     @Override
complete(@ullable T value)134     public boolean complete(@Nullable T value) {
135         boolean changed = super.complete(value);
136         if (changed) {
137             onCompleted(value, null);
138         }
139         return changed;
140     }
141 
142     @Override
completeExceptionally(@onNull Throwable ex)143     public boolean completeExceptionally(@NonNull Throwable ex) {
144         boolean changed = super.completeExceptionally(ex);
145         if (changed) {
146             onCompleted(null, ex);
147         }
148         return changed;
149     }
150 
151     @Override
cancel(boolean mayInterruptIfRunning)152     public boolean cancel(boolean mayInterruptIfRunning) {
153         boolean changed = super.cancel(mayInterruptIfRunning);
154         if (changed) {
155             try {
156                 get();
157                 throw new IllegalStateException("Expected CancellationException");
158             } catch (CancellationException ex) {
159                 onCompleted(null, ex);
160             } catch (Throwable e) {
161                 throw new IllegalStateException("Expected CancellationException", e);
162             }
163         }
164         return changed;
165     }
166 
167     @CallSuper
onCompleted(@ullable T res, @Nullable Throwable err)168     protected void onCompleted(@Nullable T res, @Nullable Throwable err) {
169         cancelTimeout();
170 
171         if (DEBUG) {
172             Log.i(LOG_TAG, this + " completed with result " + (err == null ? res : err),
173                     new RuntimeException());
174         }
175 
176         BiConsumer<? super T, ? super Throwable> listener;
177         synchronized (mLock) {
178             listener = mListener;
179             mListener = null;
180         }
181 
182         if (listener != null) {
183             callListenerAsync(listener, res, err);
184         }
185 
186         if (mRemoteOrigin != null) {
187             try {
188                 mRemoteOrigin.complete(this /* resultContainer */);
189             } catch (RemoteException e) {
190                 Log.e(LOG_TAG, "Failed to propagate completion", e);
191             }
192         }
193     }
194 
195     @Override
whenComplete(@onNull BiConsumer<? super T, ? super Throwable> action)196     public AndroidFuture<T> whenComplete(@NonNull BiConsumer<? super T, ? super Throwable> action) {
197         return whenCompleteAsync(action, DIRECT_EXECUTOR);
198     }
199 
200     @Override
whenCompleteAsync( @onNull BiConsumer<? super T, ? super Throwable> action, @NonNull Executor executor)201     public AndroidFuture<T> whenCompleteAsync(
202             @NonNull BiConsumer<? super T, ? super Throwable> action,
203             @NonNull Executor executor) {
204         Preconditions.checkNotNull(action);
205         Preconditions.checkNotNull(executor);
206         synchronized (mLock) {
207             if (!isDone()) {
208                 BiConsumer<? super T, ? super Throwable> oldListener = mListener;
209 
210                 if (oldListener != null && executor != mListenerExecutor) {
211                     // 2 listeners with different executors
212                     // Too complex - give up on saving allocations and delegate to superclass
213                     super.whenCompleteAsync(action, executor);
214                     return this;
215                 }
216 
217                 mListenerExecutor = executor;
218                 mListener = oldListener == null
219                         ? action
220                         : (res, err) -> {
221                             callListener(oldListener, res, err);
222                             callListener(action, res, err);
223                         };
224                 return this;
225             }
226         }
227 
228         // isDone() == true at this point
229         T res = null;
230         Throwable err = null;
231         try {
232             res = get();
233         } catch (ExecutionException e) {
234             err = e.getCause();
235         } catch (Throwable e) {
236             err = e;
237         }
238         callListenerAsync(action, res, err);
239         return this;
240     }
241 
callListenerAsync(BiConsumer<? super T, ? super Throwable> listener, @Nullable T res, @Nullable Throwable err)242     private void callListenerAsync(BiConsumer<? super T, ? super Throwable> listener,
243             @Nullable T res, @Nullable Throwable err) {
244         if (mListenerExecutor == DIRECT_EXECUTOR) {
245             callListener(listener, res, err);
246         } else {
247             mListenerExecutor.execute(() -> callListener(listener, res, err));
248         }
249     }
250 
251     /**
252      * Calls the provided listener, handling any exceptions that may arise.
253      */
254     // package-private to avoid synthetic method when called from lambda
callListener( @onNull BiConsumer<? super TT, ? super Throwable> listener, @Nullable TT res, @Nullable Throwable err)255     static <TT> void callListener(
256             @NonNull BiConsumer<? super TT, ? super Throwable> listener,
257             @Nullable TT res, @Nullable Throwable err) {
258         try {
259             try {
260                 listener.accept(res, err);
261             } catch (Throwable t) {
262                 if (err == null) {
263                     // listener happy-case threw, but exception case might not throw, so report the
264                     // same exception thrown by listener's happy-path to it again
265                     listener.accept(null, t);
266                 } else {
267                     // listener exception-case threw
268                     // give up on listener but preserve the original exception when throwing up
269                     t.addSuppressed(err);
270                     throw t;
271                 }
272             }
273         } catch (Throwable t2) {
274             // give up on listener and log the result & exception to logcat
275             Log.e(LOG_TAG, "Failed to call whenComplete listener. res = " + res, t2);
276         }
277     }
278 
279     /** @inheritDoc */
280     //@Override //TODO uncomment once java 9 APIs are exposed to frameworks
orTimeout(long timeout, @NonNull TimeUnit unit)281     public AndroidFuture<T> orTimeout(long timeout, @NonNull TimeUnit unit) {
282         mTimeoutHandler.postDelayed(this::triggerTimeout, this, unit.toMillis(timeout));
283         return this;
284     }
285 
triggerTimeout()286     void triggerTimeout() {
287         cancelTimeout();
288         if (!isDone()) {
289             completeExceptionally(new TimeoutException());
290         }
291     }
292 
293     /**
294      * Cancel all timeouts previously set with {@link #orTimeout}, if any.
295      *
296      * @return {@code this} for chaining
297      */
cancelTimeout()298     public AndroidFuture<T> cancelTimeout() {
299         mTimeoutHandler.removeCallbacksAndMessages(this);
300         return this;
301     }
302 
303     /**
304      * Specifies the handler on which timeout is to be triggered
305      */
setTimeoutHandler(@onNull Handler h)306     public AndroidFuture<T> setTimeoutHandler(@NonNull Handler h) {
307         cancelTimeout();
308         mTimeoutHandler = Preconditions.checkNotNull(h);
309         return this;
310     }
311 
312     @Override
thenCompose( @onNull Function<? super T, ? extends CompletionStage<U>> fn)313     public <U> AndroidFuture<U> thenCompose(
314             @NonNull Function<? super T, ? extends CompletionStage<U>> fn) {
315         return thenComposeAsync(fn, DIRECT_EXECUTOR);
316     }
317 
318     @Override
thenComposeAsync( @onNull Function<? super T, ? extends CompletionStage<U>> fn, @NonNull Executor executor)319     public <U> AndroidFuture<U> thenComposeAsync(
320             @NonNull Function<? super T, ? extends CompletionStage<U>> fn,
321             @NonNull Executor executor) {
322         return new ThenComposeAsync<>(this, fn, executor);
323     }
324 
325     private static class ThenComposeAsync<T, U> extends AndroidFuture<U>
326             implements BiConsumer<Object, Throwable>, Runnable {
327         private volatile T mSourceResult = null;
328         private final Executor mExecutor;
329         private volatile Function<? super T, ? extends CompletionStage<U>> mFn;
330 
ThenComposeAsync(@onNull AndroidFuture<T> source, @NonNull Function<? super T, ? extends CompletionStage<U>> fn, @NonNull Executor executor)331         ThenComposeAsync(@NonNull AndroidFuture<T> source,
332                 @NonNull Function<? super T, ? extends CompletionStage<U>> fn,
333                 @NonNull Executor executor) {
334             mFn = Preconditions.checkNotNull(fn);
335             mExecutor = Preconditions.checkNotNull(executor);
336 
337             // subscribe to first job completion
338             source.whenComplete(this);
339         }
340 
341         @Override
accept(Object res, Throwable err)342         public void accept(Object res, Throwable err) {
343             if (err != null) {
344                 // first or second job failed
345                 completeExceptionally(err);
346             } else if (mFn != null) {
347                 // first job completed
348                 mSourceResult = (T) res;
349                 // subscribe to second job completion asynchronously
350                 mExecutor.execute(this);
351             } else {
352                 // second job completed
353                 complete((U) res);
354             }
355         }
356 
357         @Override
run()358         public void run() {
359             CompletionStage<U> secondJob;
360             try {
361                 secondJob = Preconditions.checkNotNull(mFn.apply(mSourceResult));
362             } catch (Throwable t) {
363                 completeExceptionally(t);
364                 return;
365             } finally {
366                 // Marks first job complete
367                 mFn = null;
368             }
369             // subscribe to second job completion
370             secondJob.whenComplete(this);
371         }
372     }
373 
374     @Override
thenApply(@onNull Function<? super T, ? extends U> fn)375     public <U> AndroidFuture<U> thenApply(@NonNull Function<? super T, ? extends U> fn) {
376         return thenApplyAsync(fn, DIRECT_EXECUTOR);
377     }
378 
379     @Override
thenApplyAsync(@onNull Function<? super T, ? extends U> fn, @NonNull Executor executor)380     public <U> AndroidFuture<U> thenApplyAsync(@NonNull Function<? super T, ? extends U> fn,
381             @NonNull Executor executor) {
382         return new ThenApplyAsync<>(this, fn, executor);
383     }
384 
385     private static class ThenApplyAsync<T, U> extends AndroidFuture<U>
386             implements BiConsumer<T, Throwable>, Runnable {
387         private volatile T mSourceResult = null;
388         private final Executor mExecutor;
389         private final Function<? super T, ? extends U> mFn;
390 
ThenApplyAsync(@onNull AndroidFuture<T> source, @NonNull Function<? super T, ? extends U> fn, @NonNull Executor executor)391         ThenApplyAsync(@NonNull AndroidFuture<T> source,
392                 @NonNull Function<? super T, ? extends U> fn,
393                 @NonNull Executor executor) {
394             mExecutor = Preconditions.checkNotNull(executor);
395             mFn = Preconditions.checkNotNull(fn);
396 
397             // subscribe to job completion
398             source.whenComplete(this);
399         }
400 
401         @Override
accept(T res, Throwable err)402         public void accept(T res, Throwable err) {
403             if (err != null) {
404                 completeExceptionally(err);
405             } else {
406                 mSourceResult = res;
407                 mExecutor.execute(this);
408             }
409         }
410 
411         @Override
run()412         public void run() {
413             try {
414                 complete(mFn.apply(mSourceResult));
415             } catch (Throwable t) {
416                 completeExceptionally(t);
417             }
418         }
419     }
420 
421     @Override
thenCombine( @onNull CompletionStage<? extends U> other, @NonNull BiFunction<? super T, ? super U, ? extends V> combineResults)422     public <U, V> AndroidFuture<V> thenCombine(
423             @NonNull CompletionStage<? extends U> other,
424             @NonNull BiFunction<? super T, ? super U, ? extends V> combineResults) {
425         return new ThenCombine<T, U, V>(this, other, combineResults);
426     }
427 
428     /** @see CompletionStage#thenCombine */
thenCombine(@onNull CompletionStage<Void> other)429     public AndroidFuture<T> thenCombine(@NonNull CompletionStage<Void> other) {
430         return thenCombine(other, (res, aVoid) -> res);
431     }
432 
433     private static class ThenCombine<T, U, V> extends AndroidFuture<V>
434             implements BiConsumer<Object, Throwable> {
435         private volatile @Nullable T mResultT = null;
436         private volatile @NonNull CompletionStage<? extends U> mSourceU;
437         private final @NonNull BiFunction<? super T, ? super U, ? extends V> mCombineResults;
438 
ThenCombine(CompletableFuture<T> sourceT, CompletionStage<? extends U> sourceU, BiFunction<? super T, ? super U, ? extends V> combineResults)439         ThenCombine(CompletableFuture<T> sourceT,
440                 CompletionStage<? extends U> sourceU,
441                 BiFunction<? super T, ? super U, ? extends V> combineResults) {
442             mSourceU = Preconditions.checkNotNull(sourceU);
443             mCombineResults = Preconditions.checkNotNull(combineResults);
444 
445             sourceT.whenComplete(this);
446         }
447 
448         @Override
accept(Object res, Throwable err)449         public void accept(Object res, Throwable err) {
450             if (err != null) {
451                 completeExceptionally(err);
452                 return;
453             }
454 
455             if (mSourceU != null) {
456                 // T done
457                 mResultT = (T) res;
458 
459                 // Subscribe to the second job completion.
460                 mSourceU.whenComplete((r, e) -> {
461                     // Mark the first job completion by setting mSourceU to null, so that next time
462                     // the execution flow goes to the else case below.
463                     mSourceU = null;
464                     accept(r, e);
465                 });
466             } else {
467                 // U done
468                 try {
469                     complete(mCombineResults.apply(mResultT, (U) res));
470                 } catch (Throwable t) {
471                     completeExceptionally(t);
472                 }
473             }
474         }
475     }
476 
477     /**
478      * Similar to {@link CompletableFuture#supplyAsync} but
479      * runs the given action directly.
480      *
481      * The resulting future is immediately completed.
482      */
supply(Supplier<T> supplier)483     public static <T> AndroidFuture<T> supply(Supplier<T> supplier) {
484         return supplyAsync(supplier, DIRECT_EXECUTOR);
485     }
486 
487     /**
488      * @see CompletableFuture#supplyAsync(Supplier, Executor)
489      */
supplyAsync(Supplier<T> supplier, Executor executor)490     public static <T> AndroidFuture<T> supplyAsync(Supplier<T> supplier, Executor executor) {
491         return new SupplyAsync<>(supplier, executor);
492     }
493 
494     private static class SupplyAsync<T> extends AndroidFuture<T> implements Runnable {
495         private final @NonNull Supplier<T> mSupplier;
496 
SupplyAsync(Supplier<T> supplier, Executor executor)497         SupplyAsync(Supplier<T> supplier, Executor executor) {
498             mSupplier = supplier;
499             executor.execute(this);
500         }
501 
502         @Override
run()503         public void run() {
504             try {
505                 complete(mSupplier.get());
506             } catch (Throwable t) {
507                 completeExceptionally(t);
508             }
509         }
510     }
511 
512     @Override
writeToParcel(Parcel dest, int flags)513     public void writeToParcel(Parcel dest, int flags) {
514         boolean done = isDone();
515         dest.writeBoolean(done);
516         if (done) {
517             T result;
518             try {
519                 result = get();
520             } catch (Throwable t) {
521                 dest.writeBoolean(true);
522                 writeThrowable(dest, unwrapExecutionException(t));
523                 return;
524             }
525             dest.writeBoolean(false);
526             dest.writeValue(result);
527         } else {
528             dest.writeStrongBinder(new IAndroidFuture.Stub() {
529                 @Override
530                 public void complete(AndroidFuture resultContainer) {
531                     boolean changed;
532                     try {
533                         changed = AndroidFuture.this.complete((T) resultContainer.get());
534                     } catch (Throwable t) {
535                         changed = completeExceptionally(unwrapExecutionException(t));
536                     }
537                     if (!changed) {
538                         Log.w(LOG_TAG, "Remote result " + resultContainer
539                                 + " ignored, as local future is already completed: "
540                                 + AndroidFuture.this);
541                     }
542                 }
543             }.asBinder());
544         }
545     }
546 
547     /**
548      * Exceptions coming out of {@link #get} are wrapped in {@link ExecutionException}
549      */
unwrapExecutionException(Throwable t)550     Throwable unwrapExecutionException(Throwable t) {
551         return t instanceof ExecutionException
552                 ? t.getCause()
553                 : t;
554     }
555 
556     /**
557      * Alternative to {@link Parcel#writeException} that stores the stack trace, in a
558      * way consistent with the binder IPC exception propagation behavior.
559      */
writeThrowable(@onNull Parcel parcel, @Nullable Throwable throwable)560     private static void writeThrowable(@NonNull Parcel parcel, @Nullable Throwable throwable) {
561         boolean hasThrowable = throwable != null;
562         parcel.writeBoolean(hasThrowable);
563         if (!hasThrowable) {
564             return;
565         }
566 
567         boolean isFrameworkParcelable = throwable instanceof Parcelable
568                 && throwable.getClass().getClassLoader() == Parcelable.class.getClassLoader();
569         parcel.writeBoolean(isFrameworkParcelable);
570         if (isFrameworkParcelable) {
571             parcel.writeParcelable((Parcelable) throwable,
572                     Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
573             return;
574         }
575 
576         parcel.writeString(throwable.getClass().getName());
577         parcel.writeString(throwable.getMessage());
578         StackTraceElement[] stackTrace = throwable.getStackTrace();
579         StringBuilder stackTraceBuilder = new StringBuilder();
580         int truncatedStackTraceLength = Math.min(stackTrace != null ? stackTrace.length : 0, 5);
581         for (int i = 0; i < truncatedStackTraceLength; i++) {
582             if (i > 0) {
583                 stackTraceBuilder.append('\n');
584             }
585             stackTraceBuilder.append("\tat ").append(stackTrace[i]);
586         }
587         parcel.writeString(stackTraceBuilder.toString());
588         writeThrowable(parcel, throwable.getCause());
589     }
590 
591     /**
592      * @see #writeThrowable
593      */
594     @SuppressWarnings("UnsafeParcelApi")
readThrowable(@onNull Parcel parcel)595     private static @Nullable Throwable readThrowable(@NonNull Parcel parcel) {
596         final boolean hasThrowable = parcel.readBoolean();
597         if (!hasThrowable) {
598             return null;
599         }
600 
601         boolean isFrameworkParcelable = parcel.readBoolean();
602         if (isFrameworkParcelable) {
603             return parcel.readParcelable(Parcelable.class.getClassLoader());
604         }
605 
606         String className = parcel.readString();
607         String message = parcel.readString();
608         String stackTrace = parcel.readString();
609         String messageWithStackTrace = message + '\n' + stackTrace;
610         Throwable throwable;
611         try {
612             Class<?> clazz = Class.forName(className, true, Parcelable.class.getClassLoader());
613             if (Throwable.class.isAssignableFrom(clazz)) {
614                 Constructor<?> constructor = clazz.getConstructor(String.class);
615                 throwable = (Throwable) constructor.newInstance(messageWithStackTrace);
616             } else {
617                 android.util.EventLog.writeEvent(0x534e4554, "186530450", -1, "");
618                 throwable = new RuntimeException(className + ": " + messageWithStackTrace);
619             }
620         } catch (Throwable t) {
621             throwable = new RuntimeException(className + ": " + messageWithStackTrace);
622             throwable.addSuppressed(t);
623         }
624         throwable.setStackTrace(EMPTY_STACK_TRACE);
625         Throwable cause = readThrowable(parcel);
626         if (cause != null) {
627             throwable.initCause(cause);
628         }
629         return throwable;
630     }
631 
632     @Override
describeContents()633     public int describeContents() {
634         return 0;
635     }
636 
637     public static final @NonNull Parcelable.Creator<AndroidFuture> CREATOR =
638             new Parcelable.Creator<AndroidFuture>() {
639                 public AndroidFuture createFromParcel(Parcel parcel) {
640                     return new AndroidFuture(parcel);
641                 }
642 
643                 public AndroidFuture[] newArray(int size) {
644                     return new AndroidFuture[size];
645                 }
646             };
647 }
648