From 43fcac1ad55c24a63c7a91493a5a9302abe1e0be Mon Sep 17 00:00:00 2001 From: Biswarup Pal Date: Thu, 27 Oct 2022 09:59:13 +0000 Subject: [PATCH] Add missing information in ANR message from BroadcastQueueModernImpl This keeps the broadcast ANR message consistent with the old broadcast queue, and fixes trace analysis pipelines that consume messages in this format. This unstructured format is brittle but should keeps things working, pending a longer term fix to with structured (proto) format. This CL also adds tests for TimeoutRecord to validate ANR timeout records. Test: m Bug: 254452238 Change-Id: Ifd0fb8d4a1d0add3e762b8327defb3f25d5aa0c2 --- .../android/internal/os/TimeoutRecord.java | 13 +- .../android/server/am/BroadcastQueueImpl.java | 6 +- .../server/am/BroadcastQueueModernImpl.java | 3 +- .../internal/os/TimeoutRecordTest.java | 131 ++++++++++++++++++ 4 files changed, 146 insertions(+), 7 deletions(-) create mode 100644 tests/Internal/src/com/android/internal/os/TimeoutRecordTest.java diff --git a/core/java/com/android/internal/os/TimeoutRecord.java b/core/java/com/android/internal/os/TimeoutRecord.java index be3f1720acdd4..680f8fe6535f9 100644 --- a/core/java/com/android/internal/os/TimeoutRecord.java +++ b/core/java/com/android/internal/os/TimeoutRecord.java @@ -18,6 +18,7 @@ package com.android.internal.os; import android.annotation.IntDef; import android.annotation.NonNull; +import android.content.Intent; import android.os.SystemClock; import com.android.internal.os.anr.AnrLatencyTracker; @@ -92,7 +93,17 @@ public class TimeoutRecord { /** Record for a broadcast receiver timeout. */ @NonNull - public static TimeoutRecord forBroadcastReceiver(@NonNull String reason) { + public static TimeoutRecord forBroadcastReceiver(@NonNull Intent intent) { + String reason = "Broadcast of " + intent.toString(); + return TimeoutRecord.endingNow(TimeoutKind.BROADCAST_RECEIVER, reason); + } + + /** Record for a broadcast receiver timeout. */ + @NonNull + public static TimeoutRecord forBroadcastReceiver(@NonNull Intent intent, + long timeoutDurationMs) { + String reason = "Broadcast of " + intent.toString() + ", waited " + timeoutDurationMs + + "ms"; return TimeoutRecord.endingNow(TimeoutKind.BROADCAST_RECEIVER, reason); } diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java index 454b28402ced7..fb7e0be69d787 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java @@ -51,7 +51,6 @@ import android.content.ContentResolver; import android.content.IIntentReceiver; import android.content.Intent; import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; @@ -1639,9 +1638,8 @@ public class BroadcastQueueImpl extends BroadcastQueue { } Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver); logBroadcastReceiverDiscardLocked(r); - String anrMessage = - "Broadcast of " + r.intent.toString() + ", waited " + timeoutDurationMs + "ms"; - TimeoutRecord timeoutRecord = TimeoutRecord.forBroadcastReceiver(anrMessage); + TimeoutRecord timeoutRecord = TimeoutRecord.forBroadcastReceiver(r.intent, + timeoutDurationMs); if (curReceiver != null && curReceiver instanceof BroadcastFilter) { BroadcastFilter bf = (BroadcastFilter) curReceiver; if (bf.receiverList.pid != 0 diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index 008d95a51cd92..a7d843361762b 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -907,8 +907,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { if (deliveryState == BroadcastRecord.DELIVERY_TIMEOUT) { r.anrCount++; if (app != null && !app.isDebugging()) { - mService.appNotResponding(queue.app, TimeoutRecord - .forBroadcastReceiver("Broadcast of " + r.toShortString())); + mService.appNotResponding(queue.app, TimeoutRecord.forBroadcastReceiver(r.intent)); } } else { mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue); diff --git a/tests/Internal/src/com/android/internal/os/TimeoutRecordTest.java b/tests/Internal/src/com/android/internal/os/TimeoutRecordTest.java new file mode 100644 index 0000000000000..0f9663442740c --- /dev/null +++ b/tests/Internal/src/com/android/internal/os/TimeoutRecordTest.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2022 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 com.android.internal.os; + +import android.content.ComponentName; +import android.content.Intent; +import android.platform.test.annotations.Presubmit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link TimeoutRecord}. */ +@SmallTest +@Presubmit +@RunWith(JUnit4.class) +public class TimeoutRecordTest { + + @Test + public void forBroadcastReceiver_returnsCorrectTimeoutRecord() { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setComponent(ComponentName.createRelative("com.example.app", "ExampleClass")); + + TimeoutRecord record = TimeoutRecord.forBroadcastReceiver(intent); + + assertNotNull(record); + assertEquals(record.mKind, TimeoutRecord.TimeoutKind.BROADCAST_RECEIVER); + assertEquals(record.mReason, + "Broadcast of Intent { act=android.intent.action.MAIN cmp=com.example" + + ".app/ExampleClass }"); + assertTrue(record.mEndTakenBeforeLocks); + } + + @Test + public void forBroadcastReceiver_withTimeoutDurationMs_returnsCorrectTimeoutRecord() { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setComponent(ComponentName.createRelative("com.example.app", "ExampleClass")); + + TimeoutRecord record = TimeoutRecord.forBroadcastReceiver(intent, 1000L); + + assertNotNull(record); + assertEquals(record.mKind, TimeoutRecord.TimeoutKind.BROADCAST_RECEIVER); + assertEquals(record.mReason, + "Broadcast of Intent { act=android.intent.action.MAIN cmp=com.example" + + ".app/ExampleClass }, waited 1000ms"); + assertTrue(record.mEndTakenBeforeLocks); + } + + @Test + public void forInputDispatchNoFocusedWindow_returnsCorrectTimeoutRecord() { + TimeoutRecord record = TimeoutRecord.forInputDispatchNoFocusedWindow("Test ANR reason"); + + assertNotNull(record); + assertEquals(record.mKind, TimeoutRecord.TimeoutKind.INPUT_DISPATCH_NO_FOCUSED_WINDOW); + assertEquals(record.mReason, + "Test ANR reason"); + assertTrue(record.mEndTakenBeforeLocks); + } + + @Test + public void forInputDispatchWindowUnresponsive_returnsCorrectTimeoutRecord() { + TimeoutRecord record = TimeoutRecord.forInputDispatchWindowUnresponsive("Test ANR reason"); + + assertNotNull(record); + assertEquals(record.mKind, TimeoutRecord.TimeoutKind.INPUT_DISPATCH_WINDOW_UNRESPONSIVE); + assertEquals(record.mReason, "Test ANR reason"); + assertTrue(record.mEndTakenBeforeLocks); + } + + @Test + public void forServiceExec_returnsCorrectTimeoutRecord() { + TimeoutRecord record = TimeoutRecord.forServiceExec("Test ANR reason"); + + assertNotNull(record); + assertEquals(record.mKind, TimeoutRecord.TimeoutKind.SERVICE_EXEC); + assertEquals(record.mReason, "Test ANR reason"); + assertTrue(record.mEndTakenBeforeLocks); + } + + @Test + public void forServiceStartWithEndTime_returnsCorrectTimeoutRecord() { + TimeoutRecord record = TimeoutRecord.forServiceStartWithEndTime("Test ANR reason", 1000L); + + assertNotNull(record); + assertEquals(record.mKind, TimeoutRecord.TimeoutKind.SERVICE_START); + assertEquals(record.mReason, "Test ANR reason"); + assertEquals(record.mEndUptimeMillis, 1000L); + assertTrue(record.mEndTakenBeforeLocks); + } + + @Test + public void forContentProvider_returnsCorrectTimeoutRecord() { + TimeoutRecord record = TimeoutRecord.forContentProvider("Test ANR reason"); + + assertNotNull(record); + assertEquals(record.mKind, TimeoutRecord.TimeoutKind.CONTENT_PROVIDER); + assertEquals(record.mReason, "Test ANR reason"); + assertFalse(record.mEndTakenBeforeLocks); + } + + @Test + public void forApp_returnsCorrectTimeoutRecord() { + TimeoutRecord record = TimeoutRecord.forApp("Test ANR reason"); + + assertNotNull(record); + assertEquals(record.mKind, TimeoutRecord.TimeoutKind.APP_REGISTERED); + assertEquals(record.mReason, "Test ANR reason"); + assertFalse(record.mEndTakenBeforeLocks); + } +}