Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3bb8afe6 authored by Tim Murray's avatar Tim Murray
Browse files

Add infinitely deferred broadcasts.

- Adds a flag that prevents a broadcast from being delivered to a
  cached app until that app is no longer cached.

- As of this CL, this flag is only honored for unordered broadcasts to
  runtime receivers. Ordered broadcasts, alarm/interactive broadcasts,
  and manifest broadcasts are never deferred.

- This CL prevents the modern queue from cold starting processes for
  manifest receivers that would not be eligible to receive a
  particular broadcast, as in the case of USER_PRESENT.

- Unordered broadcasts with a completion callback (ie, resultTo
  broadcasts) are always marked as infinitely-deferred. resultTo
  broadcasts must still be dispatched only to runtime receivers or
  manifest receivers that disallow process starts in order to be
  deferred.

- This CL marks various BATTERY_CHANGED-esque broadcasts as infinitely
  deferred.

- This CL marks SCREEN_ON and SCREEN_OFF broadcasts as infinitely
  deferred.

- This CL marks USER_PRESENT broadcasts are infinitely deferred.

Test: atest BroadcastDeliveryGroupTest
Test: atest BroadcastQueueModernImplTest
Test: atest BroadcastQueueTest
Bug: 261065790
Bug: 263141882
Bug: 263143790

Change-Id: I9a356c8701d73f5687831fbe5b699e4485f07dd5
parent a9c38b7b
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -806,10 +806,12 @@ package android.app {
    method @Nullable public android.content.IntentFilter getDeliveryGroupMatchingFilter();
    method @Nullable public String getDeliveryGroupMatchingKey();
    method public int getDeliveryGroupPolicy();
    method public boolean isDeferUntilActive();
    method public boolean isPendingIntentBackgroundActivityLaunchAllowed();
    method public static android.app.BroadcastOptions makeBasic();
    method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long);
    method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
    method @NonNull public android.app.BroadcastOptions setDeferUntilActive(boolean);
    method @NonNull public android.app.BroadcastOptions setDeliveryGroupMatchingFilter(@NonNull android.content.IntentFilter);
    method @NonNull public android.app.BroadcastOptions setDeliveryGroupMatchingKey(@NonNull String, @NonNull String);
    method @NonNull public android.app.BroadcastOptions setDeliveryGroupPolicy(int);
+46 −0
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ public class BroadcastOptions extends ComponentOptions {
    private long mRequireCompatChangeId = CHANGE_INVALID;
    private boolean mRequireCompatChangeEnabled = true;
    private boolean mIsAlarmBroadcast = false;
    private boolean mIsDeferUntilActive = false;
    private long mIdForResponseEvent;
    private @Nullable IntentFilter mRemoveMatchingFilter;
    private @DeliveryGroupPolicy int mDeliveryGroupPolicy;
@@ -200,6 +201,12 @@ public class BroadcastOptions extends ComponentOptions {
    private static final String KEY_REMOVE_MATCHING_FILTER =
            "android:broadcast.removeMatchingFilter";

    /**
     * Corresponds to {@link #setDeferUntilActive(boolean)}.
     */
    private static final String KEY_DEFER_UNTIL_ACTIVE =
            "android:broadcast.deferuntilactive";

    /**
     * Corresponds to {@link #setDeliveryGroupPolicy(int)}.
     */
@@ -320,6 +327,7 @@ public class BroadcastOptions extends ComponentOptions {
                BundleMerger.class);
        mDeliveryGroupMatchingFilter = opts.getParcelable(KEY_DELIVERY_GROUP_MATCHING_FILTER,
                IntentFilter.class);
        mIsDeferUntilActive = opts.getBoolean(KEY_DEFER_UNTIL_ACTIVE, false);
    }

    /**
@@ -699,6 +707,41 @@ public class BroadcastOptions extends ComponentOptions {
        return mIdForResponseEvent;
    }

    /**
     * Sets whether the broadcast should not run until the process is in an active process state
     * (ie, a process exists for the app and the app is not in a cached process state).
     *
     * Whether an app's process state is considered active is independent of its standby bucket.
     *
     * A broadcast that is deferred until the process is active will not execute until the process
     * is brought to an active state by some other action, like a job, alarm, or service binding. As
     * a result, the broadcast may be delayed indefinitely. This deferral only applies to runtime
     * registered receivers of a broadcast. Any manifest receivers will run immediately, similar to
     * how a manifest receiver would start a new process in order to run a broadcast receiver.
     *
     * Ordered broadcasts, alarm broadcasts, interactive broadcasts, and manifest broadcasts are
     * never deferred.
     *
     * Unordered broadcasts and unordered broadcasts with completion callbacks may be
     * deferred. Completion callbacks for broadcasts deferred until active are
     * best-effort. Completion callbacks will run when all eligible processes have finished
     * executing the broadcast. Processes in inactive process states that defer the broadcast are
     * not considered eligible and may not execute the broadcast prior to the completion callback.
     *
     * @hide
     */
    @SystemApi
    public @NonNull BroadcastOptions setDeferUntilActive(boolean shouldDefer) {
        mIsDeferUntilActive = shouldDefer;
        return this;
    }

    /** @hide */
    @SystemApi
    public boolean isDeferUntilActive() {
        return mIsDeferUntilActive;
    }

    /**
     * When enqueuing this broadcast, remove all pending broadcasts previously
     * sent by this app which match the given filter.
@@ -963,6 +1006,9 @@ public class BroadcastOptions extends ComponentOptions {
        if (mDeliveryGroupMatchingFilter != null) {
            b.putParcelable(KEY_DELIVERY_GROUP_MATCHING_FILTER, mDeliveryGroupMatchingFilter);
        }
        if (mIsDeferUntilActive) {
            b.putBoolean(KEY_DEFER_UNTIL_ACTIVE, mIsDeferUntilActive);
        }
        return b.isEmpty() ? null : b;
    }
}
+11 −1
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.ActivityTaskManager;
import android.app.AlarmManager;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.app.WindowConfiguration;
@@ -388,6 +389,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
                    | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                    | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);

    private static final Bundle USER_PRESENT_INTENT_OPTIONS =
            BroadcastOptions.makeBasic()
                    .setDeferUntilActive(true)
                    .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
                    .toBundle();

    /**
     * {@link #setKeyguardEnabled} waits on this condition when it re-enables
     * the keyguard.
@@ -2300,7 +2307,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
                        Context.USER_SERVICE);
                mUiBgExecutor.execute(() -> {
                    for (int profileId : um.getProfileIdsWithDisabled(currentUser.getIdentifier())) {
                        mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, UserHandle.of(profileId));
                        mContext.sendBroadcastAsUser(USER_PRESENT_INTENT,
                                UserHandle.of(profileId),
                                null,
                                USER_PRESENT_INTENT_OPTIONS);
                    }
                    mLockPatternUtils.userPresent(currentUserId);
                });
+10 −5
Original line number Diff line number Diff line
@@ -189,15 +189,20 @@ public final class BatteryService extends SystemService {
    private long mLastBatteryLevelChangedSentMs;

    private Bundle mBatteryChangedOptions = BroadcastOptions.makeRemovingMatchingFilter(
            new IntentFilter(Intent.ACTION_BATTERY_CHANGED)).toBundle();
            new IntentFilter(Intent.ACTION_BATTERY_CHANGED)).setDeferUntilActive(true)
            .toBundle();
    private Bundle mPowerConnectedOptions = BroadcastOptions.makeRemovingMatchingFilter(
            new IntentFilter(Intent.ACTION_POWER_DISCONNECTED)).toBundle();
            new IntentFilter(Intent.ACTION_POWER_DISCONNECTED)).setDeferUntilActive(true)
            .toBundle();
    private Bundle mPowerDisconnectedOptions = BroadcastOptions.makeRemovingMatchingFilter(
            new IntentFilter(Intent.ACTION_POWER_CONNECTED)).toBundle();
            new IntentFilter(Intent.ACTION_POWER_CONNECTED)).setDeferUntilActive(true)
            .toBundle();
    private Bundle mBatteryLowOptions = BroadcastOptions.makeRemovingMatchingFilter(
            new IntentFilter(Intent.ACTION_BATTERY_OKAY)).toBundle();
            new IntentFilter(Intent.ACTION_BATTERY_OKAY)).setDeferUntilActive(true)
            .toBundle();
    private Bundle mBatteryOkayOptions = BroadcastOptions.makeRemovingMatchingFilter(
            new IntentFilter(Intent.ACTION_BATTERY_LOW)).toBundle();
            new IntentFilter(Intent.ACTION_BATTERY_LOW)).setDeferUntilActive(true)
            .toBundle();

    private MetricsLogger mMetricsLogger;

+22 −2
Original line number Diff line number Diff line
@@ -14108,9 +14108,17 @@ public class ActivityManagerService extends IActivityManager.Stub
        if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
                (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent
                + " ordered=" + ordered + " userid=" + userId);
        if ((resultTo != null) && !ordered && !mEnableModernQueue) {
        if ((resultTo != null) && !ordered) {
            if (!mEnableModernQueue) {
                Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
            }
            if (!UserHandle.isCore(callingUid)) {
                String msg = "Unauthorized unordered resultTo broadcast "
                             + intent + " sent from uid " + callingUid;
                Slog.w(TAG, msg);
                throw new SecurityException(msg);
            }
        }
        userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                ALLOW_NON_FULL, "broadcast", callerPackage);
@@ -14190,6 +14198,18 @@ public class ActivityManagerService extends IActivityManager.Stub
            }
        }
        // resultTo broadcasts are always infinitely deferrable.
        if ((resultTo != null) && !ordered && mEnableModernQueue) {
            if (brOptions == null) {
                brOptions = BroadcastOptions.makeBasic();
            }
            brOptions.setDeferUntilActive(true);
        }
        if (ordered && brOptions != null && brOptions.isDeferUntilActive()) {
            throw new IllegalArgumentException("Ordered broadcasts can't be deferred until active");
        }
        // Verify that protected broadcasts are only being sent by system code,
        // and that system code is only sending protected broadcasts.
        final boolean isProtectedBroadcast;
Loading