Loading core/java/android/os/Parcel.java +59 −47 Original line number Diff line number Diff line Loading @@ -1886,6 +1886,43 @@ public final class Parcel { public final void writeException(@NonNull Exception e) { AppOpsManager.prefixParcelWithAppOpsIfNeeded(this); int code = getExceptionCode(e); writeInt(code); StrictMode.clearGatheredViolations(); if (code == 0) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new RuntimeException(e); } writeString(e.getMessage()); final long timeNow = sParcelExceptionStackTrace ? SystemClock.elapsedRealtime() : 0; if (sParcelExceptionStackTrace && (timeNow - sLastWriteExceptionStackTrace > WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS)) { sLastWriteExceptionStackTrace = timeNow; writeStackTrace(e); } else { writeInt(0); } switch (code) { case EX_SERVICE_SPECIFIC: writeInt(((ServiceSpecificException) e).errorCode); break; case EX_PARCELABLE: // Write parceled exception prefixed by length final int sizePosition = dataPosition(); writeInt(0); writeParcelable((Parcelable) e, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); final int payloadPosition = dataPosition(); setDataPosition(sizePosition); writeInt(payloadPosition - sizePosition); setDataPosition(payloadPosition); break; } } /** @hide */ public static int getExceptionCode(@NonNull Throwable e) { int code = 0; if (e instanceof Parcelable && (e.getClass().getClassLoader() == Parcelable.class.getClassLoader())) { Loading @@ -1909,19 +1946,11 @@ public final class Parcel { } else if (e instanceof ServiceSpecificException) { code = EX_SERVICE_SPECIFIC; } writeInt(code); StrictMode.clearGatheredViolations(); if (code == 0) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new RuntimeException(e); return code; } writeString(e.getMessage()); final long timeNow = sParcelExceptionStackTrace ? SystemClock.elapsedRealtime() : 0; if (sParcelExceptionStackTrace && (timeNow - sLastWriteExceptionStackTrace > WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS)) { sLastWriteExceptionStackTrace = timeNow; /** @hide */ public void writeStackTrace(@NonNull Throwable e) { final int sizePosition = dataPosition(); writeInt(0); // Header size will be filled in later StackTraceElement[] stackTrace = e.getStackTrace(); Loading @@ -1936,24 +1965,6 @@ public final class Parcel { // Write stack trace header size. Used in native side to skip the header writeInt(payloadPosition - sizePosition); setDataPosition(payloadPosition); } else { writeInt(0); } switch (code) { case EX_SERVICE_SPECIFIC: writeInt(((ServiceSpecificException) e).errorCode); break; case EX_PARCELABLE: // Write parceled exception prefixed by length final int sizePosition = dataPosition(); writeInt(0); writeParcelable((Parcelable) e, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); final int payloadPosition = dataPosition(); setDataPosition(sizePosition); writeInt(payloadPosition - sizePosition); setDataPosition(payloadPosition); break; } } /** Loading Loading @@ -2069,14 +2080,7 @@ public final class Parcel { if (remoteStackTrace != null) { RemoteException cause = new RemoteException( "Remote stack trace:\n" + remoteStackTrace, null, false, false); try { Throwable rootCause = ExceptionUtils.getRootCause(e); if (rootCause != null) { rootCause.initCause(cause); } } catch (RuntimeException ex) { Log.e(TAG, "Cannot set cause " + cause + " for " + e, ex); } ExceptionUtils.appendCause(e, cause); } SneakyThrow.sneakyThrow(e); } Loading @@ -2088,6 +2092,14 @@ public final class Parcel { * @param msg The exception message. */ private Exception createException(int code, String msg) { Exception exception = createExceptionOrNull(code, msg); return exception != null ? exception : new RuntimeException("Unknown exception code: " + code + " msg " + msg); } /** @hide */ public Exception createExceptionOrNull(int code, String msg) { switch (code) { case EX_PARCELABLE: if (readInt() > 0) { Loading @@ -2111,9 +2123,9 @@ public final class Parcel { return new UnsupportedOperationException(msg); case EX_SERVICE_SPECIFIC: return new ServiceSpecificException(readInt(), msg); default: return null; } return new RuntimeException("Unknown exception code: " + code + " msg " + msg); } /** Loading core/java/com/android/internal/infra/AndroidFuture.java +66 −24 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable private static final boolean DEBUG = false; private static final String LOG_TAG = AndroidFuture.class.getSimpleName(); private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; private final @NonNull Object mLock = new Object(); @GuardedBy("mLock") Loading @@ -95,15 +96,7 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable // Done if (in.readBoolean()) { // Failed try { in.readException(); } catch (Throwable e) { completeExceptionally(e); } if (!isCompletedExceptionally()) { throw new IllegalStateException( "Error unparceling AndroidFuture: exception expected"); } completeExceptionally(unparcelException(in)); } else { // Success complete((T) in.readValue(null)); Loading Loading @@ -512,14 +505,9 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable T result; try { result = get(); } catch (Exception t) { // Exceptions coming out of get() are wrapped in ExecutionException, which is not // handled by Parcel. if (t instanceof ExecutionException && t.getCause() instanceof Exception) { t = (Exception) t.getCause(); } } catch (Throwable t) { dest.writeBoolean(true); dest.writeException(t); parcelException(dest, unwrapExecutionException(t)); return; } dest.writeBoolean(false); Loading @@ -528,22 +516,76 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable dest.writeStrongBinder(new IAndroidFuture.Stub() { @Override public void complete(AndroidFuture resultContainer) { boolean changed; try { AndroidFuture.this.complete((T) resultContainer.get()); changed = AndroidFuture.this.complete((T) resultContainer.get()); } catch (Throwable t) { // If resultContainer was completed exceptionally, get() wraps the // underlying exception in an ExecutionException. Unwrap it now to avoid // double-layering ExecutionExceptions. if (t instanceof ExecutionException && t.getCause() != null) { t = t.getCause(); changed = completeExceptionally(unwrapExecutionException(t)); } completeExceptionally(t); if (!changed) { Log.w(LOG_TAG, "Remote result " + resultContainer + " ignored, as local future is already completed: " + AndroidFuture.this); } } }.asBinder()); } } /** * Exceptions coming out of {@link #get} are wrapped in {@link ExecutionException} */ Throwable unwrapExecutionException(Throwable t) { return t instanceof ExecutionException ? t.getCause() : t; } /** * Alternative to {@link Parcel#writeException} that stores the stack trace, in a * way consistent with the binder IPC exception propagation behavior. */ private static void parcelException(Parcel p, @Nullable Throwable t) { p.writeBoolean(t == null); if (t == null) { return; } p.writeInt(Parcel.getExceptionCode(t)); p.writeString(t.getClass().getName()); p.writeString(t.getMessage()); p.writeStackTrace(t); parcelException(p, t.getCause()); } /** * @see #parcelException */ private static @Nullable Throwable unparcelException(Parcel p) { if (p.readBoolean()) { return null; } int exCode = p.readInt(); String cls = p.readString(); String msg = p.readString(); String stackTrace = p.readInt() > 0 ? p.readString() : "\t<stack trace unavailable>"; msg += "\n" + stackTrace; Exception ex = p.createExceptionOrNull(exCode, msg); if (ex == null) { ex = new RuntimeException(cls + ": " + msg); } ex.setStackTrace(EMPTY_STACK_TRACE); Throwable cause = unparcelException(p); if (cause != null) { ex.initCause(ex); } return ex; } @Override public int describeContents() { return 0; Loading core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java +6 −1 Original line number Diff line number Diff line Loading @@ -121,7 +121,12 @@ public class AndroidFutureTest { AndroidFuture future2 = AndroidFuture.CREATOR.createFromParcel(parcel); ExecutionException executionException = expectThrows(ExecutionException.class, future2::get); assertThat(executionException.getCause()).isInstanceOf(UnsupportedOperationException.class); Throwable cause = executionException.getCause(); String msg = cause.getMessage(); assertThat(cause).isInstanceOf(UnsupportedOperationException.class); assertThat(msg).contains(getClass().getName()); assertThat(msg).contains("testWriteToParcel_Exception"); } @Test Loading Loading
core/java/android/os/Parcel.java +59 −47 Original line number Diff line number Diff line Loading @@ -1886,6 +1886,43 @@ public final class Parcel { public final void writeException(@NonNull Exception e) { AppOpsManager.prefixParcelWithAppOpsIfNeeded(this); int code = getExceptionCode(e); writeInt(code); StrictMode.clearGatheredViolations(); if (code == 0) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new RuntimeException(e); } writeString(e.getMessage()); final long timeNow = sParcelExceptionStackTrace ? SystemClock.elapsedRealtime() : 0; if (sParcelExceptionStackTrace && (timeNow - sLastWriteExceptionStackTrace > WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS)) { sLastWriteExceptionStackTrace = timeNow; writeStackTrace(e); } else { writeInt(0); } switch (code) { case EX_SERVICE_SPECIFIC: writeInt(((ServiceSpecificException) e).errorCode); break; case EX_PARCELABLE: // Write parceled exception prefixed by length final int sizePosition = dataPosition(); writeInt(0); writeParcelable((Parcelable) e, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); final int payloadPosition = dataPosition(); setDataPosition(sizePosition); writeInt(payloadPosition - sizePosition); setDataPosition(payloadPosition); break; } } /** @hide */ public static int getExceptionCode(@NonNull Throwable e) { int code = 0; if (e instanceof Parcelable && (e.getClass().getClassLoader() == Parcelable.class.getClassLoader())) { Loading @@ -1909,19 +1946,11 @@ public final class Parcel { } else if (e instanceof ServiceSpecificException) { code = EX_SERVICE_SPECIFIC; } writeInt(code); StrictMode.clearGatheredViolations(); if (code == 0) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new RuntimeException(e); return code; } writeString(e.getMessage()); final long timeNow = sParcelExceptionStackTrace ? SystemClock.elapsedRealtime() : 0; if (sParcelExceptionStackTrace && (timeNow - sLastWriteExceptionStackTrace > WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS)) { sLastWriteExceptionStackTrace = timeNow; /** @hide */ public void writeStackTrace(@NonNull Throwable e) { final int sizePosition = dataPosition(); writeInt(0); // Header size will be filled in later StackTraceElement[] stackTrace = e.getStackTrace(); Loading @@ -1936,24 +1965,6 @@ public final class Parcel { // Write stack trace header size. Used in native side to skip the header writeInt(payloadPosition - sizePosition); setDataPosition(payloadPosition); } else { writeInt(0); } switch (code) { case EX_SERVICE_SPECIFIC: writeInt(((ServiceSpecificException) e).errorCode); break; case EX_PARCELABLE: // Write parceled exception prefixed by length final int sizePosition = dataPosition(); writeInt(0); writeParcelable((Parcelable) e, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); final int payloadPosition = dataPosition(); setDataPosition(sizePosition); writeInt(payloadPosition - sizePosition); setDataPosition(payloadPosition); break; } } /** Loading Loading @@ -2069,14 +2080,7 @@ public final class Parcel { if (remoteStackTrace != null) { RemoteException cause = new RemoteException( "Remote stack trace:\n" + remoteStackTrace, null, false, false); try { Throwable rootCause = ExceptionUtils.getRootCause(e); if (rootCause != null) { rootCause.initCause(cause); } } catch (RuntimeException ex) { Log.e(TAG, "Cannot set cause " + cause + " for " + e, ex); } ExceptionUtils.appendCause(e, cause); } SneakyThrow.sneakyThrow(e); } Loading @@ -2088,6 +2092,14 @@ public final class Parcel { * @param msg The exception message. */ private Exception createException(int code, String msg) { Exception exception = createExceptionOrNull(code, msg); return exception != null ? exception : new RuntimeException("Unknown exception code: " + code + " msg " + msg); } /** @hide */ public Exception createExceptionOrNull(int code, String msg) { switch (code) { case EX_PARCELABLE: if (readInt() > 0) { Loading @@ -2111,9 +2123,9 @@ public final class Parcel { return new UnsupportedOperationException(msg); case EX_SERVICE_SPECIFIC: return new ServiceSpecificException(readInt(), msg); default: return null; } return new RuntimeException("Unknown exception code: " + code + " msg " + msg); } /** Loading
core/java/com/android/internal/infra/AndroidFuture.java +66 −24 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable private static final boolean DEBUG = false; private static final String LOG_TAG = AndroidFuture.class.getSimpleName(); private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; private final @NonNull Object mLock = new Object(); @GuardedBy("mLock") Loading @@ -95,15 +96,7 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable // Done if (in.readBoolean()) { // Failed try { in.readException(); } catch (Throwable e) { completeExceptionally(e); } if (!isCompletedExceptionally()) { throw new IllegalStateException( "Error unparceling AndroidFuture: exception expected"); } completeExceptionally(unparcelException(in)); } else { // Success complete((T) in.readValue(null)); Loading Loading @@ -512,14 +505,9 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable T result; try { result = get(); } catch (Exception t) { // Exceptions coming out of get() are wrapped in ExecutionException, which is not // handled by Parcel. if (t instanceof ExecutionException && t.getCause() instanceof Exception) { t = (Exception) t.getCause(); } } catch (Throwable t) { dest.writeBoolean(true); dest.writeException(t); parcelException(dest, unwrapExecutionException(t)); return; } dest.writeBoolean(false); Loading @@ -528,22 +516,76 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable dest.writeStrongBinder(new IAndroidFuture.Stub() { @Override public void complete(AndroidFuture resultContainer) { boolean changed; try { AndroidFuture.this.complete((T) resultContainer.get()); changed = AndroidFuture.this.complete((T) resultContainer.get()); } catch (Throwable t) { // If resultContainer was completed exceptionally, get() wraps the // underlying exception in an ExecutionException. Unwrap it now to avoid // double-layering ExecutionExceptions. if (t instanceof ExecutionException && t.getCause() != null) { t = t.getCause(); changed = completeExceptionally(unwrapExecutionException(t)); } completeExceptionally(t); if (!changed) { Log.w(LOG_TAG, "Remote result " + resultContainer + " ignored, as local future is already completed: " + AndroidFuture.this); } } }.asBinder()); } } /** * Exceptions coming out of {@link #get} are wrapped in {@link ExecutionException} */ Throwable unwrapExecutionException(Throwable t) { return t instanceof ExecutionException ? t.getCause() : t; } /** * Alternative to {@link Parcel#writeException} that stores the stack trace, in a * way consistent with the binder IPC exception propagation behavior. */ private static void parcelException(Parcel p, @Nullable Throwable t) { p.writeBoolean(t == null); if (t == null) { return; } p.writeInt(Parcel.getExceptionCode(t)); p.writeString(t.getClass().getName()); p.writeString(t.getMessage()); p.writeStackTrace(t); parcelException(p, t.getCause()); } /** * @see #parcelException */ private static @Nullable Throwable unparcelException(Parcel p) { if (p.readBoolean()) { return null; } int exCode = p.readInt(); String cls = p.readString(); String msg = p.readString(); String stackTrace = p.readInt() > 0 ? p.readString() : "\t<stack trace unavailable>"; msg += "\n" + stackTrace; Exception ex = p.createExceptionOrNull(exCode, msg); if (ex == null) { ex = new RuntimeException(cls + ": " + msg); } ex.setStackTrace(EMPTY_STACK_TRACE); Throwable cause = unparcelException(p); if (cause != null) { ex.initCause(ex); } return ex; } @Override public int describeContents() { return 0; Loading
core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java +6 −1 Original line number Diff line number Diff line Loading @@ -121,7 +121,12 @@ public class AndroidFutureTest { AndroidFuture future2 = AndroidFuture.CREATOR.createFromParcel(parcel); ExecutionException executionException = expectThrows(ExecutionException.class, future2::get); assertThat(executionException.getCause()).isInstanceOf(UnsupportedOperationException.class); Throwable cause = executionException.getCause(); String msg = cause.getMessage(); assertThat(cause).isInstanceOf(UnsupportedOperationException.class); assertThat(msg).contains(getClass().getName()); assertThat(msg).contains("testWriteToParcel_Exception"); } @Test Loading