blob: 84391c169941549f75bb95ab7760add4b5a94707 [file] [log] [blame]
Eugene Suslaa0812502019-04-15 13:19:09 -07001/*
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
17package com.android.internal.infra;
18
Eugene Suslabe4e3af2019-05-07 18:30:48 -070019import android.annotation.CallSuper;
Eugene Suslaa0812502019-04-15 13:19:09 -070020import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.os.Handler;
Hai Zhangb7957a62020-12-30 18:30:25 -080023import android.os.Looper;
Eugene Susla53f4df32019-05-17 13:23:44 -070024import android.os.Parcel;
25import android.os.Parcelable;
26import android.os.RemoteException;
Hai Zhangb7957a62020-12-30 18:30:25 -080027import android.util.EventLog;
Eugene Suslaa0812502019-04-15 13:19:09 -070028import android.util.Log;
29
30import com.android.internal.annotations.GuardedBy;
31import com.android.internal.util.Preconditions;
Eugene Suslaa0812502019-04-15 13:19:09 -070032
Hai Zhangb7957a62020-12-30 18:30:25 -080033import java.lang.reflect.Constructor;
Eugene Susla78e6b492019-05-29 17:22:53 -070034import java.util.concurrent.CancellationException;
Eugene Suslaa0812502019-04-15 13:19:09 -070035import java.util.concurrent.CompletableFuture;
36import java.util.concurrent.CompletionStage;
37import java.util.concurrent.ExecutionException;
Eugene Suslabe4e3af2019-05-07 18:30:48 -070038import java.util.concurrent.Executor;
Eugene Suslaa0812502019-04-15 13:19:09 -070039import java.util.concurrent.TimeUnit;
40import java.util.concurrent.TimeoutException;
41import java.util.function.BiConsumer;
Eugene Suslabe4e3af2019-05-07 18:30:48 -070042import java.util.function.BiFunction;
Eugene Suslaa0812502019-04-15 13:19:09 -070043import java.util.function.Function;
Eugene Suslabe4e3af2019-05-07 18:30:48 -070044import java.util.function.Supplier;
Eugene Suslaa0812502019-04-15 13:19:09 -070045
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 *
Eugene Susla53f4df32019-05-17 13:23:44 -070050 * <p>
Eugene Suslaa0812502019-04-15 13:19:09 -070051 * In particular this involves allocations optimizations in:
52 * <ul>
53 * <li>{@link #thenCompose(Function)}</li>
Eugene Suslabe4e3af2019-05-07 18:30:48 -070054 * <li>{@link #thenApply(Function)}</li>
55 * <li>{@link #thenCombine(CompletionStage, BiFunction)}</li>
Eugene Suslaa0812502019-04-15 13:19:09 -070056 * <li>{@link #orTimeout(long, TimeUnit)}</li>
57 * <li>{@link #whenComplete(BiConsumer)}</li>
58 * </ul>
Eugene Suslabe4e3af2019-05-07 18:30:48 -070059 * As well as their *Async versions.
Eugene Suslaa0812502019-04-15 13:19:09 -070060 *
Eugene Susla53f4df32019-05-17 13:23:44 -070061 * <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 *
Eugene Suslaa0812502019-04-15 13:19:09 -070070 * @param <T> see {@link CompletableFuture}
71 */
Eugene Susla53f4df32019-05-17 13:23:44 -070072public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable {
Eugene Suslaa0812502019-04-15 13:19:09 -070073
Eugene Susla53f4df32019-05-17 13:23:44 -070074 private static final boolean DEBUG = false;
Eugene Suslaa0812502019-04-15 13:19:09 -070075 private static final String LOG_TAG = AndroidFuture.class.getSimpleName();
Hai Zhangb7957a62020-12-30 18:30:25 -080076 private static final Executor DIRECT_EXECUTOR = Runnable::run;
Eugene Susla49b8cdf2019-12-18 17:34:20 -080077 private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
Hai Zhangb7957a62020-12-30 18:30:25 -080078 private static @Nullable Handler sMainHandler;
Eugene Suslaa0812502019-04-15 13:19:09 -070079
Eugene Suslabe4e3af2019-05-07 18:30:48 -070080 private final @NonNull Object mLock = new Object();
81 @GuardedBy("mLock")
Eugene Suslaa0812502019-04-15 13:19:09 -070082 private @Nullable BiConsumer<? super T, ? super Throwable> mListener;
Eugene Suslabe4e3af2019-05-07 18:30:48 -070083 @GuardedBy("mLock")
84 private @Nullable Executor mListenerExecutor = DIRECT_EXECUTOR;
Hai Zhangb7957a62020-12-30 18:30:25 -080085 private @NonNull Handler mTimeoutHandler = getMainHandler();
Eugene Susla53f4df32019-05-17 13:23:44 -070086 private final @Nullable IAndroidFuture mRemoteOrigin;
87
88 public AndroidFuture() {
89 super();
90 mRemoteOrigin = null;
91 }
92
93 AndroidFuture(Parcel in) {
94 super();
95 if (in.readBoolean()) {
96 // Done
97 if (in.readBoolean()) {
98 // Failed
Hai Zhangb7957a62020-12-30 18:30:25 -080099 completeExceptionally(readThrowable(in));
Eugene Susla53f4df32019-05-17 13:23:44 -0700100 } 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 }
Eugene Suslaa0812502019-04-15 13:19:09 -0700110
Hai Zhangb7957a62020-12-30 18:30:25 -0800111 @NonNull
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
Hai Zhangfa970602019-07-11 13:27:33 -0700120 /**
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
128 public static <U> AndroidFuture<U> completedFuture(U value) {
129 AndroidFuture<U> future = new AndroidFuture<>();
130 future.complete(value);
131 return future;
132 }
133
Eugene Suslaa0812502019-04-15 13:19:09 -0700134 @Override
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
144 public boolean completeExceptionally(@NonNull Throwable ex) {
145 boolean changed = super.completeExceptionally(ex);
146 if (changed) {
147 onCompleted(null, ex);
148 }
Eugene Susla78e6b492019-05-29 17:22:53 -0700149 return changed;
150 }
151
152 @Override
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;
Eugene Suslaa0812502019-04-15 13:19:09 -0700166 }
167
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700168 @CallSuper
169 protected void onCompleted(@Nullable T res, @Nullable Throwable err) {
Eugene Suslaa0812502019-04-15 13:19:09 -0700170 cancelTimeout();
171
Eugene Susla53f4df32019-05-17 13:23:44 -0700172 if (DEBUG) {
173 Log.i(LOG_TAG, this + " completed with result " + (err == null ? res : err),
174 new RuntimeException());
175 }
176
Eugene Suslaa0812502019-04-15 13:19:09 -0700177 BiConsumer<? super T, ? super Throwable> listener;
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700178 synchronized (mLock) {
Eugene Suslaa0812502019-04-15 13:19:09 -0700179 listener = mListener;
180 mListener = null;
181 }
182
183 if (listener != null) {
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700184 callListenerAsync(listener, res, err);
Eugene Suslaa0812502019-04-15 13:19:09 -0700185 }
Eugene Susla53f4df32019-05-17 13:23:44 -0700186
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 }
Eugene Suslaa0812502019-04-15 13:19:09 -0700194 }
195
196 @Override
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700197 public AndroidFuture<T> whenComplete(@NonNull BiConsumer<? super T, ? super Throwable> action) {
198 return whenCompleteAsync(action, DIRECT_EXECUTOR);
199 }
200
201 @Override
202 public AndroidFuture<T> whenCompleteAsync(
203 @NonNull BiConsumer<? super T, ? super Throwable> action,
204 @NonNull Executor executor) {
Eugene Suslaa0812502019-04-15 13:19:09 -0700205 Preconditions.checkNotNull(action);
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700206 Preconditions.checkNotNull(executor);
207 synchronized (mLock) {
Eugene Suslaa0812502019-04-15 13:19:09 -0700208 if (!isDone()) {
209 BiConsumer<? super T, ? super Throwable> oldListener = mListener;
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700210
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;
Eugene Suslaa0812502019-04-15 13:19:09 -0700219 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 }
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700239 callListenerAsync(action, res, err);
Eugene Suslaa0812502019-04-15 13:19:09 -0700240 return this;
241 }
242
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700243 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 {
Hai Zhangb7957a62020-12-30 18:30:25 -0800248 mListenerExecutor.execute(() -> callListener(listener, res, err));
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700249 }
250 }
251
Eugene Suslaa0812502019-04-15 13:19:09 -0700252 /**
253 * Calls the provided listener, handling any exceptions that may arise.
254 */
255 // package-private to avoid synthetic method when called from lambda
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
Hai Zhangb7957a62020-12-30 18:30:25 -0800270 t.addSuppressed(err);
271 throw t;
Eugene Suslaa0812502019-04-15 13:19:09 -0700272 }
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
282 public AndroidFuture<T> orTimeout(long timeout, @NonNull TimeUnit unit) {
Hai Zhangb7957a62020-12-30 18:30:25 -0800283 mTimeoutHandler.postDelayed(this::triggerTimeout, this, unit.toMillis(timeout));
Eugene Suslaa0812502019-04-15 13:19:09 -0700284 return this;
285 }
286
287 void triggerTimeout() {
288 cancelTimeout();
289 if (!isDone()) {
290 completeExceptionally(new TimeoutException());
291 }
292 }
293
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700294 /**
295 * Cancel all timeouts previously set with {@link #orTimeout}, if any.
296 *
297 * @return {@code this} for chaining
298 */
299 public AndroidFuture<T> cancelTimeout() {
Eugene Suslaa0812502019-04-15 13:19:09 -0700300 mTimeoutHandler.removeCallbacksAndMessages(this);
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700301 return this;
Eugene Suslaa0812502019-04-15 13:19:09 -0700302 }
303
304 /**
305 * Specifies the handler on which timeout is to be triggered
306 */
307 public AndroidFuture<T> setTimeoutHandler(@NonNull Handler h) {
308 cancelTimeout();
309 mTimeoutHandler = Preconditions.checkNotNull(h);
310 return this;
311 }
312
313 @Override
314 public <U> AndroidFuture<U> thenCompose(
315 @NonNull Function<? super T, ? extends CompletionStage<U>> fn) {
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700316 return thenComposeAsync(fn, DIRECT_EXECUTOR);
Eugene Suslaa0812502019-04-15 13:19:09 -0700317 }
318
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700319 @Override
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 }
Eugene Suslaa0812502019-04-15 13:19:09 -0700325
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700326 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
332 ThenComposeAsync(@NonNull AndroidFuture<T> source,
333 @NonNull Function<? super T, ? extends CompletionStage<U>> fn,
334 @NonNull Executor executor) {
Eugene Suslaa0812502019-04-15 13:19:09 -0700335 mFn = Preconditions.checkNotNull(fn);
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700336 mExecutor = Preconditions.checkNotNull(executor);
337
Eugene Suslaa0812502019-04-15 13:19:09 -0700338 // subscribe to first job completion
339 source.whenComplete(this);
340 }
341
342 @Override
343 public void accept(Object res, Throwable err) {
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700344 if (err != null) {
345 // first or second job failed
346 completeExceptionally(err);
347 } else if (mFn != null) {
Eugene Suslaa0812502019-04-15 13:19:09 -0700348 // first job completed
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700349 mSourceResult = (T) res;
350 // subscribe to second job completion asynchronously
351 mExecutor.execute(this);
Eugene Suslaa0812502019-04-15 13:19:09 -0700352 } else {
353 // second job completed
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700354 complete((U) res);
355 }
356 }
357
358 @Override
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
376 public <U> AndroidFuture<U> thenApply(@NonNull Function<? super T, ? extends U> fn) {
377 return thenApplyAsync(fn, DIRECT_EXECUTOR);
378 }
379
380 @Override
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
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
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
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 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 */
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
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
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);
Eugene Suslaa0812502019-04-15 13:19:09 -0700466 }
467 }
468 }
469 }
Eugene Suslabe4e3af2019-05-07 18:30:48 -0700470
471 /**
472 * Similar to {@link CompletableFuture#supplyAsync} but
473 * runs the given action directly.
474 *
475 * The resulting future is immediately completed.
476 */
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 */
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
491 SupplyAsync(Supplier<T> supplier, Executor executor) {
492 mSupplier = supplier;
493 executor.execute(this);
494 }
495
496 @Override
497 public void run() {
498 try {
499 complete(mSupplier.get());
500 } catch (Throwable t) {
501 completeExceptionally(t);
502 }
503 }
504 }
Eugene Susla53f4df32019-05-17 13:23:44 -0700505
506 @Override
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();
Eugene Susla49b8cdf2019-12-18 17:34:20 -0800514 } catch (Throwable t) {
Eugene Susla53f4df32019-05-17 13:23:44 -0700515 dest.writeBoolean(true);
Hai Zhangb7957a62020-12-30 18:30:25 -0800516 writeThrowable(dest, unwrapExecutionException(t));
Eugene Susla53f4df32019-05-17 13:23:44 -0700517 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) {
Eugene Susla49b8cdf2019-12-18 17:34:20 -0800525 boolean changed;
Eugene Susla53f4df32019-05-17 13:23:44 -0700526 try {
Eugene Susla49b8cdf2019-12-18 17:34:20 -0800527 changed = AndroidFuture.this.complete((T) resultContainer.get());
Eugene Susla53f4df32019-05-17 13:23:44 -0700528 } catch (Throwable t) {
Eugene Susla49b8cdf2019-12-18 17:34:20 -0800529 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);
Eugene Susla53f4df32019-05-17 13:23:44 -0700535 }
536 }
537 }.asBinder());
538 }
539 }
540
Eugene Susla49b8cdf2019-12-18 17:34:20 -0800541 /**
542 * Exceptions coming out of {@link #get} are wrapped in {@link ExecutionException}
543 */
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 */
Hai Zhangb7957a62020-12-30 18:30:25 -0800554 private static void writeThrowable(@NonNull Parcel parcel, @Nullable Throwable throwable) {
555 boolean hasThrowable = throwable != null;
556 parcel.writeBoolean(hasThrowable);
557 if (!hasThrowable) {
Eugene Susla49b8cdf2019-12-18 17:34:20 -0800558 return;
559 }
560
Hai Zhangb7957a62020-12-30 18:30:25 -0800561 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());
Eugene Susla49b8cdf2019-12-18 17:34:20 -0800583 }
584
585 /**
Hai Zhangb7957a62020-12-30 18:30:25 -0800586 * @see #writeThrowable
Eugene Susla49b8cdf2019-12-18 17:34:20 -0800587 */
Hai Zhangb7957a62020-12-30 18:30:25 -0800588 private static @Nullable Throwable readThrowable(@NonNull Parcel parcel) {
589 final boolean hasThrowable = parcel.readBoolean();
590 if (!hasThrowable) {
Eugene Susla49b8cdf2019-12-18 17:34:20 -0800591 return null;
592 }
593
Hai Zhangb7957a62020-12-30 18:30:25 -0800594 boolean isFrameworkParcelable = parcel.readBoolean();
595 if (isFrameworkParcelable) {
596 return parcel.readParcelable(Parcelable.class.getClassLoader());
Eugene Susla49b8cdf2019-12-18 17:34:20 -0800597 }
Eugene Susla49b8cdf2019-12-18 17:34:20 -0800598
Hai Zhangb7957a62020-12-30 18:30:25 -0800599 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);
Eugene Susla49b8cdf2019-12-18 17:34:20 -0800619 if (cause != null) {
Hai Zhangb7957a62020-12-30 18:30:25 -0800620 throwable.initCause(cause);
Eugene Susla49b8cdf2019-12-18 17:34:20 -0800621 }
Hai Zhangb7957a62020-12-30 18:30:25 -0800622 return throwable;
Eugene Susla49b8cdf2019-12-18 17:34:20 -0800623 }
624
Eugene Susla53f4df32019-05-17 13:23:44 -0700625 @Override
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 };
Eugene Suslaa0812502019-04-15 13:19:09 -0700640}