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