Loading core/java/android/os/Handler_ravenwood.java +2 −2 Original line number Diff line number Diff line Loading @@ -24,11 +24,11 @@ public class Handler_ravenwood { } public static volatile TriFunction<MessageQueue, Message, Long, Void> sPendingExceptionThrower = (a, b, c) -> null; sOnBeforeEnqueue = (a, b, c) -> null; static void onBeforeEnqueue(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { // Check for a pendign exception, and throw it if any. sPendingExceptionThrower.apply(queue, msg, uptimeMillis); sOnBeforeEnqueue.apply(queue, msg, uptimeMillis); } } core/java/android/os/Message.java +10 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,9 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.ravenwood.annotation.RavenwoodKeepWholeClass; import android.ravenwood.annotation.RavenwoodRedirect; import android.ravenwood.annotation.RavenwoodRedirectionClass; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; Loading @@ -26,7 +29,6 @@ import com.android.internal.annotations.VisibleForTesting; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.concurrent.atomic.AtomicLong; /** * Loading @@ -39,7 +41,8 @@ import java.util.concurrent.atomic.AtomicLong; * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull * them from a pool of recycled objects.</p> */ @android.ravenwood.annotation.RavenwoodKeepWholeClass @RavenwoodKeepWholeClass @RavenwoodRedirectionClass("Message_ravenwood") public final class Message implements Parcelable { /** Loading Loading @@ -406,6 +409,11 @@ public final class Message implements Parcelable { target = null; callback = null; data = null; this.onClear(); } @RavenwoodRedirect private void onClear() { } // Sentinel values used to clear reference fields with a valid 'null' value, to avoid grabbing a Loading core/java/android/os/Message_ravenwood.java 0 → 100644 +28 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os; import android.annotation.NonNull; import android.platform.test.ravenwood.RavenwoodMessageTracker; public class Message_ravenwood { private Message_ravenwood() { } static void onClear(@NonNull Message msg) { RavenwoodMessageTracker.getInstance().untrackMessage(msg); } } ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodDriver.java +5 −1 Original line number Diff line number Diff line Loading @@ -320,7 +320,8 @@ public class RavenwoodDriver { RavenwoodUtils.sPendingExceptionThrower = RavenwoodDriver::maybeThrowPendingRecoverableUncaughtExceptionNoClear; Handler_ravenwood.sPendingExceptionThrower = (a, b, c) -> { Handler_ravenwood.sOnBeforeEnqueue = (queue, msg, uptimeMillis) -> { RavenwoodMessageTracker.getInstance().trackMessagePoster(msg); maybeThrowPendingRecoverableUncaughtExceptionNoClear(); return null; }; Loading Loading @@ -523,6 +524,9 @@ public class RavenwoodDriver { var desc = String.format("Detected %s on looper thread %s", th.getClass().getName(), Thread.currentThread()); sStdErr.println(desc); // If it's a tracked message, attach the stacktrace where we posted it as a cause. RavenwoodMessageTracker.getInstance().injectPosterAsCause(th, msg); if (isThrowableRecoverable(th)) { setPendingRecoverableUncaughtException(th); return; Loading ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodMessageTracker.java 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.platform.test.ravenwood; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Message; import com.android.internal.annotations.GuardedBy; import java.util.Objects; import java.util.WeakHashMap; /** * Keeps track of stacktraces where a message is posted. */ public final class RavenwoodMessageTracker { public static final RavenwoodMessageTracker sInstance = new RavenwoodMessageTracker(); public static RavenwoodMessageTracker getInstance() { return sInstance; } private RavenwoodMessageTracker() { } private final Object mLock = new Object(); @GuardedBy("mLock") private final WeakHashMap<Message, MessageWasPostedHereStackTrace> mMessagePosters = new WeakHashMap<>(); /** Stop tracking {@code msg} */ public void untrackMessage(@NonNull Message msg) { synchronized (mLock) { mMessagePosters.remove(Objects.requireNonNull(msg)); } } /** Start tracking {@code msg} and remember the current stacktrace. */ public void trackMessagePoster(@NonNull Message msg) { var here = new MessageWasPostedHereStackTrace(); synchronized (mLock) { mMessagePosters.put(Objects.requireNonNull(msg), here); } } /** @return the remembered stacktrace for a tracked message. */ @Nullable public MessageWasPostedHereStackTrace getPoster(@NonNull Message msg) { synchronized (mLock) { return mMessagePosters.get(Objects.requireNonNull(msg)); } } /** if {@code msg} is traced, set the remembered stacktrace as a cause to {@code th}.*/ public void injectPosterAsCause(@NonNull Throwable th, @NonNull Message msg) { Objects.requireNonNull(th); var poster = getPoster(msg); if (poster != null) { poster.injectAsCause(th); } } } Loading
core/java/android/os/Handler_ravenwood.java +2 −2 Original line number Diff line number Diff line Loading @@ -24,11 +24,11 @@ public class Handler_ravenwood { } public static volatile TriFunction<MessageQueue, Message, Long, Void> sPendingExceptionThrower = (a, b, c) -> null; sOnBeforeEnqueue = (a, b, c) -> null; static void onBeforeEnqueue(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { // Check for a pendign exception, and throw it if any. sPendingExceptionThrower.apply(queue, msg, uptimeMillis); sOnBeforeEnqueue.apply(queue, msg, uptimeMillis); } }
core/java/android/os/Message.java +10 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,9 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.ravenwood.annotation.RavenwoodKeepWholeClass; import android.ravenwood.annotation.RavenwoodRedirect; import android.ravenwood.annotation.RavenwoodRedirectionClass; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; Loading @@ -26,7 +29,6 @@ import com.android.internal.annotations.VisibleForTesting; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.concurrent.atomic.AtomicLong; /** * Loading @@ -39,7 +41,8 @@ import java.util.concurrent.atomic.AtomicLong; * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull * them from a pool of recycled objects.</p> */ @android.ravenwood.annotation.RavenwoodKeepWholeClass @RavenwoodKeepWholeClass @RavenwoodRedirectionClass("Message_ravenwood") public final class Message implements Parcelable { /** Loading Loading @@ -406,6 +409,11 @@ public final class Message implements Parcelable { target = null; callback = null; data = null; this.onClear(); } @RavenwoodRedirect private void onClear() { } // Sentinel values used to clear reference fields with a valid 'null' value, to avoid grabbing a Loading
core/java/android/os/Message_ravenwood.java 0 → 100644 +28 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os; import android.annotation.NonNull; import android.platform.test.ravenwood.RavenwoodMessageTracker; public class Message_ravenwood { private Message_ravenwood() { } static void onClear(@NonNull Message msg) { RavenwoodMessageTracker.getInstance().untrackMessage(msg); } }
ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodDriver.java +5 −1 Original line number Diff line number Diff line Loading @@ -320,7 +320,8 @@ public class RavenwoodDriver { RavenwoodUtils.sPendingExceptionThrower = RavenwoodDriver::maybeThrowPendingRecoverableUncaughtExceptionNoClear; Handler_ravenwood.sPendingExceptionThrower = (a, b, c) -> { Handler_ravenwood.sOnBeforeEnqueue = (queue, msg, uptimeMillis) -> { RavenwoodMessageTracker.getInstance().trackMessagePoster(msg); maybeThrowPendingRecoverableUncaughtExceptionNoClear(); return null; }; Loading Loading @@ -523,6 +524,9 @@ public class RavenwoodDriver { var desc = String.format("Detected %s on looper thread %s", th.getClass().getName(), Thread.currentThread()); sStdErr.println(desc); // If it's a tracked message, attach the stacktrace where we posted it as a cause. RavenwoodMessageTracker.getInstance().injectPosterAsCause(th, msg); if (isThrowableRecoverable(th)) { setPendingRecoverableUncaughtException(th); return; Loading
ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodMessageTracker.java 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.platform.test.ravenwood; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Message; import com.android.internal.annotations.GuardedBy; import java.util.Objects; import java.util.WeakHashMap; /** * Keeps track of stacktraces where a message is posted. */ public final class RavenwoodMessageTracker { public static final RavenwoodMessageTracker sInstance = new RavenwoodMessageTracker(); public static RavenwoodMessageTracker getInstance() { return sInstance; } private RavenwoodMessageTracker() { } private final Object mLock = new Object(); @GuardedBy("mLock") private final WeakHashMap<Message, MessageWasPostedHereStackTrace> mMessagePosters = new WeakHashMap<>(); /** Stop tracking {@code msg} */ public void untrackMessage(@NonNull Message msg) { synchronized (mLock) { mMessagePosters.remove(Objects.requireNonNull(msg)); } } /** Start tracking {@code msg} and remember the current stacktrace. */ public void trackMessagePoster(@NonNull Message msg) { var here = new MessageWasPostedHereStackTrace(); synchronized (mLock) { mMessagePosters.put(Objects.requireNonNull(msg), here); } } /** @return the remembered stacktrace for a tracked message. */ @Nullable public MessageWasPostedHereStackTrace getPoster(@NonNull Message msg) { synchronized (mLock) { return mMessagePosters.get(Objects.requireNonNull(msg)); } } /** if {@code msg} is traced, set the remembered stacktrace as a cause to {@code th}.*/ public void injectPosterAsCause(@NonNull Throwable th, @NonNull Message msg) { Objects.requireNonNull(th); var poster = getPoster(msg); if (poster != null) { poster.injectAsCause(th); } } }