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