diff options
5 files changed, 99 insertions, 17 deletions
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 4db3727e3de2..0e459027102e 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -38,6 +38,7 @@ import android.os.BundleMerger; import android.os.PowerExemptionManager; import android.os.PowerExemptionManager.ReasonCode; import android.os.PowerExemptionManager.TempAllowListType; +import android.os.Process; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -76,6 +77,7 @@ public class BroadcastOptions extends ComponentOptions { FLAG_IS_ALARM_BROADCAST, FLAG_SHARE_IDENTITY, FLAG_INTERACTIVE, + FLAG_DEBUG_LOG, }) @Retention(RetentionPolicy.SOURCE) public @interface Flags {} @@ -86,6 +88,7 @@ public class BroadcastOptions extends ComponentOptions { private static final int FLAG_IS_ALARM_BROADCAST = 1 << 3; private static final int FLAG_SHARE_IDENTITY = 1 << 4; private static final int FLAG_INTERACTIVE = 1 << 5; + private static final int FLAG_DEBUG_LOG = 1 << 6; /** * Change ID which is invalid. @@ -1082,6 +1085,34 @@ public class BroadcastOptions extends ComponentOptions { } /** + * If enabled, additional debug messages for broadcast delivery will be logged. + * + * <p> This will only take effect when used by {@link Process#SHELL_UID} + * or {@link Process#ROOT_UID} or by apps under instrumentation. + * + * @hide + */ + @NonNull + public BroadcastOptions setDebugLogEnabled(boolean enabled) { + if (enabled) { + mFlags |= FLAG_DEBUG_LOG; + } else { + mFlags &= ~FLAG_DEBUG_LOG; + } + return this; + } + + /** + * @return if additional debug messages for broadcast delivery are enabled. + * + * @see #setDebugLogEnabled(boolean) + * @hide + */ + public boolean isDebugLogEnabled() { + return (mFlags & FLAG_DEBUG_LOG) != 0; + } + + /** * Returns the created options as a Bundle, which can be passed to * {@link android.content.Context#sendBroadcast(android.content.Intent) * Context.sendBroadcast(Intent)} and related methods. diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java index aa06b7ecf76c..7734ff3690a7 100644 --- a/services/core/java/com/android/server/am/BroadcastController.java +++ b/services/core/java/com/android/server/am/BroadcastController.java @@ -47,6 +47,8 @@ import static com.android.server.am.ActivityManagerService.UPDATE_HTTP_PROXY_MSG import static com.android.server.am.ActivityManagerService.UPDATE_TIME_PREFERENCE_MSG; import static com.android.server.am.ActivityManagerService.UPDATE_TIME_ZONE; import static com.android.server.am.ActivityManagerService.checkComponentPermission; +import static com.android.server.am.BroadcastRecord.debugLog; +import static com.android.server.am.BroadcastRecord.intentToString; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1017,6 +1019,13 @@ class BroadcastController { android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS, callingPid, callingUid, "recordResponseEventWhileInBackground"); } + + if (brOptions.isDebugLogEnabled()) { + if (!isShellOrRoot(callingUid) + && (callerApp == null || !callerApp.hasActiveInstrumentation())) { + brOptions.setDebugLogEnabled(false); + } + } } // Verify that protected broadcasts are only being sent by system code, @@ -1622,6 +1631,10 @@ class BroadcastController { } } while (ir < NR) { + // Instant Apps cannot use FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS + if (callerInstantApp) { + intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); + } if (receivers == null) { receivers = new ArrayList(); } @@ -1647,7 +1660,9 @@ class BroadcastController { callerAppProcessState, mService.mPlatformCompat); broadcastSentEventRecord.setBroadcastRecord(r); - if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r); + if (DEBUG_BROADCAST || r.debugLog()) { + Slog.v(TAG_BROADCAST, "Enqueueing broadcast " + r); + } queue.enqueueBroadcastLocked(r); } else { // There was nobody interested in the broadcast, but we still want to record @@ -1657,11 +1672,19 @@ class BroadcastController { // This was an implicit broadcast... let's record it for posterity. addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0); } + if (DEBUG_BROADCAST || debugLog(brOptions)) { + Slog.v(TAG_BROADCAST, "Skipping broadcast " + intentToString(intent) + + " due to no receivers"); + } } return ActivityManager.BROADCAST_SUCCESS; } + private boolean isShellOrRoot(int uid) { + return uid == SHELL_UID || uid == ROOT_UID; + } + @GuardedBy("mService") private void scheduleCanceledResultTo(ProcessRecord resultToApp, IIntentReceiver resultTo, Intent intent, int userId, BroadcastOptions options, int callingUid, diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index 9e4666cca140..a5f83a832c53 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -798,7 +798,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue { mService.mOomAdjuster.mCachedAppOptimizer.freezeAppAsyncImmediateLSP(r.callerApp); return; } - if (DEBUG_BROADCAST) logv("Enqueuing " + r + " for " + r.receivers.size() + " receivers"); + if (DEBUG_BROADCAST || r.debugLog()) { + logv("Enqueuing " + r + " for " + r.receivers.size() + " receivers"); + } final int cookie = traceBegin("enqueueBroadcast"); r.applySingletonPolicy(mService); @@ -1019,7 +1021,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue { & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0; long startTimeNs = SystemClock.uptimeNanos(); - if (DEBUG_BROADCAST) logv("Scheduling " + r + " to cold " + queue); + if (DEBUG_BROADCAST || r.debugLog()) { + logv("Scheduling " + r + " to cold " + queue); + } queue.app = mService.startProcessLocked(queue.processName, info, true, intentFlags, hostingRecord, zygotePolicyFlags, allowWhileBooting, false); if (queue.app == null) { @@ -1176,7 +1180,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue { } } - if (DEBUG_BROADCAST) logv("Scheduling " + r + " to warm " + app); + if (DEBUG_BROADCAST || r.debugLog()) { + logv("Scheduling " + r + " to warm " + app); + } setDeliveryState(queue, app, r, index, receiver, BroadcastRecord.DELIVERY_SCHEDULED, "scheduleReceiverWarmLocked"); @@ -1562,12 +1568,17 @@ class BroadcastQueueModernImpl extends BroadcastQueue { // bookkeeping to update for ordered broadcasts if (!isDeliveryStateTerminal(oldDeliveryState) && isDeliveryStateTerminal(newDeliveryState)) { - if (DEBUG_BROADCAST - && newDeliveryState != BroadcastRecord.DELIVERY_DELIVERED) { - logw("Delivery state of " + r + " to " + receiver + if ((DEBUG_BROADCAST && newDeliveryState != BroadcastRecord.DELIVERY_DELIVERED) + || r.debugLog()) { + final String msg = "Delivery state of " + r + " to " + receiver + " via " + app + " changed from " + deliveryStateToString(oldDeliveryState) + " to " - + deliveryStateToString(newDeliveryState) + " because " + reason); + + deliveryStateToString(newDeliveryState) + " because " + reason; + if (newDeliveryState == BroadcastRecord.DELIVERY_DELIVERED) { + logv(msg); + } else { + logw(msg); + } } notifyFinishReceiver(queue, app, r, index, receiver); diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 8d0805d3fa13..c1b0a76b24af 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -1285,31 +1285,43 @@ final class BroadcastRecord extends Binder { } @Override + @NonNull public String toString() { if (mCachedToString == null) { - String label = intent.getAction(); - if (label == null) { - label = intent.toString(); - } mCachedToString = "BroadcastRecord{" + toShortString() + "}"; } return mCachedToString; } + @NonNull public String toShortString() { if (mCachedToShortString == null) { - String label = intent.getAction(); - if (label == null) { - label = intent.toString(); - } + final String label = intentToString(intent); mCachedToShortString = Integer.toHexString(System.identityHashCode(this)) + " " + label + "/u" + userId; } return mCachedToShortString; } + @NonNull + public static String intentToString(@NonNull Intent intent) { + String label = intent.getAction(); + if (label == null) { + label = intent.toString(); + } + return label; + } + + public boolean debugLog() { + return debugLog(options); + } + + public static boolean debugLog(@Nullable BroadcastOptions options) { + return options != null && options.isDebugLogEnabled(); + } + @NeverCompile - public void dumpDebug(ProtoOutputStream proto, long fieldId) { + public void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) { long token = proto.start(fieldId); proto.write(BroadcastRecordProto.USER_ID, userId); proto.write(BroadcastRecordProto.INTENT_ACTION, intent.getAction()); diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 49149e1fa415..1503d889c298 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -1005,6 +1005,11 @@ class ProcessRecord implements WindowProcessListener { } @GuardedBy(anyOf = {"mService", "mProcLock"}) + boolean hasActiveInstrumentation() { + return mInstr != null; + } + + @GuardedBy(anyOf = {"mService", "mProcLock"}) boolean isKilledByAm() { return mKilledByAm; } |