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