1ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen/*
2ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * Copyright (C) 2014 The Android Open Source Project
3ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen *
4ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * Licensed under the Apache License, Version 2.0 (the "License");
5ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * you may not use this file except in compliance with the License.
6ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * You may obtain a copy of the License at
7ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen *
8ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen *      http://www.apache.org/licenses/LICENSE-2.0
9ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen *
10ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * Unless required by applicable law or agreed to in writing, software
11ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * distributed under the License is distributed on an "AS IS" BASIS,
12ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * See the License for the specific language governing permissions and
14ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * limitations under the License.
15ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen */
16ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
17ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenpackage android.support.v4.app;
18ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
19b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikasimport android.app.AppOpsManager;
20ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.app.Notification;
21ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.app.NotificationManager;
22ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.app.Service;
23ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.content.ComponentName;
24ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.content.Context;
25ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.content.Intent;
26ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.content.ServiceConnection;
27b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikasimport android.content.pm.ApplicationInfo;
28ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.content.pm.PackageManager;
29ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.content.pm.ResolveInfo;
30ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.os.Build;
31ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.os.Bundle;
32ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.os.DeadObjectException;
33ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.os.Handler;
34ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.os.HandlerThread;
35ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.os.IBinder;
36ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.os.Message;
37ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.os.RemoteException;
38ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.provider.Settings;
399455ad06a2852e9edb3231c1a609fc6a9761ebb0Siyamed Sinirimport android.support.annotation.GuardedBy;
40ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.util.Log;
41ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
42b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikasimport java.lang.reflect.Field;
43b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikasimport java.lang.reflect.InvocationTargetException;
44b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikasimport java.lang.reflect.Method;
45ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport java.util.HashMap;
46ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport java.util.HashSet;
47ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport java.util.Iterator;
48ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport java.util.LinkedList;
49ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport java.util.List;
50ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport java.util.Map;
51ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport java.util.Set;
52ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
53ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen/**
54ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * Compatibility library for NotificationManager with fallbacks for older platforms.
55ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen *
56ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * <p>To use this class, call the static function {@link #from} to get a
57ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * {@link NotificationManagerCompat} object, and then call one of its
58ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * methods to post or cancel notifications.
59ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen */
60c5847d13e40f5d52459f5c0dab32dc08f1a9a683Chris Banespublic final class NotificationManagerCompat {
61ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private static final String TAG = "NotifManCompat";
62b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas    private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
63b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas    private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
64ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
65ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /**
66ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * Notification extras key: if set to true, the posted notification should use
67ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * the side channel for delivery instead of using notification manager.
68ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     */
69ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    public static final String EXTRA_USE_SIDE_CHANNEL =
70ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            NotificationCompatJellybean.EXTRA_USE_SIDE_CHANNEL;
71ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
72ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /**
73ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * Intent action to register for on a service to receive side channel
74ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * notifications. The listening service must be in the same package as an enabled
75ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * {@link android.service.notification.NotificationListenerService}.
76ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     */
77ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    public static final String ACTION_BIND_SIDE_CHANNEL =
781a7f163fad9e7d0f5bc67ad44d6bf9d73d672a86Griff Hazen            "android.support.BIND_NOTIFICATION_SIDE_CHANNEL";
79ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
806d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen    /**
816d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen     * Maximum sdk build version which needs support for side channeled notifications.
826d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen     * Currently the only needed use is for side channeling group children before KITKAT_WATCH.
836d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen     */
846d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen    static final int MAX_SIDE_CHANNEL_SDK_VERSION = 19;
856d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen
86ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /** Base time delay for a side channel listener queue retry. */
87ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private static final int SIDE_CHANNEL_RETRY_BASE_INTERVAL_MS = 1000;
88ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /** Maximum retries for a side channel listener before dropping tasks. */
89ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private static final int SIDE_CHANNEL_RETRY_MAX_COUNT = 6;
90ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
91ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private static final String SETTING_ENABLED_NOTIFICATION_LISTENERS =
92ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            "enabled_notification_listeners";
93ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
94ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /** Cache of enabled notification listener components */
95ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private static final Object sEnabledNotificationListenersLock = new Object();
969455ad06a2852e9edb3231c1a609fc6a9761ebb0Siyamed Sinir    @GuardedBy("sEnabledNotificationListenersLock")
97ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private static String sEnabledNotificationListeners;
989455ad06a2852e9edb3231c1a609fc6a9761ebb0Siyamed Sinir    @GuardedBy("sEnabledNotificationListenersLock")
99ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private static Set<String> sEnabledNotificationListenerPackages = new HashSet<String>();
100ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
101ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private final Context mContext;
102ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private final NotificationManager mNotificationManager;
103ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /** Lock for mutable static fields */
104ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private static final Object sLock = new Object();
1059455ad06a2852e9edb3231c1a609fc6a9761ebb0Siyamed Sinir    @GuardedBy("sLock")
106ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private static SideChannelManager sSideChannelManager;
107ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
108355267b12d57afc082bd253919229321396b12cdChris Wren    /**
109355267b12d57afc082bd253919229321396b12cdChris Wren     * Value signifying that the user has not expressed an importance.
110355267b12d57afc082bd253919229321396b12cdChris Wren     *
111355267b12d57afc082bd253919229321396b12cdChris Wren     * This value is for persisting preferences, and should never be associated with
112355267b12d57afc082bd253919229321396b12cdChris Wren     * an actual notification.
113355267b12d57afc082bd253919229321396b12cdChris Wren     */
114355267b12d57afc082bd253919229321396b12cdChris Wren    public static final int IMPORTANCE_UNSPECIFIED = -1000;
115355267b12d57afc082bd253919229321396b12cdChris Wren
116355267b12d57afc082bd253919229321396b12cdChris Wren    /**
117355267b12d57afc082bd253919229321396b12cdChris Wren     * A notification with no importance: shows nowhere, is blocked.
118355267b12d57afc082bd253919229321396b12cdChris Wren     */
119355267b12d57afc082bd253919229321396b12cdChris Wren    public static final int IMPORTANCE_NONE = 0;
120355267b12d57afc082bd253919229321396b12cdChris Wren
121355267b12d57afc082bd253919229321396b12cdChris Wren    /**
122355267b12d57afc082bd253919229321396b12cdChris Wren     * Min notification importance: only shows in the shade, below the fold.
123355267b12d57afc082bd253919229321396b12cdChris Wren     */
124355267b12d57afc082bd253919229321396b12cdChris Wren    public static final int IMPORTANCE_MIN = 1;
125355267b12d57afc082bd253919229321396b12cdChris Wren
126355267b12d57afc082bd253919229321396b12cdChris Wren    /**
127355267b12d57afc082bd253919229321396b12cdChris Wren     * Low notification importance: shows everywhere, but is not intrusive.
128355267b12d57afc082bd253919229321396b12cdChris Wren     */
129355267b12d57afc082bd253919229321396b12cdChris Wren    public static final int IMPORTANCE_LOW = 2;
130355267b12d57afc082bd253919229321396b12cdChris Wren
131355267b12d57afc082bd253919229321396b12cdChris Wren    /**
132355267b12d57afc082bd253919229321396b12cdChris Wren     * Default notification importance: shows everywhere, allowed to makes noise,
133355267b12d57afc082bd253919229321396b12cdChris Wren     * but does not visually intrude.
134355267b12d57afc082bd253919229321396b12cdChris Wren     */
135355267b12d57afc082bd253919229321396b12cdChris Wren    public static final int IMPORTANCE_DEFAULT = 3;
136355267b12d57afc082bd253919229321396b12cdChris Wren
137355267b12d57afc082bd253919229321396b12cdChris Wren    /**
138355267b12d57afc082bd253919229321396b12cdChris Wren     * Higher notification importance: shows everywhere, allowed to makes noise and peek.
139355267b12d57afc082bd253919229321396b12cdChris Wren     */
140355267b12d57afc082bd253919229321396b12cdChris Wren    public static final int IMPORTANCE_HIGH = 4;
141355267b12d57afc082bd253919229321396b12cdChris Wren
142355267b12d57afc082bd253919229321396b12cdChris Wren    /**
143355267b12d57afc082bd253919229321396b12cdChris Wren     * Highest notification importance: shows everywhere, allowed to makes noise, peek, and
144355267b12d57afc082bd253919229321396b12cdChris Wren     * use full screen intents.
145355267b12d57afc082bd253919229321396b12cdChris Wren     */
146355267b12d57afc082bd253919229321396b12cdChris Wren    public static final int IMPORTANCE_MAX = 5;
147355267b12d57afc082bd253919229321396b12cdChris Wren
148ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /** Get a {@link NotificationManagerCompat} instance for a provided context. */
149ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    public static NotificationManagerCompat from(Context context) {
150ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        return new NotificationManagerCompat(context);
151ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
152ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
153ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private NotificationManagerCompat(Context context) {
154ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        mContext = context;
155ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        mNotificationManager = (NotificationManager) mContext.getSystemService(
156ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                Context.NOTIFICATION_SERVICE);
157ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
158ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
159ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /**
160ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * Cancel a previously shown notification.
161ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * @param id the ID of the notification
162ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     */
163ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    public void cancel(int id) {
164ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        cancel(null, id);
165ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
166ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
167ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /**
168ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * Cancel a previously shown notification.
169ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * @param tag the string identifier of the notification.
170ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * @param id the ID of the notification
171ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     */
172ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    public void cancel(String tag, int id) {
173b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas        mNotificationManager.cancel(tag, id);
1746d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen        if (Build.VERSION.SDK_INT <= MAX_SIDE_CHANNEL_SDK_VERSION) {
1756d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen            pushSideChannelQueue(new CancelTask(mContext.getPackageName(), id, tag));
1766d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen        }
177ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
178ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
179ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /** Cancel all previously shown notifications. */
180ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    public void cancelAll() {
181ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        mNotificationManager.cancelAll();
1826d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen        if (Build.VERSION.SDK_INT <= MAX_SIDE_CHANNEL_SDK_VERSION) {
1836d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen            pushSideChannelQueue(new CancelTask(mContext.getPackageName()));
1846d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen        }
185ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
186ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
187ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /**
188ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * Post a notification to be shown in the status bar, stream, etc.
189ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * @param id the ID of the notification
190ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * @param notification the notification to post to the system
191ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     */
192ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    public void notify(int id, Notification notification) {
193ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        notify(null, id, notification);
194ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
195ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
196ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /**
197ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * Post a notification to be shown in the status bar, stream, etc.
198ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * @param tag the string identifier for a notification. Can be {@code null}.
199ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * @param id the ID of the notification. The pair (tag, id) must be unique within your app.
200ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * @param notification the notification to post to the system
201ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    */
202ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    public void notify(String tag, int id, Notification notification) {
203ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        if (useSideChannelForNotification(notification)) {
204ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            pushSideChannelQueue(new NotifyTask(mContext.getPackageName(), id, tag, notification));
2056d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen            // Cancel this notification in notification manager if it just transitioned to being
2066d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen            // side channelled.
207b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas            mNotificationManager.cancel(tag, id);
208ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        } else {
209b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas            mNotificationManager.notify(tag, id, notification);
210ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
211ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
212ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
213ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /**
21485e038ee7cac169b3a9878cba881dc7eb401de2aChris Wren     * Returns whether notifications from the calling package are not blocked.
21585e038ee7cac169b3a9878cba881dc7eb401de2aChris Wren     */
21685e038ee7cac169b3a9878cba881dc7eb401de2aChris Wren    public boolean areNotificationsEnabled() {
217b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas        if (Build.VERSION.SDK_INT >= 24) {
218b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas            return mNotificationManager.areNotificationsEnabled();
219b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas        } else if (Build.VERSION.SDK_INT >= 19) {
220b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas            AppOpsManager appOps =
221b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas                    (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
222b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas            ApplicationInfo appInfo = mContext.getApplicationInfo();
223b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas            String pkg = mContext.getApplicationContext().getPackageName();
224b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas            int uid = appInfo.uid;
225b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas            try {
226b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas                Class<?> appOpsClass = Class.forName(AppOpsManager.class.getName());
227b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas                Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE,
228b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas                        Integer.TYPE, String.class);
229b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas                Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
230b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas                int value = (int) opPostNotificationValue.get(Integer.class);
231b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas                return ((int) checkOpNoThrowMethod.invoke(appOps, value, uid, pkg)
232b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas                        == AppOpsManager.MODE_ALLOWED);
233b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas            } catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException
234b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas                    | InvocationTargetException | IllegalAccessException | RuntimeException e) {
235b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas                return true;
236b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas            }
237b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas        } else {
238b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas            return true;
239b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas        }
24085e038ee7cac169b3a9878cba881dc7eb401de2aChris Wren    }
24185e038ee7cac169b3a9878cba881dc7eb401de2aChris Wren
24285e038ee7cac169b3a9878cba881dc7eb401de2aChris Wren    /**
24368efa9b82032ba6e7cf88d56ae7f0662f2702736Chris Wren     * Returns the user specified importance for notifications from the calling package.
244355267b12d57afc082bd253919229321396b12cdChris Wren     *
245355267b12d57afc082bd253919229321396b12cdChris Wren     * @return An importance level, such as {@link #IMPORTANCE_DEFAULT}.
24668efa9b82032ba6e7cf88d56ae7f0662f2702736Chris Wren     */
24768efa9b82032ba6e7cf88d56ae7f0662f2702736Chris Wren    public int getImportance() {
248b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas        if (Build.VERSION.SDK_INT >= 24) {
249b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas            return mNotificationManager.getImportance();
250b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas        } else {
251b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas            return IMPORTANCE_UNSPECIFIED;
252b532ea071639e4799fe51d0cae397561f69bd903Aurimas Liutikas        }
25368efa9b82032ba6e7cf88d56ae7f0662f2702736Chris Wren    }
25468efa9b82032ba6e7cf88d56ae7f0662f2702736Chris Wren
25568efa9b82032ba6e7cf88d56ae7f0662f2702736Chris Wren    /**
256ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * Get the set of packages that have an enabled notification listener component within them.
257ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     */
258ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    public static Set<String> getEnabledListenerPackages(Context context) {
259ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        final String enabledNotificationListeners = Settings.Secure.getString(
260ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                context.getContentResolver(),
261ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                SETTING_ENABLED_NOTIFICATION_LISTENERS);
262c7784e83eb72a5fa7e612a9457bd938639ed211fAlan Viverette        synchronized (sEnabledNotificationListenersLock) {
263c7784e83eb72a5fa7e612a9457bd938639ed211fAlan Viverette            // Parse the string again if it is different from the last time this method was called.
264c7784e83eb72a5fa7e612a9457bd938639ed211fAlan Viverette            if (enabledNotificationListeners != null
265c7784e83eb72a5fa7e612a9457bd938639ed211fAlan Viverette                    && !enabledNotificationListeners.equals(sEnabledNotificationListeners)) {
266c7784e83eb72a5fa7e612a9457bd938639ed211fAlan Viverette                final String[] components = enabledNotificationListeners.split(":");
267c7784e83eb72a5fa7e612a9457bd938639ed211fAlan Viverette                Set<String> packageNames = new HashSet<String>(components.length);
268c7784e83eb72a5fa7e612a9457bd938639ed211fAlan Viverette                for (String component : components) {
269c7784e83eb72a5fa7e612a9457bd938639ed211fAlan Viverette                    ComponentName componentName = ComponentName.unflattenFromString(component);
270c7784e83eb72a5fa7e612a9457bd938639ed211fAlan Viverette                    if (componentName != null) {
271c7784e83eb72a5fa7e612a9457bd938639ed211fAlan Viverette                        packageNames.add(componentName.getPackageName());
272c7784e83eb72a5fa7e612a9457bd938639ed211fAlan Viverette                    }
273ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                }
274ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                sEnabledNotificationListenerPackages = packageNames;
275ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                sEnabledNotificationListeners = enabledNotificationListeners;
276ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
277c7784e83eb72a5fa7e612a9457bd938639ed211fAlan Viverette            return sEnabledNotificationListenerPackages;
278ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
279ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
280ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
281ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /**
282ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * Returns true if this notification should use the side channel for delivery.
283ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     */
284ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private static boolean useSideChannelForNotification(Notification notification) {
285ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        Bundle extras = NotificationCompat.getExtras(notification);
286ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        return extras != null && extras.getBoolean(EXTRA_USE_SIDE_CHANNEL);
287ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
288ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
289ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /**
290ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * Push a notification task for distribution to notification side channels.
291ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     */
292ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private void pushSideChannelQueue(Task task) {
293ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        synchronized (sLock) {
294ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (sSideChannelManager == null) {
295ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                sSideChannelManager = new SideChannelManager(mContext.getApplicationContext());
296ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
297c7784e83eb72a5fa7e612a9457bd938639ed211fAlan Viverette            sSideChannelManager.queueTask(task);
298ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
299ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
300ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
301ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    /**
302ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * Helper class to manage a queue of pending tasks to send to notification side channel
303ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     * listeners.
304ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen     */
305ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private static class SideChannelManager implements Handler.Callback, ServiceConnection {
306ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private static final int MSG_QUEUE_TASK = 0;
307ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private static final int MSG_SERVICE_CONNECTED = 1;
308ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private static final int MSG_SERVICE_DISCONNECTED = 2;
309ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private static final int MSG_RETRY_LISTENER_QUEUE = 3;
310ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
311ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private final Context mContext;
312ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private final HandlerThread mHandlerThread;
313ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private final Handler mHandler;
314ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private final Map<ComponentName, ListenerRecord> mRecordMap =
315ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                new HashMap<ComponentName, ListenerRecord>();
316ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private Set<String> mCachedEnabledPackages = new HashSet<String>();
317ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
318ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        public SideChannelManager(Context context) {
319ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            mContext = context;
320ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            mHandlerThread = new HandlerThread("NotificationManagerCompat");
321ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            mHandlerThread.start();
322ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            mHandler = new Handler(mHandlerThread.getLooper(), this);
323ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
324ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
325ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        /**
326ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         * Queue a new task to be sent to all listeners. This function can be called
327ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         * from any thread.
328ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         */
329ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        public void queueTask(Task task) {
330ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            mHandler.obtainMessage(MSG_QUEUE_TASK, task).sendToTarget();
331ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
332ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
333ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        @Override
334ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        public boolean handleMessage(Message msg) {
335ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            switch (msg.what) {
336ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                case MSG_QUEUE_TASK:
337ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    handleQueueTask((Task) msg.obj);
338ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    return true;
339ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                case MSG_SERVICE_CONNECTED:
340ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    ServiceConnectedEvent event = (ServiceConnectedEvent) msg.obj;
341ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    handleServiceConnected(event.componentName, event.iBinder);
342ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    return true;
343ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                case MSG_SERVICE_DISCONNECTED:
344ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    handleServiceDisconnected((ComponentName) msg.obj);
345ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    return true;
346ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                case MSG_RETRY_LISTENER_QUEUE:
347ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    handleRetryListenerQueue((ComponentName) msg.obj);
348ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    return true;
349ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
350ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            return false;
351ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
352ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
353ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private void handleQueueTask(Task task) {
354ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            updateListenerMap();
355ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            for (ListenerRecord record : mRecordMap.values()) {
356ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                record.taskQueue.add(task);
357ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                processListenerQueue(record);
358ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
359ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
360ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
361ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private void handleServiceConnected(ComponentName componentName, IBinder iBinder) {
362ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            ListenerRecord record = mRecordMap.get(componentName);
363ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (record != null) {
364ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                record.service = INotificationSideChannel.Stub.asInterface(iBinder);
365ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                record.retryCount = 0;
366ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                processListenerQueue(record);
367ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
368ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
369ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
370ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private void handleServiceDisconnected(ComponentName componentName) {
371ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            ListenerRecord record = mRecordMap.get(componentName);
372ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (record != null) {
373ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                ensureServiceUnbound(record);
374ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
375ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
376ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
377ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private void handleRetryListenerQueue(ComponentName componentName) {
378ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            ListenerRecord record = mRecordMap.get(componentName);
379ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (record != null) {
380ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                processListenerQueue(record);
381ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
382ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
383ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
384ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        @Override
385ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
386ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (Log.isLoggable(TAG, Log.DEBUG)) {
387ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                Log.d(TAG, "Connected to service " + componentName);
388ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
389ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            mHandler.obtainMessage(MSG_SERVICE_CONNECTED,
390ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    new ServiceConnectedEvent(componentName, iBinder))
391ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    .sendToTarget();
392ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
393ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
394ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        @Override
395ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        public void onServiceDisconnected(ComponentName componentName) {
396ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (Log.isLoggable(TAG, Log.DEBUG)) {
397ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                Log.d(TAG, "Disconnected from service " + componentName);
398ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
399ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            mHandler.obtainMessage(MSG_SERVICE_DISCONNECTED, componentName).sendToTarget();
400ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
401ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
402ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        /**
403ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         * Check the current list of enabled listener packages and update the records map
404ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         * accordingly.
405ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         */
406ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private void updateListenerMap() {
407ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            Set<String> enabledPackages = getEnabledListenerPackages(mContext);
408ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (enabledPackages.equals(mCachedEnabledPackages)) {
409ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                // Short-circuit when the list of enabled packages has not changed.
410ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                return;
411ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
412ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            mCachedEnabledPackages = enabledPackages;
413ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            List<ResolveInfo> resolveInfos = mContext.getPackageManager().queryIntentServices(
414ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    new Intent().setAction(ACTION_BIND_SIDE_CHANNEL), PackageManager.GET_SERVICES);
415ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            Set<ComponentName> enabledComponents = new HashSet<ComponentName>();
416ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            for (ResolveInfo resolveInfo : resolveInfos) {
417ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                if (!enabledPackages.contains(resolveInfo.serviceInfo.packageName)) {
418ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    continue;
419ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                }
420ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                ComponentName componentName = new ComponentName(
421ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                        resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
422ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                if (resolveInfo.serviceInfo.permission != null) {
423ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    Log.w(TAG, "Permission present on component " + componentName
424ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                            + ", not adding listener record.");
425ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    continue;
426ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                }
427ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                enabledComponents.add(componentName);
428ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
429ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            // Ensure all enabled components have a record in the listener map.
430ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            for (ComponentName componentName : enabledComponents) {
431ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                if (!mRecordMap.containsKey(componentName)) {
432ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    if (Log.isLoggable(TAG, Log.DEBUG)) {
433ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                        Log.d(TAG, "Adding listener record for " + componentName);
434ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    }
435ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    mRecordMap.put(componentName, new ListenerRecord(componentName));
436ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                }
437ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
438ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            // Remove listener records that are no longer for enabled components.
439ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            Iterator<Map.Entry<ComponentName, ListenerRecord>> it =
440ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    mRecordMap.entrySet().iterator();
441ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            while (it.hasNext()) {
442ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                Map.Entry<ComponentName, ListenerRecord> entry = it.next();
443ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                if (!enabledComponents.contains(entry.getKey())) {
444ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    if (Log.isLoggable(TAG, Log.DEBUG)) {
445ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                        Log.d(TAG, "Removing listener record for " + entry.getKey());
446ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    }
447ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    ensureServiceUnbound(entry.getValue());
448ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    it.remove();
449ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                }
450ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
451ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
452ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
453ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        /**
454ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         * Ensure we are already attempting to bind to a service, or start a new binding if not.
455ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         * @return Whether the service bind attempt was successful.
456ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         */
457ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private boolean ensureServiceBound(ListenerRecord record) {
458ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (record.bound) {
459ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                return true;
460ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
461ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            Intent intent = new Intent(ACTION_BIND_SIDE_CHANNEL).setComponent(record.componentName);
462bb8067a02aea52e24708f17933b3689650b2c0abAurimas Liutikas            record.bound = mContext.bindService(intent, this, Service.BIND_AUTO_CREATE
463bb8067a02aea52e24708f17933b3689650b2c0abAurimas Liutikas                    | Service.BIND_WAIVE_PRIORITY);
464ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (record.bound) {
465ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                record.retryCount = 0;
466ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            } else {
467ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                Log.w(TAG, "Unable to bind to listener " + record.componentName);
468ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                mContext.unbindService(this);
469ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
470ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            return record.bound;
471ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
472ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
473ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        /**
474ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         * Ensure we have unbound from a service.
475ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         */
476ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private void ensureServiceUnbound(ListenerRecord record) {
477ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (record.bound) {
478ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                mContext.unbindService(this);
479ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                record.bound = false;
480ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
481ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            record.service = null;
482ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
483ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
484ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        /**
485ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         * Schedule a delayed retry to communicate with a listener service.
486ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         * After a maximum number of attempts (with exponential back-off), start
487ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         * dropping pending tasks for this listener.
488ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         */
489ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private void scheduleListenerRetry(ListenerRecord record) {
490ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (mHandler.hasMessages(MSG_RETRY_LISTENER_QUEUE, record.componentName)) {
491ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                return;
492ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
493ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            record.retryCount++;
494ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (record.retryCount > SIDE_CHANNEL_RETRY_MAX_COUNT) {
495ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                Log.w(TAG, "Giving up on delivering " + record.taskQueue.size() + " tasks to "
496ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                        + record.componentName + " after " + record.retryCount + " retries");
497ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                record.taskQueue.clear();
498ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                return;
499ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
500ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            int delayMs = SIDE_CHANNEL_RETRY_BASE_INTERVAL_MS * (1 << (record.retryCount - 1));
501ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (Log.isLoggable(TAG, Log.DEBUG)) {
502ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                Log.d(TAG, "Scheduling retry for " + delayMs + " ms");
503ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
504ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            Message msg = mHandler.obtainMessage(MSG_RETRY_LISTENER_QUEUE, record.componentName);
505ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            mHandler.sendMessageDelayed(msg, delayMs);
506ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
507ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
508ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        /**
509ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         * Perform a processing step for a listener. First check the bind state, then attempt
510ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         * to flush the task queue, and if an error is encountered, schedule a retry.
511ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen         */
512ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private void processListenerQueue(ListenerRecord record) {
513ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (Log.isLoggable(TAG, Log.DEBUG)) {
514ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                Log.d(TAG, "Processing component " + record.componentName + ", "
515ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                        + record.taskQueue.size() + " queued tasks");
516ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
517ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (record.taskQueue.isEmpty()) {
518ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                return;
519ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
520ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (!ensureServiceBound(record) || record.service == null) {
521ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                // Ensure bind has started and that a service interface is ready to use.
522ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                scheduleListenerRetry(record);
523ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                return;
524ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
525ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            // Attempt to flush all items in the task queue.
526ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            while (true) {
527ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                Task task = record.taskQueue.peek();
528ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                if (task == null) {
529ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    break;
530ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                }
531ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                try {
532ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    if (Log.isLoggable(TAG, Log.DEBUG)) {
533ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                        Log.d(TAG, "Sending task " + task);
534ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    }
535ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    task.send(record.service);
536ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    record.taskQueue.remove();
537ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                } catch (DeadObjectException e) {
538ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    if (Log.isLoggable(TAG, Log.DEBUG)) {
539ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                        Log.d(TAG, "Remote service has died: " + record.componentName);
540ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    }
541ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    break;
542ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                } catch (RemoteException e) {
543ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    Log.w(TAG, "RemoteException communicating with " + record.componentName, e);
544ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    break;
545ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                }
546ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
547ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (!record.taskQueue.isEmpty()) {
548ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                // Some tasks were not sent, meaning an error was encountered, schedule a retry.
549ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                scheduleListenerRetry(record);
550ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
551ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
552ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
553ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        /** A per-side-channel-service listener state record */
554ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        private static class ListenerRecord {
555ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            public final ComponentName componentName;
556ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            /** Whether the service is currently bound to. */
557ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            public boolean bound = false;
558ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            /** The service stub provided by onServiceConnected */
559ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            public INotificationSideChannel service;
560ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            /** Queue of pending tasks to send to this listener service */
561ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            public LinkedList<Task> taskQueue = new LinkedList<Task>();
562ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            /** Number of retries attempted while connecting to this listener service */
563ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            public int retryCount = 0;
564ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
565ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            public ListenerRecord(ComponentName componentName) {
566ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                this.componentName = componentName;
567ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
568ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
569ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
570ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
571ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private static class ServiceConnectedEvent {
572ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        final ComponentName componentName;
573ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        final IBinder iBinder;
574ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
575fa79d6fc148a0642f240377b8ce82acfee7bb890Aurimas Liutikas        ServiceConnectedEvent(ComponentName componentName,
576ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                final IBinder iBinder) {
577ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            this.componentName = componentName;
578ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            this.iBinder = iBinder;
579ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
580ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
581ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
582ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private interface Task {
583fa79d6fc148a0642f240377b8ce82acfee7bb890Aurimas Liutikas        void send(INotificationSideChannel service) throws RemoteException;
584ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
585ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
586ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private static class NotifyTask implements Task {
587ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        final String packageName;
588ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        final int id;
589ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        final String tag;
590ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        final Notification notif;
591ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
592fa79d6fc148a0642f240377b8ce82acfee7bb890Aurimas Liutikas        NotifyTask(String packageName, int id, String tag, Notification notif) {
593ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            this.packageName = packageName;
594ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            this.id = id;
595ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            this.tag = tag;
596ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            this.notif = notif;
597ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
598ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
599ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        @Override
600ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        public void send(INotificationSideChannel service) throws RemoteException {
601ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            service.notify(packageName, id, tag, notif);
602ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
603ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
604fa79d6fc148a0642f240377b8ce82acfee7bb890Aurimas Liutikas        @Override
605ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        public String toString() {
606ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            StringBuilder sb = new StringBuilder("NotifyTask[");
607ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            sb.append("packageName:").append(packageName);
608ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            sb.append(", id:").append(id);
609ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            sb.append(", tag:").append(tag);
610ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            sb.append("]");
611ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            return sb.toString();
612ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
613ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
614ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
615ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    private static class CancelTask implements Task {
616ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        final String packageName;
617ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        final int id;
618ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        final String tag;
619ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        final boolean all;
620ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
621fa79d6fc148a0642f240377b8ce82acfee7bb890Aurimas Liutikas        CancelTask(String packageName) {
622ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            this.packageName = packageName;
623ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            this.id = 0;
624ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            this.tag = null;
625ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            this.all = true;
626ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
627ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
628fa79d6fc148a0642f240377b8ce82acfee7bb890Aurimas Liutikas        CancelTask(String packageName, int id, String tag) {
629ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            this.packageName = packageName;
630ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            this.id = id;
631ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            this.tag = tag;
632ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            this.all = false;
633ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
634ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
635ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        @Override
636ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        public void send(INotificationSideChannel service) throws RemoteException {
637ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (all) {
638ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                service.cancelAll(packageName);
639ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            } else {
640ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                service.cancel(packageName, id, tag);
641ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
642ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
643ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
644fa79d6fc148a0642f240377b8ce82acfee7bb890Aurimas Liutikas        @Override
645ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        public String toString() {
646ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            StringBuilder sb = new StringBuilder("CancelTask[");
647ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            sb.append("packageName:").append(packageName);
648ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            sb.append(", id:").append(id);
649ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            sb.append(", tag:").append(tag);
650ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            sb.append(", all:").append(all);
651ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            sb.append("]");
652ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            return sb.toString();
653ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
654ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
655ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen}
656