summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Spurlock <jspurlock@google.com>2014-03-24 18:02:32 -0400
committerJohn Spurlock <jspurlock@google.com>2014-03-28 08:47:01 -0400
commita4294297d46cc0b9f45897bc688c267502cce3ef (patch)
tree72f74c582f814c1a587f1949a4f50949962d1e5f
parentba6ae57f64e09fd9240640b6a0dd538e5f349bf1 (diff)
downloadframeworks_base-a4294297d46cc0b9f45897bc688c267502cce3ef.zip
frameworks_base-a4294297d46cc0b9f45897bc688c267502cce3ef.tar.gz
frameworks_base-a4294297d46cc0b9f45897bc688c267502cce3ef.tar.bz2
Enhance the NotificationListenerService api.
Add to NotificationListenerService: - getActiveNotificationKeys() : String[] - getActiveNotifications(String[] keys) : StatusBarNotification[] - cancelNotifications(String[] keys) : void - onListenerConnected(String[] keys) : void Add to StatusBarNotification: - getKey() : String Bug:13562828 Change-Id: Idd9605dad8f942d68b4b32ab9625faf6c8642db9
-rw-r--r--api/current.txt5
-rw-r--r--core/java/android/app/INotificationManager.aidl5
-rw-r--r--core/java/android/service/notification/INotificationListener.aidl1
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java68
-rw-r--r--core/java/android/service/notification/StatusBarNotification.java19
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java114
6 files changed, 180 insertions, 32 deletions
diff --git a/api/current.txt b/api/current.txt
index faeab39..190397b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24317,8 +24317,12 @@ package android.service.notification {
ctor public NotificationListenerService();
method public final void cancelAllNotifications();
method public final void cancelNotification(java.lang.String, java.lang.String, int);
+ method public final void cancelNotifications(java.lang.String[]);
+ method public java.lang.String[] getActiveNotificationKeys();
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
+ method public android.service.notification.StatusBarNotification[] getActiveNotifications(java.lang.String[]);
method public android.os.IBinder onBind(android.content.Intent);
+ method public void onListenerConnected(java.lang.String[]);
method public abstract void onNotificationPosted(android.service.notification.StatusBarNotification);
method public abstract void onNotificationRemoved(android.service.notification.StatusBarNotification);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
@@ -24330,6 +24334,7 @@ package android.service.notification {
method public android.service.notification.StatusBarNotification clone();
method public int describeContents();
method public int getId();
+ method public java.lang.String getKey();
method public android.app.Notification getNotification();
method public java.lang.String getPackageName();
method public long getPostTime();
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 9f933ca..9911467 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -45,7 +45,8 @@ interface INotificationManager
void unregisterListener(in INotificationListener listener, int userid);
void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
- void cancelAllNotificationsFromListener(in INotificationListener token);
+ void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);
- StatusBarNotification[] getActiveNotificationsFromListener(in INotificationListener token);
+ StatusBarNotification[] getActiveNotificationsFromListener(in INotificationListener token, in String[] keys);
+ String[] getActiveNotificationKeysFromListener(in INotificationListener token);
} \ No newline at end of file
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 425fdc1..d4b29d8 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -21,6 +21,7 @@ import android.service.notification.StatusBarNotification;
/** @hide */
oneway interface INotificationListener
{
+ void onListenerConnected(in String[] notificationKeys);
void onNotificationPosted(in StatusBarNotification notification);
void onNotificationRemoved(in StatusBarNotification notification);
} \ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index cf862b8..050e1a0 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -83,6 +83,17 @@ public abstract class NotificationListenerService extends Service {
*/
public abstract void onNotificationRemoved(StatusBarNotification sbn);
+ /**
+ * Implement this method to learn about when the listener is enabled and connected to
+ * the notification manager. You are safe to call {@link #getActiveNotifications(String[])
+ * at this time.
+ *
+ * @param notificationKeys The notification keys for all currently posted notifications.
+ */
+ public void onListenerConnected(String[] notificationKeys) {
+ // optional
+ }
+
private final INotificationManager getNotificationInterface() {
if (mNoMan == null) {
mNoMan = INotificationManager.Stub.asInterface(
@@ -132,9 +143,23 @@ public abstract class NotificationListenerService extends Service {
* {@see #cancelNotification(String, String, int)}
*/
public final void cancelAllNotifications() {
+ cancelNotifications(null /*all*/);
+ }
+
+ /**
+ * Inform the notification manager about dismissal of specific notifications.
+ * <p>
+ * Use this if your listener has a user interface that allows the user to dismiss
+ * multiple notifications at once.
+ *
+ * @param keys Notifications to dismiss, or {@code null} to dismiss all.
+ *
+ * {@see #cancelNotification(String, String, int)}
+ */
+ public final void cancelNotifications(String[] keys) {
if (!isBound()) return;
try {
- getNotificationInterface().cancelAllNotificationsFromListener(mWrapper);
+ getNotificationInterface().cancelNotificationsFromListener(mWrapper, keys);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}
@@ -142,14 +167,43 @@ public abstract class NotificationListenerService extends Service {
/**
* Request the list of outstanding notifications (that is, those that are visible to the
- * current user). Useful when starting up and you don't know what's already been posted.
+ * current user). Useful when you don't know what's already been posted.
*
* @return An array of active notifications.
*/
public StatusBarNotification[] getActiveNotifications() {
+ return getActiveNotifications(null /*all*/);
+ }
+
+ /**
+ * Request the list of outstanding notifications (that is, those that are visible to the
+ * current user). Useful when you don't know what's already been posted.
+ *
+ * @param keys A specific list of notification keys, or {@code null} for all.
+ * @return An array of active notifications.
+ */
+ public StatusBarNotification[] getActiveNotifications(String[] keys) {
+ if (!isBound()) return null;
+ try {
+ return getNotificationInterface().getActiveNotificationsFromListener(mWrapper, keys);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ return null;
+ }
+
+ /**
+ * Request the list of outstanding notification keys(that is, those that are visible to the
+ * current user). You can use the notification keys for subsequent retrieval via
+ * {@link #getActiveNotifications(String[]) or dismissal via
+ * {@link #cancelNotifications(String[]).
+ *
+ * @return An array of active notification keys.
+ */
+ public String[] getActiveNotificationKeys() {
if (!isBound()) return null;
try {
- return getNotificationInterface().getActiveNotificationsFromListener(mWrapper);
+ return getNotificationInterface().getActiveNotificationKeysFromListener(mWrapper);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}
@@ -189,5 +243,13 @@ public abstract class NotificationListenerService extends Service {
Log.w(TAG, "Error running onNotificationRemoved", t);
}
}
+ @Override
+ public void onListenerConnected(String[] notificationKeys) {
+ try {
+ NotificationListenerService.this.onListenerConnected(notificationKeys);
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onListenerConnected", t);
+ }
+ }
}
}
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index b5b9e14..7f15ab8 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -29,6 +29,7 @@ public class StatusBarNotification implements Parcelable {
private final String pkg;
private final int id;
private final String tag;
+ private final String key;
private final int uid;
private final String basePkg;
@@ -68,8 +69,8 @@ public class StatusBarNotification implements Parcelable {
this.notification = notification;
this.user = user;
this.notification.setUser(user);
-
this.postTime = postTime;
+ this.key = key();
}
public StatusBarNotification(Parcel in) {
@@ -88,6 +89,11 @@ public class StatusBarNotification implements Parcelable {
this.user = UserHandle.readFromParcel(in);
this.notification.setUser(this.user);
this.postTime = in.readLong();
+ this.key = key();
+ }
+
+ private String key() {
+ return pkg + '|' + basePkg + '|' + id + '|' + tag + '|' + uid;
}
public void writeToParcel(Parcel out, int flags) {
@@ -148,9 +154,9 @@ public class StatusBarNotification implements Parcelable {
@Override
public String toString() {
return String.format(
- "StatusBarNotification(pkg=%s user=%s id=%d tag=%s score=%d: %s)",
+ "StatusBarNotification(pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
this.pkg, this.user, this.id, this.tag,
- this.score, this.notification);
+ this.score, this.key, this.notification);
}
/** Convenience method to check the notification's flags for
@@ -230,4 +236,11 @@ public class StatusBarNotification implements Parcelable {
public int getScore() {
return score;
}
+
+ /**
+ * A unique instance key for this notification record.
+ */
+ public String getKey() {
+ return key;
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e25e42c..ccd6f60 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -65,8 +65,8 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.AtomicFile;
-import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -76,7 +76,6 @@ import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
import com.android.internal.R;
-
import com.android.internal.notification.NotificationScorer;
import com.android.server.EventLogTags;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -169,7 +168,8 @@ public class NotificationManagerService extends SystemService {
// used as a mutex for access to all active notifications & listeners
final ArrayList<NotificationRecord> mNotificationList =
new ArrayList<NotificationRecord>();
-
+ final ArrayMap<String, NotificationRecord> mNotificationsByKey =
+ new ArrayMap<String, NotificationRecord>();
final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
@@ -661,6 +661,7 @@ public class NotificationManagerService extends SystemService {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
+ boolean added = false;
synchronized (mNotificationList) {
mServicesBinding.remove(servicesBindingTag);
try {
@@ -669,11 +670,20 @@ public class NotificationManagerService extends SystemService {
= new NotificationListenerInfo(
mListener, name, userid, this);
service.linkToDeath(info, 0);
- mListeners.add(info);
+ added = mListeners.add(info);
} catch (RemoteException e) {
// already dead
}
}
+ if (added) {
+ final String[] keys =
+ getActiveNotificationKeysFromListener(mListener);
+ try {
+ mListener.onListenerConnected(keys);
+ } catch (RemoteException e) {
+ // we tried
+ }
+ }
}
@Override
@@ -799,6 +809,7 @@ public class NotificationManagerService extends SystemService {
pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon)
+ " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon));
pw.println(prefix + " pri=" + notification.priority + " score=" + sbn.getScore());
+ pw.println(prefix + " key=" + sbn.getKey());
pw.println(prefix + " contentIntent=" + notification.contentIntent);
pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
pw.println(prefix + " tickerText=" + notification.tickerText);
@@ -855,10 +866,11 @@ public class NotificationManagerService extends SystemService {
@Override
public final String toString() {
return String.format(
- "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d: %s)",
+ "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
System.identityHashCode(this),
this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
- this.sbn.getTag(), this.sbn.getScore(), this.sbn.getNotification());
+ this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
+ this.sbn.getNotification());
}
}
@@ -1554,21 +1566,40 @@ public class NotificationManagerService extends SystemService {
* @param token The binder for the listener, to check that the caller is allowed
*/
@Override
- public void cancelAllNotificationsFromListener(INotificationListener token) {
+ public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
long identity = Binder.clearCallingIdentity();
try {
synchronized (mNotificationList) {
- NotificationListenerInfo info = checkListenerTokenLocked(token);
- cancelAllLocked(callingUid, callingPid, info.userid,
- REASON_LISTENER_CANCEL_ALL, info);
+ final NotificationListenerInfo info = checkListenerTokenLocked(token);
+ if (keys != null) {
+ final int N = keys.length;
+ for (int i = 0; i < N; i++) {
+ NotificationRecord r = mNotificationsByKey.get(keys[i]);
+ if (r != null) {
+ cancelNotificationFromListenerLocked(info, callingUid, callingPid,
+ r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId());
+ }
+ }
+ } else {
+ cancelAllLocked(callingUid, callingPid, info.userid,
+ REASON_LISTENER_CANCEL_ALL, info);
+ }
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
+ private void cancelNotificationFromListenerLocked(NotificationListenerInfo info,
+ int callingUid, int callingPid, String pkg, String tag, int id) {
+ cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
+ Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
+ true,
+ info.userid, REASON_LISTENER_CANCEL, info);
+ }
+
/**
* Allow an INotificationListener to simulate clearing (dismissing) a single notification.
*
@@ -1584,11 +1615,9 @@ public class NotificationManagerService extends SystemService {
long identity = Binder.clearCallingIdentity();
try {
synchronized (mNotificationList) {
- NotificationListenerInfo info = checkListenerTokenLocked(token);
- cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
- Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
- true,
- info.userid, REASON_LISTENER_CANCEL, info);
+ final NotificationListenerInfo info = checkListenerTokenLocked(token);
+ cancelNotificationFromListenerLocked(info, callingUid, callingPid,
+ pkg, tag, id);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1604,15 +1633,26 @@ public class NotificationManagerService extends SystemService {
*/
@Override
public StatusBarNotification[] getActiveNotificationsFromListener(
- INotificationListener token) {
+ INotificationListener token, String[] keys) {
synchronized (mNotificationList) {
- NotificationListenerInfo info = checkListenerTokenLocked(token);
- ArrayList<StatusBarNotification> list = new ArrayList<StatusBarNotification>();
- final int N = mNotificationList.size();
- for (int i=0; i<N; i++) {
- StatusBarNotification sbn = mNotificationList.get(i).sbn;
- if (info.enabledAndUserMatches(sbn)) {
- list.add(sbn);
+ final NotificationListenerInfo info = checkListenerTokenLocked(token);
+ final ArrayList<StatusBarNotification> list
+ = new ArrayList<StatusBarNotification>();
+ if (keys == null) {
+ final int N = mNotificationList.size();
+ for (int i=0; i<N; i++) {
+ StatusBarNotification sbn = mNotificationList.get(i).sbn;
+ if (info.enabledAndUserMatches(sbn)) {
+ list.add(sbn);
+ }
+ }
+ } else {
+ final int N = keys.length;
+ for (int i=0; i<N; i++) {
+ NotificationRecord r = mNotificationsByKey.get(keys[i]);
+ if (r != null && info.enabledAndUserMatches(r.sbn)) {
+ list.add(r.sbn);
+ }
}
}
return list.toArray(new StatusBarNotification[list.size()]);
@@ -1620,6 +1660,11 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ public String[] getActiveNotificationKeysFromListener(INotificationListener token) {
+ return NotificationManagerService.this.getActiveNotificationKeysFromListener(token);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -1633,6 +1678,21 @@ public class NotificationManagerService extends SystemService {
}
};
+ private String[] getActiveNotificationKeysFromListener(INotificationListener token) {
+ synchronized (mNotificationList) {
+ final NotificationListenerInfo info = checkListenerTokenLocked(token);
+ final ArrayList<String> keys = new ArrayList<String>();
+ final int N = mNotificationList.size();
+ for (int i=0; i<N; i++) {
+ final StatusBarNotification sbn = mNotificationList.get(i).sbn;
+ if (info.enabledAndUserMatches(sbn)) {
+ keys.add(sbn.getKey());
+ }
+ }
+ return keys.toArray(new String[keys.size()]);
+ }
+ }
+
void dumpImpl(PrintWriter pw) {
pw.println("Current Notification Manager state:");
@@ -1854,6 +1914,10 @@ public class NotificationManagerService extends SystemService {
old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
}
}
+ if (old != null) {
+ mNotificationsByKey.remove(old.sbn.getKey());
+ }
+ mNotificationsByKey.put(n.getKey(), r);
// Ensure if this is a foreground service that the proper additional
// flags are set.
@@ -2331,6 +2395,7 @@ public class NotificationManagerService extends SystemService {
}
mNotificationList.remove(index);
+ mNotificationsByKey.remove(r.sbn.getKey());
cancelNotificationLocked(r, sendDelete);
updateLightsLocked();
@@ -2403,6 +2468,7 @@ public class NotificationManagerService extends SystemService {
return true;
}
mNotificationList.remove(i);
+ mNotificationsByKey.remove(r.sbn.getKey());
cancelNotificationLocked(r, false);
}
if (canceledSomething) {
@@ -2458,7 +2524,6 @@ public class NotificationManagerService extends SystemService {
final int N = mNotificationList.size();
for (int i=N-1; i>=0; i--) {
NotificationRecord r = mNotificationList.get(i);
-
if (!notificationMatchesUserIdOrRelated(r, userId)) {
continue;
}
@@ -2466,6 +2531,7 @@ public class NotificationManagerService extends SystemService {
if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
| Notification.FLAG_NO_CLEAR)) == 0) {
mNotificationList.remove(i);
+ mNotificationsByKey.remove(r.sbn.getKey());
cancelNotificationLocked(r, true);
}
}