NoListener: Factor out ZEN mode API
Bug: 17255109
Bug: 17295014
Change-Id: I7e1f6e29b8a23b8e59a8615a8012a86bd5dd22d7
diff --git a/api/current.txt b/api/current.txt
index 6fbb5ce..db3bc66 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27108,9 +27108,11 @@
method public final void cancelNotifications(java.lang.String[]);
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
method public android.service.notification.StatusBarNotification[] getActiveNotifications(java.lang.String[]);
+ method public final int getCurrentInterruptionFilter();
method public final int getCurrentListenerHints();
method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
method public android.os.IBinder onBind(android.content.Intent);
+ method public void onInterruptionFilterChanged(int);
method public void onListenerConnected();
method public void onListenerHintsChanged(int);
method public void onNotificationPosted(android.service.notification.StatusBarNotification);
@@ -27118,13 +27120,12 @@
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 requestInterruptionFilter(int);
method public final void requestListenerHints(int);
- field public static final int HINTS_NONE = 0; // 0x0
- field public static final int HINT_HOST_DISABLE_EFFECTS = 4; // 0x4
- field public static final int HINT_HOST_INTERRUPTION_LEVEL_ALL = 1; // 0x1
- field public static final int HINT_HOST_INTERRUPTION_LEVEL_NONE = 3; // 0x3
- field public static final int HINT_HOST_INTERRUPTION_LEVEL_PRIORITY = 2; // 0x2
- field public static final int HOST_INTERRUPTION_LEVEL_MASK = 3; // 0x3
+ field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
+ field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
+ field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
+ field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 07e9a94..214f50c 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -61,6 +61,8 @@
ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys);
void requestHintsFromListener(in INotificationListener token, int hints);
int getHintsFromListener(in INotificationListener token);
+ void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
+ int getInterruptionFilterFromListener(in INotificationListener token);
ComponentName getEffectsSuppressor();
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 93b2d3b..8ca9b6c 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -29,4 +29,5 @@
in NotificationRankingUpdate update);
void onNotificationRankingUpdate(in NotificationRankingUpdate update);
void onListenerHintsChanged(int hints);
-}
\ No newline at end of file
+ void onInterruptionFilterChanged(int interruptionFilter);
+}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 450b9a7..a544b2d 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -58,26 +58,28 @@
private final String TAG = NotificationListenerService.class.getSimpleName()
+ "[" + getClass().getSimpleName() + "]";
- /** {@link #getCurrentListenerHints() Listener hints} constant - default state. */
- public static final int HINTS_NONE = 0;
+ /**
+ * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
+ * Normal interruption filter.
+ */
+ public static final int INTERRUPTION_FILTER_ALL = 1;
- /** Bitmask range for {@link #getCurrentListenerHints() Listener hints} host interruption level
- * constants. */
- public static final int HOST_INTERRUPTION_LEVEL_MASK = 0x3;
+ /**
+ * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
+ * Priority interruption filter.
+ */
+ public static final int INTERRUPTION_FILTER_PRIORITY = 2;
- /** {@link #getCurrentListenerHints() Listener hints} constant - Normal interruption level. */
- public static final int HINT_HOST_INTERRUPTION_LEVEL_ALL = 1;
-
- /** {@link #getCurrentListenerHints() Listener hints} constant - Priority interruption level. */
- public static final int HINT_HOST_INTERRUPTION_LEVEL_PRIORITY = 2;
-
- /** {@link #getCurrentListenerHints() Listener hints} constant - No interruptions level. */
- public static final int HINT_HOST_INTERRUPTION_LEVEL_NONE = 3;
+ /**
+ * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
+ * No interruptions filter.
+ */
+ public static final int INTERRUPTION_FILTER_NONE = 3;
/** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI
* should disable notification sound, vibrating and other visual or aural effects.
- * This does not change the interruption level, only the effects. **/
- public static final int HINT_HOST_DISABLE_EFFECTS = 1 << 2;
+ * This does not change the interruption filter, only the effects. **/
+ public static final int HINT_HOST_DISABLE_EFFECTS = 1;
private INotificationListenerWrapper mWrapper = null;
private RankingMap mRankingMap;
@@ -197,6 +199,17 @@
// optional
}
+ /**
+ * Implement this method to be notified when the
+ * {@link #getCurrentInterruptionFilter() interruption filter} changed.
+ *
+ * @param interruptionFilter The current
+ * {@link #getCurrentInterruptionFilter() interruption filter}.
+ */
+ public void onInterruptionFilterChanged(int interruptionFilter) {
+ // optional
+ }
+
private final INotificationManager getNotificationInterface() {
if (mNoMan == null) {
mNoMan = INotificationManager.Stub.asInterface(
@@ -345,15 +358,42 @@
* shared across all listeners or a feature the notification host does not support or refuses
* to grant.
*
- * @return One or more of the HINT_ constants.
+ * @return Zero or more of the HINT_ constants.
*/
public final int getCurrentListenerHints() {
- if (!isBound()) return HINTS_NONE;
+ if (!isBound()) return 0;
try {
return getNotificationInterface().getHintsFromListener(mWrapper);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
- return HINTS_NONE;
+ return 0;
+ }
+ }
+
+ /**
+ * Gets the current notification interruption filter active on the host.
+ *
+ * <p>
+ * The interruption filter defines which notifications are allowed to interrupt the user
+ * (e.g. via sound & vibration) and is applied globally. Listeners can find out whether
+ * a specific notification matched the interruption filter via
+ * {@link Ranking#matchesInterruptionFilter()}.
+ * <p>
+ * The current filter may differ from the previously requested filter if the notification host
+ * does not support or refuses to apply the requested filter, or if another component changed
+ * the filter in the meantime.
+ * <p>
+ * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
+ *
+ * @return One of the INTERRUPTION_FILTER_ constants, or 0 on errors.
+ */
+ public final int getCurrentInterruptionFilter() {
+ if (!isBound()) return 0;
+ try {
+ return getNotificationInterface().getHintsFromListener(mWrapper);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ return 0;
}
}
@@ -361,7 +401,7 @@
* Sets the desired {@link #getCurrentListenerHints() listener hints}.
*
* <p>
- * This is merely a request, the host may or not choose to take action depending
+ * This is merely a request, the host may or may not choose to take action depending
* on other listener requests or other global state.
* <p>
* Listen for updates using {@link #onListenerHintsChanged(int)}.
@@ -378,6 +418,27 @@
}
/**
+ * Sets the desired {@link #getCurrentInterruptionFilter() interruption filter}.
+ *
+ * <p>
+ * This is merely a request, the host may or may not choose to apply the requested
+ * interruption filter depending on other listener requests or other global state.
+ * <p>
+ * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
+ *
+ * @param interruptionFilter One of the INTERRUPTION_FILTER_ constants.
+ */
+ public final void requestInterruptionFilter(int interruptionFilter) {
+ if (!isBound()) return;
+ try {
+ getNotificationInterface()
+ .requestInterruptionFilterFromListener(mWrapper, interruptionFilter);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
+ /**
* Returns current ranking information.
*
* <p>
@@ -514,6 +575,15 @@
Log.w(TAG, "Error running onListenerHintsChanged", t);
}
}
+
+ @Override
+ public void onInterruptionFilterChanged(int interruptionFilter) throws RemoteException {
+ try {
+ NotificationListenerService.this.onInterruptionFilterChanged(interruptionFilter);
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onInterruptionFilterChanged", t);
+ }
+ }
}
private void applyUpdate(NotificationRankingUpdate update) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 8c0d2c9..fc1b746 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -57,7 +57,6 @@
import android.os.IInterface;
import android.os.Looper;
import android.os.Message;
-import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -127,6 +126,7 @@
static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
static final int MESSAGE_SEND_RANKING_UPDATE = 6;
static final int MESSAGE_LISTENER_HINTS_CHANGED = 7;
+ static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8;
static final int LONG_DELAY = 3500; // 3.5 seconds
static final int SHORT_DELAY = 2000; // 2 seconds
@@ -178,6 +178,7 @@
private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>();
private ComponentName mEffectsSuppressor;
private int mListenerHints; // right now, all hints are global
+ private int mInterruptionFilter; // current ZEN mode as communicated to listeners
// for enabling and disabling notification pulse behavior
private boolean mScreenOn = true;
@@ -806,7 +807,7 @@
@Override
void onZenModeChanged() {
synchronized(mNotificationList) {
- updateListenerHintsLocked();
+ updateInterruptionFilterLocked();
}
}
});
@@ -938,8 +939,7 @@
}
private void updateListenerHintsLocked() {
- final int hints = (mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS) |
- mZenModeHelper.getZenModeListenerHint();
+ final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
if (hints == mListenerHints) return;
mListenerHints = hints;
scheduleListenerHintsChanged(hints);
@@ -954,6 +954,13 @@
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY));
}
+ private void updateInterruptionFilterLocked() {
+ int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
+ if (interruptionFilter == mInterruptionFilter) return;
+ mInterruptionFilter = interruptionFilter;
+ scheduleInterruptionFilterChanged(interruptionFilter);
+ }
+
private final IBinder mService = new INotificationManager.Stub() {
// Toasts
// ============================================================================
@@ -1318,7 +1325,6 @@
} else {
mListenersDisablingEffects.remove(info);
}
- mZenModeHelper.requestFromListener(hints);
updateListenerHintsLocked();
updateEffectsSuppressorLocked();
}
@@ -1335,6 +1341,29 @@
}
@Override
+ public void requestInterruptionFilterFromListener(INotificationListener token,
+ int interruptionFilter) throws RemoteException {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mNotificationList) {
+ mListeners.checkServiceTokenLocked(token);
+ mZenModeHelper.requestFromListener(interruptionFilter);
+ updateInterruptionFilterLocked();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public int getInterruptionFilterFromListener(INotificationListener token)
+ throws RemoteException {
+ synchronized (mNotificationLight) {
+ return mInterruptionFilter;
+ }
+ }
+
+ @Override
public ZenModeConfig getZenModeConfig() {
enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
return mZenModeHelper.getConfig();
@@ -2058,12 +2087,26 @@
mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
}
+ private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
+ mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
+ mHandler.obtainMessage(
+ MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
+ listenerInterruptionFilter,
+ 0).sendToTarget();
+ }
+
private void handleListenerHintsChanged(int hints) {
synchronized (mNotificationList) {
mListeners.notifyListenerHintsChangedLocked(hints);
}
}
+ private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
+ synchronized (mNotificationList) {
+ mListeners.notifyInterruptionFilterChanged(interruptionFilter);
+ }
+ }
+
private final class WorkerHandler extends Handler
{
@Override
@@ -2083,6 +2126,9 @@
case MESSAGE_LISTENER_HINTS_CHANGED:
handleListenerHintsChanged(msg.arg1);
break;
+ case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
+ handleListenerInterruptionFilterChanged(msg.arg1);
+ break;
}
}
@@ -2701,6 +2747,20 @@
}
}
+ public void notifyInterruptionFilterChanged(final int interruptionFilter) {
+ for (final ManagedServiceInfo serviceInfo : mServices) {
+ if (!serviceInfo.isEnabledForCurrentProfiles()) {
+ continue;
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
+ }
+ });
+ }
+ }
+
private void notifyPosted(final ManagedServiceInfo info,
final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
final INotificationListener listener = (INotificationListener)info.service;
@@ -2743,6 +2803,16 @@
}
}
+ private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
+ int interruptionFilter) {
+ final INotificationListener listener = (INotificationListener) info.service;
+ try {
+ listener.onInterruptionFilterChanged(interruptionFilter);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
+ }
+ }
+
private boolean isListenerPackage(String packageName) {
if (packageName == null) {
return false;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 0b93690..7a5336b 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -115,35 +115,35 @@
mAudioManager = audioManager;
}
- public int getZenModeListenerHint() {
- switch(mZenMode) {
+ public int getZenModeListenerInterruptionFilter() {
+ switch (mZenMode) {
case Global.ZEN_MODE_OFF:
- return NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_ALL;
+ return NotificationListenerService.INTERRUPTION_FILTER_ALL;
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
- return NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_PRIORITY;
+ return NotificationListenerService.INTERRUPTION_FILTER_PRIORITY;
case Global.ZEN_MODE_NO_INTERRUPTIONS:
- return NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_NONE;
+ return NotificationListenerService.INTERRUPTION_FILTER_NONE;
default:
return 0;
}
}
- private static int zenFromListenerHint(int hints, int defValue) {
- final int level = hints & NotificationListenerService.HOST_INTERRUPTION_LEVEL_MASK;
- switch(level) {
- case NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_ALL:
+ private static int zenModeFromListenerInterruptionFilter(int listenerInterruptionFilter,
+ int defValue) {
+ switch (listenerInterruptionFilter) {
+ case NotificationListenerService.INTERRUPTION_FILTER_ALL:
return Global.ZEN_MODE_OFF;
- case NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_PRIORITY:
+ case NotificationListenerService.INTERRUPTION_FILTER_PRIORITY:
return Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- case NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_NONE:
+ case NotificationListenerService.INTERRUPTION_FILTER_NONE:
return Global.ZEN_MODE_NO_INTERRUPTIONS;
default:
return defValue;
}
}
- public void requestFromListener(int hints) {
- final int newZen = zenFromListenerHint(hints, -1);
+ public void requestFromListener(int interruptionFilter) {
+ final int newZen = zenModeFromListenerInterruptionFilter(interruptionFilter, -1);
if (newZen != -1) {
setZenMode(newZen, "listener");
}