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