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

Commit c02fc886 authored by Christopher Tate's avatar Christopher Tate Committed by Chris Tate
Browse files

Make FGS notification deferral control tri-state

Apps can now request either immediate visibility or deferral explicitly,
versus the previous iteration's immediate-or-default only.

Bug: 179290175
Test: ApiDemos foreground service exercise
Test: atest CtsAppTestCases:ServiceTest
Test: atest CtsAppTestCases:NotificationManagerTest
Change-Id: I0b4d11a7483d2407758c810cf4a77a2e45bb737f
parent e3e4c1f9
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -5714,6 +5714,9 @@ package android.app {
    field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
    field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
    field @Deprecated public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
    field public static final int FOREGROUND_SERVICE_DEFAULT = 0; // 0x0
    field public static final int FOREGROUND_SERVICE_DEFERRED = 2; // 0x2
    field public static final int FOREGROUND_SERVICE_IMMEDIATE = 1; // 0x1
    field public static final int GROUP_ALERT_ALL = 0; // 0x0
    field public static final int GROUP_ALERT_CHILDREN = 2; // 0x2
    field public static final int GROUP_ALERT_SUMMARY = 1; // 0x1
@@ -5917,6 +5920,7 @@ package android.app {
    method @NonNull public android.app.Notification.Builder setDeleteIntent(android.app.PendingIntent);
    method @NonNull public android.app.Notification.Builder setExtras(android.os.Bundle);
    method @NonNull public android.app.Notification.Builder setFlag(int, boolean);
    method @NonNull public android.app.Notification.Builder setForegroundServiceBehavior(int);
    method @NonNull public android.app.Notification.Builder setFullScreenIntent(android.app.PendingIntent, boolean);
    method @NonNull public android.app.Notification.Builder setGroup(String);
    method @NonNull public android.app.Notification.Builder setGroupAlertBehavior(int);
@@ -5935,7 +5939,7 @@ package android.app {
    method @NonNull public android.app.Notification.Builder setRemoteInputHistory(CharSequence[]);
    method @NonNull public android.app.Notification.Builder setSettingsText(CharSequence);
    method @NonNull public android.app.Notification.Builder setShortcutId(String);
    method @NonNull public android.app.Notification.Builder setShowForegroundImmediately(boolean);
    method @Deprecated @NonNull public android.app.Notification.Builder setShowForegroundImmediately(boolean);
    method @NonNull public android.app.Notification.Builder setShowWhen(boolean);
    method @NonNull public android.app.Notification.Builder setSmallIcon(@DrawableRes int);
    method @NonNull public android.app.Notification.Builder setSmallIcon(@DrawableRes int, int);
+4 −0
Original line number Diff line number Diff line
@@ -270,6 +270,10 @@ package android.app {
    method public abstract void onHomeVisibilityChanged(boolean);
  }

  public class Notification implements android.os.Parcelable {
    method public boolean shouldShowForegroundImmediately();
  }

  public final class NotificationChannel implements android.os.Parcelable {
    method public int getOriginalImportance();
    method public boolean isBlockable();
+122 −17
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.StringRes;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
@@ -130,6 +131,52 @@ public class Notification implements Parcelable
{
    private static final String TAG = "Notification";

    /**
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({
            FOREGROUND_SERVICE_DEFAULT,
            FOREGROUND_SERVICE_IMMEDIATE,
            FOREGROUND_SERVICE_DEFERRED
    })
    public @interface ServiceNotificationPolicy {};

    /**
     * If the Notification associated with starting a foreground service has been
     * built using setForegroundServiceBehavior() with this behavior, display of
     * the notification will usually be suppressed for a short time to avoid visual
     * disturbances to the user.
     * @see Notification.Builder#setForegroundServiceBehavior(int)
     * @see #FOREGROUND_SERVICE_IMMEDIATE
     * @see #FOREGROUND_SERVICE_DEFERRED
     */
    public static final @ServiceNotificationPolicy int FOREGROUND_SERVICE_DEFAULT = 0;

    /**
     * If the Notification associated with starting a foreground service has been
     * built using setForegroundServiceBehavior() with this behavior, display of
     * the notification will be immediate even if the default behavior would be
     * to defer visibility for a short time.
     * @see Notification.Builder#setForegroundServiceBehavior(int)
     * @see #FOREGROUND_SERVICE_DEFAULT
     * @see #FOREGROUND_SERVICE_DEFERRED
     */
    public static final @ServiceNotificationPolicy int FOREGROUND_SERVICE_IMMEDIATE = 1;

    /**
     * If the Notification associated with starting a foreground service has been
     * built using setForegroundServiceBehavior() with this behavior, display of
     * the notification will usually be suppressed for a short time to avoid visual
     * disturbances to the user.
     * @see Notification.Builder#setForegroundServiceBehavior(int)
     * @see #FOREGROUND_SERVICE_DEFAULT
     * @see #FOREGROUND_SERVICE_IMMEDIATE
     */
    public static final @ServiceNotificationPolicy int FOREGROUND_SERVICE_DEFERRED = 2;

    private int mFgsDeferBehavior;

    /**
     * An activity that provides a user interface for adjusting notification preferences for its
     * containing application.
@@ -645,11 +692,6 @@ public class Notification implements Parcelable
     */
    public static final int FLAG_BUBBLE = 0x00001000;

    /**
     * @hide
     */
    public static final int FLAG_IMMEDIATE_FGS_DISPLAY = 0x00002000;

    private static final List<Class<? extends Style>> PLATFORM_STYLE_CLASSES = Arrays.asList(
            BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
            DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
@@ -659,8 +701,7 @@ public class Notification implements Parcelable
    @IntDef(flag = true, prefix = { "FLAG_" }, value = {FLAG_SHOW_LIGHTS, FLAG_ONGOING_EVENT,
            FLAG_INSISTENT, FLAG_ONLY_ALERT_ONCE,
            FLAG_AUTO_CANCEL, FLAG_NO_CLEAR, FLAG_FOREGROUND_SERVICE, FLAG_HIGH_PRIORITY,
            FLAG_LOCAL_ONLY, FLAG_GROUP_SUMMARY, FLAG_AUTOGROUP_SUMMARY, FLAG_BUBBLE,
            FLAG_IMMEDIATE_FGS_DISPLAY})
            FLAG_LOCAL_ONLY, FLAG_GROUP_SUMMARY, FLAG_AUTOGROUP_SUMMARY, FLAG_BUBBLE})
    @Retention(RetentionPolicy.SOURCE)
    public @interface NotificationFlags{};

@@ -2550,6 +2591,8 @@ public class Notification implements Parcelable
        }

        mAllowSystemGeneratedContextualActions = parcel.readBoolean();

        mFgsDeferBehavior = parcel.readInt();
    }

    @Override
@@ -2665,6 +2708,7 @@ public class Notification implements Parcelable
        that.mBadgeIcon = this.mBadgeIcon;
        that.mSettingsText = this.mSettingsText;
        that.mGroupAlertBehavior = this.mGroupAlertBehavior;
        that.mFgsDeferBehavior = this.mFgsDeferBehavior;
        that.mBubbleMetadata = this.mBubbleMetadata;
        that.mAllowSystemGeneratedContextualActions = this.mAllowSystemGeneratedContextualActions;

@@ -3064,6 +3108,8 @@ public class Notification implements Parcelable

        parcel.writeBoolean(mAllowSystemGeneratedContextualActions);

        parcel.writeInt(mFgsDeferBehavior);

        // mUsesStandardHeader is not written because it should be recomputed in listeners
    }

@@ -4532,10 +4578,40 @@ public class Notification implements Parcelable
         * foreground service is shown as soon as the service's {@code startForeground()}
         * method is called, even if the system's UI policy might otherwise defer
         * its visibility to a later time.
         * @deprecated Use setForegroundServiceBehavior(int) instead
         */
        @Deprecated
        @NonNull
        public Builder setShowForegroundImmediately(boolean showImmediately) {
            setFlag(FLAG_IMMEDIATE_FGS_DISPLAY, showImmediately);
            setForegroundServiceBehavior(showImmediately
                    ? FOREGROUND_SERVICE_IMMEDIATE
                    : FOREGROUND_SERVICE_DEFAULT);
            return this;
        }

        /**
         * Specify a desired visibility policy for a Notification associated with a
         * foreground service.  By default, the system can choose to defer
         * visibility of the notification for a short time after the service is
         * started.  Pass
         * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE BEHAVIOR_IMMEDIATE_DISPLAY}
         * to this method in order to guarantee that visibility is never deferred.  Pass
         * {@link Notification#FOREGROUND_SERVICE_DEFERRED BEHAVIOR_DEFERRED_DISPLAY}
         * to request that visibility is deferred whenever possible.
         *
         * <p class="note">Note that deferred visibility is not guaranteed.  There
         * may be some circumstances under which the system will show the foreground
         * service's associated Notification immediately even when the app has used
         * this method to explicitly request deferred display.</p>
         * @param behavior One of
         * {@link Notification#FOREGROUND_SERVICE_DEFAULT BEHAVIOR_DEFAULT},
         * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE BEHAVIOR_IMMEDIATE_DISPLAY},
         * or {@link Notification#FOREGROUND_SERVICE_DEFERRED BEHAVIOR_DEFERRED_DISPLAY}
         * @return
         */
        @NonNull
        public Builder setForegroundServiceBehavior(int behavior) {
            mN.mFgsDeferBehavior = behavior;
            return this;
        }

@@ -6717,30 +6793,59 @@ public class Notification implements Parcelable
     * immediately when tied to a foreground service, even if the system might generally
     * avoid showing the notifications for short-lived foreground service lifetimes.
     *
     * Immediate visibility of the Notification is recommended when:
     * Immediate visibility of the Notification is indicated when:
     * <ul>
     *     <li>The app specifically indicated it with
     *         {@link Notification.Builder#setShowForegroundImmediately(boolean)
     *         setShowForegroundImmediately(true)}</li>
     *         {@link Notification.Builder#setForegroundServiceBehavior(int)
     *         setForegroundServiceBehavior(BEHAVIOR_IMMEDIATE_DISPLAY)}</li>
     *     <li>It is a media notification or has an associated media session</li>
     *     <li>It is a call or navigation notification</li>
     *     <li>It provides additional action affordances</li>
     * </ul>
     * @return whether this notification should always be displayed immediately when
     *
     * If the app has specified
     * {@code NotificationBuilder.setForegroundServiceBehavior(BEHAVIOR_DEFERRED_DISPLAY)}
     * then this method will return {@code false} and notification visibility will be
     * deferred following the service's transition to the foreground state even in the
     * circumstances described above.
     *
     * @return whether this notification should be displayed immediately when
     * its associated service transitions to the foreground state
     * @hide
     */
    @TestApi
    public boolean shouldShowForegroundImmediately() {
        if ((flags & Notification.FLAG_IMMEDIATE_FGS_DISPLAY) != 0
                || isMediaNotification() || hasMediaSession()
        // Has the app demanded immediate display?
        if (mFgsDeferBehavior == FOREGROUND_SERVICE_IMMEDIATE) {
            return true;
        }

        // Has the app demanded deferred display?
        if (mFgsDeferBehavior == FOREGROUND_SERVICE_DEFERRED) {
            return false;
        }

        // We show these sorts of notifications immediately in the absence of
        // any explicit app declaration
        if (isMediaNotification() || hasMediaSession()
                    || CATEGORY_CALL.equals(category)
                    || CATEGORY_NAVIGATION.equals(category)
                    || (actions != null && actions.length > 0)) {
            return true;
        }

        // No extenuating circumstances: defer visibility
        return false;
    }

    /**
     * Has forced deferral for FGS purposes been specified?
     * @hide
     */
    public boolean isForegroundDisplayForceDeferred() {
        return FOREGROUND_SERVICE_DEFERRED == mFgsDeferBehavior;
    }

    /**
     * @return whether this notification has a media session attached
     * @hide
+1 −1
Original line number Diff line number Diff line
@@ -300,7 +300,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
                .setColorized(true)
                .setColor(getResources().getColor(R.color.GM2_red_700))
                .setOngoing(true)
                .setShowForegroundImmediately(true)
                .setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE)
                .setContentIntent(
                        PendingIntent.getService(this, REQUEST_CODE, stopIntent,
                                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
+24 −14
Original line number Diff line number Diff line
@@ -1949,14 +1949,19 @@ public final class ActiveServices {
                    && isLegacyApp;
        }
        if (!showNow) {
            // has the app forced deferral?
            if (!r.foregroundNoti.isForegroundDisplayForceDeferred()) {
                // is the notification such that it should show right away?
                showNow = r.foregroundNoti.shouldShowForegroundImmediately();
                if (DEBUG_FOREGROUND_SERVICE && showNow) {
                    Slog.d(TAG_SERVICE, "FGS " + r
                            + " notification policy says show immediately");
                }
                // or is this an type of FGS that always shows immediately?
                if (!showNow) {
                    switch (r.foregroundServiceType) {
                        case ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK:
                        case ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL:
                    case ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE:
                        case ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION:
                            if (DEBUG_FOREGROUND_SERVICE) {
                                Slog.d(TAG_SERVICE, "FGS " + r
@@ -1965,6 +1970,11 @@ public final class ActiveServices {
                            showNow = true;
                    }
                }
            } else {
                if (DEBUG_FOREGROUND_SERVICE) {
                    Slog.d(TAG_SERVICE, "FGS " + r + " notification is app deferred");
                }
            }
        }

        if (showNow) {