NotificationListenerService.java revision a263e4e438746f91fb78857bd569ba4f796a346d
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.service.notification;
18
19import android.annotation.SdkConstant;
20import android.app.INotificationManager;
21import android.app.Service;
22import android.content.Context;
23import android.content.Intent;
24import android.os.IBinder;
25import android.os.ServiceManager;
26import android.os.UserHandle;
27import android.util.Log;
28
29/**
30 * A service that receives calls from the system when new notifications are posted or removed.
31 * <p>To extend this class, you must declare the service in your manifest file with
32 * the {@link android.Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE} permission
33 * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
34 * <pre>
35 * &lt;service android:name=".NotificationListener"
36 *          android:label="&#64;string/service_name"
37 *          android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
38 *     &lt;intent-filter>
39 *         &lt;action android:name="android.service.notification.NotificationListenerService" />
40 *     &lt;/intent-filter>
41 * &lt;/service></pre>
42 */
43public abstract class NotificationListenerService extends Service {
44    // TAG = "NotificationListenerService[MySubclass]"
45    private final String TAG = NotificationListenerService.class.getSimpleName()
46            + "[" + getClass().getSimpleName() + "]";
47
48    private INotificationListenerWrapper mWrapper = null;
49
50    private INotificationManager mNoMan;
51
52    /**
53     * The {@link Intent} that must be declared as handled by the service.
54     */
55    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
56    public static final String SERVICE_INTERFACE
57            = "android.service.notification.NotificationListenerService";
58
59    /**
60     * Implement this method to learn about new notifications as they are posted by apps.
61     *
62     * @param sbn A data structure encapsulating the original {@link android.app.Notification}
63     *            object as well as its identifying information (tag and id) and source
64     *            (package name).
65     */
66    public abstract void onNotificationPosted(StatusBarNotification sbn);
67
68    /**
69     * Implement this method to learn when notifications are removed.
70     * <P>
71     * This might occur because the user has dismissed the notification using system UI (or another
72     * notification listener) or because the app has withdrawn the notification.
73     * <P>
74     * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
75     * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
76     * fields such as {@link android.app.Notification#contentView} and
77     * {@link android.app.Notification#largeIcon}. However, all other fields on
78     * {@link StatusBarNotification}, sufficient to match this call with a prior call to
79     * {@link #onNotificationPosted(StatusBarNotification)}, will be intact.
80     *
81     * @param sbn A data structure encapsulating at least the original information (tag and id)
82     *            and source (package name) used to post the {@link android.app.Notification} that
83     *            was just removed.
84     */
85    public abstract void onNotificationRemoved(StatusBarNotification sbn);
86
87    /**
88     * Implement this method to learn about when the listener is enabled and connected to
89     * the notification manager.  You are safe to call {@link #getActiveNotifications(String[])
90     * at this time.
91     *
92     * @param notificationKeys The notification keys for all currently posted notifications.
93     */
94    public void onListenerConnected(String[] notificationKeys) {
95        // optional
96    }
97
98    private final INotificationManager getNotificationInterface() {
99        if (mNoMan == null) {
100            mNoMan = INotificationManager.Stub.asInterface(
101                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
102        }
103        return mNoMan;
104    }
105
106    /**
107     * Inform the notification manager about dismissal of a single notification.
108     * <p>
109     * Use this if your listener has a user interface that allows the user to dismiss individual
110     * notifications, similar to the behavior of Android's status bar and notification panel.
111     * It should be called after the user dismisses a single notification using your UI;
112     * upon being informed, the notification manager will actually remove the notification
113     * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback.
114     * <P>
115     * <b>Note:</b> If your listener allows the user to fire a notification's
116     * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call
117     * this method at that time <i>if</i> the Notification in question has the
118     * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set.
119     *
120     * @param pkg Package of the notifying app.
121     * @param tag Tag of the notification as specified by the notifying app in
122     *     {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}.
123     * @param id  ID of the notification as specified by the notifying app in
124     *     {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}.
125     * <p>
126     * @deprecated Use {@link #cancelNotification(String key)}
127     * instead. Beginning with {@link android.os.Build.VERSION_CODES#L} this method will no longer
128     * cancel the notification. It will continue to cancel the notification for applications
129     * whose {@code targetSdkVersion} is earlier than {@link android.os.Build.VERSION_CODES#L}.
130     */
131    public final void cancelNotification(String pkg, String tag, int id) {
132        if (!isBound()) return;
133        try {
134            getNotificationInterface().cancelNotificationFromListener(
135                    mWrapper, pkg, tag, id);
136        } catch (android.os.RemoteException ex) {
137            Log.v(TAG, "Unable to contact notification manager", ex);
138        }
139    }
140
141    /**
142     * Inform the notification manager about dismissal of a single notification.
143     * <p>
144     * Use this if your listener has a user interface that allows the user to dismiss individual
145     * notifications, similar to the behavior of Android's status bar and notification panel.
146     * It should be called after the user dismisses a single notification using your UI;
147     * upon being informed, the notification manager will actually remove the notification
148     * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback.
149     * <P>
150     * <b>Note:</b> If your listener allows the user to fire a notification's
151     * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call
152     * this method at that time <i>if</i> the Notification in question has the
153     * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set.
154     * <p>
155     * @param key Notification to dismiss from {@link StatusBarNotification#getKey()}.
156     */
157    public final void cancelNotification(String key) {
158        if (!isBound()) return;
159        try {
160            getNotificationInterface().cancelNotificationsFromListener(mWrapper,
161                    new String[] {key});
162        } catch (android.os.RemoteException ex) {
163            Log.v(TAG, "Unable to contact notification manager", ex);
164        }
165    }
166
167    /**
168     * Inform the notification manager about dismissal of all notifications.
169     * <p>
170     * Use this if your listener has a user interface that allows the user to dismiss all
171     * notifications, similar to the behavior of Android's status bar and notification panel.
172     * It should be called after the user invokes the "dismiss all" function of your UI;
173     * upon being informed, the notification manager will actually remove all active notifications
174     * and you will get multiple {@link #onNotificationRemoved(StatusBarNotification)} callbacks.
175     *
176     * {@see #cancelNotification(String, String, int)}
177     */
178    public final void cancelAllNotifications() {
179        cancelNotifications(null /*all*/);
180    }
181
182    /**
183     * Inform the notification manager about dismissal of specific notifications.
184     * <p>
185     * Use this if your listener has a user interface that allows the user to dismiss
186     * multiple notifications at once.
187     *
188     * @param keys Notifications to dismiss, or {@code null} to dismiss all.
189     *
190     * {@see #cancelNotification(String, String, int)}
191     */
192    public final void cancelNotifications(String[] keys) {
193        if (!isBound()) return;
194        try {
195            getNotificationInterface().cancelNotificationsFromListener(mWrapper, keys);
196        } catch (android.os.RemoteException ex) {
197            Log.v(TAG, "Unable to contact notification manager", ex);
198        }
199    }
200
201    /**
202     * Request the list of outstanding notifications (that is, those that are visible to the
203     * current user). Useful when you don't know what's already been posted.
204     *
205     * @return An array of active notifications.
206     */
207    public StatusBarNotification[] getActiveNotifications() {
208        return getActiveNotifications(null /*all*/);
209    }
210
211    /**
212     * Request the list of outstanding notifications (that is, those that are visible to the
213     * current user). Useful when you don't know what's already been posted.
214     *
215     * @param keys A specific list of notification keys, or {@code null} for all.
216     * @return An array of active notifications.
217     */
218    public StatusBarNotification[] getActiveNotifications(String[] keys) {
219        if (!isBound()) return null;
220        try {
221            return getNotificationInterface().getActiveNotificationsFromListener(mWrapper, keys);
222        } catch (android.os.RemoteException ex) {
223            Log.v(TAG, "Unable to contact notification manager", ex);
224        }
225        return null;
226    }
227
228    /**
229     * Request the list of outstanding notification keys(that is, those that are visible to the
230     * current user).  You can use the notification keys for subsequent retrieval via
231     * {@link #getActiveNotifications(String[]) or dismissal via
232     * {@link #cancelNotifications(String[]).
233     *
234     * @return An array of active notification keys.
235     */
236    public String[] getActiveNotificationKeys() {
237        if (!isBound()) return null;
238        try {
239            return getNotificationInterface().getActiveNotificationKeysFromListener(mWrapper);
240        } catch (android.os.RemoteException ex) {
241            Log.v(TAG, "Unable to contact notification manager", ex);
242        }
243        return null;
244    }
245
246    @Override
247    public IBinder onBind(Intent intent) {
248        if (mWrapper == null) {
249            mWrapper = new INotificationListenerWrapper();
250        }
251        return mWrapper;
252    }
253
254    private boolean isBound() {
255        if (mWrapper == null) {
256            Log.w(TAG, "Notification listener service not yet bound.");
257            return false;
258        }
259        return true;
260    }
261
262    private class INotificationListenerWrapper extends INotificationListener.Stub {
263        @Override
264        public void onNotificationPosted(StatusBarNotification sbn) {
265            try {
266                NotificationListenerService.this.onNotificationPosted(sbn);
267            } catch (Throwable t) {
268                Log.w(TAG, "Error running onNotificationPosted", t);
269            }
270        }
271        @Override
272        public void onNotificationRemoved(StatusBarNotification sbn) {
273            try {
274                NotificationListenerService.this.onNotificationRemoved(sbn);
275            } catch (Throwable t) {
276                Log.w(TAG, "Error running onNotificationRemoved", t);
277            }
278        }
279        @Override
280        public void onListenerConnected(String[] notificationKeys) {
281            try {
282                NotificationListenerService.this.onListenerConnected(notificationKeys);
283            } catch (Throwable t) {
284                Log.w(TAG, "Error running onListenerConnected", t);
285            }
286        }
287    }
288}
289