Fix missing broadcast ANR culprit information.
All TimeoutRecord have unfortunately been marked as "unknown culprit" due to the Intent not having a ComponentName filled in. We fix this by passing the relevant package and class name through the record, and we expand it to identify runtime registered receiver classes to aid faster bug triage. For now, we're abusing the class name transported via "receiverId" to avoid the churn of adding yet another method argument late in the release cycle. Bug: 266169149 Test: atest InternalTests:TimeoutRecordTest Test: atest FrameworksMockingServicesTests:BroadcastQueueTest Test: atest FrameworksMockingServicesTests:BroadcastQueueModernImplTest Test: atest FrameworksMockingServicesTests:BroadcastRecordTest Change-Id: I67b6f672c24627b7281de3b218e1114ab8a48f74
This commit is contained in:
@@ -11356,12 +11356,15 @@ public class Intent implements Parcelable, Cloneable {
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder b = new StringBuilder(128);
|
||||
toString(b);
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void toString(@NonNull StringBuilder b) {
|
||||
b.append("Intent { ");
|
||||
toShortString(b, true, true, true, false);
|
||||
b.append(" }");
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.android.internal.os;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.os.SystemClock;
|
||||
|
||||
@@ -96,20 +98,43 @@ public class TimeoutRecord {
|
||||
return new TimeoutRecord(kind, reason, endUptimeMillis, /* endTakenBeforeLocks */ false);
|
||||
}
|
||||
|
||||
/** Record for a broadcast receiver timeout. */
|
||||
@NonNull
|
||||
public static TimeoutRecord forBroadcastReceiver(@NonNull Intent intent,
|
||||
@Nullable String packageName, @Nullable String className) {
|
||||
final Intent logIntent;
|
||||
if (packageName != null) {
|
||||
if (className != null) {
|
||||
logIntent = new Intent(intent);
|
||||
logIntent.setComponent(new ComponentName(packageName, className));
|
||||
} else {
|
||||
logIntent = new Intent(intent);
|
||||
logIntent.setPackage(packageName);
|
||||
}
|
||||
} else {
|
||||
logIntent = intent;
|
||||
}
|
||||
return forBroadcastReceiver(logIntent);
|
||||
}
|
||||
|
||||
/** Record for a broadcast receiver timeout. */
|
||||
@NonNull
|
||||
public static TimeoutRecord forBroadcastReceiver(@NonNull Intent intent) {
|
||||
String reason = "Broadcast of " + intent.toString();
|
||||
return TimeoutRecord.endingNow(TimeoutKind.BROADCAST_RECEIVER, reason);
|
||||
final StringBuilder reason = new StringBuilder("Broadcast of ");
|
||||
intent.toString(reason);
|
||||
return TimeoutRecord.endingNow(TimeoutKind.BROADCAST_RECEIVER, reason.toString());
|
||||
}
|
||||
|
||||
/** 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);
|
||||
final StringBuilder reason = new StringBuilder("Broadcast of ");
|
||||
intent.toString(reason);
|
||||
reason.append(", waited ");
|
||||
reason.append(timeoutDurationMs);
|
||||
reason.append("ms");
|
||||
return TimeoutRecord.endingNow(TimeoutKind.BROADCAST_RECEIVER, reason.toString());
|
||||
}
|
||||
|
||||
/** Record for an input dispatch no focused window timeout */
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.am;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.IntentFilter;
|
||||
import android.util.PrintWriterPrinter;
|
||||
import android.util.Printer;
|
||||
@@ -55,6 +56,16 @@ final class BroadcastFilter extends IntentFilter {
|
||||
exported = _exported;
|
||||
}
|
||||
|
||||
public @Nullable String getReceiverClassName() {
|
||||
if (receiverId != null) {
|
||||
final int index = receiverId.lastIndexOf('@');
|
||||
if (index > 0) {
|
||||
return receiverId.substring(0, index);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NeverCompile
|
||||
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
|
||||
long token = proto.start(fieldId);
|
||||
|
||||
@@ -32,6 +32,7 @@ import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList
|
||||
import static com.android.server.am.BroadcastProcessQueue.reasonToString;
|
||||
import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList;
|
||||
import static com.android.server.am.BroadcastRecord.deliveryStateToString;
|
||||
import static com.android.server.am.BroadcastRecord.getReceiverClassName;
|
||||
import static com.android.server.am.BroadcastRecord.getReceiverPackageName;
|
||||
import static com.android.server.am.BroadcastRecord.getReceiverProcessName;
|
||||
import static com.android.server.am.BroadcastRecord.getReceiverUid;
|
||||
@@ -1040,7 +1041,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
if (deliveryState == BroadcastRecord.DELIVERY_TIMEOUT) {
|
||||
r.anrCount++;
|
||||
if (app != null && !app.isDebugging()) {
|
||||
mService.appNotResponding(queue.app, TimeoutRecord.forBroadcastReceiver(r.intent));
|
||||
final String packageName = getReceiverPackageName(receiver);
|
||||
final String className = getReceiverClassName(receiver);
|
||||
mService.appNotResponding(queue.app,
|
||||
TimeoutRecord.forBroadcastReceiver(r.intent, packageName, className));
|
||||
}
|
||||
} else {
|
||||
mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue);
|
||||
|
||||
@@ -832,6 +832,14 @@ final class BroadcastRecord extends Binder {
|
||||
}
|
||||
}
|
||||
|
||||
static @Nullable String getReceiverClassName(@NonNull Object receiver) {
|
||||
if (receiver instanceof BroadcastFilter) {
|
||||
return ((BroadcastFilter) receiver).getReceiverClassName();
|
||||
} else /* if (receiver instanceof ResolveInfo) */ {
|
||||
return ((ResolveInfo) receiver).activityInfo.name;
|
||||
}
|
||||
}
|
||||
|
||||
static int getReceiverPriority(@NonNull Object receiver) {
|
||||
if (receiver instanceof BroadcastFilter) {
|
||||
return ((BroadcastFilter) receiver).getPriority();
|
||||
|
||||
@@ -16,15 +16,15 @@
|
||||
|
||||
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 android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -40,7 +40,7 @@ public class TimeoutRecordTest {
|
||||
@Test
|
||||
public void forBroadcastReceiver_returnsCorrectTimeoutRecord() {
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.setComponent(ComponentName.createRelative("com.example.app", "ExampleClass"));
|
||||
intent.setComponent(new ComponentName("com.example.app", "com.example.app.ExampleClass"));
|
||||
|
||||
TimeoutRecord record = TimeoutRecord.forBroadcastReceiver(intent);
|
||||
|
||||
@@ -48,14 +48,28 @@ public class TimeoutRecordTest {
|
||||
assertEquals(record.mKind, TimeoutRecord.TimeoutKind.BROADCAST_RECEIVER);
|
||||
assertEquals(record.mReason,
|
||||
"Broadcast of Intent { act=android.intent.action.MAIN cmp=com.example"
|
||||
+ ".app/ExampleClass }");
|
||||
+ ".app/.ExampleClass }");
|
||||
assertTrue(record.mEndTakenBeforeLocks);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forBroadcastReceiver_withPackageAndClass_returnsCorrectTimeoutRecord() {
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
TimeoutRecord record = TimeoutRecord.forBroadcastReceiver(intent,
|
||||
"com.example.app", "com.example.app.ExampleClass");
|
||||
|
||||
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"));
|
||||
intent.setComponent(new ComponentName("com.example.app", "com.example.app.ExampleClass"));
|
||||
|
||||
TimeoutRecord record = TimeoutRecord.forBroadcastReceiver(intent, 1000L);
|
||||
|
||||
@@ -63,7 +77,7 @@ public class TimeoutRecordTest {
|
||||
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");
|
||||
+ ".app/.ExampleClass }, waited 1000ms");
|
||||
assertTrue(record.mEndTakenBeforeLocks);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user