NotificationManager.java revision 1fc476d51203c0b76ebd0f2062adf3059437b0dc
1/*
2 * Copyright (C) 2007 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.app;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.annotation.SdkConstant;
22import android.app.Notification.Builder;
23import android.app.NotificationManager.Policy.Token;
24import android.content.ComponentName;
25import android.content.Context;
26import android.net.Uri;
27import android.os.Bundle;
28import android.os.Handler;
29import android.os.IBinder;
30import android.os.Parcel;
31import android.os.Parcelable;
32import android.os.RemoteException;
33import android.os.ServiceManager;
34import android.os.StrictMode;
35import android.os.UserHandle;
36import android.provider.Settings.Global;
37import android.service.notification.IConditionListener;
38import android.service.notification.ZenModeConfig;
39import android.util.Log;
40
41import java.util.Objects;
42
43/**
44 * Class to notify the user of events that happen.  This is how you tell
45 * the user that something has happened in the background. {@more}
46 *
47 * Notifications can take different forms:
48 * <ul>
49 *      <li>A persistent icon that goes in the status bar and is accessible
50 *          through the launcher, (when the user selects it, a designated Intent
51 *          can be launched),</li>
52 *      <li>Turning on or flashing LEDs on the device, or</li>
53 *      <li>Alerting the user by flashing the backlight, playing a sound,
54 *          or vibrating.</li>
55 * </ul>
56 *
57 * <p>
58 * Each of the notify methods takes an int id parameter and optionally a
59 * {@link String} tag parameter, which may be {@code null}.  These parameters
60 * are used to form a pair (tag, id), or ({@code null}, id) if tag is
61 * unspecified.  This pair identifies this notification from your app to the
62 * system, so that pair should be unique within your app.  If you call one
63 * of the notify methods with a (tag, id) pair that is currently active and
64 * a new set of notification parameters, it will be updated.  For example,
65 * if you pass a new status bar icon, the old icon in the status bar will
66 * be replaced with the new one.  This is also the same tag and id you pass
67 * to the {@link #cancel(int)} or {@link #cancel(String, int)} method to clear
68 * this notification.
69 *
70 * <p>
71 * You do not instantiate this class directly; instead, retrieve it through
72 * {@link android.content.Context#getSystemService}.
73 *
74 * <div class="special reference">
75 * <h3>Developer Guides</h3>
76 * <p>For a guide to creating notifications, read the
77 * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
78 * developer guide.</p>
79 * </div>
80 *
81 * @see android.app.Notification
82 * @see android.content.Context#getSystemService
83 */
84public class NotificationManager
85{
86    private static String TAG = "NotificationManager";
87    private static boolean localLOGV = false;
88
89    /**
90     * Intent that is broadcast when the state of {@link #getEffectsSuppressor()} changes.
91     * This broadcast is only sent to registered receivers.
92     *
93     * @hide
94     */
95    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
96    public static final String ACTION_EFFECTS_SUPPRESSOR_CHANGED
97            = "android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED";
98
99    /**
100     * Intent that is broadcast when the state of getNotificationPolicy() changes.
101     * This broadcast is only sent to registered receivers.
102     */
103    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
104    public static final String ACTION_NOTIFICATION_POLICY_CHANGED
105            = "android.app.action.NOTIFICATION_POLICY_CHANGED";
106
107    private static INotificationManager sService;
108
109    /** @hide */
110    static public INotificationManager getService()
111    {
112        if (sService != null) {
113            return sService;
114        }
115        IBinder b = ServiceManager.getService("notification");
116        sService = INotificationManager.Stub.asInterface(b);
117        return sService;
118    }
119
120    /*package*/ NotificationManager(Context context, Handler handler)
121    {
122        mContext = context;
123    }
124
125    /** {@hide} */
126    public static NotificationManager from(Context context) {
127        return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
128    }
129
130    /**
131     * Post a notification to be shown in the status bar. If a notification with
132     * the same id has already been posted by your application and has not yet been canceled, it
133     * will be replaced by the updated information.
134     *
135     * @param id An identifier for this notification unique within your
136     *        application.
137     * @param notification A {@link Notification} object describing what to show the user. Must not
138     *        be null.
139     */
140    public void notify(int id, Notification notification)
141    {
142        notify(null, id, notification);
143    }
144
145    /**
146     * Post a notification to be shown in the status bar. If a notification with
147     * the same tag and id has already been posted by your application and has not yet been
148     * canceled, it will be replaced by the updated information.
149     *
150     * @param tag A string identifier for this notification.  May be {@code null}.
151     * @param id An identifier for this notification.  The pair (tag, id) must be unique
152     *        within your application.
153     * @param notification A {@link Notification} object describing what to
154     *        show the user. Must not be null.
155     */
156    public void notify(String tag, int id, Notification notification)
157    {
158        int[] idOut = new int[1];
159        INotificationManager service = getService();
160        String pkg = mContext.getPackageName();
161        if (notification.sound != null) {
162            notification.sound = notification.sound.getCanonicalUri();
163            if (StrictMode.vmFileUriExposureEnabled()) {
164                notification.sound.checkFileUriExposed("Notification.sound");
165            }
166        }
167        if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
168        Notification stripped = notification.clone();
169        Builder.stripForDelivery(stripped);
170        try {
171            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
172                    stripped, idOut, UserHandle.myUserId());
173            if (id != idOut[0]) {
174                Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
175            }
176        } catch (RemoteException e) {
177        }
178    }
179
180    /**
181     * @hide
182     */
183    public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
184    {
185        int[] idOut = new int[1];
186        INotificationManager service = getService();
187        String pkg = mContext.getPackageName();
188        if (notification.sound != null) {
189            notification.sound = notification.sound.getCanonicalUri();
190            if (StrictMode.vmFileUriExposureEnabled()) {
191                notification.sound.checkFileUriExposed("Notification.sound");
192            }
193        }
194        if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
195        Notification stripped = notification.clone();
196        Builder.stripForDelivery(stripped);
197        try {
198            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
199                    stripped, idOut, user.getIdentifier());
200            if (id != idOut[0]) {
201                Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
202            }
203        } catch (RemoteException e) {
204        }
205    }
206
207    /**
208     * Cancel a previously shown notification.  If it's transient, the view
209     * will be hidden.  If it's persistent, it will be removed from the status
210     * bar.
211     */
212    public void cancel(int id)
213    {
214        cancel(null, id);
215    }
216
217    /**
218     * Cancel a previously shown notification.  If it's transient, the view
219     * will be hidden.  If it's persistent, it will be removed from the status
220     * bar.
221     */
222    public void cancel(String tag, int id)
223    {
224        INotificationManager service = getService();
225        String pkg = mContext.getPackageName();
226        if (localLOGV) Log.v(TAG, pkg + ": cancel(" + id + ")");
227        try {
228            service.cancelNotificationWithTag(pkg, tag, id, UserHandle.myUserId());
229        } catch (RemoteException e) {
230        }
231    }
232
233    /**
234     * @hide
235     */
236    public void cancelAsUser(String tag, int id, UserHandle user)
237    {
238        INotificationManager service = getService();
239        String pkg = mContext.getPackageName();
240        if (localLOGV) Log.v(TAG, pkg + ": cancel(" + id + ")");
241        try {
242            service.cancelNotificationWithTag(pkg, tag, id, user.getIdentifier());
243        } catch (RemoteException e) {
244        }
245    }
246
247    /**
248     * Cancel all previously shown notifications. See {@link #cancel} for the
249     * detailed behavior.
250     */
251    public void cancelAll()
252    {
253        INotificationManager service = getService();
254        String pkg = mContext.getPackageName();
255        if (localLOGV) Log.v(TAG, pkg + ": cancelAll()");
256        try {
257            service.cancelAllNotifications(pkg, UserHandle.myUserId());
258        } catch (RemoteException e) {
259        }
260    }
261
262    /**
263     * @hide
264     */
265    public ComponentName getEffectsSuppressor() {
266        INotificationManager service = getService();
267        try {
268            return service.getEffectsSuppressor();
269        } catch (RemoteException e) {
270            return null;
271        }
272    }
273
274    /**
275     * @hide
276     */
277    public boolean matchesCallFilter(Bundle extras) {
278        INotificationManager service = getService();
279        try {
280            return service.matchesCallFilter(extras);
281        } catch (RemoteException e) {
282            return false;
283        }
284    }
285
286    /**
287     * @hide
288     */
289    public boolean isSystemConditionProviderEnabled(String path) {
290        INotificationManager service = getService();
291        try {
292            return service.isSystemConditionProviderEnabled(path);
293        } catch (RemoteException e) {
294            return false;
295        }
296    }
297
298    /**
299     * @hide
300     */
301    public void setZenMode(int mode, Uri conditionId, String reason) {
302        INotificationManager service = getService();
303        try {
304            service.setZenMode(mode, conditionId, reason);
305        } catch (RemoteException e) {
306        }
307    }
308
309    /**
310     * @hide
311     */
312    public boolean setZenModeConfig(ZenModeConfig config, String reason) {
313        INotificationManager service = getService();
314        try {
315            return service.setZenModeConfig(config, reason);
316        } catch (RemoteException e) {
317            return false;
318        }
319    }
320
321    /**
322     * @hide
323     */
324    public void requestZenModeConditions(IConditionListener listener, int relevance) {
325        INotificationManager service = getService();
326        try {
327            service.requestZenModeConditions(listener, relevance);
328        } catch (RemoteException e) {
329        }
330    }
331
332    /**
333     * @hide
334     */
335    public int getZenMode() {
336        INotificationManager service = getService();
337        try {
338            return service.getZenMode();
339        } catch (RemoteException e) {
340        }
341        return Global.ZEN_MODE_OFF;
342    }
343
344    /**
345     * @hide
346     */
347    public ZenModeConfig getZenModeConfig() {
348        INotificationManager service = getService();
349        try {
350            return service.getZenModeConfig();
351        } catch (RemoteException e) {
352        }
353        return null;
354    }
355
356    /**
357     * Requests a notification policy token for the calling package.
358     *
359     * @param callback required, used to receive the granted token or the deny signal.
360     * @param handler The handler used when receiving the result.
361     *                If null, the current thread is used.
362     */
363    public void requestNotificationPolicyToken(@NonNull final Policy.Token.RequestCallback callback,
364            @Nullable Handler handler) {
365        checkRequired("callback", callback);
366        final Handler h = handler != null ? handler : new Handler();
367        INotificationManager service = getService();
368        try {
369            service.requestNotificationPolicyToken(mContext.getOpPackageName(),
370                    new INotificationManagerCallback.Stub() {
371                @Override
372                public void onPolicyToken(final Token token) throws RemoteException {
373                    h.post(new Runnable() {
374                        @Override
375                        public void run() {
376                            if (token != null) {
377                                callback.onTokenGranted(token);
378                            } else {
379                                callback.onTokenDenied();
380                            }
381                        }
382                    });
383                }
384            });
385        } catch (RemoteException e) {
386        }
387    }
388
389    /**
390     * Checks a given notification policy token.
391     *
392     * Returns true if the token is still valid for managing policy.
393     */
394    public boolean isNotificationPolicyTokenValid(@NonNull Policy.Token token) {
395        if (token == null) return false;
396        INotificationManager service = getService();
397        try {
398            return service.isNotificationPolicyTokenValid(mContext.getOpPackageName(), token);
399        } catch (RemoteException e) {
400        }
401        return false;
402    }
403
404    /**
405     * Gets the current notification policy.
406     *
407     * @param token A valid notification policy token is required to access the current policy.
408     */
409    public Policy getNotificationPolicy(@NonNull Policy.Token token) {
410        checkRequired("token", token);
411        INotificationManager service = getService();
412        try {
413            return service.getNotificationPolicy(token);
414        } catch (RemoteException e) {
415        }
416        return null;
417    }
418
419    /**
420     * Sets the current notification policy.
421     *
422     * @param token  A valid notification policy token is required to modify the current policy.
423     * @param policy The new desired policy.
424     */
425    public void setNotificationPolicy(@NonNull Policy.Token token, @NonNull Policy policy) {
426        checkRequired("token", token);
427        checkRequired("policy", policy);
428        INotificationManager service = getService();
429        try {
430            service.setNotificationPolicy(token, policy);
431        } catch (RemoteException e) {
432        }
433    }
434
435    private Context mContext;
436
437    private static void checkRequired(String name, Object value) {
438        if (value == null) {
439            throw new IllegalArgumentException(name + " is required");
440        }
441    }
442
443    /**
444     * Notification policy configuration.  Represents user-preferences for notification
445     * filtering and prioritization.
446     */
447    public static class Policy implements android.os.Parcelable {
448        /** Reminder notifications are prioritized. */
449        public static final int PRIORITY_CATEGORY_REMINDERS = 1 << 0;
450        /** Event notifications are prioritized. */
451        public static final int PRIORITY_CATEGORY_EVENTS = 1 << 1;
452        /** Message notifications are prioritized. */
453        public static final int PRIORITY_CATEGORY_MESSAGES = 1 << 2;
454        /** Calls are prioritized. */
455        public static final int PRIORITY_CATEGORY_CALLS = 1 << 3;
456        /** Calls from repeat callers are prioritized. */
457        public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 1 << 4;
458
459        private static final int[] ALL_PRIORITY_CATEGORIES = {
460            PRIORITY_CATEGORY_REMINDERS,
461            PRIORITY_CATEGORY_EVENTS,
462            PRIORITY_CATEGORY_MESSAGES,
463            PRIORITY_CATEGORY_CALLS,
464            PRIORITY_CATEGORY_REPEAT_CALLERS,
465        };
466
467        /** Any sender is prioritized. */
468        public static final int PRIORITY_SENDERS_ANY = 0;
469        /** Saved contacts are prioritized. */
470        public static final int PRIORITY_SENDERS_CONTACTS = 1;
471        /** Only starred contacts are prioritized. */
472        public static final int PRIORITY_SENDERS_STARRED = 2;
473
474        /** Notification categories to prioritize. Bitmask of PRIORITY_CATEGORY_* constants. */
475        public final int priorityCategories;
476
477        /** Notification senders to prioritize. One of:
478         * PRIORITY_SENDERS_ANY, PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED */
479        public final int prioritySenders;
480
481        public Policy(int priorityCategories, int prioritySenders) {
482            this.priorityCategories = priorityCategories;
483            this.prioritySenders = prioritySenders;
484        }
485
486        /** @hide */
487        public Policy(Parcel source) {
488            this(source.readInt(), source.readInt());
489        }
490
491        @Override
492        public void writeToParcel(Parcel dest, int flags) {
493            dest.writeInt(priorityCategories);
494            dest.writeInt(prioritySenders);
495        }
496
497        @Override
498        public int describeContents() {
499            return 0;
500        }
501
502        @Override
503        public int hashCode() {
504            return Objects.hash(priorityCategories, prioritySenders);
505        }
506
507        @Override
508        public boolean equals(Object o) {
509            if (!(o instanceof Policy)) return false;
510            if (o == this) return true;
511            final Policy other = (Policy) o;
512            return other.priorityCategories == priorityCategories
513                    && other.prioritySenders == prioritySenders;
514        }
515
516        @Override
517        public String toString() {
518            return "NotificationManager.Policy["
519                    + "priorityCategories=" + priorityCategoriesToString(priorityCategories)
520                    + ",prioritySenders=" + prioritySendersToString(prioritySenders)
521                    + "]";
522        }
523
524        public static String priorityCategoriesToString(int priorityCategories) {
525            if (priorityCategories == 0) return "";
526            final StringBuilder sb = new StringBuilder();
527            for (int i = 0; i < ALL_PRIORITY_CATEGORIES.length; i++) {
528                final int priorityCategory = ALL_PRIORITY_CATEGORIES[i];
529                if ((priorityCategories & priorityCategory) != 0) {
530                    if (sb.length() > 0) sb.append(',');
531                    sb.append(priorityCategoryToString(priorityCategory));
532                }
533                priorityCategories &= ~priorityCategory;
534            }
535            if (priorityCategories != 0) {
536                if (sb.length() > 0) sb.append(',');
537                sb.append("PRIORITY_CATEGORY_UNKNOWN_").append(priorityCategories);
538            }
539            return sb.toString();
540        }
541
542        private static String priorityCategoryToString(int priorityCategory) {
543            switch (priorityCategory) {
544                case PRIORITY_CATEGORY_REMINDERS: return "PRIORITY_CATEGORY_REMINDERS";
545                case PRIORITY_CATEGORY_EVENTS: return "PRIORITY_CATEGORY_EVENTS";
546                case PRIORITY_CATEGORY_MESSAGES: return "PRIORITY_CATEGORY_MESSAGES";
547                case PRIORITY_CATEGORY_CALLS: return "PRIORITY_CATEGORY_CALLS";
548                case PRIORITY_CATEGORY_REPEAT_CALLERS: return "PRIORITY_CATEGORY_REPEAT_CALLERS";
549                default: return "PRIORITY_CATEGORY_UNKNOWN_" + priorityCategory;
550            }
551        }
552
553        public static String prioritySendersToString(int prioritySenders) {
554            switch (prioritySenders) {
555                case PRIORITY_SENDERS_ANY: return "PRIORITY_SENDERS_ANY";
556                case PRIORITY_SENDERS_CONTACTS: return "PRIORITY_SENDERS_CONTACTS";
557                case PRIORITY_SENDERS_STARRED: return "PRIORITY_SENDERS_STARRED";
558                default: return "PRIORITY_SENDERS_UNKNOWN_" + prioritySenders;
559            }
560        }
561
562        public static final Parcelable.Creator<Policy> CREATOR = new Parcelable.Creator<Policy>() {
563            @Override
564            public Policy createFromParcel(Parcel in) {
565                return new Policy(in);
566            }
567
568            @Override
569            public Policy[] newArray(int size) {
570                return new Policy[size];
571            }
572        };
573
574        /**
575         * Represents a client-specific token required to manage notification policy.
576         */
577        public static class Token implements Parcelable {
578            private final IBinder mBinder;
579
580            /** @hide */
581            public Token(IBinder binder) {
582                if (binder == null) throw new IllegalArgumentException("Binder required for token");
583                mBinder = binder;
584            }
585
586            @Override
587            public int describeContents() {
588                return 0;
589            }
590
591            @Override
592            public int hashCode() {
593                return Objects.hash(mBinder);
594            }
595
596            @Override
597            public boolean equals(Object o) {
598                if (!(o instanceof Token)) return false;
599                if (o == this) return true;
600                final Token other = (Token) o;
601                return Objects.equals(other.mBinder, mBinder);
602            }
603
604            @Override
605            public String toString() {
606                return String.format("NotificationManager.Token[0x%08x]",
607                        System.identityHashCode(mBinder));
608            }
609
610            @Override
611            public void writeToParcel(Parcel dest, int flags) {
612                dest.writeStrongBinder(mBinder);
613            }
614
615            public static final Parcelable.Creator<Token> CREATOR
616                    = new Parcelable.Creator<Token>() {
617                @Override
618                public Token createFromParcel(Parcel in) {
619                    return new Token(in.readStrongBinder());
620                }
621
622                @Override
623                public Token[] newArray(int size) {
624                    return new Token[size];
625                }
626            };
627
628            /** Callback for receiving the result of a token request. */
629            public static abstract class RequestCallback {
630                /**
631                 * Received if the request was granted for this package.
632                 *
633                 * @param token can be used to manage notification policy.
634                 */
635                public abstract void onTokenGranted(Policy.Token token);
636
637                /**
638                 * Received if the request was denied for this package.
639                 */
640                public abstract void onTokenDenied();
641            }
642        }
643    }
644
645}
646