Loading services/core/java/com/android/server/am/BroadcastController.java +3 −1 Original line number Diff line number Diff line Loading @@ -1961,7 +1961,9 @@ class BroadcastController { private void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) { mService.mProcessList.sendPackageBroadcastLocked(cmd, packages, userId); }private List<ResolveInfo> collectReceiverComponents( } private List<ResolveInfo> collectReceiverComponents( Intent intent, String resolvedType, int callingUid, int callingPid, int[] users, int[] broadcastAllowList) { // TODO: come back and remove this assumption to triage all broadcasts Loading services/core/java/com/android/server/am/BroadcastProcessedEventRecord.java 0 → 100644 +142 −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 com.android.server.am; import static com.android.internal.util.FrameworkStatsLog.BROADCAST_PROCESSED; import android.annotation.NonNull; import android.annotation.Nullable; import com.android.internal.util.FrameworkStatsLog; final class BroadcastProcessedEventRecord { /** * Minimum threshold for logging the broadcast processed event. */ private static final int MIN_THRESHOLD_FOR_LOGGING_TIME_MILLIS = 10; @Nullable private String mIntentAction; private int mSenderUid; private int mReceiverUid; private int mNumberOfReceivers; @NonNull private String mReceiverProcessName; private long mTotalBroadcastFinishTimeMillis; private long mMaxReceiverFinishTimeMillis = Long.MIN_VALUE; @NonNull private int[] mBroadcastTypes; @NonNull public BroadcastProcessedEventRecord setBroadcastTypes(@NonNull int[] broadcastTypes) { this.mBroadcastTypes = broadcastTypes; return this; } @NonNull public BroadcastProcessedEventRecord setReceiverProcessName( @NonNull String receiverProcessName) { mReceiverProcessName = receiverProcessName; return this; } @NonNull public BroadcastProcessedEventRecord setIntentAction(@Nullable String intentAction) { mIntentAction = intentAction; return this; } @NonNull public BroadcastProcessedEventRecord setSenderUid(int uid) { mSenderUid = uid; return this; } @NonNull public BroadcastProcessedEventRecord setReceiverUid(int uid) { mReceiverUid = uid; return this; } public void addReceiverFinishTime(long timeMillis) { mTotalBroadcastFinishTimeMillis += timeMillis; mMaxReceiverFinishTimeMillis = Math.max(mMaxReceiverFinishTimeMillis, timeMillis); mNumberOfReceivers++; } @Nullable String getIntentActionForTest() { return mIntentAction; } int getSenderUidForTest() { return mSenderUid; } int getReceiverUidForTest() { return mReceiverUid; } int getNumberOfReceiversForTest() { return mNumberOfReceivers; } @NonNull String getReceiverProcessNameForTest() { return mReceiverProcessName; } long getTotalBroadcastFinishTimeMillisForTest() { return mTotalBroadcastFinishTimeMillis; } long getMaxReceiverFinishTimeMillisForTest() { return mMaxReceiverFinishTimeMillis; } @NonNull int[] getBroadcastTypesForTest() { return mBroadcastTypes; } public void logToStatsD() { // We do not care about the processes where total time to process the // broadcast is less than 10ms/ are quick to process the broadcast. if (mTotalBroadcastFinishTimeMillis <= MIN_THRESHOLD_FOR_LOGGING_TIME_MILLIS) { return; } FrameworkStatsLog.write( BROADCAST_PROCESSED, mIntentAction, mSenderUid, mReceiverUid, mNumberOfReceivers, mReceiverProcessName, mTotalBroadcastFinishTimeMillis, mMaxReceiverFinishTimeMillis, mBroadcastTypes); } } services/core/java/com/android/server/am/BroadcastQueueImpl.java +6 −0 Original line number Diff line number Diff line Loading @@ -2189,6 +2189,11 @@ class BroadcastQueueImpl extends BroadcastQueue { logBroadcastDeliveryEventReported(queue, app, r, index, receiver); } if (!r.isAssumedDelivered(index) && r.wasDelivered(index)) { r.updateBroadcastProcessedEventRecord(receiver, r.terminalTime[index] - r.scheduledTime[index]); } final boolean recordFinished = (r.terminalCount == r.receivers.size()); if (recordFinished) { notifyFinishBroadcast(r); Loading Loading @@ -2254,6 +2259,7 @@ class BroadcastQueueImpl extends BroadcastQueue { mHistory.onBroadcastFinishedLocked(r); logBootCompletedBroadcastCompletionLatencyIfPossible(r); r.logBroadcastProcessedEventRecord(); if (r.intent.getComponent() == null && r.intent.getPackage() == null && (r.intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) { Loading services/core/java/com/android/server/am/BroadcastRecord.java +61 −0 Original line number Diff line number Diff line Loading @@ -167,6 +167,12 @@ final class BroadcastRecord extends Binder { @Nullable private ArrayMap<BroadcastRecord, Boolean> mMatchingRecordsCache; // Stores the {@link BroadcastProcessedEventRecord} for each process associated with this // record. @NonNull private ArrayMap<String, BroadcastProcessedEventRecord> mBroadcastProcessedRecords = new ArrayMap<>(); private @Nullable String mCachedToString; private @Nullable String mCachedToShortString; Loading Loading @@ -654,6 +660,17 @@ final class BroadcastRecord extends Binder { } } boolean wasDelivered(int index) { final int deliveryState = getDeliveryState(index); switch (deliveryState) { case DELIVERY_DELIVERED: case DELIVERY_TIMEOUT: return true; default: return false; } } void copyEnqueueTimeFrom(@NonNull BroadcastRecord replacedBroadcast) { originalEnqueueClockTime = enqueueClockTime; enqueueTime = replacedBroadcast.enqueueTime; Loading Loading @@ -1327,4 +1344,48 @@ final class BroadcastRecord extends Binder { proto.write(BroadcastRecordProto.INTENT_ACTION, intent.getAction()); proto.end(token); } /** * Uses the {@link BroadcastProcessedEventRecord} pojo to store the logging information related * to {@param receiver} object. */ public void updateBroadcastProcessedEventRecord(@NonNull Object receiver, long timeMillis) { if (!Flags.logBroadcastProcessedEvent()) { return; } final String receiverProcessName = getReceiverProcessName(receiver); BroadcastProcessedEventRecord broadcastProcessedEventRecord = mBroadcastProcessedRecords.get(receiverProcessName); if (broadcastProcessedEventRecord == null) { broadcastProcessedEventRecord = new BroadcastProcessedEventRecord() .setBroadcastTypes(calculateTypesForLogging()) .setIntentAction(intent.getAction()) .setReceiverProcessName(receiverProcessName) .setReceiverUid(getReceiverUid(receiver)) .setSenderUid(callingUid); mBroadcastProcessedRecords.put(receiverProcessName, broadcastProcessedEventRecord); } broadcastProcessedEventRecord.addReceiverFinishTime(timeMillis); } public void logBroadcastProcessedEventRecord() { if (!Flags.logBroadcastProcessedEvent()) { return; } int size = mBroadcastProcessedRecords.size(); for (int i = 0; i < size; i++) { mBroadcastProcessedRecords.valueAt(i).logToStatsD(); } mBroadcastProcessedRecords.clear(); } @VisibleForTesting @NonNull ArrayMap<String, BroadcastProcessedEventRecord> getBroadcastProcessedRecordsForTest() { return mBroadcastProcessedRecords; } } services/core/java/com/android/server/am/broadcasts_flags.aconfig +12 −1 Original line number Diff line number Diff line Loading @@ -27,3 +27,14 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "log_broadcast_processed_event" namespace: "backstage_power" description: "Log the broadcast processed event to Statsd" bug: "387576580" is_fixed_read_only: true metadata { purpose: PURPOSE_BUGFIX } } Loading
services/core/java/com/android/server/am/BroadcastController.java +3 −1 Original line number Diff line number Diff line Loading @@ -1961,7 +1961,9 @@ class BroadcastController { private void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) { mService.mProcessList.sendPackageBroadcastLocked(cmd, packages, userId); }private List<ResolveInfo> collectReceiverComponents( } private List<ResolveInfo> collectReceiverComponents( Intent intent, String resolvedType, int callingUid, int callingPid, int[] users, int[] broadcastAllowList) { // TODO: come back and remove this assumption to triage all broadcasts Loading
services/core/java/com/android/server/am/BroadcastProcessedEventRecord.java 0 → 100644 +142 −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 com.android.server.am; import static com.android.internal.util.FrameworkStatsLog.BROADCAST_PROCESSED; import android.annotation.NonNull; import android.annotation.Nullable; import com.android.internal.util.FrameworkStatsLog; final class BroadcastProcessedEventRecord { /** * Minimum threshold for logging the broadcast processed event. */ private static final int MIN_THRESHOLD_FOR_LOGGING_TIME_MILLIS = 10; @Nullable private String mIntentAction; private int mSenderUid; private int mReceiverUid; private int mNumberOfReceivers; @NonNull private String mReceiverProcessName; private long mTotalBroadcastFinishTimeMillis; private long mMaxReceiverFinishTimeMillis = Long.MIN_VALUE; @NonNull private int[] mBroadcastTypes; @NonNull public BroadcastProcessedEventRecord setBroadcastTypes(@NonNull int[] broadcastTypes) { this.mBroadcastTypes = broadcastTypes; return this; } @NonNull public BroadcastProcessedEventRecord setReceiverProcessName( @NonNull String receiverProcessName) { mReceiverProcessName = receiverProcessName; return this; } @NonNull public BroadcastProcessedEventRecord setIntentAction(@Nullable String intentAction) { mIntentAction = intentAction; return this; } @NonNull public BroadcastProcessedEventRecord setSenderUid(int uid) { mSenderUid = uid; return this; } @NonNull public BroadcastProcessedEventRecord setReceiverUid(int uid) { mReceiverUid = uid; return this; } public void addReceiverFinishTime(long timeMillis) { mTotalBroadcastFinishTimeMillis += timeMillis; mMaxReceiverFinishTimeMillis = Math.max(mMaxReceiverFinishTimeMillis, timeMillis); mNumberOfReceivers++; } @Nullable String getIntentActionForTest() { return mIntentAction; } int getSenderUidForTest() { return mSenderUid; } int getReceiverUidForTest() { return mReceiverUid; } int getNumberOfReceiversForTest() { return mNumberOfReceivers; } @NonNull String getReceiverProcessNameForTest() { return mReceiverProcessName; } long getTotalBroadcastFinishTimeMillisForTest() { return mTotalBroadcastFinishTimeMillis; } long getMaxReceiverFinishTimeMillisForTest() { return mMaxReceiverFinishTimeMillis; } @NonNull int[] getBroadcastTypesForTest() { return mBroadcastTypes; } public void logToStatsD() { // We do not care about the processes where total time to process the // broadcast is less than 10ms/ are quick to process the broadcast. if (mTotalBroadcastFinishTimeMillis <= MIN_THRESHOLD_FOR_LOGGING_TIME_MILLIS) { return; } FrameworkStatsLog.write( BROADCAST_PROCESSED, mIntentAction, mSenderUid, mReceiverUid, mNumberOfReceivers, mReceiverProcessName, mTotalBroadcastFinishTimeMillis, mMaxReceiverFinishTimeMillis, mBroadcastTypes); } }
services/core/java/com/android/server/am/BroadcastQueueImpl.java +6 −0 Original line number Diff line number Diff line Loading @@ -2189,6 +2189,11 @@ class BroadcastQueueImpl extends BroadcastQueue { logBroadcastDeliveryEventReported(queue, app, r, index, receiver); } if (!r.isAssumedDelivered(index) && r.wasDelivered(index)) { r.updateBroadcastProcessedEventRecord(receiver, r.terminalTime[index] - r.scheduledTime[index]); } final boolean recordFinished = (r.terminalCount == r.receivers.size()); if (recordFinished) { notifyFinishBroadcast(r); Loading Loading @@ -2254,6 +2259,7 @@ class BroadcastQueueImpl extends BroadcastQueue { mHistory.onBroadcastFinishedLocked(r); logBootCompletedBroadcastCompletionLatencyIfPossible(r); r.logBroadcastProcessedEventRecord(); if (r.intent.getComponent() == null && r.intent.getPackage() == null && (r.intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) { Loading
services/core/java/com/android/server/am/BroadcastRecord.java +61 −0 Original line number Diff line number Diff line Loading @@ -167,6 +167,12 @@ final class BroadcastRecord extends Binder { @Nullable private ArrayMap<BroadcastRecord, Boolean> mMatchingRecordsCache; // Stores the {@link BroadcastProcessedEventRecord} for each process associated with this // record. @NonNull private ArrayMap<String, BroadcastProcessedEventRecord> mBroadcastProcessedRecords = new ArrayMap<>(); private @Nullable String mCachedToString; private @Nullable String mCachedToShortString; Loading Loading @@ -654,6 +660,17 @@ final class BroadcastRecord extends Binder { } } boolean wasDelivered(int index) { final int deliveryState = getDeliveryState(index); switch (deliveryState) { case DELIVERY_DELIVERED: case DELIVERY_TIMEOUT: return true; default: return false; } } void copyEnqueueTimeFrom(@NonNull BroadcastRecord replacedBroadcast) { originalEnqueueClockTime = enqueueClockTime; enqueueTime = replacedBroadcast.enqueueTime; Loading Loading @@ -1327,4 +1344,48 @@ final class BroadcastRecord extends Binder { proto.write(BroadcastRecordProto.INTENT_ACTION, intent.getAction()); proto.end(token); } /** * Uses the {@link BroadcastProcessedEventRecord} pojo to store the logging information related * to {@param receiver} object. */ public void updateBroadcastProcessedEventRecord(@NonNull Object receiver, long timeMillis) { if (!Flags.logBroadcastProcessedEvent()) { return; } final String receiverProcessName = getReceiverProcessName(receiver); BroadcastProcessedEventRecord broadcastProcessedEventRecord = mBroadcastProcessedRecords.get(receiverProcessName); if (broadcastProcessedEventRecord == null) { broadcastProcessedEventRecord = new BroadcastProcessedEventRecord() .setBroadcastTypes(calculateTypesForLogging()) .setIntentAction(intent.getAction()) .setReceiverProcessName(receiverProcessName) .setReceiverUid(getReceiverUid(receiver)) .setSenderUid(callingUid); mBroadcastProcessedRecords.put(receiverProcessName, broadcastProcessedEventRecord); } broadcastProcessedEventRecord.addReceiverFinishTime(timeMillis); } public void logBroadcastProcessedEventRecord() { if (!Flags.logBroadcastProcessedEvent()) { return; } int size = mBroadcastProcessedRecords.size(); for (int i = 0; i < size; i++) { mBroadcastProcessedRecords.valueAt(i).logToStatsD(); } mBroadcastProcessedRecords.clear(); } @VisibleForTesting @NonNull ArrayMap<String, BroadcastProcessedEventRecord> getBroadcastProcessedRecordsForTest() { return mBroadcastProcessedRecords; } }
services/core/java/com/android/server/am/broadcasts_flags.aconfig +12 −1 Original line number Diff line number Diff line Loading @@ -27,3 +27,14 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "log_broadcast_processed_event" namespace: "backstage_power" description: "Log the broadcast processed event to Statsd" bug: "387576580" is_fixed_read_only: true metadata { purpose: PURPOSE_BUGFIX } }