diff options
8 files changed, 130 insertions, 9 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 83963fd2141d..2d6eaf1d33eb 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -5305,6 +5305,38 @@ public class ActivityManager { } /** + * Delays delivering broadcasts to the specified package. + * + * <p> When {@code delayedDurationMs} is {@code 0}, it will clears any previously + * set forced delays. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.DUMP) + public void forceDelayBroadcastDelivery(@NonNull String targetPackage, + @IntRange(from = 0) long delayedDurationMs) { + try { + getService().forceDelayBroadcastDelivery(targetPackage, delayedDurationMs); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Checks if the "modern" broadcast queue is enabled. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.DUMP) + public boolean isModernBroadcastQueueEnabled() { + try { + return getService().isModernBroadcastQueueEnabled(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * @return The reason code of whether or not the given UID should be exempted from background * restrictions here. * diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 040111c78531..938f1f66a7d2 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -771,6 +771,14 @@ interface IActivityManager { /** Blocks until all broadcast queues become idle. */ void waitForBroadcastIdle(); + /** Delays delivering broadcasts to the specified package. */ + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.DUMP)") + void forceDelayBroadcastDelivery(in String targetPackage, long delayedDurationMs); + + /** Checks if the modern broadcast queue is enabled. */ + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.DUMP)") + boolean isModernBroadcastQueueEnabled(); + /** * @return The reason code of whether or not the given UID should be exempted from background * restrictions here. diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 699a622efe55..091782a3f838 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -18333,6 +18333,25 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public void forceDelayBroadcastDelivery(@NonNull String targetPackage, + long delayedDurationMs) { + Objects.requireNonNull(targetPackage); + Preconditions.checkArgumentNonnegative(delayedDurationMs); + Preconditions.checkState(mEnableModernQueue, "Not valid in legacy queue"); + enforceCallingPermission(permission.DUMP, "forceDelayBroadcastDelivery()"); + + for (BroadcastQueue queue : mBroadcastQueues) { + queue.forceDelayBroadcastDelivery(targetPackage, delayedDurationMs); + } + } + + @Override + public boolean isModernBroadcastQueueEnabled() { + enforceCallingPermission(permission.DUMP, "isModernBroadcastQueueEnabled()"); + return mEnableModernQueue; + } + + @Override @ReasonCode public int getBackgroundRestrictionExemptionReason(int uid) { enforceCallingPermission(android.Manifest.permission.DEVICE_POWER, diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java index 15d2fa334dcf..ec055fcb50ec 100644 --- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java +++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java @@ -183,6 +183,11 @@ class BroadcastProcessQueue { private String mCachedToString; private String mCachedToShortString; + /** + * The duration by which any broadcasts to this process need to be delayed + */ + private long mForcedDelayedDurationMs; + public BroadcastProcessQueue(@NonNull BroadcastConstants constants, @NonNull String processName, int uid) { this.constants = Objects.requireNonNull(constants); @@ -275,7 +280,7 @@ class BroadcastProcessQueue { */ @FunctionalInterface public interface BroadcastPredicate { - public boolean test(@NonNull BroadcastRecord r, int index); + boolean test(@NonNull BroadcastRecord r, int index); } /** @@ -284,7 +289,7 @@ class BroadcastProcessQueue { */ @FunctionalInterface public interface BroadcastConsumer { - public void accept(@NonNull BroadcastRecord r, int index); + void accept(@NonNull BroadcastRecord r, int index); } /** @@ -418,6 +423,13 @@ class BroadcastProcessQueue { } /** + * Get package name of the first application loaded into this process. + */ + public String getPackageName() { + return app.getApplicationInfo().packageName; + } + + /** * Set the currently active broadcast to the next pending broadcast. */ public void makeActiveNextPending() { @@ -555,6 +567,10 @@ class BroadcastProcessQueue { return mActive != null; } + void forceDelayBroadcastDelivery(long delayedDurationMs) { + mForcedDelayedDurationMs = delayedDurationMs; + } + /** * Will thrown an exception if there are no pending broadcasts; relies on * {@link #isEmpty()} being false. @@ -729,6 +745,7 @@ class BroadcastProcessQueue { static final int REASON_BLOCKED = 4; static final int REASON_INSTRUMENTED = 5; static final int REASON_PERSISTENT = 6; + static final int REASON_FORCE_DELAYED = 7; static final int REASON_CONTAINS_FOREGROUND = 10; static final int REASON_CONTAINS_ORDERED = 11; static final int REASON_CONTAINS_ALARM = 12; @@ -746,6 +763,7 @@ class BroadcastProcessQueue { REASON_BLOCKED, REASON_INSTRUMENTED, REASON_PERSISTENT, + REASON_FORCE_DELAYED, REASON_CONTAINS_FOREGROUND, REASON_CONTAINS_ORDERED, REASON_CONTAINS_ALARM, @@ -767,6 +785,7 @@ class BroadcastProcessQueue { case REASON_BLOCKED: return "BLOCKED"; case REASON_INSTRUMENTED: return "INSTRUMENTED"; case REASON_PERSISTENT: return "PERSISTENT"; + case REASON_FORCE_DELAYED: return "FORCE_DELAYED"; case REASON_CONTAINS_FOREGROUND: return "CONTAINS_FOREGROUND"; case REASON_CONTAINS_ORDERED: return "CONTAINS_ORDERED"; case REASON_CONTAINS_ALARM: return "CONTAINS_ALARM"; @@ -809,7 +828,10 @@ class BroadcastProcessQueue { return; } - if (mCountForeground > 0) { + if (mForcedDelayedDurationMs > 0) { + mRunnableAt = runnableAt + mForcedDelayedDurationMs; + mRunnableAtReason = REASON_FORCE_DELAYED; + } else if (mCountForeground > 0) { mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS; mRunnableAtReason = REASON_CONTAINS_FOREGROUND; } else if (mCountInteractive > 0) { diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 153ad1ec1b52..75e93366bce4 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -233,6 +233,16 @@ public abstract class BroadcastQueue { public abstract void waitForBarrier(@Nullable PrintWriter pw); /** + * Delays delivering broadcasts to the specified package. + * + * <p> Note that this is only valid for modern queue. + */ + public void forceDelayBroadcastDelivery(@NonNull String targetPackage, + long delayedDurationMs) { + // No default implementation. + } + + /** * Brief summary of internal state, useful for debugging purposes. */ @GuardedBy("mService") diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index 6ba1b1fd13c5..affa0cd15ea2 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -1225,12 +1225,16 @@ class BroadcastQueueModernImpl extends BroadcastQueue { return didSomething; } - private void forEachQueue(@NonNull Consumer<BroadcastProcessQueue> consumer) { - for (int i = 0; i < mProcessQueues.size(); ++i) { + private void forEachMatchingQueue( + @NonNull Predicate<BroadcastProcessQueue> queuePredicate, + @NonNull Consumer<BroadcastProcessQueue> queueConsumer) { + for (int i = 0; i < mProcessQueues.size(); i++) { BroadcastProcessQueue leaf = mProcessQueues.valueAt(i); while (leaf != null) { - consumer.accept(leaf); - updateRunnableList(leaf); + if (queuePredicate.test(leaf)) { + queueConsumer.accept(leaf); + updateRunnableList(leaf); + } leaf = leaf.processNameNext; } } @@ -1294,7 +1298,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue { final CountDownLatch latch = new CountDownLatch(1); synchronized (mService) { mWaitingFor.add(Pair.create(condition, latch)); - forEachQueue(q -> q.setPrioritizeEarliest(true)); + forEachMatchingQueue(QUEUE_PREDICATE_ANY, + (q) -> q.setPrioritizeEarliest(true)); } enqueueUpdateRunningList(); try { @@ -1304,13 +1309,24 @@ class BroadcastQueueModernImpl extends BroadcastQueue { } finally { synchronized (mService) { if (mWaitingFor.isEmpty()) { - forEachQueue(q -> q.setPrioritizeEarliest(false)); + forEachMatchingQueue(QUEUE_PREDICATE_ANY, + (q) -> q.setPrioritizeEarliest(false)); } } } } @Override + public void forceDelayBroadcastDelivery(@NonNull String targetPackage, + long delayedDurationMs) { + synchronized (mService) { + forEachMatchingQueue( + (q) -> targetPackage.equals(q.getPackageName()), + (q) -> q.forceDelayBroadcastDelivery(delayedDurationMs)); + } + } + + @Override public String describeStateLocked() { return getRunningSize() + " running"; } diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 4706c26889de..33d7f9d04185 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -1016,6 +1016,11 @@ class ProcessRecord implements WindowProcessListener { return mWindowProcessController.hasRecentTasks(); } + @GuardedBy("mService") + public ApplicationInfo getApplicationInfo() { + return info; + } + @GuardedBy({"mService", "mProcLock"}) boolean onCleanupApplicationRecordLSP(ProcessStatsService processStats, boolean allowRestart, boolean unlinkDeath) { diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING index 2a693634087b..4e1d1ca0ac66 100644 --- a/services/core/java/com/android/server/am/TEST_MAPPING +++ b/services/core/java/com/android/server/am/TEST_MAPPING @@ -97,6 +97,15 @@ { "include-filter": "com.android.server.am.BroadcastQueueTest" }, { "include-filter": "com.android.server.am.BroadcastQueueModernImplTest" } ] + }, + { + "file_patterns": ["Broadcast"], + "name": "CtsBroadcastTestCases", + "options": [ + { "exclude-annotation": "androidx.test.filters.LargeTest" }, + { "exclude-annotation": "androidx.test.filters.FlakyTest" }, + { "exclude-annotation": "org.junit.Ignore" } + ] } ], "presubmit-large": [ |