Loading core/java/android/app/ApplicationExitInfo.java +12 −0 Original line number Diff line number Diff line Loading @@ -487,6 +487,15 @@ public final class ApplicationExitInfo implements Parcelable { */ public static final int SUBREASON_FREEZER_BINDER_ASYNC_FULL = 31; /** * The process was killed because it was sending too many broadcasts while it is in the * Cached state. This would be set only when the reason is {@link #REASON_OTHER}. * * For internal use only. * @hide */ public static final int SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED = 32; // If there is any OEM code which involves additional app kill reasons, it should // be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000. Loading Loading @@ -665,6 +674,7 @@ public final class ApplicationExitInfo implements Parcelable { SUBREASON_EXCESSIVE_BINDER_OBJECTS, SUBREASON_OOM_KILL, SUBREASON_FREEZER_BINDER_ASYNC_FULL, SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED, }) @Retention(RetentionPolicy.SOURCE) public @interface SubReason {} Loading Loading @@ -1396,6 +1406,8 @@ public final class ApplicationExitInfo implements Parcelable { return "OOM KILL"; case SUBREASON_FREEZER_BINDER_ASYNC_FULL: return "FREEZER BINDER ASYNC FULL"; case SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED: return "EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED"; default: return "UNKNOWN"; } Loading services/core/java/com/android/server/am/BroadcastConstants.java +1 −1 Original line number Diff line number Diff line Loading @@ -281,7 +281,7 @@ public class BroadcastConstants { * For {@link BroadcastQueueModernImpl}: Maximum number of outgoing broadcasts from a * freezable process that will be allowed before killing the process. */ public long MAX_FROZEN_OUTGOING_BROADCASTS = DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS; public int MAX_FROZEN_OUTGOING_BROADCASTS = DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS; private static final String KEY_MAX_FROZEN_OUTGOING_BROADCASTS = "max_frozen_outgoing_broadcasts"; private static final int DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS = 32; Loading services/core/java/com/android/server/am/BroadcastProcessQueue.java +4 −0 Original line number Diff line number Diff line Loading @@ -277,6 +277,10 @@ class BroadcastProcessQueue { mOutgoingBroadcasts.clear(); } public void clearOutgoingBroadcasts() { mOutgoingBroadcasts.clear(); } /** * Enqueue the given broadcast to be dispatched to this process at some * future point in time. The target receiver is indicated by the given index Loading services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +12 −3 Original line number Diff line number Diff line Loading @@ -166,7 +166,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { /** * Map from UID to per-process broadcast queues. If a UID hosts more than * one process, each additional process is stored as a linked list using * {@link BroadcastProcessQueue#next}. * {@link BroadcastProcessQueue#processNameNext}. * * @see #getProcessQueue * @see #getOrCreateProcessQueue Loading Loading @@ -661,6 +661,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue { final BroadcastProcessQueue queue = getProcessQueue(app); if (queue != null) { setQueueProcess(queue, app); // Outgoing broadcasts should be cleared when the process dies but there have been // issues due to AMS not always informing the BroadcastQueue of process deaths. // So, clear them when a new process starts as well. queue.clearOutgoingBroadcasts(); } boolean didSomething = false; Loading Loading @@ -730,6 +734,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue { demoteFromRunningLocked(queue); } queue.clearOutgoingBroadcasts(); // Skip any pending registered receivers, since the old process // would never be around to receive them boolean didSomething = queue.forEachMatchingBroadcast((r, i) -> { Loading Loading @@ -781,8 +787,11 @@ class BroadcastQueueModernImpl extends BroadcastQueue { final BroadcastProcessQueue queue = getOrCreateProcessQueue( r.callerApp.processName, r.callerApp.uid); if (queue.getOutgoingBroadcastCount() >= mConstants.MAX_FROZEN_OUTGOING_BROADCASTS) { // TODO: Kill the process if the outgoing broadcasts count is // beyond a certain limit. r.callerApp.killLocked("Too many outgoing broadcasts in cached state", ApplicationExitInfo.REASON_OTHER, ApplicationExitInfo.SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED, true /* noisy */); return; } queue.enqueueOutgoingBroadcast(r); mHistory.onBroadcastFrozenLocked(r); Loading services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +31 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; Loading @@ -57,6 +58,7 @@ import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.ApplicationExitInfo; import android.app.BackgroundStartPrivileges; import android.app.BroadcastOptions; import android.app.IApplicationThread; Loading Loading @@ -239,6 +241,7 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { mConstants.TIMEOUT = 200; mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0; mConstants.PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 500; mConstants.MAX_FROZEN_OUTGOING_BROADCASTS = 10; } @After Loading Loading @@ -2368,6 +2371,34 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { verifyScheduleReceiver(times(1), receiverYellowApp, timeTick); } @Test @RequiresFlagsEnabled(Flags.FLAG_DEFER_OUTGOING_BROADCASTS) public void testKillProcess_excessiveOutgoingBroadcastsWhileCached() throws Exception { final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); setProcessFreezable(callerApp, true /* pendingFreeze */, false /* frozen */); waitForIdle(); final int count = mConstants.MAX_FROZEN_OUTGOING_BROADCASTS + 1; for (int i = 0; i < count; ++i) { final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK + "_" + i); enqueueBroadcast(makeBroadcastRecord(timeTick, callerApp, List.of( makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE)))); } // Verify that we invoke the call to freeze the caller app. verify(mAms.mOomAdjuster.mCachedAppOptimizer, atLeastOnce()) .freezeAppAsyncImmediateLSP(callerApp); // Verify that the caller process is killed assertTrue(callerApp.isKilled()); verify(mProcessList).noteAppKill(same(callerApp), eq(ApplicationExitInfo.REASON_OTHER), eq(ApplicationExitInfo.SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED), any(String.class)); waitForIdle(); assertNull(mAms.getProcessRecordLocked(PACKAGE_BLUE, getUidForPackage(PACKAGE_BLUE))); } private long getReceiverScheduledTime(@NonNull BroadcastRecord r, @NonNull Object receiver) { for (int i = 0; i < r.receivers.size(); ++i) { if (isReceiverEquals(receiver, r.receivers.get(i))) { Loading Loading
core/java/android/app/ApplicationExitInfo.java +12 −0 Original line number Diff line number Diff line Loading @@ -487,6 +487,15 @@ public final class ApplicationExitInfo implements Parcelable { */ public static final int SUBREASON_FREEZER_BINDER_ASYNC_FULL = 31; /** * The process was killed because it was sending too many broadcasts while it is in the * Cached state. This would be set only when the reason is {@link #REASON_OTHER}. * * For internal use only. * @hide */ public static final int SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED = 32; // If there is any OEM code which involves additional app kill reasons, it should // be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000. Loading Loading @@ -665,6 +674,7 @@ public final class ApplicationExitInfo implements Parcelable { SUBREASON_EXCESSIVE_BINDER_OBJECTS, SUBREASON_OOM_KILL, SUBREASON_FREEZER_BINDER_ASYNC_FULL, SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED, }) @Retention(RetentionPolicy.SOURCE) public @interface SubReason {} Loading Loading @@ -1396,6 +1406,8 @@ public final class ApplicationExitInfo implements Parcelable { return "OOM KILL"; case SUBREASON_FREEZER_BINDER_ASYNC_FULL: return "FREEZER BINDER ASYNC FULL"; case SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED: return "EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED"; default: return "UNKNOWN"; } Loading
services/core/java/com/android/server/am/BroadcastConstants.java +1 −1 Original line number Diff line number Diff line Loading @@ -281,7 +281,7 @@ public class BroadcastConstants { * For {@link BroadcastQueueModernImpl}: Maximum number of outgoing broadcasts from a * freezable process that will be allowed before killing the process. */ public long MAX_FROZEN_OUTGOING_BROADCASTS = DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS; public int MAX_FROZEN_OUTGOING_BROADCASTS = DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS; private static final String KEY_MAX_FROZEN_OUTGOING_BROADCASTS = "max_frozen_outgoing_broadcasts"; private static final int DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS = 32; Loading
services/core/java/com/android/server/am/BroadcastProcessQueue.java +4 −0 Original line number Diff line number Diff line Loading @@ -277,6 +277,10 @@ class BroadcastProcessQueue { mOutgoingBroadcasts.clear(); } public void clearOutgoingBroadcasts() { mOutgoingBroadcasts.clear(); } /** * Enqueue the given broadcast to be dispatched to this process at some * future point in time. The target receiver is indicated by the given index Loading
services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +12 −3 Original line number Diff line number Diff line Loading @@ -166,7 +166,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { /** * Map from UID to per-process broadcast queues. If a UID hosts more than * one process, each additional process is stored as a linked list using * {@link BroadcastProcessQueue#next}. * {@link BroadcastProcessQueue#processNameNext}. * * @see #getProcessQueue * @see #getOrCreateProcessQueue Loading Loading @@ -661,6 +661,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue { final BroadcastProcessQueue queue = getProcessQueue(app); if (queue != null) { setQueueProcess(queue, app); // Outgoing broadcasts should be cleared when the process dies but there have been // issues due to AMS not always informing the BroadcastQueue of process deaths. // So, clear them when a new process starts as well. queue.clearOutgoingBroadcasts(); } boolean didSomething = false; Loading Loading @@ -730,6 +734,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue { demoteFromRunningLocked(queue); } queue.clearOutgoingBroadcasts(); // Skip any pending registered receivers, since the old process // would never be around to receive them boolean didSomething = queue.forEachMatchingBroadcast((r, i) -> { Loading Loading @@ -781,8 +787,11 @@ class BroadcastQueueModernImpl extends BroadcastQueue { final BroadcastProcessQueue queue = getOrCreateProcessQueue( r.callerApp.processName, r.callerApp.uid); if (queue.getOutgoingBroadcastCount() >= mConstants.MAX_FROZEN_OUTGOING_BROADCASTS) { // TODO: Kill the process if the outgoing broadcasts count is // beyond a certain limit. r.callerApp.killLocked("Too many outgoing broadcasts in cached state", ApplicationExitInfo.REASON_OTHER, ApplicationExitInfo.SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED, true /* noisy */); return; } queue.enqueueOutgoingBroadcast(r); mHistory.onBroadcastFrozenLocked(r); Loading
services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +31 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; Loading @@ -57,6 +58,7 @@ import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.ApplicationExitInfo; import android.app.BackgroundStartPrivileges; import android.app.BroadcastOptions; import android.app.IApplicationThread; Loading Loading @@ -239,6 +241,7 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { mConstants.TIMEOUT = 200; mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0; mConstants.PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 500; mConstants.MAX_FROZEN_OUTGOING_BROADCASTS = 10; } @After Loading Loading @@ -2368,6 +2371,34 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { verifyScheduleReceiver(times(1), receiverYellowApp, timeTick); } @Test @RequiresFlagsEnabled(Flags.FLAG_DEFER_OUTGOING_BROADCASTS) public void testKillProcess_excessiveOutgoingBroadcastsWhileCached() throws Exception { final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); setProcessFreezable(callerApp, true /* pendingFreeze */, false /* frozen */); waitForIdle(); final int count = mConstants.MAX_FROZEN_OUTGOING_BROADCASTS + 1; for (int i = 0; i < count; ++i) { final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK + "_" + i); enqueueBroadcast(makeBroadcastRecord(timeTick, callerApp, List.of( makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE)))); } // Verify that we invoke the call to freeze the caller app. verify(mAms.mOomAdjuster.mCachedAppOptimizer, atLeastOnce()) .freezeAppAsyncImmediateLSP(callerApp); // Verify that the caller process is killed assertTrue(callerApp.isKilled()); verify(mProcessList).noteAppKill(same(callerApp), eq(ApplicationExitInfo.REASON_OTHER), eq(ApplicationExitInfo.SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED), any(String.class)); waitForIdle(); assertNull(mAms.getProcessRecordLocked(PACKAGE_BLUE, getUidForPackage(PACKAGE_BLUE))); } private long getReceiverScheduledTime(@NonNull BroadcastRecord r, @NonNull Object receiver) { for (int i = 0; i < r.receivers.size(); ++i) { if (isReceiverEquals(receiver, r.receivers.get(i))) { Loading