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

Commit ee076eda authored by Alexander Roederer's avatar Alexander Roederer Committed by Android (Google) Code Review
Browse files

Merge changes from topic "b230652175-LifetimeExtension-keep" into main

* changes:
  Use NotifManagerService to handle lifetime ext
  Add new Notif flag on DirectReply and SmartReply
parents 3fc09fe3 9cb05c2d
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -12191,6 +12191,7 @@ package android.service.notification {
    method public boolean hasExpanded();
    method public boolean hasInteracted();
    method public boolean hasSeen();
    method @FlaggedApi("android.app.lifetime_extension_refactor") public boolean hasSmartReplied();
    method public boolean hasSnoozed();
    method public boolean hasViewedSettings();
    method public void setDirectReplied();
@@ -12198,6 +12199,7 @@ package android.service.notification {
    method public void setDismissalSurface(int);
    method public void setExpanded();
    method public void setSeen();
    method @FlaggedApi("android.app.lifetime_extension_refactor") public void setSmartReplied();
    method public void setSnoozed();
    method public void setViewedSettings();
    method public void writeToParcel(android.os.Parcel, int);
+10 −0
Original line number Diff line number Diff line
@@ -745,6 +745,16 @@ public class Notification implements Parcelable
    @TestApi
    public static final int FLAG_USER_INITIATED_JOB = 0x00008000;
    /**
     * Bit to be bitwise-ored into the {@link #flags} field that should be
     * set if this notification has been lifetime extended due to a direct reply.
     *
     * This flag is for internal use only; applications cannot set this flag directly.
     * @hide
     */
    @FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
    public static final int FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY = 0x00010000;
    private static final List<Class<? extends Style>> PLATFORM_STYLE_CLASSES = Arrays.asList(
            BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
            DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
+35 −0
Original line number Diff line number Diff line
@@ -15,10 +15,12 @@
 */
package android.service.notification;

import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Flags;
import android.app.RemoteInput;
import android.os.Parcel;
import android.os.Parcelable;
@@ -36,6 +38,7 @@ public final class NotificationStats implements Parcelable {
    private boolean mSeen;
    private boolean mExpanded;
    private boolean mDirectReplied;
    private boolean mSmartReplied;
    private boolean mSnoozed;
    private boolean mViewedSettings;
    private boolean mInteracted;
@@ -125,6 +128,9 @@ public final class NotificationStats implements Parcelable {
        mSeen = in.readByte() != 0;
        mExpanded = in.readByte() != 0;
        mDirectReplied = in.readByte() != 0;
        if (Flags.lifetimeExtensionRefactor()) {
            mSmartReplied = in.readByte() != 0;
        }
        mSnoozed = in.readByte() != 0;
        mViewedSettings = in.readByte() != 0;
        mInteracted = in.readByte() != 0;
@@ -137,6 +143,9 @@ public final class NotificationStats implements Parcelable {
        dest.writeByte((byte) (mSeen ? 1 : 0));
        dest.writeByte((byte) (mExpanded ? 1 : 0));
        dest.writeByte((byte) (mDirectReplied ? 1 : 0));
        if (Flags.lifetimeExtensionRefactor()) {
            dest.writeByte((byte) (mSmartReplied ? 1 : 0));
        }
        dest.writeByte((byte) (mSnoozed ? 1 : 0));
        dest.writeByte((byte) (mViewedSettings ? 1 : 0));
        dest.writeByte((byte) (mInteracted ? 1 : 0));
@@ -209,6 +218,23 @@ public final class NotificationStats implements Parcelable {
        mInteracted = true;
    }

    /**
     * Returns whether the user has replied to a notification that has a smart reply at least once.
     */
    @FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
    public boolean hasSmartReplied() {
        return mSmartReplied;
    }

    /**
     * Records that the user has replied to a notification that has a smart reply at least once.
     */
    @FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
    public void setSmartReplied() {
        mSmartReplied = true;
        mInteracted = true;
    }

    /**
     * Returns whether the user has snoozed this notification at least once.
     */
@@ -286,6 +312,9 @@ public final class NotificationStats implements Parcelable {
        if (mSeen != that.mSeen) return false;
        if (mExpanded != that.mExpanded) return false;
        if (mDirectReplied != that.mDirectReplied) return false;
        if (Flags.lifetimeExtensionRefactor()) {
            if (mSmartReplied != that.mSmartReplied) return false;
        }
        if (mSnoozed != that.mSnoozed) return false;
        if (mViewedSettings != that.mViewedSettings) return false;
        if (mInteracted != that.mInteracted) return false;
@@ -297,6 +326,9 @@ public final class NotificationStats implements Parcelable {
        int result = (mSeen ? 1 : 0);
        result = 31 * result + (mExpanded ? 1 : 0);
        result = 31 * result + (mDirectReplied ? 1 : 0);
        if (Flags.lifetimeExtensionRefactor()) {
            result = 31 * result + (mSmartReplied ? 1 : 0);
        }
        result = 31 * result + (mSnoozed ? 1 : 0);
        result = 31 * result + (mViewedSettings ? 1 : 0);
        result = 31 * result + (mInteracted ? 1 : 0);
@@ -311,6 +343,9 @@ public final class NotificationStats implements Parcelable {
        sb.append("mSeen=").append(mSeen);
        sb.append(", mExpanded=").append(mExpanded);
        sb.append(", mDirectReplied=").append(mDirectReplied);
        if (Flags.lifetimeExtensionRefactor()) {
            sb.append(", mSmartReplied=").append(mSmartReplied);
        }
        sb.append(", mSnoozed=").append(mSnoozed);
        sb.append(", mViewedSettings=").append(mViewedSettings);
        sb.append(", mInteracted=").append(mInteracted);
+66 −20
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED;
import static android.app.Notification.FLAG_INSISTENT;
import static android.app.Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_NO_DISMISS;
import static android.app.Notification.FLAG_ONGOING_EVENT;
@@ -58,6 +59,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
import static android.app.Flags.lifetimeExtensionRefactor;
import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
@@ -1224,7 +1226,7 @@ public class NotificationManagerService extends SystemService {
        public void onClearAll(int callingUid, int callingPid, int userId) {
            synchronized (mNotificationLock) {
                cancelAllLocked(callingUid, callingPid, userId, REASON_CANCEL_ALL, null,
                        /*includeCurrentProfiles*/ true);
                        /*includeCurrentProfiles*/ true, FLAG_ONGOING_EVENT | FLAG_NO_CLEAR);
            }
        }
@@ -1498,6 +1500,7 @@ public class NotificationManagerService extends SystemService {
            synchronized (mNotificationLock) {
                NotificationRecord r = mNotificationsByKey.get(key);
                if (r != null) {
                    r.recordSmartReplied();
                    LogMaker logMaker = r.getLogMaker()
                            .setCategory(MetricsEvent.SMART_REPLY_ACTION)
                            .setSubtype(replyIndex)
@@ -1804,7 +1807,17 @@ public class NotificationManagerService extends SystemService {
                    record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
                }
                if (record != null) {
                    cancelNotification(record.getSbn().getUid(), record.getSbn().getInitialPid(),
                    if (lifetimeExtensionRefactor()) {
                        cancelNotification(record.getSbn().getUid(),
                                record.getSbn().getInitialPid(),
                                record.getSbn().getPackageName(), record.getSbn().getTag(),
                                record.getSbn().getId(), 0,
                                FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB
                                        | FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY,
                                true, record.getUserId(), REASON_TIMEOUT, null);
                    } else {
                        cancelNotification(record.getSbn().getUid(),
                                record.getSbn().getInitialPid(),
                                record.getSbn().getPackageName(), record.getSbn().getTag(),
                                record.getSbn().getId(), 0,
                                FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB,
@@ -1812,6 +1825,7 @@ public class NotificationManagerService extends SystemService {
                    }
                }
            }
        }
    };
    private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
@@ -3728,8 +3742,16 @@ public class NotificationManagerService extends SystemService {
        @Override
        public void cancelNotificationWithTag(String pkg, String opPkg, String tag, int id,
                int userId) {
            // Don't allow client applications to cancel foreground service notifs, user-initiated
            // job notifs, autobundled summaries, or notifs that have been replied to.
            int mustNotHaveFlags = isCallingUidSystem() ? 0 :
                    (FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB | FLAG_AUTOGROUP_SUMMARY);
            if (lifetimeExtensionRefactor()) {
                mustNotHaveFlags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
            }
            cancelNotificationInternal(pkg, opPkg, Binder.getCallingUid(), Binder.getCallingPid(),
                    tag, id, userId);
                    tag, id, userId, mustNotHaveFlags);
        }
        @Override
@@ -3740,10 +3762,17 @@ public class NotificationManagerService extends SystemService {
                    Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
            // Don't allow the app to cancel active FGS or UIJ notifications
            if (lifetimeExtensionRefactor()) {
                cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
                        pkg, null, 0, FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB
                                | FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY,
                        userId, REASON_APP_CANCEL_ALL);
            } else {
                cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
                        pkg, null, 0, FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB,
                        userId, REASON_APP_CANCEL_ALL);
            }
        }
        @Override
        public void silenceNotificationSound() {
@@ -4808,8 +4837,16 @@ public class NotificationManagerService extends SystemService {
                                    r.getSbn().getId(), userId, reason);
                        }
                    } else {
                        if (lifetimeExtensionRefactor()) {
                            cancelAllLocked(callingUid, callingPid, info.userid,
                                REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
                                    REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles(),
                                    FLAG_ONGOING_EVENT | FLAG_NO_CLEAR
                                            | FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY);
                        } else {
                            cancelAllLocked(callingUid, callingPid, info.userid,
                                    REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles(),
                                    FLAG_ONGOING_EVENT | FLAG_NO_CLEAR);
                        }
                    }
                }
            } finally {
@@ -4923,6 +4960,9 @@ public class NotificationManagerService extends SystemService {
                int callingUid, int callingPid, String pkg, String tag, int id, int userId,
                int reason) {
            int mustNotHaveFlags = FLAG_ONGOING_EVENT;
            if (lifetimeExtensionRefactor()) {
                mustNotHaveFlags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
            }
            cancelNotification(callingUid, callingPid, pkg, tag, id, 0 /* mustHaveFlags */,
                    mustNotHaveFlags,
                    true,
@@ -6712,7 +6752,12 @@ public class NotificationManagerService extends SystemService {
        @Override
        public void cancelNotification(String pkg, String opPkg, int callingUid, int callingPid,
                String tag, int id, int userId) {
            cancelNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, userId);
            // Don't allow client applications to cancel foreground service notifs,
            // user-initiated job notifs or autobundled summaries.
            final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
                    (FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB | FLAG_AUTOGROUP_SUMMARY);
            cancelNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, userId,
                    mustNotHaveFlags);
        }
        @Override
@@ -6907,7 +6952,7 @@ public class NotificationManagerService extends SystemService {
    }
    void cancelNotificationInternal(String pkg, String opPkg, int callingUid, int callingPid,
            String tag, int id, int userId) {
            String tag, int id, int userId, int mustNotHaveFlags) {
        userId = ActivityManager.handleIncomingUser(callingPid,
                callingUid, userId, true, false, "cancelNotificationWithTag", pkg);
@@ -6935,10 +6980,6 @@ public class NotificationManagerService extends SystemService {
            }
        }
        // Don't allow client applications to cancel foreground service notifs, user-initiated job
        // notifs or autobundled summaries.
        final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
                (FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB | FLAG_AUTOGROUP_SUMMARY);
        cancelNotification(uid, callingPid, pkg, tag, id, 0,
                mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
    }
@@ -7294,6 +7335,11 @@ public class NotificationManagerService extends SystemService {
        notification.flags &= ~FLAG_FSI_REQUESTED_BUT_DENIED;
        // Apps should not create notifications that are lifetime extended.
        if (lifetimeExtensionRefactor()) {
            notification.flags &= ~FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
        }
        if (notification.fullScreenIntent != null) {
            final AttributionSource attributionSource =
                    new AttributionSource.Builder(notificationUid).setPackageName(pkg).build();
@@ -10088,7 +10134,7 @@ public class NotificationManagerService extends SystemService {
    @GuardedBy("mNotificationLock")
    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
            ManagedServiceInfo listener, boolean includeCurrentProfiles, int mustNotHaveFlags) {
        final long cancellationElapsedTimeMs = SystemClock.elapsedRealtime();
        mHandler.post(new Runnable() {
            @Override
@@ -10100,7 +10146,7 @@ public class NotificationManagerService extends SystemService {
                            null, userId, 0, 0, reason, listenerName);
                    FlagChecker flagChecker = (int flags) -> {
                        int flagsToCheck = FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
                        int flagsToCheck = mustNotHaveFlags;
                        if (REASON_LISTENER_CANCEL_ALL == reason
                                || REASON_CANCEL_ALL == reason) {
                            flagsToCheck |= FLAG_BUBBLE;
+19 −0
Original line number Diff line number Diff line
@@ -24,7 +24,9 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;

import android.annotation.FlaggedApi;
import android.annotation.Nullable;
import android.app.Flags;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -1257,10 +1259,27 @@ public final class NotificationRecord {
        mStats.setExpanded();
    }

    /** Run when the notification is direct replied. */
    public void recordDirectReplied() {
        if (Flags.lifetimeExtensionRefactor()) {
            // Mark the NotificationRecord as lifetime extended.
            Notification notification = getSbn().getNotification();
            notification.flags |= Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
        }

        mStats.setDirectReplied();
    }


    /** Run when the notification is smart replied. */
    @FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
    public void recordSmartReplied() {
        Notification notification = getSbn().getNotification();
        notification.flags |= Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;

        mStats.setSmartReplied();
    }

    public void recordDismissalSurface(@NotificationStats.DismissalSurface int surface) {
        mStats.setDismissalSurface(surface);
    }
Loading