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

Commit 1fa865f3 authored by John Spurlock's avatar John Spurlock
Browse files

New NotificationListenerService listener flags api.

Give activated listeners the ability to request changes to
listener-level or host-level state.

Currently this consists of the ability to suppress notification alerts
(alerts = the haptic / audio feedback of a notification).

Bug:15888672
Change-Id: I045e3b99d1f15e3f96ebaf17d3083a97e02ecb42
parent 9f7c25eb
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -27226,14 +27226,19 @@ package android.service.notification {
    method public final void cancelNotification(java.lang.String);
    method public final void cancelNotifications(java.lang.String[]);
    method public android.service.notification.StatusBarNotification[] getActiveNotifications();
    method public final int getCurrentListenerFlags();
    method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
    method public android.os.IBinder onBind(android.content.Intent);
    method public void onListenerConnected();
    method public void onListenerFlagsChanged(int);
    method public void onNotificationPosted(android.service.notification.StatusBarNotification);
    method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
    method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap);
    method public void onNotificationRemoved(android.service.notification.StatusBarNotification);
    method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
    method public final void requestListenerFlags(int);
    field public static final int FLAG_DISABLE_HOST_ALERTS = 1; // 0x1
    field public static final int FLAG_NONE = 0; // 0x0
    field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
  }
+2 −0
Original line number Diff line number Diff line
@@ -59,6 +59,8 @@ interface INotificationManager
    void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);

    ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token);
    void requestFlagsFromListener(in INotificationListener token, int flags);
    int getFlagsFromListener(in INotificationListener token);

    ZenModeConfig getZenModeConfig();
    boolean setZenModeConfig(in ZenModeConfig config);
+1 −0
Original line number Diff line number Diff line
@@ -28,4 +28,5 @@ oneway interface INotificationListener
    void onNotificationRemoved(in StatusBarNotification notification,
            in NotificationRankingUpdate update);
    void onNotificationRankingUpdate(in NotificationRankingUpdate update);
    void onListenerFlagsChanged(int flags);
}
 No newline at end of file
+64 −0
Original line number Diff line number Diff line
@@ -53,6 +53,12 @@ public abstract class NotificationListenerService extends Service {
    private final String TAG = NotificationListenerService.class.getSimpleName()
            + "[" + getClass().getSimpleName() + "]";

    /** {@link #getCurrentListenerFlags() Listener flags} constant - default state. **/
    public static final int FLAG_NONE = 0;
    /** {@link #getCurrentListenerFlags() Listener flags} constant - the primary device UI
     * should disable notification sound, vibrating and other visual or aural effects. **/
    public static final int FLAG_DISABLE_HOST_ALERTS = 1;

    private INotificationListenerWrapper mWrapper = null;
    private RankingMap mRankingMap;

@@ -156,6 +162,16 @@ public abstract class NotificationListenerService extends Service {
        // optional
    }

    /**
     * Implement this method to be notified when the
     * {@link #getCurrentListenerFlags() listener flags} change.
     *
     * @param flags The current {@link #getCurrentListenerFlags() listener flags}.
     */
    public void onListenerFlagsChanged(int flags) {
        // optional
    }

    private final INotificationManager getNotificationInterface() {
        if (mNoMan == null) {
            mNoMan = INotificationManager.Stub.asInterface(
@@ -278,6 +294,46 @@ public abstract class NotificationListenerService extends Service {
        return null;
    }

    /**
     * Gets the set of flags representing current state.
     *
     * <p>
     * The current state may differ from the requested state if the flag represents state
     * shared across all listeners or a feature the notification host does not support or refuses
     * to grant.
     *
     * @return One or more of the FLAG_ constants.
     */
    public final int getCurrentListenerFlags() {
        if (!isBound()) return FLAG_NONE;
        try {
            return getNotificationInterface().getFlagsFromListener(mWrapper);
        } catch (android.os.RemoteException ex) {
            Log.v(TAG, "Unable to contact notification manager", ex);
            return FLAG_NONE;
        }
    }

    /**
     * Sets the desired {@link #getCurrentListenerFlags() listener flags}.
     *
     * <p>
     * This is merely a request, the host may or not choose to take action depending
     * on other listener requests or other global state.
     * <p>
     * Listen for updates using {@link #onListenerFlagsChanged(int)}.
     *
     * @param flags One or more of the FLAG_ constants.
     */
    public final void requestListenerFlags(int flags) {
        if (!isBound()) return;
        try {
            getNotificationInterface().requestFlagsFromListener(mWrapper, flags);
        } catch (android.os.RemoteException ex) {
            Log.v(TAG, "Unable to contact notification manager", ex);
        }
    }

    /**
     * Returns current ranking information.
     *
@@ -402,6 +458,14 @@ public abstract class NotificationListenerService extends Service {
                }
            }
        }
        @Override
        public void onListenerFlagsChanged(int flags) throws RemoteException {
            try {
                NotificationListenerService.this.onListenerFlagsChanged(flags);
            } catch (Throwable t) {
                Log.w(TAG, "Error running onListenerFlagsChanged", t);
            }
        }
    }

    private void applyUpdate(NotificationRankingUpdate update) {
+93 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.notification;

import static android.service.notification.NotificationListenerService.FLAG_DISABLE_HOST_ALERTS;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -71,6 +72,7 @@ import android.service.notification.ZenModeConfig;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
@@ -121,6 +123,7 @@ public class NotificationManagerService extends SystemService {
    static final int MESSAGE_RECONSIDER_RANKING = 4;
    static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
    static final int MESSAGE_SEND_RANKING_UPDATE = 6;
    static final int MESSAGE_LISTENER_FLAGS_CHANGED = 7;

    static final int LONG_DELAY = 3500; // 3.5 seconds
    static final int SHORT_DELAY = 2000; // 2 seconds
@@ -169,6 +172,9 @@ public class NotificationManagerService extends SystemService {
    NotificationRecord mSoundNotification;
    NotificationRecord mVibrateNotification;

    private final ArraySet<ManagedServiceInfo> mListenersDisablingAlerts = new ArraySet<>();
    private int mListenerFlags;  // right now, all flags are global

    // for enabling and disabling notification pulse behavior
    private boolean mScreenOn = true;
    private boolean mInCall = false;
@@ -463,7 +469,7 @@ public class NotificationManagerService extends SystemService {
        public void onSetDisabled(int status) {
            synchronized (mNotificationList) {
                mDisableNotificationAlerts = (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
                if (mDisableNotificationAlerts) {
                if (disableNotificationAlerts()) {
                    // cancel whatever's going on
                    long identity = Binder.clearCallingIdentity();
                    try {
@@ -904,6 +910,13 @@ public class NotificationManagerService extends SystemService {
        }
    }

    private void updateListenerFlagsLocked() {
        final int flags = mListenersDisablingAlerts.isEmpty() ? 0 : FLAG_DISABLE_HOST_ALERTS;
        if (flags == mListenerFlags) return;
        mListenerFlags = flags;
        scheduleListenerFlagsChanged(flags);
    }

    private final IBinder mService = new INotificationManager.Stub() {
        // Toasts
        // ============================================================================
@@ -1248,6 +1261,27 @@ public class NotificationManagerService extends SystemService {
            }
        }

        @Override
        public void requestFlagsFromListener(INotificationListener token, int flags) {
            synchronized (mNotificationList) {
                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                final boolean disableAlerts = (flags & FLAG_DISABLE_HOST_ALERTS) != 0;
                if (disableAlerts) {
                    mListenersDisablingAlerts.add(info);
                } else {
                    mListenersDisablingAlerts.remove(info);
                }
                updateListenerFlagsLocked();
            }
        }

        @Override
        public int getFlagsFromListener(INotificationListener token) {
            synchronized (mNotificationList) {
                return mListenerFlags;
            }
        }

        @Override
        public ZenModeConfig getZenModeConfig() {
            enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
@@ -1339,6 +1373,10 @@ public class NotificationManagerService extends SystemService {
        return keys.toArray(new String[keys.size()]);
    }

    private boolean disableNotificationAlerts() {
        return mDisableNotificationAlerts || (mListenerFlags & FLAG_DISABLE_HOST_ALERTS) != 0;
    }

    void dumpImpl(PrintWriter pw, DumpFilter filter) {
        pw.print("Current Notification Manager state");
        if (filter != null) {
@@ -1422,6 +1460,15 @@ public class NotificationManagerService extends SystemService {

                pw.println("\n  Notification listeners:");
                mListeners.dump(pw, filter);
                pw.print("    mListenerFlags: "); pw.println(mListenerFlags);
                pw.print("    mListenersDisablingAlerts: (");
                N = mListenersDisablingAlerts.size();
                for (int i = 0; i < N; i++) {
                    final ManagedServiceInfo listener = mListenersDisablingAlerts.valueAt(i);
                    if (i > 0) pw.print(',');
                    pw.print(listener.component);
                }
                pw.println(')');
            }

            pw.println("\n  Condition providers:");
@@ -1618,7 +1665,7 @@ public class NotificationManagerService extends SystemService {
        }

        // If we're not supposed to beep, vibrate, etc. then don't.
        if (!mDisableNotificationAlerts
        if (!disableNotificationAlerts()
                && (!(record.isUpdate
                    && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
                && (record.getUserId() == UserHandle.USER_ALL ||
@@ -1920,6 +1967,17 @@ public class NotificationManagerService extends SystemService {
        }
    }

    private void scheduleListenerFlagsChanged(int state) {
        mHandler.removeMessages(MESSAGE_LISTENER_FLAGS_CHANGED);
        mHandler.obtainMessage(MESSAGE_LISTENER_FLAGS_CHANGED, state, 0).sendToTarget();
    }

    private void handleListenerFlagsChanged(int state) {
        synchronized (mNotificationList) {
            mListeners.notifyListenerFlagsChangedLocked(state);
        }
    }

    private final class WorkerHandler extends Handler
    {
        @Override
@@ -1936,6 +1994,9 @@ public class NotificationManagerService extends SystemService {
                case MESSAGE_SEND_RANKING_UPDATE:
                    handleSendRankingUpdate();
                    break;
                case MESSAGE_LISTENER_FLAGS_CHANGED:
                    handleListenerFlagsChanged(msg.arg1);
                    break;
            }
        }

@@ -2445,6 +2506,13 @@ public class NotificationManagerService extends SystemService {
            }
        }

        @Override
        protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
            if (mListenersDisablingAlerts.remove(removed)) {
                updateListenerFlagsLocked();
            }
        }

        /**
         * asynchronously notify all listeners about a new notification
         */
@@ -2509,6 +2577,20 @@ public class NotificationManagerService extends SystemService {
            }
        }

        public void notifyListenerFlagsChangedLocked(final int flags) {
            for (final ManagedServiceInfo serviceInfo : mServices) {
                if (!serviceInfo.isEnabledForCurrentProfiles()) {
                    continue;
                }
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        notifyListenerFlagsChanged(serviceInfo, flags);
                    }
                });
            }
        }

        private void notifyPostedIfUserMatch(final ManagedServiceInfo info,
                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
            if (!info.enabledAndUserMatches(sbn.getUserId())) {
@@ -2544,6 +2626,15 @@ public class NotificationManagerService extends SystemService {
                Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
            }
        }

        private void notifyListenerFlagsChanged(ManagedServiceInfo info, int state) {
            final INotificationListener listener = (INotificationListener) info.service;
            try {
                listener.onListenerFlagsChanged(state);
            } catch (RemoteException ex) {
                Log.e(TAG, "unable to notify listener (listener flags): " + listener, ex);
            }
        }
    }

    public static final class DumpFilter {