OSDN Git Service

Revert "Clearing up invalid entries when SyncStorageEngine starts" am: 4a9d358448...
[android-x86/frameworks-base.git] / services / core / java / com / android / server / notification / NotificationManagerService.java
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
17 package com.android.server.notification;
18
19 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
20 import static android.service.notification.NotificationListenerService.TRIM_FULL;
21 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
22 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
23 import static org.xmlpull.v1.XmlPullParser.END_TAG;
24 import static org.xmlpull.v1.XmlPullParser.START_TAG;
25
26 import android.app.ActivityManager;
27 import android.app.ActivityManagerNative;
28 import android.app.AppGlobals;
29 import android.app.AppOpsManager;
30 import android.app.IActivityManager;
31 import android.app.INotificationManager;
32 import android.app.ITransientNotification;
33 import android.app.Notification;
34 import android.app.NotificationManager;
35 import android.app.NotificationManager.Policy;
36 import android.app.PendingIntent;
37 import android.app.StatusBarManager;
38 import android.app.backup.BackupManager;
39 import android.app.usage.UsageEvents;
40 import android.app.usage.UsageStatsManagerInternal;
41 import android.content.BroadcastReceiver;
42 import android.content.ComponentName;
43 import android.content.ContentResolver;
44 import android.content.Context;
45 import android.content.Intent;
46 import android.content.IntentFilter;
47 import android.content.pm.ApplicationInfo;
48 import android.content.pm.IPackageManager;
49 import android.content.pm.PackageInfo;
50 import android.content.pm.PackageManager;
51 import android.content.pm.PackageManager.NameNotFoundException;
52 import android.content.pm.ParceledListSlice;
53 import android.content.pm.UserInfo;
54 import android.content.res.Resources;
55 import android.database.ContentObserver;
56 import android.media.AudioAttributes;
57 import android.media.AudioManager;
58 import android.media.AudioManagerInternal;
59 import android.media.AudioSystem;
60 import android.media.IRingtonePlayer;
61 import android.net.Uri;
62 import android.os.Binder;
63 import android.os.Build;
64 import android.os.Bundle;
65 import android.os.Environment;
66 import android.os.Handler;
67 import android.os.HandlerThread;
68 import android.os.IBinder;
69 import android.os.IInterface;
70 import android.os.Looper;
71 import android.os.Message;
72 import android.os.Process;
73 import android.os.RemoteException;
74 import android.os.SystemProperties;
75 import android.os.UserHandle;
76 import android.os.UserManager;
77 import android.os.Vibrator;
78 import android.provider.Settings;
79 import android.service.notification.Condition;
80 import android.service.notification.IConditionListener;
81 import android.service.notification.IConditionProvider;
82 import android.service.notification.INotificationListener;
83 import android.service.notification.IStatusBarNotificationHolder;
84 import android.service.notification.NotificationListenerService;
85 import android.service.notification.NotificationRankingUpdate;
86 import android.service.notification.StatusBarNotification;
87 import android.service.notification.ZenModeConfig;
88 import android.telephony.PhoneStateListener;
89 import android.telephony.TelephonyManager;
90 import android.text.TextUtils;
91 import android.util.ArrayMap;
92 import android.util.ArraySet;
93 import android.util.AtomicFile;
94 import android.util.Log;
95 import android.util.Slog;
96 import android.util.Xml;
97 import android.view.accessibility.AccessibilityEvent;
98 import android.view.accessibility.AccessibilityManager;
99 import android.widget.Toast;
100
101 import com.android.internal.R;
102 import com.android.internal.statusbar.NotificationVisibility;
103 import com.android.internal.util.FastXmlSerializer;
104 import com.android.server.EventLogTags;
105 import com.android.server.LocalServices;
106 import com.android.server.SystemService;
107 import com.android.server.lights.Light;
108 import com.android.server.lights.LightsManager;
109 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
110 import com.android.server.notification.ManagedServices.UserProfiles;
111 import com.android.server.statusbar.StatusBarManagerInternal;
112
113 import libcore.io.IoUtils;
114
115 import org.json.JSONArray;
116 import org.json.JSONException;
117 import org.json.JSONObject;
118 import org.xmlpull.v1.XmlPullParser;
119 import org.xmlpull.v1.XmlPullParserException;
120 import org.xmlpull.v1.XmlSerializer;
121
122 import java.io.ByteArrayInputStream;
123 import java.io.ByteArrayOutputStream;
124 import java.io.File;
125 import java.io.FileDescriptor;
126 import java.io.FileInputStream;
127 import java.io.FileNotFoundException;
128 import java.io.FileOutputStream;
129 import java.io.IOException;
130 import java.io.InputStream;
131 import java.io.OutputStream;
132 import java.io.PrintWriter;
133 import java.nio.charset.StandardCharsets;
134 import java.util.ArrayDeque;
135 import java.util.ArrayList;
136 import java.util.HashSet;
137 import java.util.Iterator;
138 import java.util.List;
139 import java.util.Map.Entry;
140 import java.util.Objects;
141
142 /** {@hide} */
143 public class NotificationManagerService extends SystemService {
144     static final String TAG = "NotificationService";
145     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
146     public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE
147             && SystemProperties.getBoolean("debug.child_notifs", false);
148
149     static final int MAX_PACKAGE_NOTIFICATIONS = 50;
150
151     // message codes
152     static final int MESSAGE_TIMEOUT = 2;
153     static final int MESSAGE_SAVE_POLICY_FILE = 3;
154     static final int MESSAGE_RECONSIDER_RANKING = 4;
155     static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
156     static final int MESSAGE_SEND_RANKING_UPDATE = 6;
157     static final int MESSAGE_LISTENER_HINTS_CHANGED = 7;
158     static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8;
159
160     static final int LONG_DELAY = 3500; // 3.5 seconds
161     static final int SHORT_DELAY = 2000; // 2 seconds
162
163     static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
164
165     static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
166
167     static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
168     static final boolean SCORE_ONGOING_HIGHER = false;
169
170     static final int JUNK_SCORE = -1000;
171     static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
172     static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
173
174     // Notifications with scores below this will not interrupt the user, either via LED or
175     // sound or vibration
176     static final int SCORE_INTERRUPTION_THRESHOLD =
177             Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
178
179     static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
180     static final boolean ENABLE_BLOCKED_TOASTS = true;
181
182     // When #matchesCallFilter is called from the ringer, wait at most
183     // 3s to resolve the contacts. This timeout is required since
184     // ContactsProvider might take a long time to start up.
185     //
186     // Return STARRED_CONTACT when the timeout is hit in order to avoid
187     // missed calls in ZEN mode "Important".
188     static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
189     static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
190             ValidateNotificationPeople.STARRED_CONTACT;
191
192     /** notification_enqueue status value for a newly enqueued notification. */
193     private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
194
195     /** notification_enqueue status value for an existing notification. */
196     private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
197
198     /** notification_enqueue status value for an ignored notification. */
199     private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
200
201     private IActivityManager mAm;
202     AudioManager mAudioManager;
203     AudioManagerInternal mAudioManagerInternal;
204     StatusBarManagerInternal mStatusBar;
205     Vibrator mVibrator;
206
207     final IBinder mForegroundToken = new Binder();
208     private WorkerHandler mHandler;
209     private final HandlerThread mRankingThread = new HandlerThread("ranker",
210             Process.THREAD_PRIORITY_BACKGROUND);
211
212     private Light mNotificationLight;
213     Light mAttentionLight;
214     private int mDefaultNotificationColor;
215     private int mDefaultNotificationLedOn;
216
217     private int mDefaultNotificationLedOff;
218     private long[] mDefaultVibrationPattern;
219
220     private long[] mFallbackVibrationPattern;
221     private boolean mUseAttentionLight;
222     boolean mSystemReady;
223
224     private boolean mDisableNotificationEffects;
225     private int mCallState;
226     private String mSoundNotificationKey;
227     private String mVibrateNotificationKey;
228
229     private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>();
230     private ComponentName mEffectsSuppressor;
231     private int mListenerHints;  // right now, all hints are global
232     private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
233
234     // for enabling and disabling notification pulse behavior
235     private boolean mScreenOn = true;
236     private boolean mInCall = false;
237     private boolean mNotificationPulseEnabled;
238
239     // used as a mutex for access to all active notifications & listeners
240     final ArrayList<NotificationRecord> mNotificationList =
241             new ArrayList<NotificationRecord>();
242     final ArrayMap<String, NotificationRecord> mNotificationsByKey =
243             new ArrayMap<String, NotificationRecord>();
244     final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
245     final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
246     final PolicyAccess mPolicyAccess = new PolicyAccess();
247
248     // The last key in this list owns the hardware.
249     ArrayList<String> mLights = new ArrayList<>();
250
251     private AppOpsManager mAppOps;
252     private UsageStatsManagerInternal mAppUsageStats;
253
254     private Archive mArchive;
255
256     // Persistent storage for notification policy
257     private AtomicFile mPolicyFile;
258
259     // Temporary holder for <blocked-packages> config coming from old policy files.
260     private HashSet<String> mBlockedPackages = new HashSet<String>();
261
262     private static final int DB_VERSION = 1;
263
264     private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
265     private static final String ATTR_VERSION = "version";
266
267     // Obsolete:  converted if present, but not resaved to disk.
268     private static final String TAG_BLOCKED_PKGS = "blocked-packages";
269     private static final String TAG_PACKAGE = "package";
270     private static final String ATTR_NAME = "name";
271
272     private RankingHelper mRankingHelper;
273
274     private final UserProfiles mUserProfiles = new UserProfiles();
275     private NotificationListeners mListeners;
276     private ConditionProviders mConditionProviders;
277     private NotificationUsageStats mUsageStats;
278
279     private static final int MY_UID = Process.myUid();
280     private static final int MY_PID = Process.myPid();
281     private static final int REASON_DELEGATE_CLICK = 1;
282     private static final int REASON_DELEGATE_CANCEL = 2;
283     private static final int REASON_DELEGATE_CANCEL_ALL = 3;
284     private static final int REASON_DELEGATE_ERROR = 4;
285     private static final int REASON_PACKAGE_CHANGED = 5;
286     private static final int REASON_USER_STOPPED = 6;
287     private static final int REASON_PACKAGE_BANNED = 7;
288     private static final int REASON_NOMAN_CANCEL = 8;
289     private static final int REASON_NOMAN_CANCEL_ALL = 9;
290     private static final int REASON_LISTENER_CANCEL = 10;
291     private static final int REASON_LISTENER_CANCEL_ALL = 11;
292     private static final int REASON_GROUP_SUMMARY_CANCELED = 12;
293     private static final int REASON_GROUP_OPTIMIZATION = 13;
294
295     private static class Archive {
296         final int mBufferSize;
297         final ArrayDeque<StatusBarNotification> mBuffer;
298
299         public Archive(int size) {
300             mBufferSize = size;
301             mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
302         }
303
304         public String toString() {
305             final StringBuilder sb = new StringBuilder();
306             final int N = mBuffer.size();
307             sb.append("Archive (");
308             sb.append(N);
309             sb.append(" notification");
310             sb.append((N==1)?")":"s)");
311             return sb.toString();
312         }
313
314         public void record(StatusBarNotification nr) {
315             if (mBuffer.size() == mBufferSize) {
316                 mBuffer.removeFirst();
317             }
318
319             // We don't want to store the heavy bits of the notification in the archive,
320             // but other clients in the system process might be using the object, so we
321             // store a (lightened) copy.
322             mBuffer.addLast(nr.cloneLight());
323         }
324
325         public Iterator<StatusBarNotification> descendingIterator() {
326             return mBuffer.descendingIterator();
327         }
328
329         public StatusBarNotification[] getArray(int count) {
330             if (count == 0) count = mBufferSize;
331             final StatusBarNotification[] a
332                     = new StatusBarNotification[Math.min(count, mBuffer.size())];
333             Iterator<StatusBarNotification> iter = descendingIterator();
334             int i=0;
335             while (iter.hasNext() && i < count) {
336                 a[i++] = iter.next();
337             }
338             return a;
339         }
340
341     }
342
343     private void readPolicyXml(InputStream stream, boolean forRestore)
344             throws XmlPullParserException, NumberFormatException, IOException {
345         final XmlPullParser parser = Xml.newPullParser();
346         parser.setInput(stream, StandardCharsets.UTF_8.name());
347
348         int type;
349         String tag;
350         int version = DB_VERSION;
351         while ((type = parser.next()) != END_DOCUMENT) {
352             tag = parser.getName();
353             if (type == START_TAG) {
354                 if (TAG_NOTIFICATION_POLICY.equals(tag)) {
355                     version = Integer.parseInt(
356                             parser.getAttributeValue(null, ATTR_VERSION));
357                 } else if (TAG_BLOCKED_PKGS.equals(tag)) {
358                     while ((type = parser.next()) != END_DOCUMENT) {
359                         tag = parser.getName();
360                         if (TAG_PACKAGE.equals(tag)) {
361                             mBlockedPackages.add(
362                                     parser.getAttributeValue(null, ATTR_NAME));
363                         } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
364                             break;
365                         }
366                     }
367                 }
368             }
369             mZenModeHelper.readXml(parser, forRestore);
370             mRankingHelper.readXml(parser, forRestore);
371         }
372     }
373
374     private void loadPolicyFile() {
375         if (DBG) Slog.d(TAG, "loadPolicyFile");
376         synchronized(mPolicyFile) {
377             mBlockedPackages.clear();
378
379             FileInputStream infile = null;
380             try {
381                 infile = mPolicyFile.openRead();
382                 readPolicyXml(infile, false /*forRestore*/);
383             } catch (FileNotFoundException e) {
384                 // No data yet
385             } catch (IOException e) {
386                 Log.wtf(TAG, "Unable to read notification policy", e);
387             } catch (NumberFormatException e) {
388                 Log.wtf(TAG, "Unable to parse notification policy", e);
389             } catch (XmlPullParserException e) {
390                 Log.wtf(TAG, "Unable to parse notification policy", e);
391             } finally {
392                 IoUtils.closeQuietly(infile);
393             }
394         }
395     }
396
397     public void savePolicyFile() {
398         mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
399         mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
400     }
401
402     private void handleSavePolicyFile() {
403         if (DBG) Slog.d(TAG, "handleSavePolicyFile");
404         synchronized (mPolicyFile) {
405             final FileOutputStream stream;
406             try {
407                 stream = mPolicyFile.startWrite();
408             } catch (IOException e) {
409                 Slog.w(TAG, "Failed to save policy file", e);
410                 return;
411             }
412
413             try {
414                 writePolicyXml(stream, false /*forBackup*/);
415                 mPolicyFile.finishWrite(stream);
416             } catch (IOException e) {
417                 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
418                 mPolicyFile.failWrite(stream);
419             }
420         }
421         BackupManager.dataChanged(getContext().getPackageName());
422     }
423
424     private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
425         final XmlSerializer out = new FastXmlSerializer();
426         out.setOutput(stream, StandardCharsets.UTF_8.name());
427         out.startDocument(null, true);
428         out.startTag(null, TAG_NOTIFICATION_POLICY);
429         out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
430         mZenModeHelper.writeXml(out, forBackup);
431         mRankingHelper.writeXml(out, forBackup);
432         out.endTag(null, TAG_NOTIFICATION_POLICY);
433         out.endDocument();
434     }
435
436     /** Use this when you actually want to post a notification or toast.
437      *
438      * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
439      */
440     private boolean noteNotificationOp(String pkg, int uid) {
441         if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
442                 != AppOpsManager.MODE_ALLOWED) {
443             Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
444             return false;
445         }
446         return true;
447     }
448
449     /** Use this to check if a package can post a notification or toast. */
450     private boolean checkNotificationOp(String pkg, int uid) {
451         return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
452                 == AppOpsManager.MODE_ALLOWED;
453     }
454
455     private static final class ToastRecord
456     {
457         final int pid;
458         final String pkg;
459         final ITransientNotification callback;
460         int duration;
461
462         ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
463         {
464             this.pid = pid;
465             this.pkg = pkg;
466             this.callback = callback;
467             this.duration = duration;
468         }
469
470         void update(int duration) {
471             this.duration = duration;
472         }
473
474         void dump(PrintWriter pw, String prefix, DumpFilter filter) {
475             if (filter != null && !filter.matches(pkg)) return;
476             pw.println(prefix + this);
477         }
478
479         @Override
480         public final String toString()
481         {
482             return "ToastRecord{"
483                 + Integer.toHexString(System.identityHashCode(this))
484                 + " pkg=" + pkg
485                 + " callback=" + callback
486                 + " duration=" + duration;
487         }
488     }
489
490     private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
491
492         @Override
493         public void onSetDisabled(int status) {
494             synchronized (mNotificationList) {
495                 mDisableNotificationEffects =
496                         (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
497                 if (disableNotificationEffects(null) != null) {
498                     // cancel whatever's going on
499                     long identity = Binder.clearCallingIdentity();
500                     try {
501                         final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
502                         if (player != null) {
503                             player.stopAsync();
504                         }
505                     } catch (RemoteException e) {
506                     } finally {
507                         Binder.restoreCallingIdentity(identity);
508                     }
509
510                     identity = Binder.clearCallingIdentity();
511                     try {
512                         mVibrator.cancel();
513                     } finally {
514                         Binder.restoreCallingIdentity(identity);
515                     }
516                 }
517             }
518         }
519
520         @Override
521         public void onClearAll(int callingUid, int callingPid, int userId) {
522             synchronized (mNotificationList) {
523                 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
524                         /*includeCurrentProfiles*/ true);
525             }
526         }
527
528         @Override
529         public void onNotificationClick(int callingUid, int callingPid, String key) {
530             synchronized (mNotificationList) {
531                 NotificationRecord r = mNotificationsByKey.get(key);
532                 if (r == null) {
533                     Log.w(TAG, "No notification with key: " + key);
534                     return;
535                 }
536                 final long now = System.currentTimeMillis();
537                 EventLogTags.writeNotificationClicked(key,
538                         r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
539
540                 StatusBarNotification sbn = r.sbn;
541                 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
542                         sbn.getId(), Notification.FLAG_AUTO_CANCEL,
543                         Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
544                         REASON_DELEGATE_CLICK, null);
545             }
546         }
547
548         @Override
549         public void onNotificationActionClick(int callingUid, int callingPid, String key,
550                 int actionIndex) {
551             synchronized (mNotificationList) {
552                 NotificationRecord r = mNotificationsByKey.get(key);
553                 if (r == null) {
554                     Log.w(TAG, "No notification with key: " + key);
555                     return;
556                 }
557                 final long now = System.currentTimeMillis();
558                 EventLogTags.writeNotificationActionClicked(key, actionIndex,
559                         r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
560                 // TODO: Log action click via UsageStats.
561             }
562         }
563
564         @Override
565         public void onNotificationClear(int callingUid, int callingPid,
566                 String pkg, String tag, int id, int userId) {
567             cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
568                     Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
569                     true, userId, REASON_DELEGATE_CANCEL, null);
570         }
571
572         @Override
573         public void onPanelRevealed(boolean clearEffects, int items) {
574             EventLogTags.writeNotificationPanelRevealed(items);
575             if (clearEffects) {
576                 clearEffects();
577             }
578         }
579
580         @Override
581         public void onPanelHidden() {
582             EventLogTags.writeNotificationPanelHidden();
583         }
584
585         @Override
586         public void clearEffects() {
587             synchronized (mNotificationList) {
588                 if (DBG) Slog.d(TAG, "clearEffects");
589
590                 // sound
591                 mSoundNotificationKey = null;
592
593                 long identity = Binder.clearCallingIdentity();
594                 try {
595                     final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
596                     if (player != null) {
597                         player.stopAsync();
598                     }
599                 } catch (RemoteException e) {
600                 } finally {
601                     Binder.restoreCallingIdentity(identity);
602                 }
603
604                 // vibrate
605                 mVibrateNotificationKey = null;
606                 identity = Binder.clearCallingIdentity();
607                 try {
608                     mVibrator.cancel();
609                 } finally {
610                     Binder.restoreCallingIdentity(identity);
611                 }
612
613                 // light
614                 mLights.clear();
615                 updateLightsLocked();
616             }
617         }
618
619         @Override
620         public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
621                 int uid, int initialPid, String message, int userId) {
622             Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
623                     + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
624             cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
625                     REASON_DELEGATE_ERROR, null);
626             long ident = Binder.clearCallingIdentity();
627             try {
628                 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
629                         "Bad notification posted from package " + pkg
630                         + ": " + message);
631             } catch (RemoteException e) {
632             }
633             Binder.restoreCallingIdentity(ident);
634         }
635
636         @Override
637         public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
638                 NotificationVisibility[] noLongerVisibleKeys) {
639             synchronized (mNotificationList) {
640                 for (NotificationVisibility nv : newlyVisibleKeys) {
641                     NotificationRecord r = mNotificationsByKey.get(nv.key);
642                     if (r == null) continue;
643                     r.setVisibility(true, nv.rank);
644                     nv.recycle();
645                 }
646                 // Note that we might receive this event after notifications
647                 // have already left the system, e.g. after dismissing from the
648                 // shade. Hence not finding notifications in
649                 // mNotificationsByKey is not an exceptional condition.
650                 for (NotificationVisibility nv : noLongerVisibleKeys) {
651                     NotificationRecord r = mNotificationsByKey.get(nv.key);
652                     if (r == null) continue;
653                     r.setVisibility(false, nv.rank);
654                     nv.recycle();
655                 }
656             }
657         }
658
659         @Override
660         public void onNotificationExpansionChanged(String key,
661                 boolean userAction, boolean expanded) {
662             synchronized (mNotificationList) {
663                 NotificationRecord r = mNotificationsByKey.get(key);
664                 if (r != null) {
665                     r.stats.onExpansionChanged(userAction, expanded);
666                     final long now = System.currentTimeMillis();
667                     EventLogTags.writeNotificationExpansion(key,
668                             userAction ? 1 : 0, expanded ? 1 : 0,
669                             r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
670                 }
671             }
672         }
673     };
674
675     private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
676         @Override
677         public void onReceive(Context context, Intent intent) {
678             String action = intent.getAction();
679             if (action == null) {
680                 return;
681             }
682
683             boolean queryRestart = false;
684             boolean queryRemove = false;
685             boolean packageChanged = false;
686             boolean cancelNotifications = true;
687
688             if (action.equals(Intent.ACTION_PACKAGE_ADDED)
689                     || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
690                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
691                     || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
692                     || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
693                     || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
694                 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
695                         UserHandle.USER_ALL);
696                 String pkgList[] = null;
697                 boolean queryReplace = queryRemove &&
698                         intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
699                 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
700                 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
701                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
702                 } else if (queryRestart) {
703                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
704                 } else {
705                     Uri uri = intent.getData();
706                     if (uri == null) {
707                         return;
708                     }
709                     String pkgName = uri.getSchemeSpecificPart();
710                     if (pkgName == null) {
711                         return;
712                     }
713                     if (packageChanged) {
714                         // We cancel notifications for packages which have just been disabled
715                         try {
716                             final IPackageManager pm = AppGlobals.getPackageManager();
717                             final int enabled = pm.getApplicationEnabledSetting(pkgName,
718                                     changeUserId != UserHandle.USER_ALL ? changeUserId :
719                                     UserHandle.USER_OWNER);
720                             if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
721                                     || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
722                                 cancelNotifications = false;
723                             }
724                         } catch (IllegalArgumentException e) {
725                             // Package doesn't exist; probably racing with uninstall.
726                             // cancelNotifications is already true, so nothing to do here.
727                             if (DBG) {
728                                 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
729                             }
730                         } catch (RemoteException e) {
731                             // Failed to talk to PackageManagerService Should never happen!
732                         }
733                     }
734                     pkgList = new String[]{pkgName};
735                 }
736
737                 if (pkgList != null && (pkgList.length > 0)) {
738                     for (String pkgName : pkgList) {
739                         if (cancelNotifications) {
740                             cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
741                                     changeUserId, REASON_PACKAGE_CHANGED, null);
742                         }
743                     }
744                 }
745                 mListeners.onPackagesChanged(queryReplace, pkgList);
746                 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
747                 mRankingHelper.onPackagesChanged(queryReplace, pkgList);
748             }
749         }
750     };
751
752     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
753         @Override
754         public void onReceive(Context context, Intent intent) {
755             String action = intent.getAction();
756
757             if (action.equals(Intent.ACTION_SCREEN_ON)) {
758                 // Keep track of screen on/off state, but do not turn off the notification light
759                 // until user passes through the lock screen or views the notification.
760                 mScreenOn = true;
761                 updateNotificationPulse();
762             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
763                 mScreenOn = false;
764                 updateNotificationPulse();
765             } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
766                 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
767                         .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
768                 updateNotificationPulse();
769             } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
770                 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
771                 if (userHandle >= 0) {
772                     cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
773                             REASON_USER_STOPPED, null);
774                 }
775             } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
776                 // turn off LED when user passes through lock screen
777                 mNotificationLight.turnOff();
778                 mStatusBar.notificationLightOff();
779             } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
780                 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
781                 // reload per-user settings
782                 mSettingsObserver.update(null);
783                 mUserProfiles.updateCache(context);
784                 // Refresh managed services
785                 mConditionProviders.onUserSwitched(user);
786                 mListeners.onUserSwitched(user);
787                 mZenModeHelper.onUserSwitched(user);
788             } else if (action.equals(Intent.ACTION_USER_ADDED)) {
789                 mUserProfiles.updateCache(context);
790             } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
791                 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
792                 mZenModeHelper.onUserRemoved(user);
793             }
794         }
795     };
796
797     private final class SettingsObserver extends ContentObserver {
798         private final Uri NOTIFICATION_LIGHT_PULSE_URI
799                 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
800
801         SettingsObserver(Handler handler) {
802             super(handler);
803         }
804
805         void observe() {
806             ContentResolver resolver = getContext().getContentResolver();
807             resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
808                     false, this, UserHandle.USER_ALL);
809             update(null);
810         }
811
812         @Override public void onChange(boolean selfChange, Uri uri) {
813             update(uri);
814         }
815
816         public void update(Uri uri) {
817             ContentResolver resolver = getContext().getContentResolver();
818             if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
819                 boolean pulseEnabled = Settings.System.getInt(resolver,
820                             Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
821                 if (mNotificationPulseEnabled != pulseEnabled) {
822                     mNotificationPulseEnabled = pulseEnabled;
823                     updateNotificationPulse();
824                 }
825             }
826         }
827     }
828
829     private SettingsObserver mSettingsObserver;
830     private ZenModeHelper mZenModeHelper;
831
832     private final Runnable mBuzzBeepBlinked = new Runnable() {
833         @Override
834         public void run() {
835             mStatusBar.buzzBeepBlinked();
836         }
837     };
838
839     static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
840         int[] ar = r.getIntArray(resid);
841         if (ar == null) {
842             return def;
843         }
844         final int len = ar.length > maxlen ? maxlen : ar.length;
845         long[] out = new long[len];
846         for (int i=0; i<len; i++) {
847             out[i] = ar[i];
848         }
849         return out;
850     }
851
852     public NotificationManagerService(Context context) {
853         super(context);
854     }
855
856     @Override
857     public void onStart() {
858         Resources resources = getContext().getResources();
859
860         mAm = ActivityManagerNative.getDefault();
861         mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
862         mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
863         mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
864
865         mHandler = new WorkerHandler();
866         mRankingThread.start();
867         String[] extractorNames;
868         try {
869             extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
870         } catch (Resources.NotFoundException e) {
871             extractorNames = new String[0];
872         }
873         mUsageStats = new NotificationUsageStats(getContext());
874         mRankingHelper = new RankingHelper(getContext(),
875                 new RankingWorkerHandler(mRankingThread.getLooper()),
876                 mUsageStats,
877                 extractorNames);
878         mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
879         mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
880         mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
881             @Override
882             public void onConfigChanged() {
883                 savePolicyFile();
884             }
885
886             @Override
887             void onZenModeChanged() {
888                 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
889                 synchronized(mNotificationList) {
890                     updateInterruptionFilterLocked();
891                 }
892             }
893
894             @Override
895             void onPolicyChanged() {
896                 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
897             }
898         });
899         final File systemDir = new File(Environment.getDataDirectory(), "system");
900         mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
901
902         importOldBlockDb();
903
904         mListeners = new NotificationListeners();
905         mStatusBar = getLocalService(StatusBarManagerInternal.class);
906         mStatusBar.setNotificationDelegate(mNotificationDelegate);
907
908         final LightsManager lights = getLocalService(LightsManager.class);
909         mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
910         mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
911
912         mDefaultNotificationColor = resources.getColor(
913                 R.color.config_defaultNotificationColor);
914         mDefaultNotificationLedOn = resources.getInteger(
915                 R.integer.config_defaultNotificationLedOn);
916         mDefaultNotificationLedOff = resources.getInteger(
917                 R.integer.config_defaultNotificationLedOff);
918
919         mDefaultVibrationPattern = getLongArray(resources,
920                 R.array.config_defaultNotificationVibePattern,
921                 VIBRATE_PATTERN_MAXLEN,
922                 DEFAULT_VIBRATE_PATTERN);
923
924         mFallbackVibrationPattern = getLongArray(resources,
925                 R.array.config_notificationFallbackVibePattern,
926                 VIBRATE_PATTERN_MAXLEN,
927                 DEFAULT_VIBRATE_PATTERN);
928
929         mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
930
931         // Don't start allowing notifications until the setup wizard has run once.
932         // After that, including subsequent boots, init with notifications turned on.
933         // This works on the first boot because the setup wizard will toggle this
934         // flag at least once and we'll go back to 0 after that.
935         if (0 == Settings.Global.getInt(getContext().getContentResolver(),
936                     Settings.Global.DEVICE_PROVISIONED, 0)) {
937             mDisableNotificationEffects = true;
938         }
939         mZenModeHelper.initZenMode();
940         mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
941
942         mUserProfiles.updateCache(getContext());
943         listenForCallState();
944
945         // register for various Intents
946         IntentFilter filter = new IntentFilter();
947         filter.addAction(Intent.ACTION_SCREEN_ON);
948         filter.addAction(Intent.ACTION_SCREEN_OFF);
949         filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
950         filter.addAction(Intent.ACTION_USER_PRESENT);
951         filter.addAction(Intent.ACTION_USER_STOPPED);
952         filter.addAction(Intent.ACTION_USER_SWITCHED);
953         filter.addAction(Intent.ACTION_USER_ADDED);
954         filter.addAction(Intent.ACTION_USER_REMOVED);
955         getContext().registerReceiver(mIntentReceiver, filter);
956
957         IntentFilter pkgFilter = new IntentFilter();
958         pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
959         pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
960         pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
961         pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
962         pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
963         pkgFilter.addDataScheme("package");
964         getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
965                 null);
966
967         IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
968         getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
969                 null);
970
971         mSettingsObserver = new SettingsObserver(mHandler);
972
973         mArchive = new Archive(resources.getInteger(
974                 R.integer.config_notificationServiceArchiveSize));
975
976         publishBinderService(Context.NOTIFICATION_SERVICE, mService);
977         publishLocalService(NotificationManagerInternal.class, mInternalService);
978     }
979
980     private void sendRegisteredOnlyBroadcast(String action) {
981         getContext().sendBroadcastAsUser(new Intent(action)
982                 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
983     }
984
985     /**
986      * Read the old XML-based app block database and import those blockages into the AppOps system.
987      */
988     private void importOldBlockDb() {
989         loadPolicyFile();
990
991         PackageManager pm = getContext().getPackageManager();
992         for (String pkg : mBlockedPackages) {
993             PackageInfo info = null;
994             try {
995                 info = pm.getPackageInfo(pkg, 0);
996                 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
997             } catch (NameNotFoundException e) {
998                 // forget you
999             }
1000         }
1001         mBlockedPackages.clear();
1002     }
1003
1004     @Override
1005     public void onBootPhase(int phase) {
1006         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1007             // no beeping until we're basically done booting
1008             mSystemReady = true;
1009
1010             // Grab our optional AudioService
1011             mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1012             mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
1013             mZenModeHelper.onSystemReady();
1014         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1015             // This observer will force an update when observe is called, causing us to
1016             // bind to listener services.
1017             mSettingsObserver.observe();
1018             mListeners.onBootPhaseAppsCanStart();
1019             mConditionProviders.onBootPhaseAppsCanStart();
1020         }
1021     }
1022
1023     void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1024         Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
1025
1026         mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1027                 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
1028
1029         // Now, cancel any outstanding notifications that are part of a just-disabled app
1030         if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
1031             cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1032                     REASON_PACKAGE_BANNED, null);
1033         }
1034     }
1035
1036     private void updateListenerHintsLocked() {
1037         final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
1038         if (hints == mListenerHints) return;
1039         ZenLog.traceListenerHintsChanged(mListenerHints, hints, mListenersDisablingEffects.size());
1040         mListenerHints = hints;
1041         scheduleListenerHintsChanged(hints);
1042     }
1043
1044     private void updateEffectsSuppressorLocked() {
1045         final ComponentName suppressor = !mListenersDisablingEffects.isEmpty()
1046                 ? mListenersDisablingEffects.valueAt(0).component : null;
1047         if (Objects.equals(suppressor, mEffectsSuppressor)) return;
1048         ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressor, suppressor);
1049         mEffectsSuppressor = suppressor;
1050         mZenModeHelper.setEffectsSuppressed(suppressor != null);
1051         sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1052     }
1053
1054     private void updateInterruptionFilterLocked() {
1055         int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1056         if (interruptionFilter == mInterruptionFilter) return;
1057         mInterruptionFilter = interruptionFilter;
1058         scheduleInterruptionFilterChanged(interruptionFilter);
1059     }
1060
1061     private final IBinder mService = new INotificationManager.Stub() {
1062         // Toasts
1063         // ============================================================================
1064
1065         @Override
1066         public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1067         {
1068             if (DBG) {
1069                 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1070                         + " duration=" + duration);
1071             }
1072
1073             if (pkg == null || callback == null) {
1074                 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1075                 return ;
1076             }
1077
1078             final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1079
1080             if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1081                 if (!isSystemToast) {
1082                     Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1083                     return;
1084                 }
1085             }
1086
1087             synchronized (mToastQueue) {
1088                 int callingPid = Binder.getCallingPid();
1089                 long callingId = Binder.clearCallingIdentity();
1090                 try {
1091                     ToastRecord record;
1092                     int index = indexOfToastLocked(pkg, callback);
1093                     // If it's already in the queue, we update it in place, we don't
1094                     // move it to the end of the queue.
1095                     if (index >= 0) {
1096                         record = mToastQueue.get(index);
1097                         record.update(duration);
1098                     } else {
1099                         // Limit the number of toasts that any given package except the android
1100                         // package can enqueue.  Prevents DOS attacks and deals with leaks.
1101                         if (!isSystemToast) {
1102                             int count = 0;
1103                             final int N = mToastQueue.size();
1104                             for (int i=0; i<N; i++) {
1105                                  final ToastRecord r = mToastQueue.get(i);
1106                                  if (r.pkg.equals(pkg)) {
1107                                      count++;
1108                                      if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1109                                          Slog.e(TAG, "Package has already posted " + count
1110                                                 + " toasts. Not showing more. Package=" + pkg);
1111                                          return;
1112                                      }
1113                                  }
1114                             }
1115                         }
1116
1117                         record = new ToastRecord(callingPid, pkg, callback, duration);
1118                         mToastQueue.add(record);
1119                         index = mToastQueue.size() - 1;
1120                         keepProcessAliveLocked(callingPid);
1121                     }
1122                     // If it's at index 0, it's the current toast.  It doesn't matter if it's
1123                     // new or just been updated.  Call back and tell it to show itself.
1124                     // If the callback fails, this will remove it from the list, so don't
1125                     // assume that it's valid after this.
1126                     if (index == 0) {
1127                         showNextToastLocked();
1128                     }
1129                 } finally {
1130                     Binder.restoreCallingIdentity(callingId);
1131                 }
1132             }
1133         }
1134
1135         @Override
1136         public void cancelToast(String pkg, ITransientNotification callback) {
1137             Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1138
1139             if (pkg == null || callback == null) {
1140                 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1141                 return ;
1142             }
1143
1144             synchronized (mToastQueue) {
1145                 long callingId = Binder.clearCallingIdentity();
1146                 try {
1147                     int index = indexOfToastLocked(pkg, callback);
1148                     if (index >= 0) {
1149                         cancelToastLocked(index);
1150                     } else {
1151                         Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1152                                 + " callback=" + callback);
1153                     }
1154                 } finally {
1155                     Binder.restoreCallingIdentity(callingId);
1156                 }
1157             }
1158         }
1159
1160         @Override
1161         public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1162                 Notification notification, int[] idOut, int userId) throws RemoteException {
1163             enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1164                     Binder.getCallingPid(), tag, id, notification, idOut, userId);
1165         }
1166
1167         @Override
1168         public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1169             checkCallerIsSystemOrSameApp(pkg);
1170             userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1171                     Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1172             // Don't allow client applications to cancel foreground service notis.
1173             cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1174                     Binder.getCallingUid() == Process.SYSTEM_UID
1175                     ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1176                     null);
1177         }
1178
1179         @Override
1180         public void cancelAllNotifications(String pkg, int userId) {
1181             checkCallerIsSystemOrSameApp(pkg);
1182
1183             userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1184                     Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1185
1186             // Calling from user space, don't allow the canceling of actively
1187             // running foreground services.
1188             cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1189                     pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1190                     REASON_NOMAN_CANCEL_ALL, null);
1191         }
1192
1193         @Override
1194         public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1195             checkCallerIsSystem();
1196
1197             setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1198         }
1199
1200         /**
1201          * Use this when you just want to know if notifications are OK for this package.
1202          */
1203         @Override
1204         public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1205             checkCallerIsSystem();
1206             return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1207                     == AppOpsManager.MODE_ALLOWED);
1208         }
1209
1210         @Override
1211         public void setPackagePriority(String pkg, int uid, int priority) {
1212             checkCallerIsSystem();
1213             mRankingHelper.setPackagePriority(pkg, uid, priority);
1214             savePolicyFile();
1215         }
1216
1217         @Override
1218         public int getPackagePriority(String pkg, int uid) {
1219             checkCallerIsSystem();
1220             return mRankingHelper.getPackagePriority(pkg, uid);
1221         }
1222
1223         @Override
1224         public void setPackagePeekable(String pkg, int uid, boolean peekable) {
1225             checkCallerIsSystem();
1226
1227             mRankingHelper.setPackagePeekable(pkg, uid, peekable);
1228         }
1229
1230         @Override
1231         public boolean getPackagePeekable(String pkg, int uid) {
1232             checkCallerIsSystem();
1233             return mRankingHelper.getPackagePeekable(pkg, uid);
1234         }
1235
1236         @Override
1237         public void setPackageVisibilityOverride(String pkg, int uid, int visibility) {
1238             checkCallerIsSystem();
1239             mRankingHelper.setPackageVisibilityOverride(pkg, uid, visibility);
1240             savePolicyFile();
1241         }
1242
1243         @Override
1244         public int getPackageVisibilityOverride(String pkg, int uid) {
1245             checkCallerIsSystem();
1246             return mRankingHelper.getPackageVisibilityOverride(pkg, uid);
1247         }
1248
1249         /**
1250          * System-only API for getting a list of current (i.e. not cleared) notifications.
1251          *
1252          * Requires ACCESS_NOTIFICATIONS which is signature|system.
1253          * @returns A list of all the notifications, in natural order.
1254          */
1255         @Override
1256         public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1257             // enforce() will ensure the calling uid has the correct permission
1258             getContext().enforceCallingOrSelfPermission(
1259                     android.Manifest.permission.ACCESS_NOTIFICATIONS,
1260                     "NotificationManagerService.getActiveNotifications");
1261
1262             StatusBarNotification[] tmp = null;
1263             int uid = Binder.getCallingUid();
1264
1265             // noteOp will check to make sure the callingPkg matches the uid
1266             if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1267                     == AppOpsManager.MODE_ALLOWED) {
1268                 synchronized (mNotificationList) {
1269                     tmp = new StatusBarNotification[mNotificationList.size()];
1270                     final int N = mNotificationList.size();
1271                     for (int i=0; i<N; i++) {
1272                         tmp[i] = mNotificationList.get(i).sbn;
1273                     }
1274                 }
1275             }
1276             return tmp;
1277         }
1278
1279         /**
1280          * Public API for getting a list of current notifications for the calling package/uid.
1281          *
1282          * @returns A list of all the package's notifications, in natural order.
1283          */
1284         @Override
1285         public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1286                 int incomingUserId) {
1287             checkCallerIsSystemOrSameApp(pkg);
1288             int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1289                     Binder.getCallingUid(), incomingUserId, true, false,
1290                     "getAppActiveNotifications", pkg);
1291
1292             final ArrayList<StatusBarNotification> list
1293                     = new ArrayList<StatusBarNotification>(mNotificationList.size());
1294
1295             synchronized (mNotificationList) {
1296                 final int N = mNotificationList.size();
1297                 for (int i = 0; i < N; i++) {
1298                     final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1299                     if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
1300                         // We could pass back a cloneLight() but clients might get confused and
1301                         // try to send this thing back to notify() again, which would not work
1302                         // very well.
1303                         final StatusBarNotification sbnOut = new StatusBarNotification(
1304                                 sbn.getPackageName(),
1305                                 sbn.getOpPkg(),
1306                                 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1307                                 0, // hide score from apps
1308                                 sbn.getNotification().clone(),
1309                                 sbn.getUser(), sbn.getPostTime());
1310                         list.add(sbnOut);
1311                     }
1312                 }
1313             }
1314
1315             return new ParceledListSlice<StatusBarNotification>(list);
1316         }
1317
1318         /**
1319          * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1320          *
1321          * Requires ACCESS_NOTIFICATIONS which is signature|system.
1322          */
1323         @Override
1324         public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1325             // enforce() will ensure the calling uid has the correct permission
1326             getContext().enforceCallingOrSelfPermission(
1327                     android.Manifest.permission.ACCESS_NOTIFICATIONS,
1328                     "NotificationManagerService.getHistoricalNotifications");
1329
1330             StatusBarNotification[] tmp = null;
1331             int uid = Binder.getCallingUid();
1332
1333             // noteOp will check to make sure the callingPkg matches the uid
1334             if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1335                     == AppOpsManager.MODE_ALLOWED) {
1336                 synchronized (mArchive) {
1337                     tmp = mArchive.getArray(count);
1338                 }
1339             }
1340             return tmp;
1341         }
1342
1343         /**
1344          * Register a listener binder directly with the notification manager.
1345          *
1346          * Only works with system callers. Apps should extend
1347          * {@link android.service.notification.NotificationListenerService}.
1348          */
1349         @Override
1350         public void registerListener(final INotificationListener listener,
1351                 final ComponentName component, final int userid) {
1352             enforceSystemOrSystemUI("INotificationManager.registerListener");
1353             mListeners.registerService(listener, component, userid);
1354         }
1355
1356         /**
1357          * Remove a listener binder directly
1358          */
1359         @Override
1360         public void unregisterListener(INotificationListener listener, int userid) {
1361             mListeners.unregisterService(listener, userid);
1362         }
1363
1364         /**
1365          * Allow an INotificationListener to simulate a "clear all" operation.
1366          *
1367          * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1368          *
1369          * @param token The binder for the listener, to check that the caller is allowed
1370          */
1371         @Override
1372         public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1373             final int callingUid = Binder.getCallingUid();
1374             final int callingPid = Binder.getCallingPid();
1375             long identity = Binder.clearCallingIdentity();
1376             try {
1377                 synchronized (mNotificationList) {
1378                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1379                     if (keys != null) {
1380                         final int N = keys.length;
1381                         for (int i = 0; i < N; i++) {
1382                             NotificationRecord r = mNotificationsByKey.get(keys[i]);
1383                             if (r == null) continue;
1384                             final int userId = r.sbn.getUserId();
1385                             if (userId != info.userid && userId != UserHandle.USER_ALL &&
1386                                     !mUserProfiles.isCurrentProfile(userId)) {
1387                                 throw new SecurityException("Disallowed call from listener: "
1388                                         + info.service);
1389                             }
1390                             cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1391                                     r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1392                                     userId);
1393                         }
1394                     } else {
1395                         cancelAllLocked(callingUid, callingPid, info.userid,
1396                                 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1397                     }
1398                 }
1399             } finally {
1400                 Binder.restoreCallingIdentity(identity);
1401             }
1402         }
1403
1404         @Override
1405         public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
1406             long identity = Binder.clearCallingIdentity();
1407             try {
1408                 synchronized (mNotificationList) {
1409                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1410                     if (keys != null) {
1411                         final int N = keys.length;
1412                         for (int i = 0; i < N; i++) {
1413                             NotificationRecord r = mNotificationsByKey.get(keys[i]);
1414                             if (r == null) continue;
1415                             final int userId = r.sbn.getUserId();
1416                             if (userId != info.userid && userId != UserHandle.USER_ALL &&
1417                                     !mUserProfiles.isCurrentProfile(userId)) {
1418                                 throw new SecurityException("Disallowed call from listener: "
1419                                         + info.service);
1420                             }
1421                             if (!r.isSeen()) {
1422                                 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
1423                                 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
1424                                         userId == UserHandle.USER_ALL ? UserHandle.USER_OWNER
1425                                                 : userId,
1426                                         UsageEvents.Event.USER_INTERACTION);
1427                                 r.setSeen();
1428                             }
1429                         }
1430                     }
1431                 }
1432             } finally {
1433                 Binder.restoreCallingIdentity(identity);
1434             }
1435         }
1436
1437         private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
1438                 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
1439             cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1440                     Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1441                     true,
1442                     userId, REASON_LISTENER_CANCEL, info);
1443         }
1444
1445         /**
1446          * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1447          *
1448          * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1449          *
1450          * @param token The binder for the listener, to check that the caller is allowed
1451          */
1452         @Override
1453         public void cancelNotificationFromListener(INotificationListener token, String pkg,
1454                 String tag, int id) {
1455             final int callingUid = Binder.getCallingUid();
1456             final int callingPid = Binder.getCallingPid();
1457             long identity = Binder.clearCallingIdentity();
1458             try {
1459                 synchronized (mNotificationList) {
1460                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1461                     if (info.supportsProfiles()) {
1462                         Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1463                                 + "from " + info.component
1464                                 + " use cancelNotification(key) instead.");
1465                     } else {
1466                         cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1467                                 pkg, tag, id, info.userid);
1468                     }
1469                 }
1470             } finally {
1471                 Binder.restoreCallingIdentity(identity);
1472             }
1473         }
1474
1475         /**
1476          * Allow an INotificationListener to request the list of outstanding notifications seen by
1477          * the current user. Useful when starting up, after which point the listener callbacks
1478          * should be used.
1479          *
1480          * @param token The binder for the listener, to check that the caller is allowed
1481          * @param keys An array of notification keys to fetch, or null to fetch everything
1482          * @returns The return value will contain the notifications specified in keys, in that
1483          *      order, or if keys is null, all the notifications, in natural order.
1484          */
1485         @Override
1486         public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
1487                 INotificationListener token, String[] keys, int trim) {
1488             synchronized (mNotificationList) {
1489                 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1490                 final boolean getKeys = keys != null;
1491                 final int N = getKeys ? keys.length : mNotificationList.size();
1492                 final ArrayList<StatusBarNotification> list
1493                         = new ArrayList<StatusBarNotification>(N);
1494                 for (int i=0; i<N; i++) {
1495                     final NotificationRecord r = getKeys
1496                             ? mNotificationsByKey.get(keys[i])
1497                             : mNotificationList.get(i);
1498                     if (r == null) continue;
1499                     StatusBarNotification sbn = r.sbn;
1500                     if (!isVisibleToListener(sbn, info)) continue;
1501                     StatusBarNotification sbnToSend =
1502                             (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1503                     list.add(sbnToSend);
1504                 }
1505                 return new ParceledListSlice<StatusBarNotification>(list);
1506             }
1507         }
1508
1509         @Override
1510         public void requestHintsFromListener(INotificationListener token, int hints) {
1511             final long identity = Binder.clearCallingIdentity();
1512             try {
1513                 synchronized (mNotificationList) {
1514                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1515                     final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0;
1516                     if (disableEffects) {
1517                         mListenersDisablingEffects.add(info);
1518                     } else {
1519                         mListenersDisablingEffects.remove(info);
1520                     }
1521                     updateListenerHintsLocked();
1522                     updateEffectsSuppressorLocked();
1523                 }
1524             } finally {
1525                 Binder.restoreCallingIdentity(identity);
1526             }
1527         }
1528
1529         @Override
1530         public int getHintsFromListener(INotificationListener token) {
1531             synchronized (mNotificationList) {
1532                 return mListenerHints;
1533             }
1534         }
1535
1536         @Override
1537         public void requestInterruptionFilterFromListener(INotificationListener token,
1538                 int interruptionFilter) throws RemoteException {
1539             final long identity = Binder.clearCallingIdentity();
1540             try {
1541                 synchronized (mNotificationList) {
1542                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1543                     mZenModeHelper.requestFromListener(info.component, interruptionFilter);
1544                     updateInterruptionFilterLocked();
1545                 }
1546             } finally {
1547                 Binder.restoreCallingIdentity(identity);
1548             }
1549         }
1550
1551         @Override
1552         public int getInterruptionFilterFromListener(INotificationListener token)
1553                 throws RemoteException {
1554             synchronized (mNotificationLight) {
1555                 return mInterruptionFilter;
1556             }
1557         }
1558
1559         @Override
1560         public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1561                 throws RemoteException {
1562             synchronized (mNotificationList) {
1563                 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1564                 if (info == null) return;
1565                 mListeners.setOnNotificationPostedTrimLocked(info, trim);
1566             }
1567         }
1568
1569         @Override
1570         public int getZenMode() {
1571             return mZenModeHelper.getZenMode();
1572         }
1573
1574         @Override
1575         public ZenModeConfig getZenModeConfig() {
1576             enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig");
1577             return mZenModeHelper.getConfig();
1578         }
1579
1580         @Override
1581         public boolean setZenModeConfig(ZenModeConfig config, String reason) {
1582             checkCallerIsSystem();
1583             return mZenModeHelper.setConfig(config, reason);
1584         }
1585
1586         @Override
1587         public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
1588             enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
1589             final long identity = Binder.clearCallingIdentity();
1590             try {
1591                 mZenModeHelper.setManualZenMode(mode, conditionId, reason);
1592             } finally {
1593                 Binder.restoreCallingIdentity(identity);
1594             }
1595         }
1596
1597         @Override
1598         public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
1599             enforcePolicyAccess(pkg, "setInterruptionFilter");
1600             final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
1601             if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
1602             final long identity = Binder.clearCallingIdentity();
1603             try {
1604                 mZenModeHelper.setManualZenMode(zen, null, "setInterruptionFilter");
1605             } finally {
1606                 Binder.restoreCallingIdentity(identity);
1607             }
1608         }
1609
1610         @Override
1611         public void notifyConditions(final String pkg, IConditionProvider provider,
1612                 final Condition[] conditions) {
1613             final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1614             checkCallerIsSystemOrSameApp(pkg);
1615             mHandler.post(new Runnable() {
1616                 @Override
1617                 public void run() {
1618                     mConditionProviders.notifyConditions(pkg, info, conditions);
1619                 }
1620             });
1621         }
1622
1623         @Override
1624         public void requestZenModeConditions(IConditionListener callback, int relevance) {
1625             enforceSystemOrSystemUIOrVolume("INotificationManager.requestZenModeConditions");
1626             mZenModeHelper.requestZenModeConditions(callback, relevance);
1627         }
1628
1629         private void enforceSystemOrSystemUIOrVolume(String message) {
1630             if (mAudioManagerInternal != null) {
1631                 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
1632                 if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
1633                     return;
1634                 }
1635             }
1636             enforceSystemOrSystemUI(message);
1637         }
1638
1639         private void enforceSystemOrSystemUI(String message) {
1640             if (isCallerSystem()) return;
1641             getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1642                     message);
1643         }
1644
1645         private void enforcePolicyAccess(String pkg, String method) {
1646             checkCallerIsSameApp(pkg);
1647             if (!checkPolicyAccess(pkg)) {
1648                 Slog.w(TAG, "Notification policy access denied calling " + method);
1649                 throw new SecurityException("Notification policy access denied");
1650             }
1651         }
1652
1653         private boolean checkPackagePolicyAccess(String pkg) {
1654             return mPolicyAccess.isPackageGranted(pkg);
1655         }
1656
1657         private boolean checkPolicyAccess(String pkg) {
1658             return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
1659         }
1660
1661         @Override
1662         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1663             if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1664                     != PackageManager.PERMISSION_GRANTED) {
1665                 pw.println("Permission Denial: can't dump NotificationManager from pid="
1666                         + Binder.getCallingPid()
1667                         + ", uid=" + Binder.getCallingUid());
1668                 return;
1669             }
1670
1671             final DumpFilter filter = DumpFilter.parseFromArguments(args);
1672             if (filter != null && filter.stats) {
1673                 dumpJson(pw, filter);
1674             } else {
1675                 dumpImpl(pw, filter);
1676             }
1677         }
1678
1679         @Override
1680         public ComponentName getEffectsSuppressor() {
1681             enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
1682             return mEffectsSuppressor;
1683         }
1684
1685         @Override
1686         public boolean matchesCallFilter(Bundle extras) {
1687             enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
1688             return mZenModeHelper.matchesCallFilter(
1689                     UserHandle.getCallingUserHandle(),
1690                     extras,
1691                     mRankingHelper.findExtractor(ValidateNotificationPeople.class),
1692                     MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
1693                     MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
1694         }
1695
1696         @Override
1697         public boolean isSystemConditionProviderEnabled(String path) {
1698             enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
1699             return mConditionProviders.isSystemProviderEnabled(path);
1700         }
1701
1702         // Backup/restore interface
1703         @Override
1704         public byte[] getBackupPayload(int user) {
1705             if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
1706             if (user != UserHandle.USER_OWNER) {
1707                 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
1708                 return null;
1709             }
1710             final ByteArrayOutputStream baos = new ByteArrayOutputStream();
1711             try {
1712                 writePolicyXml(baos, true /*forBackup*/);
1713                 return baos.toByteArray();
1714             } catch (IOException e) {
1715                 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
1716             }
1717             return null;
1718         }
1719
1720         @Override
1721         public void applyRestore(byte[] payload, int user) {
1722             if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
1723                     + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
1724             if (payload == null) {
1725                 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
1726                 return;
1727             }
1728             if (user != UserHandle.USER_OWNER) {
1729                 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
1730                 return;
1731             }
1732             final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
1733             try {
1734                 readPolicyXml(bais, true /*forRestore*/);
1735                 savePolicyFile();
1736             } catch (NumberFormatException | XmlPullParserException | IOException e) {
1737                 Slog.w(TAG, "applyRestore: error reading payload", e);
1738             }
1739         }
1740
1741         @Override
1742         public boolean isNotificationPolicyAccessGranted(String pkg) {
1743             return checkPolicyAccess(pkg);
1744         }
1745
1746         @Override
1747         public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {
1748             enforceSystemOrSystemUI("request policy access status for another package");
1749             return checkPackagePolicyAccess(pkg);
1750         }
1751
1752         @Override
1753         public String[] getPackagesRequestingNotificationPolicyAccess()
1754                 throws RemoteException {
1755             enforceSystemOrSystemUI("request policy access packages");
1756             final long identity = Binder.clearCallingIdentity();
1757             try {
1758                 return mPolicyAccess.getRequestingPackages();
1759             } finally {
1760                 Binder.restoreCallingIdentity(identity);
1761             }
1762         }
1763
1764         @Override
1765         public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
1766                 throws RemoteException {
1767             enforceSystemOrSystemUI("grant notification policy access");
1768             final long identity = Binder.clearCallingIdentity();
1769             try {
1770                 synchronized (mNotificationList) {
1771                     mPolicyAccess.put(pkg, granted);
1772                 }
1773             } finally {
1774                 Binder.restoreCallingIdentity(identity);
1775             }
1776         }
1777
1778         @Override
1779         public Policy getNotificationPolicy(String pkg) {
1780             enforcePolicyAccess(pkg, "getNotificationPolicy");
1781             final long identity = Binder.clearCallingIdentity();
1782             try {
1783                 return mZenModeHelper.getNotificationPolicy();
1784             } finally {
1785                 Binder.restoreCallingIdentity(identity);
1786             }
1787         }
1788
1789         @Override
1790         public void setNotificationPolicy(String pkg, Policy policy) {
1791             enforcePolicyAccess(pkg, "setNotificationPolicy");
1792             final long identity = Binder.clearCallingIdentity();
1793             try {
1794                 mZenModeHelper.setNotificationPolicy(policy);
1795             } finally {
1796                 Binder.restoreCallingIdentity(identity);
1797             }
1798         }
1799     };
1800
1801     private String disableNotificationEffects(NotificationRecord record) {
1802         if (mDisableNotificationEffects) {
1803             return "booleanState";
1804         }
1805         if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1806             return "listenerHints";
1807         }
1808         if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
1809             return "callState";
1810         }
1811         return null;
1812     };
1813
1814     private void dumpJson(PrintWriter pw, DumpFilter filter) {
1815         JSONObject dump = new JSONObject();
1816         try {
1817             dump.put("service", "Notification Manager");
1818             JSONArray bans = new JSONArray();
1819             try {
1820                 ArrayMap<Integer, ArrayList<String>> packageBans = getPackageBans(filter);
1821                 for (Integer userId : packageBans.keySet()) {
1822                     for (String packageName : packageBans.get(userId)) {
1823                         JSONObject ban = new JSONObject();
1824                         ban.put("userId", userId);
1825                         ban.put("packageName", packageName);
1826                         bans.put(ban);
1827                     }
1828                 }
1829             } catch (NameNotFoundException e) {
1830                 // pass
1831             }
1832             dump.put("bans", bans);
1833             dump.put("stats", mUsageStats.dumpJson(filter));
1834         } catch (JSONException e) {
1835             e.printStackTrace();
1836         }
1837         pw.println(dump);
1838     }
1839
1840     void dumpImpl(PrintWriter pw, DumpFilter filter) {
1841         pw.print("Current Notification Manager state");
1842         if (filter.filtered) {
1843             pw.print(" (filtered to "); pw.print(filter); pw.print(")");
1844         }
1845         pw.println(':');
1846         int N;
1847         final boolean zenOnly = filter.filtered && filter.zen;
1848
1849         if (!zenOnly) {
1850             synchronized (mToastQueue) {
1851                 N = mToastQueue.size();
1852                 if (N > 0) {
1853                     pw.println("  Toast Queue:");
1854                     for (int i=0; i<N; i++) {
1855                         mToastQueue.get(i).dump(pw, "    ", filter);
1856                     }
1857                     pw.println("  ");
1858                 }
1859             }
1860         }
1861
1862         synchronized (mNotificationList) {
1863             if (!zenOnly) {
1864                 N = mNotificationList.size();
1865                 if (N > 0) {
1866                     pw.println("  Notification List:");
1867                     for (int i=0; i<N; i++) {
1868                         final NotificationRecord nr = mNotificationList.get(i);
1869                         if (filter.filtered && !filter.matches(nr.sbn)) continue;
1870                         nr.dump(pw, "    ", getContext(), filter.redact);
1871                     }
1872                     pw.println("  ");
1873                 }
1874
1875                 if (!filter.filtered) {
1876                     N = mLights.size();
1877                     if (N > 0) {
1878                         pw.println("  Lights List:");
1879                         for (int i=0; i<N; i++) {
1880                             if (i == N - 1) {
1881                                 pw.print("  > ");
1882                             } else {
1883                                 pw.print("    ");
1884                             }
1885                             pw.println(mLights.get(i));
1886                         }
1887                         pw.println("  ");
1888                     }
1889                     pw.println("  mUseAttentionLight=" + mUseAttentionLight);
1890                     pw.println("  mNotificationPulseEnabled=" + mNotificationPulseEnabled);
1891                     pw.println("  mSoundNotificationKey=" + mSoundNotificationKey);
1892                     pw.println("  mVibrateNotificationKey=" + mVibrateNotificationKey);
1893                     pw.println("  mDisableNotificationEffects=" + mDisableNotificationEffects);
1894                     pw.println("  mCallState=" + callStateToString(mCallState));
1895                     pw.println("  mSystemReady=" + mSystemReady);
1896                 }
1897                 pw.println("  mArchive=" + mArchive.toString());
1898                 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1899                 int i=0;
1900                 while (iter.hasNext()) {
1901                     final StatusBarNotification sbn = iter.next();
1902                     if (filter != null && !filter.matches(sbn)) continue;
1903                     pw.println("    " + sbn);
1904                     if (++i >= 5) {
1905                         if (iter.hasNext()) pw.println("    ...");
1906                         break;
1907                     }
1908                 }
1909             }
1910
1911             if (!zenOnly) {
1912                 pw.println("\n  Usage Stats:");
1913                 mUsageStats.dump(pw, "    ", filter);
1914             }
1915
1916             if (!filter.filtered || zenOnly) {
1917                 pw.println("\n  Zen Mode:");
1918                 pw.print("    mInterruptionFilter="); pw.println(mInterruptionFilter);
1919                 mZenModeHelper.dump(pw, "    ");
1920
1921                 pw.println("\n  Zen Log:");
1922                 ZenLog.dump(pw, "    ");
1923             }
1924
1925             if (!zenOnly) {
1926                 pw.println("\n  Ranking Config:");
1927                 mRankingHelper.dump(pw, "    ", filter);
1928
1929                 pw.println("\n  Notification listeners:");
1930                 mListeners.dump(pw, filter);
1931                 pw.print("    mListenerHints: "); pw.println(mListenerHints);
1932                 pw.print("    mListenersDisablingEffects: (");
1933                 N = mListenersDisablingEffects.size();
1934                 for (int i = 0; i < N; i++) {
1935                     final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i);
1936                     if (i > 0) pw.print(',');
1937                     pw.print(listener.component);
1938                 }
1939                 pw.println(')');
1940             }
1941             pw.println("\n  Policy access:");
1942             pw.print("    mPolicyAccess: "); pw.println(mPolicyAccess);
1943
1944             pw.println("\n  Condition providers:");
1945             mConditionProviders.dump(pw, filter);
1946
1947             pw.println("\n  Group summaries:");
1948             for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
1949                 NotificationRecord r = entry.getValue();
1950                 pw.println("    " + entry.getKey() + " -> " + r.getKey());
1951                 if (mNotificationsByKey.get(r.getKey()) != r) {
1952                     pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
1953                     r.dump(pw, "      ", getContext(), filter.redact);
1954                 }
1955             }
1956
1957             try {
1958                 pw.println("\n  Banned Packages:");
1959                 ArrayMap<Integer, ArrayList<String>> packageBans = getPackageBans(filter);
1960                 for (Integer userId : packageBans.keySet()) {
1961                     for (String packageName : packageBans.get(userId)) {
1962                         pw.println("    " + userId + ": " + packageName);
1963                     }
1964                 }
1965             } catch (NameNotFoundException e) {
1966                 // pass
1967             }
1968         }
1969     }
1970
1971     private ArrayMap<Integer, ArrayList<String>> getPackageBans(DumpFilter filter)
1972             throws NameNotFoundException {
1973         ArrayMap<Integer, ArrayList<String>> packageBans = new ArrayMap<>();
1974         ArrayList<String> packageNames = new ArrayList<>();
1975         for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1976             final int userId = user.getUserHandle().getIdentifier();
1977             final PackageManager packageManager = getContext().getPackageManager();
1978             List<PackageInfo> packages = packageManager.getInstalledPackages(0, userId);
1979             final int packageCount = packages.size();
1980             for (int p = 0; p < packageCount; p++) {
1981                 final String packageName = packages.get(p).packageName;
1982                 if (filter == null || filter.matches(packageName)) {
1983                     final int uid = packageManager.getPackageUid(packageName, userId);
1984                     if (!checkNotificationOp(packageName, uid)) {
1985                         packageNames.add(packageName);
1986                     }
1987                 }
1988             }
1989             if (!packageNames.isEmpty()) {
1990                 packageBans.put(userId, packageNames);
1991                 packageNames = new ArrayList<>();
1992             }
1993         }
1994         return packageBans;
1995     }
1996
1997     /**
1998      * The private API only accessible to the system process.
1999      */
2000     private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
2001         @Override
2002         public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
2003                 String tag, int id, Notification notification, int[] idReceived, int userId) {
2004             enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
2005                     idReceived, userId);
2006         }
2007
2008         @Override
2009         public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
2010                 int userId) {
2011             checkCallerIsSystem();
2012             synchronized (mNotificationList) {
2013                 int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
2014                 if (i < 0) {
2015                     Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
2016                             + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
2017                     return;
2018                 }
2019                 NotificationRecord r = mNotificationList.get(i);
2020                 StatusBarNotification sbn = r.sbn;
2021                 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
2022                 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
2023                 // we have to revert to the flags we received initially *and* force remove
2024                 // FLAG_FOREGROUND_SERVICE.
2025                 sbn.getNotification().flags =
2026                         (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
2027                 mRankingHelper.sort(mNotificationList);
2028                 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
2029             }
2030         }
2031     };
2032
2033     void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
2034             final int callingPid, final String tag, final int id, final Notification notification,
2035             int[] idOut, int incomingUserId) {
2036         if (DBG) {
2037             Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
2038                     + " notification=" + notification);
2039         }
2040         checkCallerIsSystemOrSameApp(pkg);
2041         final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
2042         final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
2043
2044         final int userId = ActivityManager.handleIncomingUser(callingPid,
2045                 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
2046         final UserHandle user = new UserHandle(userId);
2047
2048         // Limit the number of notifications that any given package except the android
2049         // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
2050         if (!isSystemNotification && !isNotificationFromListener) {
2051             synchronized (mNotificationList) {
2052                 int count = 0;
2053                 final int N = mNotificationList.size();
2054                 for (int i=0; i<N; i++) {
2055                     final NotificationRecord r = mNotificationList.get(i);
2056                     if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
2057                         if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {
2058                             break;  // Allow updating existing notification
2059                         }
2060                         count++;
2061                         if (count >= MAX_PACKAGE_NOTIFICATIONS) {
2062                             Slog.e(TAG, "Package has already posted " + count
2063                                     + " notifications.  Not showing more.  package=" + pkg);
2064                             return;
2065                         }
2066                     }
2067                 }
2068             }
2069         }
2070
2071         if (pkg == null || notification == null) {
2072             throw new IllegalArgumentException("null not allowed: pkg=" + pkg
2073                     + " id=" + id + " notification=" + notification);
2074         }
2075
2076         if (notification.getSmallIcon() != null) {
2077             if (!notification.isValid()) {
2078                 throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg
2079                         + " id=" + id + " notification=" + notification);
2080             }
2081         }
2082
2083         mHandler.post(new Runnable() {
2084             @Override
2085             public void run() {
2086
2087                 synchronized (mNotificationList) {
2088
2089                     // === Scoring ===
2090
2091                     // 0. Sanitize inputs
2092                     notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
2093                             Notification.PRIORITY_MAX);
2094                     // Migrate notification flags to scores
2095                     if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
2096                         if (notification.priority < Notification.PRIORITY_MAX) {
2097                             notification.priority = Notification.PRIORITY_MAX;
2098                         }
2099                     } else if (SCORE_ONGOING_HIGHER &&
2100                             0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
2101                         if (notification.priority < Notification.PRIORITY_HIGH) {
2102                             notification.priority = Notification.PRIORITY_HIGH;
2103                         }
2104                     }
2105                     // force no heads up per package config
2106                     if (!mRankingHelper.getPackagePeekable(pkg, callingUid)) {
2107                         if (notification.extras == null) {
2108                             notification.extras = new Bundle();
2109                         }
2110                         notification.extras.putInt(Notification.EXTRA_AS_HEADS_UP,
2111                                 Notification.HEADS_UP_NEVER);
2112                     }
2113
2114                     // 1. initial score: buckets of 10, around the app [-20..20]
2115                     final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
2116
2117                     // 2. extract ranking signals from the notification data
2118                     final StatusBarNotification n = new StatusBarNotification(
2119                             pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
2120                             user);
2121                     NotificationRecord r = new NotificationRecord(n, score);
2122                     NotificationRecord old = mNotificationsByKey.get(n.getKey());
2123                     if (old != null) {
2124                         // Retain ranking information from previous record
2125                         r.copyRankingInformation(old);
2126                     }
2127
2128                     // Handle grouped notifications and bail out early if we
2129                     // can to avoid extracting signals.
2130                     handleGroupedNotificationLocked(r, old, callingUid, callingPid);
2131                     boolean ignoreNotification =
2132                             removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);
2133
2134                     // This conditional is a dirty hack to limit the logging done on
2135                     //     behalf of the download manager without affecting other apps.
2136                     if (!pkg.equals("com.android.providers.downloads")
2137                             || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
2138                         int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
2139                         if (ignoreNotification) {
2140                             enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
2141                         } else if (old != null) {
2142                             enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
2143                         }
2144                         EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
2145                                 pkg, id, tag, userId, notification.toString(),
2146                                 enqueueStatus);
2147                     }
2148
2149                     if (ignoreNotification) {
2150                         return;
2151                     }
2152
2153                     mRankingHelper.extractSignals(r);
2154
2155                     // 3. Apply local rules
2156
2157                     // blocked apps
2158                     if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
2159                         if (!isSystemNotification) {
2160                             r.score = JUNK_SCORE;
2161                             Slog.e(TAG, "Suppressing notification from package " + pkg
2162                                     + " by user request.");
2163                             mUsageStats.registerBlocked(r);
2164                         }
2165                     }
2166
2167                     if (r.score < SCORE_DISPLAY_THRESHOLD) {
2168                         // Notification will be blocked because the score is too low.
2169                         return;
2170                     }
2171
2172                     int index = indexOfNotificationLocked(n.getKey());
2173                     if (index < 0) {
2174                         mNotificationList.add(r);
2175                         mUsageStats.registerPostedByApp(r);
2176                     } else {
2177                         old = mNotificationList.get(index);
2178                         mNotificationList.set(index, r);
2179                         mUsageStats.registerUpdatedByApp(r, old);
2180                         // Make sure we don't lose the foreground service state.
2181                         notification.flags |=
2182                                 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
2183                         r.isUpdate = true;
2184                     }
2185
2186                     mNotificationsByKey.put(n.getKey(), r);
2187
2188                     // Ensure if this is a foreground service that the proper additional
2189                     // flags are set.
2190                     if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2191                         notification.flags |= Notification.FLAG_ONGOING_EVENT
2192                                 | Notification.FLAG_NO_CLEAR;
2193                     }
2194
2195                     applyZenModeLocked(r);
2196                     mRankingHelper.sort(mNotificationList);
2197
2198                     if (notification.getSmallIcon() != null) {
2199                         StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
2200                         mListeners.notifyPostedLocked(n, oldSbn);
2201                     } else {
2202                         Slog.e(TAG, "Not posting notification without small icon: " + notification);
2203                         if (old != null && !old.isCanceled) {
2204                             mListeners.notifyRemovedLocked(n);
2205                         }
2206                         // ATTENTION: in a future release we will bail out here
2207                         // so that we do not play sounds, show lights, etc. for invalid
2208                         // notifications
2209                         Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2210                                 + n.getPackageName());
2211                     }
2212
2213                     buzzBeepBlinkLocked(r);
2214                 }
2215             }
2216         });
2217
2218         idOut[0] = id;
2219     }
2220
2221     /**
2222      * Ensures that grouped notification receive their special treatment.
2223      *
2224      * <p>Cancels group children if the new notification causes a group to lose
2225      * its summary.</p>
2226      *
2227      * <p>Updates mSummaryByGroupKey.</p>
2228      */
2229     private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
2230             int callingUid, int callingPid) {
2231         StatusBarNotification sbn = r.sbn;
2232         Notification n = sbn.getNotification();
2233         String group = sbn.getGroupKey();
2234         boolean isSummary = n.isGroupSummary();
2235
2236         Notification oldN = old != null ? old.sbn.getNotification() : null;
2237         String oldGroup = old != null ? old.sbn.getGroupKey() : null;
2238         boolean oldIsSummary = old != null && oldN.isGroupSummary();
2239
2240         if (oldIsSummary) {
2241             NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
2242             if (removedSummary != old) {
2243                 String removedKey =
2244                         removedSummary != null ? removedSummary.getKey() : "<null>";
2245                 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
2246                         ", removed=" + removedKey);
2247             }
2248         }
2249         if (isSummary) {
2250             mSummaryByGroupKey.put(group, r);
2251         }
2252
2253         // Clear out group children of the old notification if the update
2254         // causes the group summary to go away. This happens when the old
2255         // notification was a summary and the new one isn't, or when the old
2256         // notification was a summary and its group key changed.
2257         if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
2258             cancelGroupChildrenLocked(old, callingUid, callingPid, null,
2259                     REASON_GROUP_SUMMARY_CANCELED);
2260         }
2261     }
2262
2263     /**
2264      * Performs group notification optimizations if SysUI is the only active
2265      * notification listener and returns whether the given notification should
2266      * be ignored.
2267      *
2268      * <p>Returns true if the given notification is a child of a group with a
2269      * summary, which means that SysUI will never show it, and hence the new
2270      * notification can be safely ignored. Also cancels any previous instance
2271      * of the ignored notification.</p>
2272      *
2273      * <p>For summaries, cancels all children of that group, as SysUI will
2274      * never show them anymore.</p>
2275      *
2276      * @return true if the given notification can be ignored as an optimization
2277      */
2278     private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r,
2279             NotificationRecord old, int callingUid, int callingPid) {
2280         if (!ENABLE_CHILD_NOTIFICATIONS) {
2281             // No optimizations are possible if listeners want groups.
2282             if (mListeners.notificationGroupsDesired()) {
2283                 return false;
2284             }
2285
2286             StatusBarNotification sbn = r.sbn;
2287             String group = sbn.getGroupKey();
2288             boolean isSummary = sbn.getNotification().isGroupSummary();
2289             boolean isChild = sbn.getNotification().isGroupChild();
2290
2291             NotificationRecord summary = mSummaryByGroupKey.get(group);
2292             if (isChild && summary != null) {
2293                 // Child with an active summary -> ignore
2294                 if (DBG) {
2295                     Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
2296                             + summary.getKey());
2297                 }
2298                 // Make sure we don't leave an old version of the notification around.
2299                 if (old != null) {
2300                     if (DBG) {
2301                         Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey());
2302                     }
2303                     cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION);
2304                 }
2305                 return true;
2306             } else if (isSummary) {
2307                 // Summary -> cancel children
2308                 cancelGroupChildrenLocked(r, callingUid, callingPid, null,
2309                         REASON_GROUP_OPTIMIZATION);
2310             }
2311         }
2312         return false;
2313     }
2314
2315     private void buzzBeepBlinkLocked(NotificationRecord record) {
2316         boolean buzz = false;
2317         boolean beep = false;
2318         boolean blink = false;
2319
2320         final Notification notification = record.sbn.getNotification();
2321
2322         // Should this notification make noise, vibe, or use the LED?
2323         final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD;
2324         final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
2325         if (DBG || record.isIntercepted())
2326             Slog.v(TAG,
2327                     "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
2328                             " intercept=" + record.isIntercepted()
2329             );
2330
2331         final int currentUser;
2332         final long token = Binder.clearCallingIdentity();
2333         try {
2334             currentUser = ActivityManager.getCurrentUser();
2335         } finally {
2336             Binder.restoreCallingIdentity(token);
2337         }
2338
2339         // If we're not supposed to beep, vibrate, etc. then don't.
2340         final String disableEffects = disableNotificationEffects(record);
2341         if (disableEffects != null) {
2342             ZenLog.traceDisableEffects(record, disableEffects);
2343         }
2344         if (disableEffects == null
2345                 && (!(record.isUpdate
2346                     && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
2347                 && (record.getUserId() == UserHandle.USER_ALL ||
2348                     record.getUserId() == currentUser ||
2349                     mUserProfiles.isCurrentProfile(record.getUserId()))
2350                 && canInterrupt
2351                 && mSystemReady
2352                 && mAudioManager != null) {
2353             if (DBG) Slog.v(TAG, "Interrupting!");
2354
2355             sendAccessibilityEvent(notification, record.sbn.getPackageName());
2356
2357             // sound
2358
2359             // should we use the default notification sound? (indicated either by
2360             // DEFAULT_SOUND or because notification.sound is pointing at
2361             // Settings.System.NOTIFICATION_SOUND)
2362             final boolean useDefaultSound =
2363                    (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2364                            Settings.System.DEFAULT_NOTIFICATION_URI
2365                                    .equals(notification.sound);
2366
2367             Uri soundUri = null;
2368             boolean hasValidSound = false;
2369
2370             if (useDefaultSound) {
2371                 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2372
2373                 // check to see if the default notification sound is silent
2374                 ContentResolver resolver = getContext().getContentResolver();
2375                 hasValidSound = Settings.System.getString(resolver,
2376                        Settings.System.NOTIFICATION_SOUND) != null;
2377             } else if (notification.sound != null) {
2378                 soundUri = notification.sound;
2379                 hasValidSound = (soundUri != null);
2380             }
2381
2382             if (hasValidSound) {
2383                 boolean looping =
2384                         (notification.flags & Notification.FLAG_INSISTENT) != 0;
2385                 AudioAttributes audioAttributes = audioAttributesForNotification(notification);
2386                 mSoundNotificationKey = record.getKey();
2387                 // do not play notifications if stream volume is 0 (typically because
2388                 // ringer mode is silent) or if there is a user of exclusive audio focus
2389                 if ((mAudioManager.getStreamVolume(
2390                         AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
2391                             && !mAudioManager.isAudioFocusExclusive()) {
2392                     final long identity = Binder.clearCallingIdentity();
2393                     try {
2394                         final IRingtonePlayer player =
2395                                 mAudioManager.getRingtonePlayer();
2396                         if (player != null) {
2397                             if (DBG) Slog.v(TAG, "Playing sound " + soundUri
2398                                     + " with attributes " + audioAttributes);
2399                             player.playAsync(soundUri, record.sbn.getUser(), looping,
2400                                     audioAttributes);
2401                             beep = true;
2402                         }
2403                     } catch (RemoteException e) {
2404                     } finally {
2405                         Binder.restoreCallingIdentity(identity);
2406                     }
2407                 }
2408             }
2409
2410             // vibrate
2411             // Does the notification want to specify its own vibration?
2412             final boolean hasCustomVibrate = notification.vibrate != null;
2413
2414             // new in 4.2: if there was supposed to be a sound and we're in vibrate
2415             // mode, and no other vibration is specified, we fall back to vibration
2416             final boolean convertSoundToVibration =
2417                        !hasCustomVibrate
2418                     && hasValidSound
2419                     && (mAudioManager.getRingerModeInternal()
2420                                == AudioManager.RINGER_MODE_VIBRATE);
2421
2422             // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2423             final boolean useDefaultVibrate =
2424                     (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2425
2426             if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
2427                     && !(mAudioManager.getRingerModeInternal()
2428                             == AudioManager.RINGER_MODE_SILENT)) {
2429                 mVibrateNotificationKey = record.getKey();
2430
2431                 if (useDefaultVibrate || convertSoundToVibration) {
2432                     // Escalate privileges so we can use the vibrator even if the
2433                     // notifying app does not have the VIBRATE permission.
2434                     long identity = Binder.clearCallingIdentity();
2435                     try {
2436                         mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2437                             useDefaultVibrate ? mDefaultVibrationPattern
2438                                 : mFallbackVibrationPattern,
2439                             ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2440                                 ? 0: -1, audioAttributesForNotification(notification));
2441                         buzz = true;
2442                     } finally {
2443                         Binder.restoreCallingIdentity(identity);
2444                     }
2445                 } else if (notification.vibrate.length > 1) {
2446                     // If you want your own vibration pattern, you need the VIBRATE
2447                     // permission
2448                     mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2449                             notification.vibrate,
2450                         ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2451                                 ? 0: -1, audioAttributesForNotification(notification));
2452                     buzz = true;
2453                 }
2454             }
2455         }
2456
2457         // light
2458         // release the light
2459         boolean wasShowLights = mLights.remove(record.getKey());
2460         if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) {
2461             mLights.add(record.getKey());
2462             updateLightsLocked();
2463             if (mUseAttentionLight) {
2464                 mAttentionLight.pulse();
2465             }
2466             blink = true;
2467         } else if (wasShowLights) {
2468             updateLightsLocked();
2469         }
2470         if (buzz || beep || blink) {
2471             EventLogTags.writeNotificationAlert(record.getKey(),
2472                     buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
2473             mHandler.post(mBuzzBeepBlinked);
2474         }
2475     }
2476
2477     private static AudioAttributes audioAttributesForNotification(Notification n) {
2478         if (n.audioAttributes != null
2479                 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
2480             // the audio attributes are set and different from the default, use them
2481             return n.audioAttributes;
2482         } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
2483             // the stream type is valid, use it
2484             return new AudioAttributes.Builder()
2485                     .setInternalLegacyStreamType(n.audioStreamType)
2486                     .build();
2487         } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
2488             return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2489         } else {
2490             Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
2491             return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2492         }
2493     }
2494
2495     void showNextToastLocked() {
2496         ToastRecord record = mToastQueue.get(0);
2497         while (record != null) {
2498             if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2499             try {
2500                 record.callback.show();
2501                 scheduleTimeoutLocked(record);
2502                 return;
2503             } catch (RemoteException e) {
2504                 Slog.w(TAG, "Object died trying to show notification " + record.callback
2505                         + " in package " + record.pkg);
2506                 // remove it from the list and let the process die
2507                 int index = mToastQueue.indexOf(record);
2508                 if (index >= 0) {
2509                     mToastQueue.remove(index);
2510                 }
2511                 keepProcessAliveLocked(record.pid);
2512                 if (mToastQueue.size() > 0) {
2513                     record = mToastQueue.get(0);
2514                 } else {
2515                     record = null;
2516                 }
2517             }
2518         }
2519     }
2520
2521     void cancelToastLocked(int index) {
2522         ToastRecord record = mToastQueue.get(index);
2523         try {
2524             record.callback.hide();
2525         } catch (RemoteException e) {
2526             Slog.w(TAG, "Object died trying to hide notification " + record.callback
2527                     + " in package " + record.pkg);
2528             // don't worry about this, we're about to remove it from
2529             // the list anyway
2530         }
2531         mToastQueue.remove(index);
2532         keepProcessAliveLocked(record.pid);
2533         if (mToastQueue.size() > 0) {
2534             // Show the next one. If the callback fails, this will remove
2535             // it from the list, so don't assume that the list hasn't changed
2536             // after this point.
2537             showNextToastLocked();
2538         }
2539     }
2540
2541     private void scheduleTimeoutLocked(ToastRecord r)
2542     {
2543         mHandler.removeCallbacksAndMessages(r);
2544         Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2545         long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2546         mHandler.sendMessageDelayed(m, delay);
2547     }
2548
2549     private void handleTimeout(ToastRecord record)
2550     {
2551         if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2552         synchronized (mToastQueue) {
2553             int index = indexOfToastLocked(record.pkg, record.callback);
2554             if (index >= 0) {
2555                 cancelToastLocked(index);
2556             }
2557         }
2558     }
2559
2560     // lock on mToastQueue
2561     int indexOfToastLocked(String pkg, ITransientNotification callback)
2562     {
2563         IBinder cbak = callback.asBinder();
2564         ArrayList<ToastRecord> list = mToastQueue;
2565         int len = list.size();
2566         for (int i=0; i<len; i++) {
2567             ToastRecord r = list.get(i);
2568             if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2569                 return i;
2570             }
2571         }
2572         return -1;
2573     }
2574
2575     // lock on mToastQueue
2576     void keepProcessAliveLocked(int pid)
2577     {
2578         int toastCount = 0; // toasts from this pid
2579         ArrayList<ToastRecord> list = mToastQueue;
2580         int N = list.size();
2581         for (int i=0; i<N; i++) {
2582             ToastRecord r = list.get(i);
2583             if (r.pid == pid) {
2584                 toastCount++;
2585             }
2586         }
2587         try {
2588             mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2589         } catch (RemoteException e) {
2590             // Shouldn't happen.
2591         }
2592     }
2593
2594     private void handleRankingReconsideration(Message message) {
2595         if (!(message.obj instanceof RankingReconsideration)) return;
2596         RankingReconsideration recon = (RankingReconsideration) message.obj;
2597         recon.run();
2598         boolean changed;
2599         synchronized (mNotificationList) {
2600             final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
2601             if (record == null) {
2602                 return;
2603             }
2604             int indexBefore = findNotificationRecordIndexLocked(record);
2605             boolean interceptBefore = record.isIntercepted();
2606             int visibilityBefore = record.getPackageVisibilityOverride();
2607             recon.applyChangesLocked(record);
2608             applyZenModeLocked(record);
2609             mRankingHelper.sort(mNotificationList);
2610             int indexAfter = findNotificationRecordIndexLocked(record);
2611             boolean interceptAfter = record.isIntercepted();
2612             int visibilityAfter = record.getPackageVisibilityOverride();
2613             changed = indexBefore != indexAfter || interceptBefore != interceptAfter
2614                     || visibilityBefore != visibilityAfter;
2615             if (interceptBefore && !interceptAfter) {
2616                 buzzBeepBlinkLocked(record);
2617             }
2618         }
2619         if (changed) {
2620             scheduleSendRankingUpdate();
2621         }
2622     }
2623
2624     private void handleRankingConfigChange() {
2625         synchronized (mNotificationList) {
2626             final int N = mNotificationList.size();
2627             ArrayList<String> orderBefore = new ArrayList<String>(N);
2628             int[] visibilities = new int[N];
2629             for (int i = 0; i < N; i++) {
2630                 final NotificationRecord r = mNotificationList.get(i);
2631                 orderBefore.add(r.getKey());
2632                 visibilities[i] = r.getPackageVisibilityOverride();
2633                 mRankingHelper.extractSignals(r);
2634             }
2635             for (int i = 0; i < N; i++) {
2636                 mRankingHelper.sort(mNotificationList);
2637                 final NotificationRecord r = mNotificationList.get(i);
2638                 if (!orderBefore.get(i).equals(r.getKey())
2639                         || visibilities[i] != r.getPackageVisibilityOverride()) {
2640                     scheduleSendRankingUpdate();
2641                     return;
2642                 }
2643             }
2644         }
2645     }
2646
2647     // let zen mode evaluate this record
2648     private void applyZenModeLocked(NotificationRecord record) {
2649         record.setIntercepted(mZenModeHelper.shouldIntercept(record));
2650     }
2651
2652     // lock on mNotificationList
2653     private int findNotificationRecordIndexLocked(NotificationRecord target) {
2654         return mRankingHelper.indexOf(mNotificationList, target);
2655     }
2656
2657     private void scheduleSendRankingUpdate() {
2658         mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
2659         Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
2660         mHandler.sendMessage(m);
2661     }
2662
2663     private void handleSendRankingUpdate() {
2664         synchronized (mNotificationList) {
2665             mListeners.notifyRankingUpdateLocked();
2666         }
2667     }
2668
2669     private void scheduleListenerHintsChanged(int state) {
2670         mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
2671         mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
2672     }
2673
2674     private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
2675         mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
2676         mHandler.obtainMessage(
2677                 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
2678                 listenerInterruptionFilter,
2679                 0).sendToTarget();
2680     }
2681
2682     private void handleListenerHintsChanged(int hints) {
2683         synchronized (mNotificationList) {
2684             mListeners.notifyListenerHintsChangedLocked(hints);
2685         }
2686     }
2687
2688     private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
2689         synchronized (mNotificationList) {
2690             mListeners.notifyInterruptionFilterChanged(interruptionFilter);
2691         }
2692     }
2693
2694     private final class WorkerHandler extends Handler
2695     {
2696         @Override
2697         public void handleMessage(Message msg)
2698         {
2699             switch (msg.what)
2700             {
2701                 case MESSAGE_TIMEOUT:
2702                     handleTimeout((ToastRecord)msg.obj);
2703                     break;
2704                 case MESSAGE_SAVE_POLICY_FILE:
2705                     handleSavePolicyFile();
2706                     break;
2707                 case MESSAGE_SEND_RANKING_UPDATE:
2708                     handleSendRankingUpdate();
2709                     break;
2710                 case MESSAGE_LISTENER_HINTS_CHANGED:
2711                     handleListenerHintsChanged(msg.arg1);
2712                     break;
2713                 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
2714                     handleListenerInterruptionFilterChanged(msg.arg1);
2715                     break;
2716             }
2717         }
2718
2719     }
2720
2721     private final class RankingWorkerHandler extends Handler
2722     {
2723         public RankingWorkerHandler(Looper looper) {
2724             super(looper);
2725         }
2726
2727         @Override
2728         public void handleMessage(Message msg) {
2729             switch (msg.what) {
2730                 case MESSAGE_RECONSIDER_RANKING:
2731                     handleRankingReconsideration(msg);
2732                     break;
2733                 case MESSAGE_RANKING_CONFIG_CHANGE:
2734                     handleRankingConfigChange();
2735                     break;
2736             }
2737         }
2738     }
2739
2740     // Notifications
2741     // ============================================================================
2742     static int clamp(int x, int low, int high) {
2743         return (x < low) ? low : ((x > high) ? high : x);
2744     }
2745
2746     void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2747         AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
2748         if (!manager.isEnabled()) {
2749             return;
2750         }
2751
2752         AccessibilityEvent event =
2753             AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2754         event.setPackageName(packageName);
2755         event.setClassName(Notification.class.getName());
2756         event.setParcelableData(notification);
2757         CharSequence tickerText = notification.tickerText;
2758         if (!TextUtils.isEmpty(tickerText)) {
2759             event.getText().add(tickerText);
2760         }
2761
2762         manager.sendAccessibilityEvent(event);
2763     }
2764
2765     private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
2766         // tell the app
2767         if (sendDelete) {
2768             if (r.getNotification().deleteIntent != null) {
2769                 try {
2770                     r.getNotification().deleteIntent.send();
2771                 } catch (PendingIntent.CanceledException ex) {
2772                     // do nothing - there's no relevant way to recover, and
2773                     //     no reason to let this propagate
2774                     Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
2775                 }
2776             }
2777         }
2778
2779         // status bar
2780         if (r.getNotification().getSmallIcon() != null) {
2781             r.isCanceled = true;
2782             mListeners.notifyRemovedLocked(r.sbn);
2783         }
2784
2785         final String canceledKey = r.getKey();
2786
2787         // sound
2788         if (canceledKey.equals(mSoundNotificationKey)) {
2789             mSoundNotificationKey = null;
2790             final long identity = Binder.clearCallingIdentity();
2791             try {
2792                 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2793                 if (player != null) {
2794                     player.stopAsync();
2795                 }
2796             } catch (RemoteException e) {
2797             } finally {
2798                 Binder.restoreCallingIdentity(identity);
2799             }
2800         }
2801
2802         // vibrate
2803         if (canceledKey.equals(mVibrateNotificationKey)) {
2804             mVibrateNotificationKey = null;
2805             long identity = Binder.clearCallingIdentity();
2806             try {
2807                 mVibrator.cancel();
2808             }
2809             finally {
2810                 Binder.restoreCallingIdentity(identity);
2811             }
2812         }
2813
2814         // light
2815         mLights.remove(canceledKey);
2816
2817         // Record usage stats
2818         switch (reason) {
2819             case REASON_DELEGATE_CANCEL:
2820             case REASON_DELEGATE_CANCEL_ALL:
2821             case REASON_LISTENER_CANCEL:
2822             case REASON_LISTENER_CANCEL_ALL:
2823                 mUsageStats.registerDismissedByUser(r);
2824                 break;
2825             case REASON_NOMAN_CANCEL:
2826             case REASON_NOMAN_CANCEL_ALL:
2827                 mUsageStats.registerRemovedByApp(r);
2828                 break;
2829         }
2830
2831         mNotificationsByKey.remove(r.sbn.getKey());
2832         String groupKey = r.getGroupKey();
2833         NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
2834         if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
2835             mSummaryByGroupKey.remove(groupKey);
2836         }
2837
2838         // Save it for users of getHistoricalNotifications()
2839         mArchive.record(r.sbn);
2840
2841         final long now = System.currentTimeMillis();
2842         EventLogTags.writeNotificationCanceled(canceledKey, reason,
2843                 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
2844     }
2845
2846     /**
2847      * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2848      * and none of the {@code mustNotHaveFlags}.
2849      */
2850     void cancelNotification(final int callingUid, final int callingPid,
2851             final String pkg, final String tag, final int id,
2852             final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2853             final int userId, final int reason, final ManagedServiceInfo listener) {
2854         // In enqueueNotificationInternal notifications are added by scheduling the
2855         // work on the worker handler. Hence, we also schedule the cancel on this
2856         // handler to avoid a scenario where an add notification call followed by a
2857         // remove notification call ends up in not removing the notification.
2858         mHandler.post(new Runnable() {
2859             @Override
2860             public void run() {
2861                 String listenerName = listener == null ? null : listener.component.toShortString();
2862                 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
2863                         userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
2864
2865                 synchronized (mNotificationList) {
2866                     int index = indexOfNotificationLocked(pkg, tag, id, userId);
2867                     if (index >= 0) {
2868                         NotificationRecord r = mNotificationList.get(index);
2869
2870                         // Ideally we'd do this in the caller of this method. However, that would
2871                         // require the caller to also find the notification.
2872                         if (reason == REASON_DELEGATE_CLICK) {
2873                             mUsageStats.registerClickedByUser(r);
2874                         }
2875
2876                         if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2877                             return;
2878                         }
2879                         if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2880                             return;
2881                         }
2882
2883                         mNotificationList.remove(index);
2884
2885                         cancelNotificationLocked(r, sendDelete, reason);
2886                         cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
2887                                 REASON_GROUP_SUMMARY_CANCELED);
2888                         updateLightsLocked();
2889                     }
2890                 }
2891             }
2892         });
2893     }
2894
2895     /**
2896      * Determine whether the userId applies to the notification in question, either because
2897      * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2898      */
2899     private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2900         return
2901                 // looking for USER_ALL notifications? match everything
2902                    userId == UserHandle.USER_ALL
2903                 // a notification sent to USER_ALL matches any query
2904                 || r.getUserId() == UserHandle.USER_ALL
2905                 // an exact user match
2906                 || r.getUserId() == userId;
2907     }
2908
2909     /**
2910      * Determine whether the userId applies to the notification in question, either because
2911      * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2912      * because it matches one of the users profiles.
2913      */
2914     private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2915         return notificationMatchesUserId(r, userId)
2916                 || mUserProfiles.isCurrentProfile(r.getUserId());
2917     }
2918
2919     /**
2920      * Cancels all notifications from a given package that have all of the
2921      * {@code mustHaveFlags}.
2922      */
2923     boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2924             int mustNotHaveFlags, boolean doit, int userId, int reason,
2925             ManagedServiceInfo listener) {
2926         String listenerName = listener == null ? null : listener.component.toShortString();
2927         EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2928                 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2929                 listenerName);
2930
2931         synchronized (mNotificationList) {
2932             final int N = mNotificationList.size();
2933             ArrayList<NotificationRecord> canceledNotifications = null;
2934             for (int i = N-1; i >= 0; --i) {
2935                 NotificationRecord r = mNotificationList.get(i);
2936                 if (!notificationMatchesUserId(r, userId)) {
2937                     continue;
2938                 }
2939                 // Don't remove notifications to all, if there's no package name specified
2940                 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2941                     continue;
2942                 }
2943                 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2944                     continue;
2945                 }
2946                 if ((r.getFlags() & mustNotHaveFlags) != 0) {
2947                     continue;
2948                 }
2949                 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2950                     continue;
2951                 }
2952                 if (canceledNotifications == null) {
2953                     canceledNotifications = new ArrayList<>();
2954                 }
2955                 canceledNotifications.add(r);
2956                 if (!doit) {
2957                     return true;
2958                 }
2959                 mNotificationList.remove(i);
2960                 cancelNotificationLocked(r, false, reason);
2961             }
2962             if (doit && canceledNotifications != null) {
2963                 final int M = canceledNotifications.size();
2964                 for (int i = 0; i < M; i++) {
2965                     cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2966                             listenerName, REASON_GROUP_SUMMARY_CANCELED);
2967                 }
2968             }
2969             if (canceledNotifications != null) {
2970                 updateLightsLocked();
2971             }
2972             return canceledNotifications != null;
2973         }
2974     }
2975
2976     void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2977             ManagedServiceInfo listener, boolean includeCurrentProfiles) {
2978         String listenerName = listener == null ? null : listener.component.toShortString();
2979         EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2980                 null, userId, 0, 0, reason, listenerName);
2981
2982         ArrayList<NotificationRecord> canceledNotifications = null;
2983         final int N = mNotificationList.size();
2984         for (int i=N-1; i>=0; i--) {
2985             NotificationRecord r = mNotificationList.get(i);
2986             if (includeCurrentProfiles) {
2987                 if (!notificationMatchesCurrentProfiles(r, userId)) {
2988                     continue;
2989                 }
2990             } else {
2991                 if (!notificationMatchesUserId(r, userId)) {
2992                     continue;
2993                 }
2994             }
2995
2996             if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2997                             | Notification.FLAG_NO_CLEAR)) == 0) {
2998                 mNotificationList.remove(i);
2999                 cancelNotificationLocked(r, true, reason);
3000                 // Make a note so we can cancel children later.
3001                 if (canceledNotifications == null) {
3002                     canceledNotifications = new ArrayList<>();
3003                 }
3004                 canceledNotifications.add(r);
3005             }
3006         }
3007         int M = canceledNotifications != null ? canceledNotifications.size() : 0;
3008         for (int i = 0; i < M; i++) {
3009             cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
3010                     listenerName, REASON_GROUP_SUMMARY_CANCELED);
3011         }
3012         updateLightsLocked();
3013     }
3014
3015     // Warning: The caller is responsible for invoking updateLightsLocked().
3016     private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
3017             String listenerName, int reason) {
3018         Notification n = r.getNotification();
3019         if (!n.isGroupSummary()) {
3020             return;
3021         }
3022
3023         String pkg = r.sbn.getPackageName();
3024         int userId = r.getUserId();
3025
3026         if (pkg == null) {
3027             if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
3028             return;
3029         }
3030
3031         final int N = mNotificationList.size();
3032         for (int i = N - 1; i >= 0; i--) {
3033             NotificationRecord childR = mNotificationList.get(i);
3034             StatusBarNotification childSbn = childR.sbn;
3035             if (childR.getNotification().isGroupChild() &&
3036                     childR.getGroupKey().equals(r.getGroupKey())) {
3037                 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
3038                         childSbn.getTag(), userId, 0, 0, reason, listenerName);
3039                 mNotificationList.remove(i);
3040                 cancelNotificationLocked(childR, false, reason);
3041             }
3042         }
3043     }
3044
3045     // lock on mNotificationList
3046     void updateLightsLocked()
3047     {
3048         // handle notification lights
3049         NotificationRecord ledNotification = null;
3050         while (ledNotification == null && !mLights.isEmpty()) {
3051             final String owner = mLights.get(mLights.size() - 1);
3052             ledNotification = mNotificationsByKey.get(owner);
3053             if (ledNotification == null) {
3054                 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
3055                 mLights.remove(owner);
3056             }
3057         }
3058
3059         // Don't flash while we are in a call or screen is on
3060         if (ledNotification == null || mInCall || mScreenOn) {
3061             mNotificationLight.turnOff();
3062             mStatusBar.notificationLightOff();
3063         } else {
3064             final Notification ledno = ledNotification.sbn.getNotification();
3065             int ledARGB = ledno.ledARGB;
3066             int ledOnMS = ledno.ledOnMS;
3067             int ledOffMS = ledno.ledOffMS;
3068             if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
3069                 ledARGB = mDefaultNotificationColor;
3070                 ledOnMS = mDefaultNotificationLedOn;
3071                 ledOffMS = mDefaultNotificationLedOff;
3072             }
3073             if (mNotificationPulseEnabled) {
3074                 // pulse repeatedly
3075                 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
3076                         ledOnMS, ledOffMS);
3077             }
3078             // let SystemUI make an independent decision
3079             mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
3080         }
3081     }
3082
3083     // lock on mNotificationList
3084     int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
3085     {
3086         ArrayList<NotificationRecord> list = mNotificationList;
3087         final int len = list.size();
3088         for (int i=0; i<len; i++) {
3089             NotificationRecord r = list.get(i);
3090             if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
3091                     TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
3092                 return i;
3093             }
3094         }
3095         return -1;
3096     }
3097
3098     // lock on mNotificationList
3099     int indexOfNotificationLocked(String key) {
3100         final int N = mNotificationList.size();
3101         for (int i = 0; i < N; i++) {
3102             if (key.equals(mNotificationList.get(i).getKey())) {
3103                 return i;
3104             }
3105         }
3106         return -1;
3107     }
3108
3109     private void updateNotificationPulse() {
3110         synchronized (mNotificationList) {
3111             updateLightsLocked();
3112         }
3113     }
3114
3115     private static boolean isUidSystem(int uid) {
3116         final int appid = UserHandle.getAppId(uid);
3117         return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
3118     }
3119
3120     private static boolean isCallerSystem() {
3121         return isUidSystem(Binder.getCallingUid());
3122     }
3123
3124     private static void checkCallerIsSystem() {
3125         if (isCallerSystem()) {
3126             return;
3127         }
3128         throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
3129     }
3130
3131     private static void checkCallerIsSystemOrSameApp(String pkg) {
3132         if (isCallerSystem()) {
3133             return;
3134         }
3135         checkCallerIsSameApp(pkg);
3136     }
3137
3138     private static void checkCallerIsSameApp(String pkg) {
3139         final int uid = Binder.getCallingUid();
3140         try {
3141             ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
3142                     pkg, 0, UserHandle.getCallingUserId());
3143             if (ai == null) {
3144                 throw new SecurityException("Unknown package " + pkg);
3145             }
3146             if (!UserHandle.isSameApp(ai.uid, uid)) {
3147                 throw new SecurityException("Calling uid " + uid + " gave package"
3148                         + pkg + " which is owned by uid " + ai.uid);
3149             }
3150         } catch (RemoteException re) {
3151             throw new SecurityException("Unknown package " + pkg + "\n" + re);
3152         }
3153     }
3154
3155     private static String callStateToString(int state) {
3156         switch (state) {
3157             case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
3158             case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
3159             case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
3160             default: return "CALL_STATE_UNKNOWN_" + state;
3161         }
3162     }
3163
3164     private void listenForCallState() {
3165         TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
3166             @Override
3167             public void onCallStateChanged(int state, String incomingNumber) {
3168                 if (mCallState == state) return;
3169                 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
3170                 mCallState = state;
3171             }
3172         }, PhoneStateListener.LISTEN_CALL_STATE);
3173     }
3174
3175     /**
3176      * Generates a NotificationRankingUpdate from 'sbns', considering only
3177      * notifications visible to the given listener.
3178      *
3179      * <p>Caller must hold a lock on mNotificationList.</p>
3180      */
3181     private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
3182         int speedBumpIndex = -1;
3183         final int N = mNotificationList.size();
3184         ArrayList<String> keys = new ArrayList<String>(N);
3185         ArrayList<String> interceptedKeys = new ArrayList<String>(N);
3186         Bundle visibilityOverrides = new Bundle();
3187         for (int i = 0; i < N; i++) {
3188             NotificationRecord record = mNotificationList.get(i);
3189             if (!isVisibleToListener(record.sbn, info)) {
3190                 continue;
3191             }
3192             keys.add(record.sbn.getKey());
3193             if (record.isIntercepted()) {
3194                 interceptedKeys.add(record.sbn.getKey());
3195             }
3196             if (record.getPackageVisibilityOverride()
3197                     != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
3198                 visibilityOverrides.putInt(record.sbn.getKey(),
3199                         record.getPackageVisibilityOverride());
3200             }
3201             // Find first min-prio notification for speedbump placement.
3202             if (speedBumpIndex == -1 &&
3203                     // Intrusiveness trumps priority, hence ignore intrusives.
3204                     !record.isRecentlyIntrusive() &&
3205                     // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so
3206                     // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT
3207                     // (or lower as a safeguard) is sufficient to find the speedbump index.
3208                     // We'll have to revisit this when more package priority buckets are introduced.
3209                     record.getPackagePriority() <= Notification.PRIORITY_DEFAULT &&
3210                     record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
3211                 speedBumpIndex = keys.size() - 1;
3212             }
3213         }
3214         String[] keysAr = keys.toArray(new String[keys.size()]);
3215         String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
3216         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
3217                 speedBumpIndex);
3218     }
3219
3220     private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
3221         if (!listener.enabledAndUserMatches(sbn.getUserId())) {
3222             return false;
3223         }
3224         // TODO: remove this for older listeners.
3225         return true;
3226     }
3227
3228     public class NotificationListeners extends ManagedServices {
3229
3230         private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
3231         private boolean mNotificationGroupsDesired;
3232
3233         public NotificationListeners() {
3234             super(getContext(), mHandler, mNotificationList, mUserProfiles);
3235         }
3236
3237         @Override
3238         protected Config getConfig() {
3239             Config c = new Config();
3240             c.caption = "notification listener";
3241             c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
3242             c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
3243             c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
3244             c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
3245             c.clientLabel = R.string.notification_listener_binding_label;
3246             return c;
3247         }
3248
3249         @Override
3250         protected IInterface asInterface(IBinder binder) {
3251             return INotificationListener.Stub.asInterface(binder);
3252         }
3253
3254         @Override
3255         public void onServiceAdded(ManagedServiceInfo info) {
3256             final INotificationListener listener = (INotificationListener) info.service;
3257             final NotificationRankingUpdate update;
3258             synchronized (mNotificationList) {
3259                 updateNotificationGroupsDesiredLocked();
3260                 update = makeRankingUpdateLocked(info);
3261             }
3262             try {
3263                 listener.onListenerConnected(update);
3264             } catch (RemoteException e) {
3265                 // we tried
3266             }
3267         }
3268
3269         @Override
3270         protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
3271             if (mListenersDisablingEffects.remove(removed)) {
3272                 updateListenerHintsLocked();
3273                 updateEffectsSuppressorLocked();
3274             }
3275             mLightTrimListeners.remove(removed);
3276             updateNotificationGroupsDesiredLocked();
3277         }
3278
3279         public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
3280             if (trim == TRIM_LIGHT) {
3281                 mLightTrimListeners.add(info);
3282             } else {
3283                 mLightTrimListeners.remove(info);
3284             }
3285         }
3286
3287         public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
3288             return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
3289
3290         }
3291
3292         /**
3293          * asynchronously notify all listeners about a new notification
3294          *
3295          * <p>
3296          * Also takes care of removing a notification that has been visible to a listener before,
3297          * but isn't anymore.
3298          */
3299         public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
3300             // Lazily initialized snapshots of the notification.
3301             StatusBarNotification sbnClone = null;
3302             StatusBarNotification sbnCloneLight = null;
3303
3304             for (final ManagedServiceInfo info : mServices) {
3305                 boolean sbnVisible = isVisibleToListener(sbn, info);
3306                 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
3307                 // This notification hasn't been and still isn't visible -> ignore.
3308                 if (!oldSbnVisible && !sbnVisible) {
3309                     continue;
3310                 }
3311                 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
3312
3313                 // This notification became invisible -> remove the old one.
3314                 if (oldSbnVisible && !sbnVisible) {
3315                     final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
3316                     mHandler.post(new Runnable() {
3317                         @Override
3318                         public void run() {
3319                             notifyRemoved(info, oldSbnLightClone, update);
3320                         }
3321                     });
3322                     continue;
3323                 }
3324
3325                 final int trim = mListeners.getOnNotificationPostedTrim(info);
3326
3327                 if (trim == TRIM_LIGHT && sbnCloneLight == null) {
3328                     sbnCloneLight = sbn.cloneLight();
3329                 } else if (trim == TRIM_FULL && sbnClone == null) {
3330                     sbnClone = sbn.clone();
3331                 }
3332                 final StatusBarNotification sbnToPost =
3333                         (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;
3334
3335                 mHandler.post(new Runnable() {
3336                     @Override
3337                     public void run() {
3338                         notifyPosted(info, sbnToPost, update);
3339                     }
3340                 });
3341             }
3342         }
3343
3344         /**
3345          * asynchronously notify all listeners about a removed notification
3346          */
3347         public void notifyRemovedLocked(StatusBarNotification sbn) {
3348             // make a copy in case changes are made to the underlying Notification object
3349             // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
3350             // notification
3351             final StatusBarNotification sbnLight = sbn.cloneLight();
3352             for (final ManagedServiceInfo info : mServices) {
3353                 if (!isVisibleToListener(sbn, info)) {
3354                     continue;
3355                 }
3356                 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
3357                 mHandler.post(new Runnable() {
3358                     @Override
3359                     public void run() {
3360                         notifyRemoved(info, sbnLight, update);
3361                     }
3362                 });
3363             }
3364         }
3365
3366         /**
3367          * asynchronously notify all listeners about a reordering of notifications
3368          */
3369         public void notifyRankingUpdateLocked() {
3370             for (final ManagedServiceInfo serviceInfo : mServices) {
3371                 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3372                     continue;
3373                 }
3374                 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
3375                 mHandler.post(new Runnable() {
3376                     @Override
3377                     public void run() {
3378                         notifyRankingUpdate(serviceInfo, update);
3379                     }
3380                 });
3381             }
3382         }
3383
3384         public void notifyListenerHintsChangedLocked(final int hints) {
3385             for (final ManagedServiceInfo serviceInfo : mServices) {
3386                 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3387                     continue;
3388                 }
3389                 mHandler.post(new Runnable() {
3390                     @Override
3391                     public void run() {
3392                         notifyListenerHintsChanged(serviceInfo, hints);
3393                     }
3394                 });
3395             }
3396         }
3397
3398         public void notifyInterruptionFilterChanged(final int interruptionFilter) {
3399             for (final ManagedServiceInfo serviceInfo : mServices) {
3400                 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3401                     continue;
3402                 }
3403                 mHandler.post(new Runnable() {
3404                     @Override
3405                     public void run() {
3406                         notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
3407                     }
3408                 });
3409             }
3410         }
3411
3412         private void notifyPosted(final ManagedServiceInfo info,
3413                 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
3414             final INotificationListener listener = (INotificationListener)info.service;
3415             StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
3416             try {
3417                 listener.onNotificationPosted(sbnHolder, rankingUpdate);
3418             } catch (RemoteException ex) {
3419                 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
3420             }
3421         }
3422
3423         private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
3424                 NotificationRankingUpdate rankingUpdate) {
3425             if (!info.enabledAndUserMatches(sbn.getUserId())) {
3426                 return;
3427             }
3428             final INotificationListener listener = (INotificationListener) info.service;
3429             StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
3430             try {
3431                 listener.onNotificationRemoved(sbnHolder, rankingUpdate);
3432             } catch (RemoteException ex) {
3433                 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
3434             }
3435         }
3436
3437         private void notifyRankingUpdate(ManagedServiceInfo info,
3438                                          NotificationRankingUpdate rankingUpdate) {
3439             final INotificationListener listener = (INotificationListener) info.service;
3440             try {
3441                 listener.onNotificationRankingUpdate(rankingUpdate);
3442             } catch (RemoteException ex) {
3443                 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
3444             }
3445         }
3446
3447         private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
3448             final INotificationListener listener = (INotificationListener) info.service;
3449             try {
3450                 listener.onListenerHintsChanged(hints);
3451             } catch (RemoteException ex) {
3452                 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
3453             }
3454         }
3455
3456         private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
3457                 int interruptionFilter) {
3458             final INotificationListener listener = (INotificationListener) info.service;
3459             try {
3460                 listener.onInterruptionFilterChanged(interruptionFilter);
3461             } catch (RemoteException ex) {
3462                 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
3463             }
3464         }
3465
3466         private boolean isListenerPackage(String packageName) {
3467             if (packageName == null) {
3468                 return false;
3469             }
3470             // TODO: clean up locking object later
3471             synchronized (mNotificationList) {
3472                 for (final ManagedServiceInfo serviceInfo : mServices) {
3473                     if (packageName.equals(serviceInfo.component.getPackageName())) {
3474                         return true;
3475                     }
3476                 }
3477             }
3478             return false;
3479         }
3480
3481         /**
3482          * Returns whether any of the currently registered listeners wants to receive notification
3483          * groups.
3484          *
3485          * <p>Currently we assume groups are desired by non-SystemUI listeners.</p>
3486          */
3487         public boolean notificationGroupsDesired() {
3488             return mNotificationGroupsDesired;
3489         }
3490
3491         private void updateNotificationGroupsDesiredLocked() {
3492             mNotificationGroupsDesired = true;
3493             // No listeners, no groups.
3494             if (mServices.isEmpty()) {
3495                 mNotificationGroupsDesired = false;
3496                 return;
3497             }
3498             // One listener: Check whether it's SysUI.
3499             if (mServices.size() == 1 &&
3500                     mServices.get(0).component.getPackageName().equals("com.android.systemui")) {
3501                 mNotificationGroupsDesired = false;
3502                 return;
3503             }
3504         }
3505     }
3506
3507     public static final class DumpFilter {
3508         public boolean filtered = false;
3509         public String pkgFilter;
3510         public boolean zen;
3511         public long since;
3512         public boolean stats;
3513         public boolean redact = true;
3514
3515         public static DumpFilter parseFromArguments(String[] args) {
3516             final DumpFilter filter = new DumpFilter();
3517             for (int ai = 0; ai < args.length; ai++) {
3518                 final String a = args[ai];
3519                 if ("--noredact".equals(a) || "--reveal".equals(a)) {
3520                     filter.redact = false;
3521                 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
3522                     if (ai < args.length-1) {
3523                         ai++;
3524                         filter.pkgFilter = args[ai].trim().toLowerCase();
3525                         if (filter.pkgFilter.isEmpty()) {
3526                             filter.pkgFilter = null;
3527                         } else {
3528                             filter.filtered = true;
3529                         }
3530                     }
3531                 } else if ("--zen".equals(a) || "zen".equals(a)) {
3532                     filter.filtered = true;
3533                     filter.zen = true;
3534                 } else if ("--stats".equals(a)) {
3535                     filter.stats = true;
3536                     if (ai < args.length-1) {
3537                         ai++;
3538                         filter.since = Long.valueOf(args[ai]);
3539                     } else {
3540                         filter.since = 0;
3541                     }
3542                 }
3543             }
3544             return filter;
3545         }
3546
3547         public boolean matches(StatusBarNotification sbn) {
3548             if (!filtered) return true;
3549             return zen ? true : sbn != null
3550                     && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
3551         }
3552
3553         public boolean matches(ComponentName component) {
3554             if (!filtered) return true;
3555             return zen ? true : component != null && matches(component.getPackageName());
3556         }
3557
3558         public boolean matches(String pkg) {
3559             if (!filtered) return true;
3560             return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
3561         }
3562
3563         @Override
3564         public String toString() {
3565             return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
3566         }
3567     }
3568
3569     /**
3570      * Wrapper for a StatusBarNotification object that allows transfer across a oneway
3571      * binder without sending large amounts of data over a oneway transaction.
3572      */
3573     private static final class StatusBarNotificationHolder
3574             extends IStatusBarNotificationHolder.Stub {
3575         private StatusBarNotification mValue;
3576
3577         public StatusBarNotificationHolder(StatusBarNotification value) {
3578             mValue = value;
3579         }
3580
3581         /** Get the held value and clear it. This function should only be called once per holder */
3582         @Override
3583         public StatusBarNotification get() {
3584             StatusBarNotification value = mValue;
3585             mValue = null;
3586             return value;
3587         }
3588     }
3589
3590     private final class PolicyAccess {
3591         private static final String SEPARATOR = ":";
3592         private final String[] PERM = {
3593             android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
3594         };
3595
3596         public boolean isPackageGranted(String pkg) {
3597             return pkg != null && getGrantedPackages().contains(pkg);
3598         }
3599
3600         public void put(String pkg, boolean granted) {
3601             if (pkg == null) return;
3602             final ArraySet<String> pkgs = getGrantedPackages();
3603             boolean changed;
3604             if (granted) {
3605                 changed = pkgs.add(pkg);
3606             } else {
3607                 changed = pkgs.remove(pkg);
3608             }
3609             if (!changed) return;
3610             final String setting = TextUtils.join(SEPARATOR, pkgs);
3611             final int currentUser = ActivityManager.getCurrentUser();
3612             Settings.Secure.putStringForUser(getContext().getContentResolver(),
3613                     Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
3614                     setting,
3615                     currentUser);
3616             getContext().sendBroadcastAsUser(new Intent(NotificationManager
3617                     .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
3618                 .setPackage(pkg)
3619                 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
3620         }
3621
3622         public ArraySet<String> getGrantedPackages() {
3623             final ArraySet<String> pkgs = new ArraySet<>();
3624
3625             long identity = Binder.clearCallingIdentity();
3626             try {
3627                 final String setting = Settings.Secure.getStringForUser(
3628                         getContext().getContentResolver(),
3629                         Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
3630                         ActivityManager.getCurrentUser());
3631                 if (setting != null) {
3632                     final String[] tokens = setting.split(SEPARATOR);
3633                     for (int i = 0; i < tokens.length; i++) {
3634                         String token = tokens[i];
3635                         if (token != null) {
3636                             token.trim();
3637                         }
3638                         if (TextUtils.isEmpty(token)) {
3639                             continue;
3640                         }
3641                         pkgs.add(token);
3642                     }
3643                 }
3644             } finally {
3645                 Binder.restoreCallingIdentity(identity);
3646             }
3647             return pkgs;
3648         }
3649
3650         public String[] getRequestingPackages() throws RemoteException {
3651             final ParceledListSlice list = AppGlobals.getPackageManager()
3652                     .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
3653                             ActivityManager.getCurrentUser());
3654             final List<PackageInfo> pkgs = list.getList();
3655             if (pkgs == null || pkgs.isEmpty()) return new String[0];
3656             final int N = pkgs.size();
3657             final String[] rt = new String[N];
3658             for (int i = 0; i < N; i++) {
3659                 rt[i] = pkgs.get(i).packageName;
3660             }
3661             return rt;
3662         }
3663     }
3664 }