NotificationManagerService.java revision b979f23c5970138d75d5c2e76ded1470f7a8eb83
1326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams/*
2326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * Copyright (C) 2007 The Android Open Source Project
3326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams *
4326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * Licensed under the Apache License, Version 2.0 (the "License");
5326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * you may not use this file except in compliance with the License.
6326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * You may obtain a copy of the License at
7326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams *
8326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams *      http://www.apache.org/licenses/LICENSE-2.0
9326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams *
10326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * Unless required by applicable law or agreed to in writing, software
11326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * distributed under the License is distributed on an "AS IS" BASIS,
12326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * See the License for the specific language governing permissions and
14326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * limitations under the License.
15326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams */
16326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
17326e0ddf89e8df2837752fbfd7a014814b32082cJason Samspackage com.android.server.notification;
18fb6b614bcea88a587a7ea4530be45ff0ffa0210eAlex Sakhartchouk
19326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
20fb6b614bcea88a587a7ea4530be45ff0ffa0210eAlex Sakhartchoukimport static org.xmlpull.v1.XmlPullParser.END_TAG;
21fb6b614bcea88a587a7ea4530be45ff0ffa0210eAlex Sakhartchoukimport static org.xmlpull.v1.XmlPullParser.START_TAG;
22fb6b614bcea88a587a7ea4530be45ff0ffa0210eAlex Sakhartchouk
23326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.app.ActivityManager;
24326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.app.ActivityManagerNative;
25326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.app.AppGlobals;
26326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.app.AppOpsManager;
27afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchoukimport android.app.IActivityManager;
28326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.app.INotificationManager;
29326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.app.ITransientNotification;
30326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.app.Notification;
31afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchoukimport android.app.PendingIntent;
32326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.app.StatusBarManager;
33326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.content.BroadcastReceiver;
34326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.content.ComponentName;
35326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.content.ContentResolver;
36afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchoukimport android.content.Context;
37326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.content.Intent;
38326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.content.IntentFilter;
39326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.content.pm.ApplicationInfo;
40326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.content.pm.PackageInfo;
41326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.content.pm.PackageManager;
42326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.content.pm.PackageManager.NameNotFoundException;
43afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchoukimport android.content.res.Resources;
44326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.database.ContentObserver;
45326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.graphics.Bitmap;
46326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.media.AudioManager;
47326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.media.IRingtonePlayer;
48326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.net.Uri;
49326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.os.Binder;
50326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.os.Environment;
51326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.os.Handler;
52afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchoukimport android.os.HandlerThread;
53326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.os.IBinder;
54326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.os.IInterface;
55326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.os.Looper;
56326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.os.Message;
57326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.os.Process;
58326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.os.RemoteException;
59326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.os.UserHandle;
60afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchoukimport android.os.Vibrator;
61d3c8de2efc8f4f9287e0a8dfdeefb03ba6aaec98Jason Samsimport android.provider.Settings;
62d3c8de2efc8f4f9287e0a8dfdeefb03ba6aaec98Jason Samsimport android.service.notification.INotificationListener;
63326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.service.notification.IConditionListener;
64326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.service.notification.IConditionProvider;
65326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.service.notification.NotificationListenerService;
66afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchoukimport android.service.notification.NotificationRankingUpdate;
67fb6b614bcea88a587a7ea4530be45ff0ffa0210eAlex Sakhartchoukimport android.service.notification.StatusBarNotification;
68fb6b614bcea88a587a7ea4530be45ff0ffa0210eAlex Sakhartchoukimport android.service.notification.Condition;
69afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchoukimport android.service.notification.ZenModeConfig;
70fb6b614bcea88a587a7ea4530be45ff0ffa0210eAlex Sakhartchoukimport android.telephony.TelephonyManager;
71fb6b614bcea88a587a7ea4530be45ff0ffa0210eAlex Sakhartchoukimport android.text.TextUtils;
72fb6b614bcea88a587a7ea4530be45ff0ffa0210eAlex Sakhartchoukimport android.util.ArrayMap;
73326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.util.AtomicFile;
74326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.util.Log;
75326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport android.util.Slog;
76afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchoukimport android.util.Xml;
77e514b45de8561fbc6ef6770845102ca10b0a69d7Jason Samsimport android.view.accessibility.AccessibilityEvent;
789397e30ce5fe3f6af9212a93b490836b04fdfffaJason Samsimport android.view.accessibility.AccessibilityManager;
79d3c8de2efc8f4f9287e0a8dfdeefb03ba6aaec98Jason Samsimport android.widget.Toast;
80326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
81326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport com.android.internal.R;
82afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchoukimport com.android.internal.util.FastXmlSerializer;
83326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport com.android.server.EventLogTags;
84326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport com.android.server.SystemService;
85326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport com.android.server.lights.Light;
86326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport com.android.server.lights.LightsManager;
87326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport com.android.server.notification.ManagedServices.ManagedServiceInfo;
88afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchoukimport com.android.server.notification.ManagedServices.UserProfiles;
89326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport com.android.server.notification.NotificationUsageStats.SingleNotificationStats;
90afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchoukimport com.android.server.statusbar.StatusBarManagerInternal;
91326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
92326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport libcore.io.IoUtils;
93326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
94326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport org.xmlpull.v1.XmlPullParser;
95326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport org.xmlpull.v1.XmlPullParserException;
96326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport org.xmlpull.v1.XmlSerializer;
97326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
98326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.io.File;
99326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.io.FileDescriptor;
100326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.io.FileInputStream;
101326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.io.FileNotFoundException;
102326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.io.FileOutputStream;
103326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.io.IOException;
104326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.io.PrintWriter;
105326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.lang.reflect.Array;
106326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.util.ArrayDeque;
107326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.util.ArrayList;
108326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.util.Arrays;
109326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.util.Collections;
110326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.util.HashSet;
111326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.util.Iterator;
112afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchoukimport java.util.NoSuchElementException;
113326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.util.concurrent.ExecutionException;
114326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsimport java.util.concurrent.TimeUnit;
115326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
116326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams/** {@hide} */
117afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchoukpublic class NotificationManagerService extends SystemService {
118326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    static final String TAG = "NotificationService";
119326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    static final boolean DBG = false;
120326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
121326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    static final int MAX_PACKAGE_NOTIFICATIONS = 50;
122326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
123326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    // message codes
124326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    static final int MESSAGE_TIMEOUT = 2;
125326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    static final int MESSAGE_SAVE_POLICY_FILE = 3;
126326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    static final int MESSAGE_RECONSIDER_RANKING = 4;
127afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchouk    static final int MESSAGE_SEND_RANKING_UPDATE = 5;
128326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
129326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    static final int LONG_DELAY = 3500; // 3.5 seconds
130326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    static final int SHORT_DELAY = 2000; // 2 seconds
131afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchouk
132326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
133326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
134326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
135326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
136afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchouk    static final boolean SCORE_ONGOING_HIGHER = false;
137326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
138326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    static final int JUNK_SCORE = -1000;
139326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
140326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
141326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
142afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchouk    // Notifications with scores below this will not interrupt the user, either via LED or
143326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    // sound or vibration
144326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    static final int SCORE_INTERRUPTION_THRESHOLD =
145326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams            Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
14684e4027f83b20af59f5b1fc52be6e45f159d3970Alex Sakhartchouk
14784e4027f83b20af59f5b1fc52be6e45f159d3970Alex Sakhartchouk    static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
14884e4027f83b20af59f5b1fc52be6e45f159d3970Alex Sakhartchouk    static final boolean ENABLE_BLOCKED_TOASTS = true;
14984e4027f83b20af59f5b1fc52be6e45f159d3970Alex Sakhartchouk
15084e4027f83b20af59f5b1fc52be6e45f159d3970Alex Sakhartchouk    private IActivityManager mAm;
151326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    AudioManager mAudioManager;
152326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    StatusBarManagerInternal mStatusBar;
15384e4027f83b20af59f5b1fc52be6e45f159d3970Alex Sakhartchouk    Vibrator mVibrator;
15484e4027f83b20af59f5b1fc52be6e45f159d3970Alex Sakhartchouk
15584e4027f83b20af59f5b1fc52be6e45f159d3970Alex Sakhartchouk    final IBinder mForegroundToken = new Binder();
15684e4027f83b20af59f5b1fc52be6e45f159d3970Alex Sakhartchouk    private WorkerHandler mHandler;
15784e4027f83b20af59f5b1fc52be6e45f159d3970Alex Sakhartchouk    private final HandlerThread mRankingThread = new HandlerThread("ranker",
15884e4027f83b20af59f5b1fc52be6e45f159d3970Alex Sakhartchouk            Process.THREAD_PRIORITY_BACKGROUND);
159326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private Handler mRankingHandler = null;
160326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
161326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private Light mNotificationLight;
162afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchouk    Light mAttentionLight;
163326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private int mDefaultNotificationColor;
164326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private int mDefaultNotificationLedOn;
165326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
166326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private int mDefaultNotificationLedOff;
167326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private long[] mDefaultVibrationPattern;
168326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
169326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private long[] mFallbackVibrationPattern;
170326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    boolean mSystemReady;
171326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
172326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private boolean mDisableNotificationAlerts;
173326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    NotificationRecord mSoundNotification;
174326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    NotificationRecord mVibrateNotification;
175326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
176326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    // for enabling and disabling notification pulse behavior
177afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchouk    private boolean mScreenOn = true;
178d3c8de2efc8f4f9287e0a8dfdeefb03ba6aaec98Jason Sams    private boolean mInCall = false;
179d3c8de2efc8f4f9287e0a8dfdeefb03ba6aaec98Jason Sams    private boolean mNotificationPulseEnabled;
180326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
181326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    // used as a mutex for access to all active notifications & listeners
182326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    final ArrayList<NotificationRecord> mNotificationList =
183afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchouk            new ArrayList<NotificationRecord>();
184fb6b614bcea88a587a7ea4530be45ff0ffa0210eAlex Sakhartchouk    final NotificationComparator mRankingComparator = new NotificationComparator();
185fb6b614bcea88a587a7ea4530be45ff0ffa0210eAlex Sakhartchouk    final ArrayMap<String, NotificationRecord> mNotificationsByKey =
186afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchouk            new ArrayMap<String, NotificationRecord>();
187fb6b614bcea88a587a7ea4530be45ff0ffa0210eAlex Sakhartchouk    final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
188fb6b614bcea88a587a7ea4530be45ff0ffa0210eAlex Sakhartchouk
189326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
190326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    NotificationRecord mLedNotification;
191326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
192326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private AppOpsManager mAppOps;
193326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
194afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchouk    // Notification control database. For now just contains disabled packages.
195e514b45de8561fbc6ef6770845102ca10b0a69d7Jason Sams    private AtomicFile mPolicyFile;
1969397e30ce5fe3f6af9212a93b490836b04fdfffaJason Sams    private HashSet<String> mBlockedPackages = new HashSet<String>();
197d3c8de2efc8f4f9287e0a8dfdeefb03ba6aaec98Jason Sams
198326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final int DB_VERSION = 1;
199326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
200afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchouk    private static final String TAG_BODY = "notification-policy";
201326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final String ATTR_VERSION = "version";
202326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
203326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final String TAG_BLOCKED_PKGS = "blocked-packages";
204326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final String TAG_PACKAGE = "package";
205326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final String ATTR_NAME = "name";
206afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchouk
207326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    final ArrayList<NotificationSignalExtractor> mSignalExtractors = new ArrayList<NotificationSignalExtractor>();
208afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchouk
209326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private final UserProfiles mUserProfiles = new UserProfiles();
210326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private NotificationListeners mListeners;
211326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private ConditionProviders mConditionProviders;
212326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private NotificationUsageStats mUsageStats;
213326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
214326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final String EXTRA_INTERCEPT = "android.intercept";
215326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
216326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final int MY_UID = Process.myUid();
217326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final int MY_PID = Process.myPid();
218326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final int REASON_DELEGATE_CLICK = 1;
219326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final int REASON_DELEGATE_CANCEL = 2;
220326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final int REASON_DELEGATE_CANCEL_ALL = 3;
221326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final int REASON_DELEGATE_ERROR = 4;
222326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final int REASON_PACKAGE_CHANGED = 5;
223326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final int REASON_USER_STOPPED = 6;
224326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final int REASON_PACKAGE_BANNED = 7;
225326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final int REASON_NOMAN_CANCEL = 8;
226326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final int REASON_NOMAN_CANCEL_ALL = 9;
227326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final int REASON_LISTENER_CANCEL = 10;
228326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    private static final int REASON_LISTENER_CANCEL_ALL = 11;
229326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
230afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchouk    private static class Archive {
231326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams        static final int BUFFER_SIZE = 250;
232326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams        ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE);
233326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
234326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams        public Archive() {
235afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchouk        }
236326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
237326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams        public String toString() {
238326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams            final StringBuilder sb = new StringBuilder();
239326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams            final int N = mBuffer.size();
240326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams            sb.append("Archive (");
241326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams            sb.append(N);
242            sb.append(" notification");
243            sb.append((N==1)?")":"s)");
244            return sb.toString();
245        }
246
247        public void record(StatusBarNotification nr) {
248            if (mBuffer.size() == BUFFER_SIZE) {
249                mBuffer.removeFirst();
250            }
251
252            // We don't want to store the heavy bits of the notification in the archive,
253            // but other clients in the system process might be using the object, so we
254            // store a (lightened) copy.
255            mBuffer.addLast(nr.cloneLight());
256        }
257
258
259        public void clear() {
260            mBuffer.clear();
261        }
262
263        public Iterator<StatusBarNotification> descendingIterator() {
264            return mBuffer.descendingIterator();
265        }
266        public Iterator<StatusBarNotification> ascendingIterator() {
267            return mBuffer.iterator();
268        }
269        public Iterator<StatusBarNotification> filter(
270                final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
271            return new Iterator<StatusBarNotification>() {
272                StatusBarNotification mNext = findNext();
273
274                private StatusBarNotification findNext() {
275                    while (iter.hasNext()) {
276                        StatusBarNotification nr = iter.next();
277                        if ((pkg == null || nr.getPackageName() == pkg)
278                                && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
279                            return nr;
280                        }
281                    }
282                    return null;
283                }
284
285                @Override
286                public boolean hasNext() {
287                    return mNext == null;
288                }
289
290                @Override
291                public StatusBarNotification next() {
292                    StatusBarNotification next = mNext;
293                    if (next == null) {
294                        throw new NoSuchElementException();
295                    }
296                    mNext = findNext();
297                    return next;
298                }
299
300                @Override
301                public void remove() {
302                    iter.remove();
303                }
304            };
305        }
306
307        public StatusBarNotification[] getArray(int count) {
308            if (count == 0) count = Archive.BUFFER_SIZE;
309            final StatusBarNotification[] a
310                    = new StatusBarNotification[Math.min(count, mBuffer.size())];
311            Iterator<StatusBarNotification> iter = descendingIterator();
312            int i=0;
313            while (iter.hasNext() && i < count) {
314                a[i++] = iter.next();
315            }
316            return a;
317        }
318
319        public StatusBarNotification[] getArray(int count, String pkg, int userId) {
320            if (count == 0) count = Archive.BUFFER_SIZE;
321            final StatusBarNotification[] a
322                    = new StatusBarNotification[Math.min(count, mBuffer.size())];
323            Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
324            int i=0;
325            while (iter.hasNext() && i < count) {
326                a[i++] = iter.next();
327            }
328            return a;
329        }
330
331    }
332
333    Archive mArchive = new Archive();
334
335    private void loadPolicyFile() {
336        synchronized(mPolicyFile) {
337            mBlockedPackages.clear();
338
339            FileInputStream infile = null;
340            try {
341                infile = mPolicyFile.openRead();
342                final XmlPullParser parser = Xml.newPullParser();
343                parser.setInput(infile, null);
344
345                int type;
346                String tag;
347                int version = DB_VERSION;
348                while ((type = parser.next()) != END_DOCUMENT) {
349                    tag = parser.getName();
350                    if (type == START_TAG) {
351                        if (TAG_BODY.equals(tag)) {
352                            version = Integer.parseInt(
353                                    parser.getAttributeValue(null, ATTR_VERSION));
354                        } else if (TAG_BLOCKED_PKGS.equals(tag)) {
355                            while ((type = parser.next()) != END_DOCUMENT) {
356                                tag = parser.getName();
357                                if (TAG_PACKAGE.equals(tag)) {
358                                    mBlockedPackages.add(
359                                            parser.getAttributeValue(null, ATTR_NAME));
360                                } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
361                                    break;
362                                }
363                            }
364                        }
365                    }
366                    mZenModeHelper.readXml(parser);
367                }
368            } catch (FileNotFoundException e) {
369                // No data yet
370            } catch (IOException e) {
371                Log.wtf(TAG, "Unable to read notification policy", e);
372            } catch (NumberFormatException e) {
373                Log.wtf(TAG, "Unable to parse notification policy", e);
374            } catch (XmlPullParserException e) {
375                Log.wtf(TAG, "Unable to parse notification policy", e);
376            } finally {
377                IoUtils.closeQuietly(infile);
378            }
379        }
380    }
381
382    public void savePolicyFile() {
383        mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
384        mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
385    }
386
387    private void handleSavePolicyFile() {
388        Slog.d(TAG, "handleSavePolicyFile");
389        synchronized (mPolicyFile) {
390            final FileOutputStream stream;
391            try {
392                stream = mPolicyFile.startWrite();
393            } catch (IOException e) {
394                Slog.w(TAG, "Failed to save policy file", e);
395                return;
396            }
397
398            try {
399                final XmlSerializer out = new FastXmlSerializer();
400                out.setOutput(stream, "utf-8");
401                out.startDocument(null, true);
402                out.startTag(null, TAG_BODY);
403                out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
404                mZenModeHelper.writeXml(out);
405                out.endTag(null, TAG_BODY);
406                out.endDocument();
407                mPolicyFile.finishWrite(stream);
408            } catch (IOException e) {
409                Slog.w(TAG, "Failed to save policy file, restoring backup", e);
410                mPolicyFile.failWrite(stream);
411            }
412        }
413    }
414
415    /** Use this when you actually want to post a notification or toast.
416     *
417     * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
418     */
419    private boolean noteNotificationOp(String pkg, int uid) {
420        if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
421                != AppOpsManager.MODE_ALLOWED) {
422            Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
423            return false;
424        }
425        return true;
426    }
427
428    private static String idDebugString(Context baseContext, String packageName, int id) {
429        Context c = null;
430
431        if (packageName != null) {
432            try {
433                c = baseContext.createPackageContext(packageName, 0);
434            } catch (NameNotFoundException e) {
435                c = baseContext;
436            }
437        } else {
438            c = baseContext;
439        }
440
441        String pkg;
442        String type;
443        String name;
444
445        Resources r = c.getResources();
446        try {
447            return r.getResourceName(id);
448        } catch (Resources.NotFoundException e) {
449            return "<name unknown>";
450        }
451    }
452
453
454
455    public static final class NotificationRecord
456    {
457        final StatusBarNotification sbn;
458        SingleNotificationStats stats;
459        IBinder statusBarKey;
460
461        // These members are used by NotificationSignalExtractors
462        // to communicate with the ranking module.
463        private float mContactAffinity;
464        private boolean mRecentlyIntrusive;
465
466        NotificationRecord(StatusBarNotification sbn)
467        {
468            this.sbn = sbn;
469        }
470
471        public Notification getNotification() { return sbn.getNotification(); }
472        public int getFlags() { return sbn.getNotification().flags; }
473        public int getUserId() { return sbn.getUserId(); }
474
475        void dump(PrintWriter pw, String prefix, Context baseContext) {
476            final Notification notification = sbn.getNotification();
477            pw.println(prefix + this);
478            pw.println(prefix + "  uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
479            pw.println(prefix + "  icon=0x" + Integer.toHexString(notification.icon)
480                    + " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon));
481            pw.println(prefix + "  pri=" + notification.priority + " score=" + sbn.getScore());
482            pw.println(prefix + "  key=" + sbn.getKey());
483            pw.println(prefix + "  contentIntent=" + notification.contentIntent);
484            pw.println(prefix + "  deleteIntent=" + notification.deleteIntent);
485            pw.println(prefix + "  tickerText=" + notification.tickerText);
486            pw.println(prefix + "  contentView=" + notification.contentView);
487            pw.println(prefix + String.format("  defaults=0x%08x flags=0x%08x",
488                    notification.defaults, notification.flags));
489            pw.println(prefix + "  sound=" + notification.sound);
490            pw.println(prefix + String.format("  color=0x%08x", notification.color));
491            pw.println(prefix + "  vibrate=" + Arrays.toString(notification.vibrate));
492            pw.println(prefix + String.format("  led=0x%08x onMs=%d offMs=%d",
493                    notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
494            if (notification.actions != null && notification.actions.length > 0) {
495                pw.println(prefix + "  actions={");
496                final int N = notification.actions.length;
497                for (int i=0; i<N; i++) {
498                    final Notification.Action action = notification.actions[i];
499                    pw.println(String.format("%s    [%d] \"%s\" -> %s",
500                            prefix,
501                            i,
502                            action.title,
503                            action.actionIntent.toString()
504                            ));
505                }
506                pw.println(prefix + "  }");
507            }
508            if (notification.extras != null && notification.extras.size() > 0) {
509                pw.println(prefix + "  extras={");
510                for (String key : notification.extras.keySet()) {
511                    pw.print(prefix + "    " + key + "=");
512                    Object val = notification.extras.get(key);
513                    if (val == null) {
514                        pw.println("null");
515                    } else {
516                        pw.print(val.getClass().getSimpleName());
517                        if (val instanceof CharSequence || val instanceof String) {
518                            // redact contents from bugreports
519                        } else if (val instanceof Bitmap) {
520                            pw.print(String.format(" (%dx%d)",
521                                    ((Bitmap) val).getWidth(),
522                                    ((Bitmap) val).getHeight()));
523                        } else if (val.getClass().isArray()) {
524                            final int N = Array.getLength(val);
525                            pw.println(" (" + N + ")");
526                        } else {
527                            pw.print(" (" + String.valueOf(val) + ")");
528                        }
529                        pw.println();
530                    }
531                }
532                pw.println(prefix + "  }");
533            }
534            pw.println(prefix + "  stats=" + stats.toString());
535            pw.println(prefix + "  mContactAffinity=" + mContactAffinity);
536            pw.println(prefix + "  mRecentlyIntrusive=" + mRecentlyIntrusive);
537        }
538
539        @Override
540        public final String toString() {
541            return String.format(
542                    "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
543                    System.identityHashCode(this),
544                    this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
545                    this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
546                    this.sbn.getNotification());
547        }
548
549        public void setContactAffinity(float contactAffinity) {
550            mContactAffinity = contactAffinity;
551        }
552
553        public float getContactAffinity() {
554            return mContactAffinity;
555        }
556
557        public boolean isRecentlyIntrusive() {
558            return mRecentlyIntrusive;
559        }
560
561        public void setRecentlyIntusive(boolean recentlyIntrusive) {
562            mRecentlyIntrusive = recentlyIntrusive;
563        }
564    }
565
566    private static final class ToastRecord
567    {
568        final int pid;
569        final String pkg;
570        final ITransientNotification callback;
571        int duration;
572
573        ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
574        {
575            this.pid = pid;
576            this.pkg = pkg;
577            this.callback = callback;
578            this.duration = duration;
579        }
580
581        void update(int duration) {
582            this.duration = duration;
583        }
584
585        void dump(PrintWriter pw, String prefix) {
586            pw.println(prefix + this);
587        }
588
589        @Override
590        public final String toString()
591        {
592            return "ToastRecord{"
593                + Integer.toHexString(System.identityHashCode(this))
594                + " pkg=" + pkg
595                + " callback=" + callback
596                + " duration=" + duration;
597        }
598    }
599
600    private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
601
602        @Override
603        public void onSetDisabled(int status) {
604            synchronized (mNotificationList) {
605                mDisableNotificationAlerts = (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
606                if (mDisableNotificationAlerts) {
607                    // cancel whatever's going on
608                    long identity = Binder.clearCallingIdentity();
609                    try {
610                        final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
611                        if (player != null) {
612                            player.stopAsync();
613                        }
614                    } catch (RemoteException e) {
615                    } finally {
616                        Binder.restoreCallingIdentity(identity);
617                    }
618
619                    identity = Binder.clearCallingIdentity();
620                    try {
621                        mVibrator.cancel();
622                    } finally {
623                        Binder.restoreCallingIdentity(identity);
624                    }
625                }
626            }
627        }
628
629        @Override
630        public void onClearAll(int callingUid, int callingPid, int userId) {
631            synchronized (mNotificationList) {
632                cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
633                        /*includeCurrentProfiles*/ true);
634            }
635        }
636
637        @Override
638        public void onNotificationClick(int callingUid, int callingPid, String key) {
639            synchronized (mNotificationList) {
640                EventLogTags.writeNotificationClicked(key);
641                NotificationRecord r = mNotificationsByKey.get(key);
642                if (r == null) {
643                    Log.w(TAG, "No notification with key: " + key);
644                    return;
645                }
646                StatusBarNotification sbn = r.sbn;
647                cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
648                        sbn.getId(), Notification.FLAG_AUTO_CANCEL,
649                        Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
650                        REASON_DELEGATE_CLICK, null);
651            }
652        }
653
654        @Override
655        public void onNotificationClear(int callingUid, int callingPid,
656                String pkg, String tag, int id, int userId) {
657            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
658                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
659                    true, userId, REASON_DELEGATE_CANCEL, null);
660        }
661
662        @Override
663        public void onPanelRevealed() {
664            EventLogTags.writeNotificationPanelRevealed();
665            synchronized (mNotificationList) {
666                // sound
667                mSoundNotification = null;
668
669                long identity = Binder.clearCallingIdentity();
670                try {
671                    final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
672                    if (player != null) {
673                        player.stopAsync();
674                    }
675                } catch (RemoteException e) {
676                } finally {
677                    Binder.restoreCallingIdentity(identity);
678                }
679
680                // vibrate
681                mVibrateNotification = null;
682                identity = Binder.clearCallingIdentity();
683                try {
684                    mVibrator.cancel();
685                } finally {
686                    Binder.restoreCallingIdentity(identity);
687                }
688
689                // light
690                mLights.clear();
691                mLedNotification = null;
692                updateLightsLocked();
693            }
694        }
695
696        @Override
697        public void onPanelHidden() {
698            EventLogTags.writeNotificationPanelHidden();
699        }
700
701        @Override
702        public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
703                int uid, int initialPid, String message, int userId) {
704            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
705                    + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
706            cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
707                    REASON_DELEGATE_ERROR, null);
708            long ident = Binder.clearCallingIdentity();
709            try {
710                ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
711                        "Bad notification posted from package " + pkg
712                        + ": " + message);
713            } catch (RemoteException e) {
714            }
715            Binder.restoreCallingIdentity(ident);
716        }
717
718        @Override
719        public boolean allowDisable(int what, IBinder token, String pkg) {
720            return mZenModeHelper.allowDisable(what, token, pkg);
721        }
722
723        @Override
724        public void onNotificationVisibilityChanged(
725                String[] newlyVisibleKeys, String[] noLongerVisibleKeys) {
726            // Using ';' as separator since eventlogs uses ',' to separate
727            // args.
728            EventLogTags.writeNotificationVisibilityChanged(
729                    TextUtils.join(";", newlyVisibleKeys),
730                    TextUtils.join(";", noLongerVisibleKeys));
731            synchronized (mNotificationList) {
732                for (String key : newlyVisibleKeys) {
733                    NotificationRecord r = mNotificationsByKey.get(key);
734                    if (r == null) continue;
735                    r.stats.onVisibilityChanged(true);
736                }
737                // Note that we might receive this event after notifications
738                // have already left the system, e.g. after dismissing from the
739                // shade. Hence not finding notifications in
740                // mNotificationsByKey is not an exceptional condition.
741                for (String key : noLongerVisibleKeys) {
742                    NotificationRecord r = mNotificationsByKey.get(key);
743                    if (r == null) continue;
744                    r.stats.onVisibilityChanged(false);
745                }
746            }
747        }
748    };
749
750    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
751        @Override
752        public void onReceive(Context context, Intent intent) {
753            String action = intent.getAction();
754
755            boolean queryRestart = false;
756            boolean queryRemove = false;
757            boolean packageChanged = false;
758            boolean cancelNotifications = true;
759
760            if (action.equals(Intent.ACTION_PACKAGE_ADDED)
761                    || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
762                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
763                    || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
764                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
765                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
766                String pkgList[] = null;
767                boolean queryReplace = queryRemove &&
768                        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
769                if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
770                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
771                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
772                } else if (queryRestart) {
773                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
774                } else {
775                    Uri uri = intent.getData();
776                    if (uri == null) {
777                        return;
778                    }
779                    String pkgName = uri.getSchemeSpecificPart();
780                    if (pkgName == null) {
781                        return;
782                    }
783                    if (packageChanged) {
784                        // We cancel notifications for packages which have just been disabled
785                        try {
786                            final int enabled = getContext().getPackageManager()
787                                    .getApplicationEnabledSetting(pkgName);
788                            if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
789                                    || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
790                                cancelNotifications = false;
791                            }
792                        } catch (IllegalArgumentException e) {
793                            // Package doesn't exist; probably racing with uninstall.
794                            // cancelNotifications is already true, so nothing to do here.
795                            if (DBG) {
796                                Slog.i(TAG, "Exception trying to look up app enabled setting", e);
797                            }
798                        }
799                    }
800                    pkgList = new String[]{pkgName};
801                }
802
803                if (pkgList != null && (pkgList.length > 0)) {
804                    for (String pkgName : pkgList) {
805                        if (cancelNotifications) {
806                            cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
807                                    UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null);
808                        }
809                    }
810                }
811                mListeners.onPackagesChanged(queryReplace, pkgList);
812                mConditionProviders.onPackagesChanged(queryReplace, pkgList);
813            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
814                // Keep track of screen on/off state, but do not turn off the notification light
815                // until user passes through the lock screen or views the notification.
816                mScreenOn = true;
817            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
818                mScreenOn = false;
819            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
820                mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
821                        .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
822                updateNotificationPulse();
823            } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
824                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
825                if (userHandle >= 0) {
826                    cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
827                            REASON_USER_STOPPED, null);
828                }
829            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
830                // turn off LED when user passes through lock screen
831                mNotificationLight.turnOff();
832            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
833                // reload per-user settings
834                mSettingsObserver.update(null);
835                mUserProfiles.updateCache(context);
836            } else if (action.equals(Intent.ACTION_USER_ADDED)) {
837                mUserProfiles.updateCache(context);
838            }
839        }
840    };
841
842    class SettingsObserver extends ContentObserver {
843        private final Uri NOTIFICATION_LIGHT_PULSE_URI
844                = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
845
846        SettingsObserver(Handler handler) {
847            super(handler);
848        }
849
850        void observe() {
851            ContentResolver resolver = getContext().getContentResolver();
852            resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
853                    false, this, UserHandle.USER_ALL);
854            update(null);
855        }
856
857        @Override public void onChange(boolean selfChange, Uri uri) {
858            update(uri);
859        }
860
861        public void update(Uri uri) {
862            ContentResolver resolver = getContext().getContentResolver();
863            if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
864                boolean pulseEnabled = Settings.System.getInt(resolver,
865                            Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
866                if (mNotificationPulseEnabled != pulseEnabled) {
867                    mNotificationPulseEnabled = pulseEnabled;
868                    updateNotificationPulse();
869                }
870            }
871        }
872    }
873
874    private SettingsObserver mSettingsObserver;
875    private ZenModeHelper mZenModeHelper;
876
877    static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
878        int[] ar = r.getIntArray(resid);
879        if (ar == null) {
880            return def;
881        }
882        final int len = ar.length > maxlen ? maxlen : ar.length;
883        long[] out = new long[len];
884        for (int i=0; i<len; i++) {
885            out[i] = ar[i];
886        }
887        return out;
888    }
889
890    public NotificationManagerService(Context context) {
891        super(context);
892    }
893
894    @Override
895    public void onStart() {
896        mAm = ActivityManagerNative.getDefault();
897        mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
898        mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
899
900        mHandler = new WorkerHandler();
901        mRankingThread.start();
902        mRankingHandler = new RankingWorkerHandler(mRankingThread.getLooper());
903        mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
904        mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
905            @Override
906            public void onConfigChanged() {
907                savePolicyFile();
908            }
909        });
910        final File systemDir = new File(Environment.getDataDirectory(), "system");
911        mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
912        mUsageStats = new NotificationUsageStats(getContext());
913
914        importOldBlockDb();
915
916        mListeners = new NotificationListeners();
917        mConditionProviders = new ConditionProviders(getContext(),
918                mHandler, mUserProfiles, mZenModeHelper);
919        mStatusBar = getLocalService(StatusBarManagerInternal.class);
920        mStatusBar.setNotificationDelegate(mNotificationDelegate);
921
922        final LightsManager lights = getLocalService(LightsManager.class);
923        mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
924        mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
925
926        Resources resources = getContext().getResources();
927        mDefaultNotificationColor = resources.getColor(
928                R.color.config_defaultNotificationColor);
929        mDefaultNotificationLedOn = resources.getInteger(
930                R.integer.config_defaultNotificationLedOn);
931        mDefaultNotificationLedOff = resources.getInteger(
932                R.integer.config_defaultNotificationLedOff);
933
934        mDefaultVibrationPattern = getLongArray(resources,
935                R.array.config_defaultNotificationVibePattern,
936                VIBRATE_PATTERN_MAXLEN,
937                DEFAULT_VIBRATE_PATTERN);
938
939        mFallbackVibrationPattern = getLongArray(resources,
940                R.array.config_notificationFallbackVibePattern,
941                VIBRATE_PATTERN_MAXLEN,
942                DEFAULT_VIBRATE_PATTERN);
943
944        // Don't start allowing notifications until the setup wizard has run once.
945        // After that, including subsequent boots, init with notifications turned on.
946        // This works on the first boot because the setup wizard will toggle this
947        // flag at least once and we'll go back to 0 after that.
948        if (0 == Settings.Global.getInt(getContext().getContentResolver(),
949                    Settings.Global.DEVICE_PROVISIONED, 0)) {
950            mDisableNotificationAlerts = true;
951        }
952        mZenModeHelper.updateZenMode();
953
954        mUserProfiles.updateCache(getContext());
955
956        // register for various Intents
957        IntentFilter filter = new IntentFilter();
958        filter.addAction(Intent.ACTION_SCREEN_ON);
959        filter.addAction(Intent.ACTION_SCREEN_OFF);
960        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
961        filter.addAction(Intent.ACTION_USER_PRESENT);
962        filter.addAction(Intent.ACTION_USER_STOPPED);
963        filter.addAction(Intent.ACTION_USER_SWITCHED);
964        filter.addAction(Intent.ACTION_USER_ADDED);
965        getContext().registerReceiver(mIntentReceiver, filter);
966        IntentFilter pkgFilter = new IntentFilter();
967        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
968        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
969        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
970        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
971        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
972        pkgFilter.addDataScheme("package");
973        getContext().registerReceiver(mIntentReceiver, pkgFilter);
974        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
975        getContext().registerReceiver(mIntentReceiver, sdFilter);
976
977        mSettingsObserver = new SettingsObserver(mHandler);
978
979        // spin up NotificationSignalExtractors
980        String[] extractorNames = resources.getStringArray(
981                R.array.config_notificationSignalExtractors);
982        for (String extractorName : extractorNames) {
983            try {
984                Class<?> extractorClass = getContext().getClassLoader().loadClass(extractorName);
985                NotificationSignalExtractor extractor =
986                        (NotificationSignalExtractor) extractorClass.newInstance();
987                extractor.initialize(getContext());
988                mSignalExtractors.add(extractor);
989            } catch (ClassNotFoundException e) {
990                Slog.w(TAG, "Couldn't find extractor " + extractorName + ".", e);
991            } catch (InstantiationException e) {
992                Slog.w(TAG, "Couldn't instantiate extractor " + extractorName + ".", e);
993            } catch (IllegalAccessException e) {
994                Slog.w(TAG, "Problem accessing extractor " + extractorName + ".", e);
995            }
996        }
997
998        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
999        publishLocalService(NotificationManagerInternal.class, mInternalService);
1000    }
1001
1002    /**
1003     * Read the old XML-based app block database and import those blockages into the AppOps system.
1004     */
1005    private void importOldBlockDb() {
1006        loadPolicyFile();
1007
1008        PackageManager pm = getContext().getPackageManager();
1009        for (String pkg : mBlockedPackages) {
1010            PackageInfo info = null;
1011            try {
1012                info = pm.getPackageInfo(pkg, 0);
1013                setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
1014            } catch (NameNotFoundException e) {
1015                // forget you
1016            }
1017        }
1018        mBlockedPackages.clear();
1019    }
1020
1021    @Override
1022    public void onBootPhase(int phase) {
1023        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1024            // no beeping until we're basically done booting
1025            mSystemReady = true;
1026
1027            // Grab our optional AudioService
1028            mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1029
1030        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1031            // This observer will force an update when observe is called, causing us to
1032            // bind to listener services.
1033            mSettingsObserver.observe();
1034            mListeners.onBootPhaseAppsCanStart();
1035            mConditionProviders.onBootPhaseAppsCanStart();
1036        }
1037    }
1038
1039    void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1040        Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
1041
1042        mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1043                enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
1044
1045        // Now, cancel any outstanding notifications that are part of a just-disabled app
1046        if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
1047            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1048                    REASON_PACKAGE_BANNED, null);
1049        }
1050    }
1051
1052    private final IBinder mService = new INotificationManager.Stub() {
1053        // Toasts
1054        // ============================================================================
1055
1056        @Override
1057        public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1058        {
1059            if (DBG) {
1060                Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1061                        + " duration=" + duration);
1062            }
1063
1064            if (pkg == null || callback == null) {
1065                Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1066                return ;
1067            }
1068
1069            final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1070
1071            if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1072                if (!isSystemToast) {
1073                    Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1074                    return;
1075                }
1076            }
1077
1078            synchronized (mToastQueue) {
1079                int callingPid = Binder.getCallingPid();
1080                long callingId = Binder.clearCallingIdentity();
1081                try {
1082                    ToastRecord record;
1083                    int index = indexOfToastLocked(pkg, callback);
1084                    // If it's already in the queue, we update it in place, we don't
1085                    // move it to the end of the queue.
1086                    if (index >= 0) {
1087                        record = mToastQueue.get(index);
1088                        record.update(duration);
1089                    } else {
1090                        // Limit the number of toasts that any given package except the android
1091                        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1092                        if (!isSystemToast) {
1093                            int count = 0;
1094                            final int N = mToastQueue.size();
1095                            for (int i=0; i<N; i++) {
1096                                 final ToastRecord r = mToastQueue.get(i);
1097                                 if (r.pkg.equals(pkg)) {
1098                                     count++;
1099                                     if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1100                                         Slog.e(TAG, "Package has already posted " + count
1101                                                + " toasts. Not showing more. Package=" + pkg);
1102                                         return;
1103                                     }
1104                                 }
1105                            }
1106                        }
1107
1108                        record = new ToastRecord(callingPid, pkg, callback, duration);
1109                        mToastQueue.add(record);
1110                        index = mToastQueue.size() - 1;
1111                        keepProcessAliveLocked(callingPid);
1112                    }
1113                    // If it's at index 0, it's the current toast.  It doesn't matter if it's
1114                    // new or just been updated.  Call back and tell it to show itself.
1115                    // If the callback fails, this will remove it from the list, so don't
1116                    // assume that it's valid after this.
1117                    if (index == 0) {
1118                        showNextToastLocked();
1119                    }
1120                } finally {
1121                    Binder.restoreCallingIdentity(callingId);
1122                }
1123            }
1124        }
1125
1126        @Override
1127        public void cancelToast(String pkg, ITransientNotification callback) {
1128            Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1129
1130            if (pkg == null || callback == null) {
1131                Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1132                return ;
1133            }
1134
1135            synchronized (mToastQueue) {
1136                long callingId = Binder.clearCallingIdentity();
1137                try {
1138                    int index = indexOfToastLocked(pkg, callback);
1139                    if (index >= 0) {
1140                        cancelToastLocked(index);
1141                    } else {
1142                        Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1143                                + " callback=" + callback);
1144                    }
1145                } finally {
1146                    Binder.restoreCallingIdentity(callingId);
1147                }
1148            }
1149        }
1150
1151        @Override
1152        public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1153                Notification notification, int[] idOut, int userId) throws RemoteException {
1154            enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1155                    Binder.getCallingPid(), tag, id, notification, idOut, userId);
1156        }
1157
1158        @Override
1159        public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1160            checkCallerIsSystemOrSameApp(pkg);
1161            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1162                    Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1163            // Don't allow client applications to cancel foreground service notis.
1164            cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1165                    Binder.getCallingUid() == Process.SYSTEM_UID
1166                    ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1167                    null);
1168        }
1169
1170        @Override
1171        public void cancelAllNotifications(String pkg, int userId) {
1172            checkCallerIsSystemOrSameApp(pkg);
1173
1174            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1175                    Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1176
1177            // Calling from user space, don't allow the canceling of actively
1178            // running foreground services.
1179            cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1180                    pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1181                    REASON_NOMAN_CANCEL_ALL, null);
1182        }
1183
1184        @Override
1185        public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1186            checkCallerIsSystem();
1187
1188            setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1189        }
1190
1191        /**
1192         * Use this when you just want to know if notifications are OK for this package.
1193         */
1194        @Override
1195        public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1196            checkCallerIsSystem();
1197            return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1198                    == AppOpsManager.MODE_ALLOWED);
1199        }
1200
1201        /**
1202         * System-only API for getting a list of current (i.e. not cleared) notifications.
1203         *
1204         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1205         * @returns A list of all the notifications, in natural order.
1206         */
1207        @Override
1208        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1209            // enforce() will ensure the calling uid has the correct permission
1210            getContext().enforceCallingOrSelfPermission(
1211                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1212                    "NotificationManagerService.getActiveNotifications");
1213
1214            StatusBarNotification[] tmp = null;
1215            int uid = Binder.getCallingUid();
1216
1217            // noteOp will check to make sure the callingPkg matches the uid
1218            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1219                    == AppOpsManager.MODE_ALLOWED) {
1220                synchronized (mNotificationList) {
1221                    tmp = new StatusBarNotification[mNotificationList.size()];
1222                    final int N = mNotificationList.size();
1223                    for (int i=0; i<N; i++) {
1224                        tmp[i] = mNotificationList.get(i).sbn;
1225                    }
1226                }
1227            }
1228            return tmp;
1229        }
1230
1231        /**
1232         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1233         *
1234         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1235         */
1236        @Override
1237        public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1238            // enforce() will ensure the calling uid has the correct permission
1239            getContext().enforceCallingOrSelfPermission(
1240                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1241                    "NotificationManagerService.getHistoricalNotifications");
1242
1243            StatusBarNotification[] tmp = null;
1244            int uid = Binder.getCallingUid();
1245
1246            // noteOp will check to make sure the callingPkg matches the uid
1247            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1248                    == AppOpsManager.MODE_ALLOWED) {
1249                synchronized (mArchive) {
1250                    tmp = mArchive.getArray(count);
1251                }
1252            }
1253            return tmp;
1254        }
1255
1256        /**
1257         * Register a listener binder directly with the notification manager.
1258         *
1259         * Only works with system callers. Apps should extend
1260         * {@link android.service.notification.NotificationListenerService}.
1261         */
1262        @Override
1263        public void registerListener(final INotificationListener listener,
1264                final ComponentName component, final int userid) {
1265            checkCallerIsSystem();
1266            mListeners.registerService(listener, component, userid);
1267        }
1268
1269        /**
1270         * Remove a listener binder directly
1271         */
1272        @Override
1273        public void unregisterListener(INotificationListener listener, int userid) {
1274            mListeners.unregisterService(listener, userid);
1275        }
1276
1277        /**
1278         * Allow an INotificationListener to simulate a "clear all" operation.
1279         *
1280         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1281         *
1282         * @param token The binder for the listener, to check that the caller is allowed
1283         */
1284        @Override
1285        public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1286            final int callingUid = Binder.getCallingUid();
1287            final int callingPid = Binder.getCallingPid();
1288            long identity = Binder.clearCallingIdentity();
1289            try {
1290                synchronized (mNotificationList) {
1291                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1292                    if (keys != null) {
1293                        final int N = keys.length;
1294                        for (int i = 0; i < N; i++) {
1295                            NotificationRecord r = mNotificationsByKey.get(keys[i]);
1296                            final int userId = r.sbn.getUserId();
1297                            if (userId != info.userid && userId != UserHandle.USER_ALL &&
1298                                    !mUserProfiles.isCurrentProfile(userId)) {
1299                                throw new SecurityException("Disallowed call from listener: "
1300                                        + info.service);
1301                            }
1302                            if (r != null) {
1303                                cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1304                                        r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1305                                        userId);
1306                            }
1307                        }
1308                    } else {
1309                        cancelAllLocked(callingUid, callingPid, info.userid,
1310                                REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1311                    }
1312                }
1313            } finally {
1314                Binder.restoreCallingIdentity(identity);
1315            }
1316        }
1317
1318        private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
1319                int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
1320            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1321                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1322                    true,
1323                    userId, REASON_LISTENER_CANCEL, info);
1324        }
1325
1326        /**
1327         * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1328         *
1329         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1330         *
1331         * @param token The binder for the listener, to check that the caller is allowed
1332         */
1333        @Override
1334        public void cancelNotificationFromListener(INotificationListener token, String pkg,
1335                String tag, int id) {
1336            final int callingUid = Binder.getCallingUid();
1337            final int callingPid = Binder.getCallingPid();
1338            long identity = Binder.clearCallingIdentity();
1339            try {
1340                synchronized (mNotificationList) {
1341                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1342                    if (info.supportsProfiles()) {
1343                        Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1344                                + "from " + info.component
1345                                + " use cancelNotification(key) instead.");
1346                    } else {
1347                        cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1348                                pkg, tag, id, info.userid);
1349                    }
1350                }
1351            } finally {
1352                Binder.restoreCallingIdentity(identity);
1353            }
1354        }
1355
1356        /**
1357         * Allow an INotificationListener to request the list of outstanding notifications seen by
1358         * the current user. Useful when starting up, after which point the listener callbacks
1359         * should be used.
1360         *
1361         * @param token The binder for the listener, to check that the caller is allowed
1362         * @param keys the notification keys to fetch, or null for all active notifications.
1363         * @returns The return value will contain the notifications specified in keys, in that
1364         *      order, or if keys is null, all the notifications, in natural order.
1365         */
1366        @Override
1367        public StatusBarNotification[] getActiveNotificationsFromListener(
1368                INotificationListener token, String[] keys) {
1369            synchronized (mNotificationList) {
1370                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1371                final ArrayList<StatusBarNotification> list
1372                        = new ArrayList<StatusBarNotification>();
1373                if (keys == null) {
1374                    final int N = mNotificationList.size();
1375                    for (int i=0; i<N; i++) {
1376                        StatusBarNotification sbn = mNotificationList.get(i).sbn;
1377                        if (info.enabledAndUserMatches(sbn.getUserId())) {
1378                            list.add(sbn);
1379                        }
1380                    }
1381                } else {
1382                    final int N = keys.length;
1383                    for (int i=0; i<N; i++) {
1384                        NotificationRecord r = mNotificationsByKey.get(keys[i]);
1385                        if (r != null && info.enabledAndUserMatches(r.sbn.getUserId())) {
1386                            list.add(r.sbn);
1387                        }
1388                    }
1389                }
1390                return list.toArray(new StatusBarNotification[list.size()]);
1391            }
1392        }
1393
1394        @Override
1395        public String[] getActiveNotificationKeysFromListener(INotificationListener token) {
1396            return NotificationManagerService.this.getActiveNotificationKeys(token);
1397        }
1398
1399        @Override
1400        public ZenModeConfig getZenModeConfig() {
1401            checkCallerIsSystem();
1402            return mZenModeHelper.getConfig();
1403        }
1404
1405        @Override
1406        public boolean setZenModeConfig(ZenModeConfig config) {
1407            checkCallerIsSystem();
1408            return mZenModeHelper.setConfig(config);
1409        }
1410
1411        @Override
1412        public void notifyConditions(String pkg, IConditionProvider provider,
1413                Condition[] conditions) {
1414            final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1415            checkCallerIsSystemOrSameApp(pkg);
1416            final long identity = Binder.clearCallingIdentity();
1417            try {
1418                mConditionProviders.notifyConditions(pkg, info, conditions);
1419            } finally {
1420                Binder.restoreCallingIdentity(identity);
1421            }
1422        }
1423
1424        @Override
1425        public void requestZenModeConditions(IConditionListener callback, int relevance) {
1426            enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions");
1427            mConditionProviders.requestZenModeConditions(callback, relevance);
1428        }
1429
1430        @Override
1431        public void setZenModeCondition(Uri conditionId) {
1432            enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
1433            final long identity = Binder.clearCallingIdentity();
1434            try {
1435                mConditionProviders.setZenModeCondition(conditionId);
1436            } finally {
1437                Binder.restoreCallingIdentity(identity);
1438            }
1439        }
1440
1441        @Override
1442        public void setAutomaticZenModeConditions(Uri[] conditionIds) {
1443            enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions");
1444            mConditionProviders.setAutomaticZenModeConditions(conditionIds);
1445        }
1446
1447        @Override
1448        public Condition[] getAutomaticZenModeConditions() {
1449            enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions");
1450            return mConditionProviders.getAutomaticZenModeConditions();
1451        }
1452
1453        private void enforceSystemOrSystemUI(String message) {
1454            if (isCallerSystem()) return;
1455            getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1456                    message);
1457        }
1458
1459        @Override
1460        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1461            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1462                    != PackageManager.PERMISSION_GRANTED) {
1463                pw.println("Permission Denial: can't dump NotificationManager from from pid="
1464                        + Binder.getCallingPid()
1465                        + ", uid=" + Binder.getCallingUid());
1466                return;
1467            }
1468
1469            dumpImpl(pw);
1470        }
1471    };
1472
1473    private String[] getActiveNotificationKeys(INotificationListener token) {
1474        final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1475        final ArrayList<String> keys = new ArrayList<String>();
1476        if (info.isEnabledForCurrentProfiles()) {
1477            synchronized (mNotificationList) {
1478                final int N = mNotificationList.size();
1479                for (int i = 0; i < N; i++) {
1480                    final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1481                    if (info.enabledAndUserMatches(sbn.getUserId())) {
1482                        keys.add(sbn.getKey());
1483                    }
1484                }
1485            }
1486        }
1487        return keys.toArray(new String[keys.size()]);
1488    }
1489
1490    void dumpImpl(PrintWriter pw) {
1491        pw.println("Current Notification Manager state:");
1492
1493        int N;
1494
1495        synchronized (mToastQueue) {
1496            N = mToastQueue.size();
1497            if (N > 0) {
1498                pw.println("  Toast Queue:");
1499                for (int i=0; i<N; i++) {
1500                    mToastQueue.get(i).dump(pw, "    ");
1501                }
1502                pw.println("  ");
1503            }
1504
1505        }
1506
1507        synchronized (mNotificationList) {
1508            N = mNotificationList.size();
1509            if (N > 0) {
1510                pw.println("  Notification List:");
1511                for (int i=0; i<N; i++) {
1512                    mNotificationList.get(i).dump(pw, "    ", getContext());
1513                }
1514                pw.println("  ");
1515            }
1516
1517            N = mLights.size();
1518            if (N > 0) {
1519                pw.println("  Lights List:");
1520                for (int i=0; i<N; i++) {
1521                    pw.println("    " + mLights.get(i));
1522                }
1523                pw.println("  ");
1524            }
1525
1526            pw.println("  mSoundNotification=" + mSoundNotification);
1527            pw.println("  mVibrateNotification=" + mVibrateNotification);
1528            pw.println("  mDisableNotificationAlerts=" + mDisableNotificationAlerts);
1529            pw.println("  mSystemReady=" + mSystemReady);
1530            pw.println("  mArchive=" + mArchive.toString());
1531            Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1532            int i=0;
1533            while (iter.hasNext()) {
1534                pw.println("    " + iter.next());
1535                if (++i >= 5) {
1536                    if (iter.hasNext()) pw.println("    ...");
1537                    break;
1538                }
1539            }
1540
1541            pw.println("\n  Usage Stats:");
1542            mUsageStats.dump(pw, "    ");
1543
1544            pw.println("\n  Zen Mode:");
1545            mZenModeHelper.dump(pw, "    ");
1546
1547            pw.println("\n  Notification listeners:");
1548            mListeners.dump(pw);
1549
1550            pw.println("\n  Condition providers:");
1551            mConditionProviders.dump(pw);
1552        }
1553    }
1554
1555    /**
1556     * The private API only accessible to the system process.
1557     */
1558    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1559        @Override
1560        public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
1561                String tag, int id, Notification notification, int[] idReceived, int userId) {
1562            enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
1563                    idReceived, userId);
1564        }
1565    };
1566
1567    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
1568            final int callingPid, final String tag, final int id, final Notification notification,
1569            int[] idOut, int incomingUserId) {
1570        if (DBG) {
1571            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1572                    + " notification=" + notification);
1573        }
1574        checkCallerIsSystemOrSameApp(pkg);
1575        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
1576
1577        final int userId = ActivityManager.handleIncomingUser(callingPid,
1578                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
1579        final UserHandle user = new UserHandle(userId);
1580
1581        // Limit the number of notifications that any given package except the android
1582        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1583        if (!isSystemNotification) {
1584            synchronized (mNotificationList) {
1585                int count = 0;
1586                final int N = mNotificationList.size();
1587                for (int i=0; i<N; i++) {
1588                    final NotificationRecord r = mNotificationList.get(i);
1589                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
1590                        count++;
1591                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1592                            Slog.e(TAG, "Package has already posted " + count
1593                                    + " notifications.  Not showing more.  package=" + pkg);
1594                            return;
1595                        }
1596                    }
1597                }
1598            }
1599        }
1600
1601        // This conditional is a dirty hack to limit the logging done on
1602        //     behalf of the download manager without affecting other apps.
1603        if (!pkg.equals("com.android.providers.downloads")
1604                || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
1605            EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
1606                    pkg, id, tag, userId, notification.toString());
1607        }
1608
1609        if (pkg == null || notification == null) {
1610            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1611                    + " id=" + id + " notification=" + notification);
1612        }
1613        if (notification.icon != 0) {
1614            if (notification.contentView == null) {
1615                throw new IllegalArgumentException("contentView required: pkg=" + pkg
1616                        + " id=" + id + " notification=" + notification);
1617            }
1618        }
1619
1620        mHandler.post(new Runnable() {
1621            @Override
1622            public void run() {
1623
1624                // === Scoring ===
1625
1626                // 0. Sanitize inputs
1627                notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1628                        Notification.PRIORITY_MAX);
1629                // Migrate notification flags to scores
1630                if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1631                    if (notification.priority < Notification.PRIORITY_MAX) {
1632                        notification.priority = Notification.PRIORITY_MAX;
1633                    }
1634                } else if (SCORE_ONGOING_HIGHER &&
1635                        0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1636                    if (notification.priority < Notification.PRIORITY_HIGH) {
1637                        notification.priority = Notification.PRIORITY_HIGH;
1638                    }
1639                }
1640
1641                // 1. initial score: buckets of 10, around the app
1642                int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
1643
1644                // 2. extract ranking signals from the notification data
1645                final StatusBarNotification n = new StatusBarNotification(
1646                        pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
1647                        user);
1648                NotificationRecord r = new NotificationRecord(n);
1649                if (!mSignalExtractors.isEmpty()) {
1650                    for (NotificationSignalExtractor extractor : mSignalExtractors) {
1651                        try {
1652                            RankingFuture future = extractor.process(r);
1653                            scheduleRankingReconsideration(future);
1654                        } catch (Throwable t) {
1655                            Slog.w(TAG, "NotificationSignalExtractor failed.", t);
1656                        }
1657                    }
1658                }
1659
1660                // 3. Apply local rules
1661
1662                // blocked apps
1663                if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1664                    if (!isSystemNotification) {
1665                        score = JUNK_SCORE;
1666                        Slog.e(TAG, "Suppressing notification from package " + pkg
1667                                + " by user request.");
1668                    }
1669                }
1670
1671                if (score < SCORE_DISPLAY_THRESHOLD) {
1672                    // Notification will be blocked because the score is too low.
1673                    return;
1674                }
1675
1676                // Is this notification intercepted by zen mode?
1677                final boolean intercept = mZenModeHelper.shouldIntercept(pkg, notification);
1678                notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
1679
1680                // Should this notification make noise, vibe, or use the LED?
1681                final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept;
1682                if (DBG || intercept) Slog.v(TAG,
1683                        "pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept);
1684                synchronized (mNotificationList) {
1685                    NotificationRecord old = null;
1686                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
1687                    if (index < 0) {
1688                        mNotificationList.add(r);
1689                        mUsageStats.registerPostedByApp(r);
1690                    } else {
1691                        old = mNotificationList.get(index);
1692                        mNotificationList.set(index, r);
1693                        mUsageStats.registerUpdatedByApp(r, old);
1694                        // Make sure we don't lose the foreground service state.
1695                        if (old != null) {
1696                            notification.flags |=
1697                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
1698                        }
1699                    }
1700                    if (old != null) {
1701                        mNotificationsByKey.remove(old.sbn.getKey());
1702                    }
1703                    mNotificationsByKey.put(n.getKey(), r);
1704
1705                    Collections.sort(mNotificationList, mRankingComparator);
1706
1707                    // Ensure if this is a foreground service that the proper additional
1708                    // flags are set.
1709                    if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1710                        notification.flags |= Notification.FLAG_ONGOING_EVENT
1711                                | Notification.FLAG_NO_CLEAR;
1712                    }
1713
1714                    final int currentUser;
1715                    final long token = Binder.clearCallingIdentity();
1716                    try {
1717                        currentUser = ActivityManager.getCurrentUser();
1718                    } finally {
1719                        Binder.restoreCallingIdentity(token);
1720                    }
1721
1722                    if (notification.icon != 0) {
1723                        if (old != null && old.statusBarKey != null) {
1724                            r.statusBarKey = old.statusBarKey;
1725                            final long identity = Binder.clearCallingIdentity();
1726                            try {
1727                                mStatusBar.updateNotification(r.statusBarKey, n);
1728                            } finally {
1729                                Binder.restoreCallingIdentity(identity);
1730                            }
1731                        } else {
1732                            final long identity = Binder.clearCallingIdentity();
1733                            try {
1734                                r.statusBarKey = mStatusBar.addNotification(n);
1735                                if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
1736                                        && canInterrupt) {
1737                                    mAttentionLight.pulse();
1738                                }
1739                            } finally {
1740                                Binder.restoreCallingIdentity(identity);
1741                            }
1742                        }
1743                        // Send accessibility events only for the current user.
1744                        if (currentUser == userId) {
1745                            sendAccessibilityEvent(notification, pkg);
1746                        }
1747
1748                        mListeners.notifyPostedLocked(r.sbn, cloneNotificationListLocked());
1749                    } else {
1750                        Slog.e(TAG, "Not posting notification with icon==0: " + notification);
1751                        if (old != null && old.statusBarKey != null) {
1752                            final long identity = Binder.clearCallingIdentity();
1753                            try {
1754                                mStatusBar.removeNotification(old.statusBarKey);
1755                            } finally {
1756                                Binder.restoreCallingIdentity(identity);
1757                            }
1758
1759                            mListeners.notifyRemovedLocked(r.sbn, cloneNotificationListLocked());
1760                        }
1761                        // ATTENTION: in a future release we will bail out here
1762                        // so that we do not play sounds, show lights, etc. for invalid
1763                        // notifications
1764                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1765                                + n.getPackageName());
1766                    }
1767
1768                    // If we're not supposed to beep, vibrate, etc. then don't.
1769                    if (!mDisableNotificationAlerts
1770                            && (!(old != null
1771                                && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1772                            && (r.getUserId() == UserHandle.USER_ALL ||
1773                                (r.getUserId() == userId && r.getUserId() == currentUser) ||
1774                                mUserProfiles.isCurrentProfile(r.getUserId()))
1775                            && canInterrupt
1776                            && mSystemReady
1777                            && mAudioManager != null) {
1778                        if (DBG) Slog.v(TAG, "Interrupting!");
1779                        // sound
1780
1781                        // should we use the default notification sound? (indicated either by
1782                        // DEFAULT_SOUND or because notification.sound is pointing at
1783                        // Settings.System.NOTIFICATION_SOUND)
1784                        final boolean useDefaultSound =
1785                               (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
1786                                       Settings.System.DEFAULT_NOTIFICATION_URI
1787                                               .equals(notification.sound);
1788
1789                        Uri soundUri = null;
1790                        boolean hasValidSound = false;
1791
1792                        if (useDefaultSound) {
1793                            soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1794
1795                            // check to see if the default notification sound is silent
1796                            ContentResolver resolver = getContext().getContentResolver();
1797                            hasValidSound = Settings.System.getString(resolver,
1798                                   Settings.System.NOTIFICATION_SOUND) != null;
1799                        } else if (notification.sound != null) {
1800                            soundUri = notification.sound;
1801                            hasValidSound = (soundUri != null);
1802                        }
1803
1804                        if (hasValidSound) {
1805                            boolean looping =
1806                                    (notification.flags & Notification.FLAG_INSISTENT) != 0;
1807                            int audioStreamType;
1808                            if (notification.audioStreamType >= 0) {
1809                                audioStreamType = notification.audioStreamType;
1810                            } else {
1811                                audioStreamType = DEFAULT_STREAM_TYPE;
1812                            }
1813                            mSoundNotification = r;
1814                            // do not play notifications if stream volume is 0 (typically because
1815                            // ringer mode is silent) or if there is a user of exclusive audio focus
1816                            if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
1817                                    && !mAudioManager.isAudioFocusExclusive()) {
1818                                final long identity = Binder.clearCallingIdentity();
1819                                try {
1820                                    final IRingtonePlayer player =
1821                                            mAudioManager.getRingtonePlayer();
1822                                    if (player != null) {
1823                                        if (DBG) Slog.v(TAG, "Playing sound " + soundUri
1824                                                + " on stream " + audioStreamType);
1825                                        player.playAsync(soundUri, user, looping, audioStreamType);
1826                                    }
1827                                } catch (RemoteException e) {
1828                                } finally {
1829                                    Binder.restoreCallingIdentity(identity);
1830                                }
1831                            }
1832                        }
1833
1834                        // vibrate
1835                        // Does the notification want to specify its own vibration?
1836                        final boolean hasCustomVibrate = notification.vibrate != null;
1837
1838                        // new in 4.2: if there was supposed to be a sound and we're in vibrate
1839                        // mode, and no other vibration is specified, we fall back to vibration
1840                        final boolean convertSoundToVibration =
1841                                   !hasCustomVibrate
1842                                && hasValidSound
1843                                && (mAudioManager.getRingerMode()
1844                                           == AudioManager.RINGER_MODE_VIBRATE);
1845
1846                        // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1847                        final boolean useDefaultVibrate =
1848                                (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1849
1850                        if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
1851                                && !(mAudioManager.getRingerMode()
1852                                        == AudioManager.RINGER_MODE_SILENT)) {
1853                            mVibrateNotification = r;
1854
1855                            if (useDefaultVibrate || convertSoundToVibration) {
1856                                // Escalate privileges so we can use the vibrator even if the
1857                                // notifying app does not have the VIBRATE permission.
1858                                long identity = Binder.clearCallingIdentity();
1859                                try {
1860                                    mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
1861                                        useDefaultVibrate ? mDefaultVibrationPattern
1862                                            : mFallbackVibrationPattern,
1863                                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1864                                                ? 0: -1, notification.audioStreamType);
1865                                } finally {
1866                                    Binder.restoreCallingIdentity(identity);
1867                                }
1868                            } else if (notification.vibrate.length > 1) {
1869                                // If you want your own vibration pattern, you need the VIBRATE
1870                                // permission
1871                                mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
1872                                        notification.vibrate,
1873                                    ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1874                                            ? 0: -1, notification.audioStreamType);
1875                            }
1876                        }
1877                    }
1878
1879                    // light
1880                    // the most recent thing gets the light
1881                    mLights.remove(old);
1882                    if (mLedNotification == old) {
1883                        mLedNotification = null;
1884                    }
1885                    //Slog.i(TAG, "notification.lights="
1886                    //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
1887                    //                  != 0));
1888                    if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
1889                            && canInterrupt) {
1890                        mLights.add(r);
1891                        updateLightsLocked();
1892                    } else {
1893                        if (old != null
1894                                && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
1895                            updateLightsLocked();
1896                        }
1897                    }
1898                }
1899            }
1900        });
1901
1902        idOut[0] = id;
1903    }
1904
1905    void showNextToastLocked() {
1906        ToastRecord record = mToastQueue.get(0);
1907        while (record != null) {
1908            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
1909            try {
1910                record.callback.show();
1911                scheduleTimeoutLocked(record);
1912                return;
1913            } catch (RemoteException e) {
1914                Slog.w(TAG, "Object died trying to show notification " + record.callback
1915                        + " in package " + record.pkg);
1916                // remove it from the list and let the process die
1917                int index = mToastQueue.indexOf(record);
1918                if (index >= 0) {
1919                    mToastQueue.remove(index);
1920                }
1921                keepProcessAliveLocked(record.pid);
1922                if (mToastQueue.size() > 0) {
1923                    record = mToastQueue.get(0);
1924                } else {
1925                    record = null;
1926                }
1927            }
1928        }
1929    }
1930
1931    void cancelToastLocked(int index) {
1932        ToastRecord record = mToastQueue.get(index);
1933        try {
1934            record.callback.hide();
1935        } catch (RemoteException e) {
1936            Slog.w(TAG, "Object died trying to hide notification " + record.callback
1937                    + " in package " + record.pkg);
1938            // don't worry about this, we're about to remove it from
1939            // the list anyway
1940        }
1941        mToastQueue.remove(index);
1942        keepProcessAliveLocked(record.pid);
1943        if (mToastQueue.size() > 0) {
1944            // Show the next one. If the callback fails, this will remove
1945            // it from the list, so don't assume that the list hasn't changed
1946            // after this point.
1947            showNextToastLocked();
1948        }
1949    }
1950
1951    private void scheduleTimeoutLocked(ToastRecord r)
1952    {
1953        mHandler.removeCallbacksAndMessages(r);
1954        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1955        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
1956        mHandler.sendMessageDelayed(m, delay);
1957    }
1958
1959    private void handleTimeout(ToastRecord record)
1960    {
1961        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
1962        synchronized (mToastQueue) {
1963            int index = indexOfToastLocked(record.pkg, record.callback);
1964            if (index >= 0) {
1965                cancelToastLocked(index);
1966            }
1967        }
1968    }
1969
1970    // lock on mToastQueue
1971    int indexOfToastLocked(String pkg, ITransientNotification callback)
1972    {
1973        IBinder cbak = callback.asBinder();
1974        ArrayList<ToastRecord> list = mToastQueue;
1975        int len = list.size();
1976        for (int i=0; i<len; i++) {
1977            ToastRecord r = list.get(i);
1978            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
1979                return i;
1980            }
1981        }
1982        return -1;
1983    }
1984
1985    // lock on mToastQueue
1986    void keepProcessAliveLocked(int pid)
1987    {
1988        int toastCount = 0; // toasts from this pid
1989        ArrayList<ToastRecord> list = mToastQueue;
1990        int N = list.size();
1991        for (int i=0; i<N; i++) {
1992            ToastRecord r = list.get(i);
1993            if (r.pid == pid) {
1994                toastCount++;
1995            }
1996        }
1997        try {
1998            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
1999        } catch (RemoteException e) {
2000            // Shouldn't happen.
2001        }
2002    }
2003
2004    private void scheduleRankingReconsideration(RankingFuture future) {
2005        if (future != null) {
2006            Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, future);
2007            long delay = future.getDelay(TimeUnit.MILLISECONDS);
2008            mRankingHandler.sendMessageDelayed(m, delay);
2009        }
2010    }
2011
2012    private void handleRankingReconsideration(Message message) {
2013        if (!(message.obj instanceof RankingFuture)) return;
2014
2015        RankingFuture future = (RankingFuture) message.obj;
2016        future.run();
2017        try {
2018            NotificationRecord record = future.get();
2019            synchronized (mNotificationList) {
2020                int before = mNotificationList.indexOf(record);
2021                if (before != -1) {
2022                    Collections.sort(mNotificationList, mRankingComparator);
2023                    int after = mNotificationList.indexOf(record);
2024
2025                    if (before != after) {
2026                        scheduleSendRankingUpdate();
2027                    }
2028                }
2029            }
2030        } catch (InterruptedException e) {
2031            // we're running the future explicitly, so this should never happen
2032        } catch (ExecutionException e) {
2033            // we're running the future explicitly, so this should never happen
2034        }
2035    }
2036
2037    private void scheduleSendRankingUpdate() {
2038        mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
2039        Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
2040        mHandler.sendMessage(m);
2041    }
2042
2043    private void handleSendRankingUpdate() {
2044        synchronized (mNotificationList) {
2045            mListeners.notifyRankingUpdateLocked(cloneNotificationListLocked());
2046        }
2047    }
2048
2049    private ArrayList<StatusBarNotification> cloneNotificationListLocked() {
2050        final int N = mNotificationList.size();
2051        ArrayList<StatusBarNotification> sbns = new ArrayList<StatusBarNotification>(N);
2052        for (int i = 0; i < N; i++) {
2053            sbns.add(mNotificationList.get(i).sbn);
2054        }
2055        return sbns;
2056    }
2057
2058    private final class WorkerHandler extends Handler
2059    {
2060        @Override
2061        public void handleMessage(Message msg)
2062        {
2063            switch (msg.what)
2064            {
2065                case MESSAGE_TIMEOUT:
2066                    handleTimeout((ToastRecord)msg.obj);
2067                    break;
2068                case MESSAGE_SAVE_POLICY_FILE:
2069                    handleSavePolicyFile();
2070                    break;
2071                case MESSAGE_SEND_RANKING_UPDATE:
2072                    handleSendRankingUpdate();
2073                    break;
2074            }
2075        }
2076
2077    }
2078
2079    private final class RankingWorkerHandler extends Handler
2080    {
2081        public RankingWorkerHandler(Looper looper) {
2082            super(looper);
2083        }
2084
2085        @Override
2086        public void handleMessage(Message msg) {
2087            switch (msg.what) {
2088                case MESSAGE_RECONSIDER_RANKING:
2089                    handleRankingReconsideration(msg);
2090                    break;
2091            }
2092        }
2093    }
2094
2095    // Notifications
2096    // ============================================================================
2097    static int clamp(int x, int low, int high) {
2098        return (x < low) ? low : ((x > high) ? high : x);
2099    }
2100
2101    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2102        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
2103        if (!manager.isEnabled()) {
2104            return;
2105        }
2106
2107        AccessibilityEvent event =
2108            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2109        event.setPackageName(packageName);
2110        event.setClassName(Notification.class.getName());
2111        event.setParcelableData(notification);
2112        CharSequence tickerText = notification.tickerText;
2113        if (!TextUtils.isEmpty(tickerText)) {
2114            event.getText().add(tickerText);
2115        }
2116
2117        manager.sendAccessibilityEvent(event);
2118    }
2119
2120    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
2121        // tell the app
2122        if (sendDelete) {
2123            if (r.getNotification().deleteIntent != null) {
2124                try {
2125                    r.getNotification().deleteIntent.send();
2126                } catch (PendingIntent.CanceledException ex) {
2127                    // do nothing - there's no relevant way to recover, and
2128                    //     no reason to let this propagate
2129                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
2130                }
2131            }
2132        }
2133
2134        // status bar
2135        if (r.getNotification().icon != 0) {
2136            final long identity = Binder.clearCallingIdentity();
2137            try {
2138                mStatusBar.removeNotification(r.statusBarKey);
2139            } finally {
2140                Binder.restoreCallingIdentity(identity);
2141            }
2142            r.statusBarKey = null;
2143            mListeners.notifyRemovedLocked(r.sbn, cloneNotificationListLocked());
2144        }
2145
2146        // sound
2147        if (mSoundNotification == r) {
2148            mSoundNotification = null;
2149            final long identity = Binder.clearCallingIdentity();
2150            try {
2151                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2152                if (player != null) {
2153                    player.stopAsync();
2154                }
2155            } catch (RemoteException e) {
2156            } finally {
2157                Binder.restoreCallingIdentity(identity);
2158            }
2159        }
2160
2161        // vibrate
2162        if (mVibrateNotification == r) {
2163            mVibrateNotification = null;
2164            long identity = Binder.clearCallingIdentity();
2165            try {
2166                mVibrator.cancel();
2167            }
2168            finally {
2169                Binder.restoreCallingIdentity(identity);
2170            }
2171        }
2172
2173        // light
2174        mLights.remove(r);
2175        if (mLedNotification == r) {
2176            mLedNotification = null;
2177        }
2178
2179        // Record usage stats
2180        switch (reason) {
2181            case REASON_DELEGATE_CANCEL:
2182            case REASON_DELEGATE_CANCEL_ALL:
2183            case REASON_LISTENER_CANCEL:
2184            case REASON_LISTENER_CANCEL_ALL:
2185                mUsageStats.registerDismissedByUser(r);
2186                break;
2187            case REASON_NOMAN_CANCEL:
2188            case REASON_NOMAN_CANCEL_ALL:
2189                mUsageStats.registerRemovedByApp(r);
2190                break;
2191            case REASON_DELEGATE_CLICK:
2192                mUsageStats.registerCancelDueToClick(r);
2193                break;
2194            default:
2195                mUsageStats.registerCancelUnknown(r);
2196                break;
2197        }
2198
2199        // Save it for users of getHistoricalNotifications()
2200        mArchive.record(r.sbn);
2201    }
2202
2203    /**
2204     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2205     * and none of the {@code mustNotHaveFlags}.
2206     */
2207    void cancelNotification(final int callingUid, final int callingPid,
2208            final String pkg, final String tag, final int id,
2209            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2210            final int userId, final int reason, final ManagedServiceInfo listener) {
2211        // In enqueueNotificationInternal notifications are added by scheduling the
2212        // work on the worker handler. Hence, we also schedule the cancel on this
2213        // handler to avoid a scenario where an add notification call followed by a
2214        // remove notification call ends up in not removing the notification.
2215        mHandler.post(new Runnable() {
2216            @Override
2217            public void run() {
2218                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2219                        mustHaveFlags, mustNotHaveFlags, reason,
2220                        listener == null ? null : listener.component.toShortString());
2221
2222                synchronized (mNotificationList) {
2223                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
2224                    if (index >= 0) {
2225                        NotificationRecord r = mNotificationList.get(index);
2226
2227                        // Ideally we'd do this in the caller of this method. However, that would
2228                        // require the caller to also find the notification.
2229                        if (reason == REASON_DELEGATE_CLICK) {
2230                            mUsageStats.registerClickedByUser(r);
2231                        }
2232
2233                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2234                            return;
2235                        }
2236                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2237                            return;
2238                        }
2239
2240                        mNotificationList.remove(index);
2241                        mNotificationsByKey.remove(r.sbn.getKey());
2242
2243                        cancelNotificationLocked(r, sendDelete, reason);
2244                        updateLightsLocked();
2245                    }
2246                }
2247            }
2248        });
2249    }
2250
2251    /**
2252     * Determine whether the userId applies to the notification in question, either because
2253     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2254     */
2255    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2256        return
2257                // looking for USER_ALL notifications? match everything
2258                   userId == UserHandle.USER_ALL
2259                // a notification sent to USER_ALL matches any query
2260                || r.getUserId() == UserHandle.USER_ALL
2261                // an exact user match
2262                || r.getUserId() == userId;
2263    }
2264
2265    /**
2266     * Determine whether the userId applies to the notification in question, either because
2267     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2268     * because it matches one of the users profiles.
2269     */
2270    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2271        return notificationMatchesUserId(r, userId)
2272                || mUserProfiles.isCurrentProfile(r.getUserId());
2273    }
2274
2275    /**
2276     * Cancels all notifications from a given package that have all of the
2277     * {@code mustHaveFlags}.
2278     */
2279    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2280            int mustNotHaveFlags, boolean doit, int userId, int reason,
2281            ManagedServiceInfo listener) {
2282        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2283                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2284                listener == null ? null : listener.component.toShortString());
2285
2286        synchronized (mNotificationList) {
2287            final int N = mNotificationList.size();
2288            boolean canceledSomething = false;
2289            for (int i = N-1; i >= 0; --i) {
2290                NotificationRecord r = mNotificationList.get(i);
2291                if (!notificationMatchesUserId(r, userId)) {
2292                    continue;
2293                }
2294                // Don't remove notifications to all, if there's no package name specified
2295                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2296                    continue;
2297                }
2298                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2299                    continue;
2300                }
2301                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2302                    continue;
2303                }
2304                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2305                    continue;
2306                }
2307                canceledSomething = true;
2308                if (!doit) {
2309                    return true;
2310                }
2311                mNotificationList.remove(i);
2312                mNotificationsByKey.remove(r.sbn.getKey());
2313                cancelNotificationLocked(r, false, reason);
2314            }
2315            if (canceledSomething) {
2316                updateLightsLocked();
2317            }
2318            return canceledSomething;
2319        }
2320    }
2321
2322    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2323            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
2324        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2325                null, userId, 0, 0, reason,
2326                listener == null ? null : listener.component.toShortString());
2327
2328        final int N = mNotificationList.size();
2329        for (int i=N-1; i>=0; i--) {
2330            NotificationRecord r = mNotificationList.get(i);
2331            if (includeCurrentProfiles) {
2332                if (!notificationMatchesCurrentProfiles(r, userId)) {
2333                    continue;
2334                }
2335            } else {
2336                if (!notificationMatchesUserId(r, userId)) {
2337                    continue;
2338                }
2339            }
2340
2341            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2342                            | Notification.FLAG_NO_CLEAR)) == 0) {
2343                mNotificationList.remove(i);
2344                mNotificationsByKey.remove(r.sbn.getKey());
2345                cancelNotificationLocked(r, true, reason);
2346            }
2347        }
2348        updateLightsLocked();
2349    }
2350
2351    // lock on mNotificationList
2352    void updateLightsLocked()
2353    {
2354        // handle notification lights
2355        if (mLedNotification == null) {
2356            // get next notification, if any
2357            int n = mLights.size();
2358            if (n > 0) {
2359                mLedNotification = mLights.get(n-1);
2360            }
2361        }
2362
2363        // Don't flash while we are in a call or screen is on
2364        if (mLedNotification == null || mInCall || mScreenOn) {
2365            mNotificationLight.turnOff();
2366        } else {
2367            final Notification ledno = mLedNotification.sbn.getNotification();
2368            int ledARGB = ledno.ledARGB;
2369            int ledOnMS = ledno.ledOnMS;
2370            int ledOffMS = ledno.ledOffMS;
2371            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2372                ledARGB = mDefaultNotificationColor;
2373                ledOnMS = mDefaultNotificationLedOn;
2374                ledOffMS = mDefaultNotificationLedOff;
2375            }
2376            if (mNotificationPulseEnabled) {
2377                // pulse repeatedly
2378                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2379                        ledOnMS, ledOffMS);
2380            }
2381        }
2382    }
2383
2384    // lock on mNotificationList
2385    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2386    {
2387        ArrayList<NotificationRecord> list = mNotificationList;
2388        final int len = list.size();
2389        for (int i=0; i<len; i++) {
2390            NotificationRecord r = list.get(i);
2391            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2392                continue;
2393            }
2394            if (tag == null) {
2395                if (r.sbn.getTag() != null) {
2396                    continue;
2397                }
2398            } else {
2399                if (!tag.equals(r.sbn.getTag())) {
2400                    continue;
2401                }
2402            }
2403            if (r.sbn.getPackageName().equals(pkg)) {
2404                return i;
2405            }
2406        }
2407        return -1;
2408    }
2409
2410    private void updateNotificationPulse() {
2411        synchronized (mNotificationList) {
2412            updateLightsLocked();
2413        }
2414    }
2415
2416    private static boolean isUidSystem(int uid) {
2417        final int appid = UserHandle.getAppId(uid);
2418        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2419    }
2420
2421    private static boolean isCallerSystem() {
2422        return isUidSystem(Binder.getCallingUid());
2423    }
2424
2425    private static void checkCallerIsSystem() {
2426        if (isCallerSystem()) {
2427            return;
2428        }
2429        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2430    }
2431
2432    private static void checkCallerIsSystemOrSameApp(String pkg) {
2433        if (isCallerSystem()) {
2434            return;
2435        }
2436        final int uid = Binder.getCallingUid();
2437        try {
2438            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2439                    pkg, 0, UserHandle.getCallingUserId());
2440            if (!UserHandle.isSameApp(ai.uid, uid)) {
2441                throw new SecurityException("Calling uid " + uid + " gave package"
2442                        + pkg + " which is owned by uid " + ai.uid);
2443            }
2444        } catch (RemoteException re) {
2445            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2446        }
2447    }
2448
2449    /**
2450     * Generates a NotificationRankingUpdate from 'sbns', considering only
2451     * notifications visible to the given listener.
2452     */
2453    private static NotificationRankingUpdate makeRankingUpdateForListener(ManagedServiceInfo info,
2454            ArrayList<StatusBarNotification> sbns) {
2455        int speedBumpIndex = -1;
2456        ArrayList<String> keys = new ArrayList<String>(sbns.size());
2457        ArrayList<String> dndKeys = new ArrayList<String>(sbns.size());
2458        for (StatusBarNotification sbn: sbns) {
2459            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2460                continue;
2461            }
2462            keys.add(sbn.getKey());
2463            if (sbn.getNotification().extras.getBoolean(EXTRA_INTERCEPT)) {
2464                dndKeys.add(sbn.getKey());
2465            }
2466            if (speedBumpIndex == -1 &&
2467                    sbn.getNotification().priority == Notification.PRIORITY_MIN) {
2468                speedBumpIndex = keys.size() - 1;
2469            }
2470        }
2471        String[] keysAr = keys.toArray(new String[keys.size()]);
2472        String[] dndKeysAr = dndKeys.toArray(new String[dndKeys.size()]);
2473        return new NotificationRankingUpdate(keysAr, dndKeysAr, speedBumpIndex);
2474    }
2475
2476    public class NotificationListeners extends ManagedServices {
2477
2478        public NotificationListeners() {
2479            super(getContext(), mHandler, mNotificationList, mUserProfiles);
2480        }
2481
2482        @Override
2483        protected Config getConfig() {
2484            Config c = new Config();
2485            c.caption = "notification listener";
2486            c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
2487            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
2488            c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
2489            c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
2490            c.clientLabel = R.string.notification_listener_binding_label;
2491            return c;
2492        }
2493
2494        @Override
2495        protected IInterface asInterface(IBinder binder) {
2496            return INotificationListener.Stub.asInterface(binder);
2497        }
2498
2499        @Override
2500        public void onServiceAdded(ManagedServiceInfo info) {
2501            final INotificationListener listener = (INotificationListener) info.service;
2502            final ArrayList<StatusBarNotification> sbns;
2503            synchronized (mNotificationList) {
2504                sbns = cloneNotificationListLocked();
2505            }
2506            try {
2507                listener.onListenerConnected(makeRankingUpdateForListener(info, sbns));
2508            } catch (RemoteException e) {
2509                // we tried
2510            }
2511        }
2512
2513        /**
2514         * asynchronously notify all listeners about a new notification
2515         */
2516        public void notifyPostedLocked(StatusBarNotification sbn,
2517                final ArrayList<StatusBarNotification> sbns) {
2518            // make a copy in case changes are made to the underlying Notification object
2519            final StatusBarNotification sbnClone = sbn.clone();
2520            for (final ManagedServiceInfo info : mServices) {
2521                if (!info.isEnabledForCurrentProfiles()) {
2522                    continue;
2523                }
2524                final NotificationRankingUpdate update = makeRankingUpdateForListener(info, sbns);
2525                if (update.getOrderedKeys().length == 0) {
2526                    continue;
2527                }
2528                mHandler.post(new Runnable() {
2529                    @Override
2530                    public void run() {
2531                        notifyPostedIfUserMatch(info, sbnClone, update);
2532                    }
2533                });
2534            }
2535        }
2536
2537        /**
2538         * asynchronously notify all listeners about a removed notification
2539         */
2540        public void notifyRemovedLocked(StatusBarNotification sbn,
2541                final ArrayList<StatusBarNotification> sbns) {
2542            // make a copy in case changes are made to the underlying Notification object
2543            // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
2544            // notification
2545            final StatusBarNotification sbnLight = sbn.cloneLight();
2546            for (final ManagedServiceInfo info : mServices) {
2547                if (!info.isEnabledForCurrentProfiles()) {
2548                    continue;
2549                }
2550                mHandler.post(new Runnable() {
2551                    @Override
2552                    public void run() {
2553                        notifyRemovedIfUserMatch(info, sbnLight,
2554                                makeRankingUpdateForListener(info, sbns));
2555                    }
2556                });
2557            }
2558        }
2559
2560        /**
2561         * asynchronously notify all listeners about a reordering of notifications
2562         * @param sbns an array of {@link StatusBarNotification}s to consider.  This code
2563         *             must not rely on mutable members of these objects, such as the
2564         *             {@link Notification}.
2565         */
2566        public void notifyRankingUpdateLocked(final ArrayList<StatusBarNotification> sbns) {
2567            for (final ManagedServiceInfo serviceInfo : mServices) {
2568                if (!serviceInfo.isEnabledForCurrentProfiles()) {
2569                    continue;
2570                }
2571                mHandler.post(new Runnable() {
2572                    @Override
2573                    public void run() {
2574                        notifyRankingUpdate(serviceInfo,
2575                                makeRankingUpdateForListener(serviceInfo, sbns));
2576                    }
2577                });
2578            }
2579        }
2580
2581        private void notifyPostedIfUserMatch(final ManagedServiceInfo info,
2582                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
2583            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2584                return;
2585            }
2586            final INotificationListener listener = (INotificationListener)info.service;
2587            try {
2588                listener.onNotificationPosted(sbn, rankingUpdate);
2589            } catch (RemoteException ex) {
2590                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
2591            }
2592        }
2593
2594        private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn,
2595                NotificationRankingUpdate rankingUpdate) {
2596            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2597                return;
2598            }
2599            final INotificationListener listener = (INotificationListener) info.service;
2600            try {
2601                listener.onNotificationRemoved(sbn, rankingUpdate);
2602            } catch (RemoteException ex) {
2603                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
2604            }
2605        }
2606
2607        private void notifyRankingUpdate(ManagedServiceInfo info,
2608                                         NotificationRankingUpdate rankingUpdate) {
2609            final INotificationListener listener = (INotificationListener) info.service;
2610            try {
2611                listener.onNotificationRankingUpdate(rankingUpdate);
2612            } catch (RemoteException ex) {
2613                Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
2614            }
2615        }
2616    }
2617}
2618