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