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