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

Commit c6dde4f8 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

BroadcastQueue: remove matching broadcasts.

There are common broadcasts that should remove any still-pending
broadcasts of a particular flavor.  For example, sending SCREEN_ON
should remove any still-pending SCREEN_OFF broadcasts.

This change adds a BroadcastOption to support this use-case, along
with tests to validate, and starts wiring up the option to a handful
of straightforward broadcasts.

For this to work effectively, we need to actually remove skipped
events from our per-process queues, which means we need a more
robust way of finishing ordered broadcasts.  This change factors
out the "finishing" of each broadcast to a local setDeliveryState()
method which can then be invoked when a broadcast is removed.

Bug: 245771249
Test: atest FrameworksMockingServicesTests:BroadcastQueueTest
Change-Id: I8c81893841026074ef78d88f9ecc82518164e50e
parent 125e3687
Loading
Loading
Loading
Loading
+8 −3
Original line number Diff line number Diff line
@@ -57,6 +57,8 @@ import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_R

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManagerInternal;
@@ -289,6 +291,7 @@ public class AlarmManagerService extends SystemService {
    final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
    IBinder.DeathRecipient mListenerDeathRecipient;
    Intent mTimeTickIntent;
    Bundle mTimeTickOptions;
    IAlarmListener mTimeTickTrigger;
    PendingIntent mDateChangeSender;
    boolean mInteractive = true;
@@ -1909,7 +1912,9 @@ public class AlarmManagerService extends SystemService {
                    Intent.FLAG_RECEIVER_REGISTERED_ONLY
                            | Intent.FLAG_RECEIVER_FOREGROUND
                            | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);

            mTimeTickOptions = BroadcastOptions
                    .makeRemovingMatchingFilter(new IntentFilter(Intent.ACTION_TIME_TICK))
                    .toBundle();
            mTimeTickTrigger = new IAlarmListener.Stub() {
                @Override
                public void doAlarm(final IAlarmCompleteListener callback) throws RemoteException {
@@ -1921,8 +1926,8 @@ public class AlarmManagerService extends SystemService {
                    // takes care of this automatically, but we're using the direct internal
                    // interface here rather than that client-side wrapper infrastructure.
                    mHandler.post(() -> {
                        getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL);

                        getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL, null,
                                mTimeTickOptions);
                        try {
                            callback.alarmComplete(this);
                        } catch (RemoteException e) { /* local method call */ }
+11 −2
Original line number Diff line number Diff line
@@ -4667,7 +4667,7 @@ public class ActivityManager {
     * @hide
     */
    public static void broadcastStickyIntent(Intent intent, int userId) {
        broadcastStickyIntent(intent, AppOpsManager.OP_NONE, userId);
        broadcastStickyIntent(intent, AppOpsManager.OP_NONE, null, userId);
    }

    /**
@@ -4676,11 +4676,20 @@ public class ActivityManager {
     * @hide
     */
    public static void broadcastStickyIntent(Intent intent, int appOp, int userId) {
        broadcastStickyIntent(intent, appOp, null, userId);
    }

    /**
     * Convenience for sending a sticky broadcast.  For internal use only.
     *
     * @hide
     */
    public static void broadcastStickyIntent(Intent intent, int appOp, Bundle options, int userId) {
        try {
            getService().broadcastIntentWithFeature(
                    null, null, intent, null, null, Activity.RESULT_OK, null, null,
                    null /*requiredPermissions*/, null /*excludedPermissions*/,
                    null /*excludedPackages*/, appOp, null, false, true, userId);
                    null /*excludedPackages*/, appOp, options, false, true, userId);
        } catch (RemoteException ex) {
        }
    }
+46 −0
Original line number Diff line number Diff line
@@ -27,12 +27,15 @@ import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledSince;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerExemptionManager;
import android.os.PowerExemptionManager.ReasonCode;
import android.os.PowerExemptionManager.TempAllowListType;

import java.util.Objects;

/**
 * Helper class for building an options Bundle that can be used with
 * {@link android.content.Context#sendBroadcast(android.content.Intent)
@@ -55,6 +58,7 @@ public class BroadcastOptions extends ComponentOptions {
    private boolean mRequireCompatChangeEnabled = true;
    private boolean mIsAlarmBroadcast = false;
    private long mIdForResponseEvent;
    private @Nullable IntentFilter mRemoveMatchingFilter;

    /**
     * Change ID which is invalid.
@@ -180,11 +184,25 @@ public class BroadcastOptions extends ComponentOptions {
    private static final String KEY_ID_FOR_RESPONSE_EVENT =
            "android:broadcast.idForResponseEvent";

    /**
     * Corresponds to {@link #setRemoveMatchingFilter}.
     */
    private static final String KEY_REMOVE_MATCHING_FILTER =
            "android:broadcast.removeMatchingFilter";

    public static BroadcastOptions makeBasic() {
        BroadcastOptions opts = new BroadcastOptions();
        return opts;
    }

    /** {@hide} */
    public static @NonNull BroadcastOptions makeRemovingMatchingFilter(
            @NonNull IntentFilter removeMatchingFilter) {
        BroadcastOptions opts = new BroadcastOptions();
        opts.setRemoveMatchingFilter(removeMatchingFilter);
        return opts;
    }

    private BroadcastOptions() {
        super();
        resetTemporaryAppAllowlist();
@@ -216,6 +234,8 @@ public class BroadcastOptions extends ComponentOptions {
        mRequireCompatChangeEnabled = opts.getBoolean(KEY_REQUIRE_COMPAT_CHANGE_ENABLED, true);
        mIdForResponseEvent = opts.getLong(KEY_ID_FOR_RESPONSE_EVENT);
        mIsAlarmBroadcast = opts.getBoolean(KEY_ALARM_BROADCAST, false);
        mRemoveMatchingFilter = opts.getParcelable(KEY_REMOVE_MATCHING_FILTER,
                IntentFilter.class);
    }

    /**
@@ -595,6 +615,29 @@ public class BroadcastOptions extends ComponentOptions {
        return mIdForResponseEvent;
    }

    /**
     * When enqueuing this broadcast, remove all pending broadcasts previously
     * sent by this app which match the given filter.
     * <p>
     * For example, sending {@link Intent#ACTION_SCREEN_ON} would typically want
     * to remove any pending {@link Intent#ACTION_SCREEN_OFF} broadcasts.
     *
     * @hide
     */
    public void setRemoveMatchingFilter(@NonNull IntentFilter removeMatchingFilter) {
        mRemoveMatchingFilter = Objects.requireNonNull(removeMatchingFilter);
    }

    /** @hide */
    public void clearRemoveMatchingFilter() {
        mRemoveMatchingFilter = null;
    }

    /** @hide */
    public @Nullable IntentFilter getRemoveMatchingFilter() {
        return mRemoveMatchingFilter;
    }

    /**
     * Returns the created options as a Bundle, which can be passed to
     * {@link android.content.Context#sendBroadcast(android.content.Intent)
@@ -640,6 +683,9 @@ public class BroadcastOptions extends ComponentOptions {
        if (mIdForResponseEvent != 0) {
            b.putLong(KEY_ID_FOR_RESPONSE_EVENT, mIdForResponseEvent);
        }
        if (mRemoveMatchingFilter != null) {
            b.putParcelable(KEY_REMOVE_MATCHING_FILTER, mRemoveMatchingFilter);
        }
        return b.isEmpty() ? null : b;
    }
}
+24 −5
Original line number Diff line number Diff line
@@ -22,9 +22,12 @@ import static com.android.server.health.Utils.copyV1Battery;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.hardware.health.HealthInfo;
import android.hardware.health.V2_1.BatteryCapacityLevel;
@@ -185,6 +188,17 @@ public final class BatteryService extends SystemService {
    private ArrayDeque<Bundle> mBatteryLevelsEventQueue;
    private long mLastBatteryLevelChangedSentMs;

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

    private MetricsLogger mMetricsLogger;

    public BatteryService(Context context) {
@@ -606,7 +620,8 @@ public final class BatteryService extends SystemService {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
                                mPowerConnectedOptions);
                    }
                });
            }
@@ -617,7 +632,8 @@ public final class BatteryService extends SystemService {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
                                mPowerDisconnectedOptions);
                    }
                });
            }
@@ -630,7 +646,8 @@ public final class BatteryService extends SystemService {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
                                mBatteryLowOptions);
                    }
                });
            } else if (mSentLowBatteryBroadcast &&
@@ -642,7 +659,8 @@ public final class BatteryService extends SystemService {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
                                mBatteryOkayOptions);
                    }
                });
            }
@@ -712,7 +730,8 @@ public final class BatteryService extends SystemService {
                    + ", info:" + mHealthInfo.toString());
        }

        mHandler.post(() -> ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL));
        mHandler.post(() -> ActivityManager.broadcastStickyIntent(intent, AppOpsManager.OP_NONE,
                mBatteryChangedOptions, UserHandle.USER_ALL));
    }

    private void sendBatteryLevelChangedIntentLocked() {
+2 −0
Original line number Diff line number Diff line
@@ -17354,6 +17354,8 @@ public class ActivityManagerService extends IActivityManager.Stub
                    bOptions.setTemporaryAppAllowlist(mInternal.getBootTimeTempAllowListDuration(),
                            TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
                            PowerExemptionManager.REASON_LOCALE_CHANGED, "");
                    bOptions.setRemoveMatchingFilter(
                            new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
                    broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
                            null, null, OP_NONE, bOptions.toBundle(), false, false, MY_PID,
                            SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(),
Loading