NotificationMangerService处理显示通知

设置——>应用——>点击“已下载”列表中的任一APP,如图:

 代码位置:SettingssrccomandroidsettingsapplicationsInstalledAppDetails.java

 1 //CheckBox显示通知处理
 2 private void setNotificationsEnabled(boolean enabled) {
 3         String packageName = mAppEntry.info.packageName;
 4         INotificationManager nm = INotificationManager.Stub.asInterface(
 5                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
 6         try {
 7             final boolean enable = mNotificationSwitch.isChecked();
 8             
 9             nm.setNotificationsEnabledForPackage(packageName, mAppEntry.info.uid, enabled);
10         } catch (android.os.RemoteException ex) {
11             mNotificationSwitch.setChecked(!enabled); // revert
12         }
13     }
INotificationManager 是通过AIDL处理,在java层就是NotificationMangerService处理。
androidframeworksaseservicesjavacomandroidserverNotificationManagerService.java
 1 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
 2         checkCallerIsSystem();//这里校验Uid检查调用程序有没有权限
 3 
 4         Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
 5 
 6         mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
 7                 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
 8 
 9         // Now, cancel any outstanding notifications that are part of a just-disabled app
10         if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
11             cancelAllNotificationsInt(pkg, 0, 0, true, UserHandle.getUserId(uid));
12         }
13     }
校验Uid检查调用程序有没有权限,了解sharedUserId可以参考http://www.cnblogs.com/Ashia/articles/2474321.html
 1 void checkCallerIsSystem() {
 2         if (isCallerSystem()) {
 3             return;
 4         }
 5         throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
 6     }
 7 
 8 boolean isCallerSystem() {
 9         return isUidSystem(Binder.getCallingUid());
10     }
11 
12 // Return true if the UID is a system or phone UID and therefore should not have
13     // any notifications or toasts blocked.
14     boolean isUidSystem(int uid) {
15         Slog.v(TAG, "isUidSystem , uid = " + uid);
16         final int appid = UserHandle.getAppId(uid);
17         return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
18     }
View Code

以上处理中mAppOps将需要处理的pkg做了标记,表示是否显示pkg发出的Notification

Notification的显示过程也是NotificationMangerService处理,在enqueueNotificationInternal(......)中处理,这里会判断pkg是否可以显示通知。

  1 // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
  2     // uid/pid of another application)
  3 
  4     public void enqueueNotificationInternal(final String pkg, String basePkg, final int callingUid,
  5             final int callingPid, final String tag, final int id, final Notification notification,
  6             int[] idOut, int incomingUserId)
  7     {
  8         if (DBG) {
  9             Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification);
 10         }
 11         checkCallerIsSystemOrSameApp(pkg);
 12         final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));//再次校验Uid
 13 
 14         final int userId = ActivityManager.handleIncomingUser(callingPid,
 15                 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
 16         final UserHandle user = new UserHandle(userId);
 17 
 18         // Limit the number of notifications that any given package except the android
 19         // package can enqueue.  Prevents DOS attacks and deals with leaks.
 20         if (!isSystemNotification) {
 21             synchronized (mNotificationList) {
 22                 int count = 0;
 23                 final int N = mNotificationList.size();
 24                 for (int i=0; i<N; i++) {
 25                     final NotificationRecord r = mNotificationList.get(i);
 26                     if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
 27                         count++;
 28                         if (count >= MAX_PACKAGE_NOTIFICATIONS) {//判断允许的最大通知数
 29                             Slog.e(TAG, "Package has already posted " + count
 30                                     + " notifications.  Not showing more.  package=" + pkg);
 31                             return;
 32                         }
 33                     }
 34                 }
 35             }
 36         }
 37 
 38         // This conditional is a dirty hack to limit the logging done on
 39         //     behalf of the download manager without affecting other apps.
 40         if (!pkg.equals("com.android.providers.downloads")
 41                 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
 42             EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, userId,
 43                     notification.toString());
 44         }
 45 
 46         if (pkg == null || notification == null) {
 47             throw new IllegalArgumentException("null not allowed: pkg=" + pkg
 48                     + " id=" + id + " notification=" + notification);
 49         }
 50         if (notification.icon != 0) {
 51             if (notification.contentView == null) {
 52                 throw new IllegalArgumentException("contentView required: pkg=" + pkg
 53                         + " id=" + id + " notification=" + notification);
 54             }
 55         }
 56 
 57         mHandler.post(new Runnable() {
 58             @Override
 59             public void run() {
 60 
 61                 // === Scoring ===
 62 
 63                 // 0. Sanitize inputs
 64                 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
 65                         Notification.PRIORITY_MAX);
 66                 // Migrate notification flags to scores
 67                 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
 68                     if (notification.priority < Notification.PRIORITY_MAX) {
 69                         notification.priority = Notification.PRIORITY_MAX;
 70                     }
 71                 } else if (SCORE_ONGOING_HIGHER &&
 72                         0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
 73                     if (notification.priority < Notification.PRIORITY_HIGH) {
 74                         notification.priority = Notification.PRIORITY_HIGH;
 75                     }
 76                 }
 77 
 78                 // 1. initial score: buckets of 10, around the app
 79                 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
 80 
 81                 // 2. Consult external heuristics (TBD)
 82 
 83                 // 3. Apply local rules
 84 
 85                 int initialScore = score;
 86                 if (!mScorers.isEmpty()) {
 87                     if (DBG) Slog.v(TAG, "Initial score is " + score + ".");
 88                     for (NotificationScorer scorer : mScorers) {
 89                         try {
 90                             score = scorer.getScore(notification, score);
 91                         } catch (Throwable t) {
 92                             Slog.w(TAG, "Scorer threw on .getScore.", t);
 93                         }
 94                     }
 95                     if (DBG) Slog.v(TAG, "Final score is " + score + ".");
 96                 }
 97 
 98                 // add extra to indicate score modified by NotificationScorer
 99                 notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED,
100                         score != initialScore);
101 
102                 // blocked apps
103                 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {//判断pkg是否可以显示通知
104                     if (!isSystemNotification) {//不拦截系统通知
105                         score = JUNK_SCORE; //在设置中禁止显示通知的pkg,会进到这里,JUNK_SCORE=-1000
106                         Slog.e(TAG, "Suppressing notification from package " + pkg
107                                 + " by user request.");
108                     }
109                 }
110 
111                 if (DBG) {
112                     Slog.v(TAG, "Assigned score=" + score + " to " + notification);
113                 }
114 
115                 if (score < SCORE_DISPLAY_THRESHOLD) {//进行"显示通知"拦截判断,SCORE_DIAPLAY_THRESHOLD=-20
116                     // Notification will be blocked because the score is too low.
117                     return; //完成拦截
118                 }
119 
120                 
// Should this notification make noise, vibe, or use the LED?
121                 final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD);
122 
123                 synchronized (mNotificationList) {
124                     final StatusBarNotification n = new StatusBarNotification(
125                             pkg, id, tag, callingUid, callingPid, score, notification, user);
126                     NotificationRecord r = new NotificationRecord(n);
127                     NotificationRecord old = null;
128 
129                     int index = indexOfNotificationLocked(pkg, tag, id, userId);
130                     if (index < 0) {
131                         mNotificationList.add(r);
132                     } else {
133                         old = mNotificationList.remove(index);
134                         mNotificationList.add(index, r);
135                         // Make sure we don't lose the foreground service state.
136                         if (old != null) {
137                             notification.flags |=
138                                 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
139                         }
140                     }
141 
142                     // Ensure if this is a foreground service that the proper additional
143                     // flags are set.
144                     if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
145                         notification.flags |= Notification.FLAG_ONGOING_EVENT
146                                 | Notification.FLAG_NO_CLEAR;
147                     }
148 
149                     final int currentUser;
150                     final long token = Binder.clearCallingIdentity();
151                     try {
152                         currentUser = ActivityManager.getCurrentUser();
153                     } finally {
154                         Binder.restoreCallingIdentity(token);
155                     }
156 
157                     if (notification.icon != 0) {
158                         if (old != null && old.statusBarKey != null) {
159                             r.statusBarKey = old.statusBarKey;
160                             long identity = Binder.clearCallingIdentity();
161                             try {
162                                 mStatusBar.updateNotification(r.statusBarKey, n);
163                             }
164                             finally {
165                                 Binder.restoreCallingIdentity(identity);
166                             }
167                         } else {
168                             long identity = Binder.clearCallingIdentity();
169                             try {
170                                 r.statusBarKey = mStatusBar.addNotification(n);
171                                 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
172                                         && canInterrupt) {
173                                     mAttentionLight.pulse();
174                                 }
175                             }
176                             finally {
177                                 Binder.restoreCallingIdentity(identity);
178                             }
179                         }
180                         // Send accessibility events only for the current user.
181                         if (currentUser == userId) {
182                             sendAccessibilityEvent(notification, pkg);
183                         }
184 
185                         notifyPostedLocked(r);
186                     } else {
187                         Slog.e(TAG, "Not posting notification with icon==0: " + notification);
188                         if (old != null && old.statusBarKey != null) {
189                             long identity = Binder.clearCallingIdentity();
190                             try {
191                                 mStatusBar.removeNotification(old.statusBarKey);
192                             }
193                             finally {
194                                 Binder.restoreCallingIdentity(identity);
195                             }
196 
197                             notifyRemovedLocked(r);
198                         }
199                         // ATTENTION: in a future release we will bail out here
200                         // so that we do not play sounds, show lights, etc. for invalid notifications
201                         Slog.e(TAG, "WARNING: In a future release this will crash the app: "
202                                 + n.getPackageName());
203                     }
204 
205                     // Have ring tone when received SMS when the device is CT mode
206                     boolean smsRingtone = mContext.getResources().getBoolean(
207                             com.android.internal.R.bool.config_sms_ringtone_incall);
208 
209                     // If we're not supposed to beep, vibrate, etc. then don't.
210                     if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS)
211                             == 0 || (smsRingtone && mInCall))
212                             && (!(old != null
213                                 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
214                             && (r.getUserId() == UserHandle.USER_ALL ||
215                                 (r.getUserId() == userId && r.getUserId() == currentUser))
216                             && canInterrupt
217                             && mSystemReady) {
218 
219                         final AudioManager audioManager = (AudioManager) mContext
220                         .getSystemService(Context.AUDIO_SERVICE);
221 
222                         // sound
223 
224                         // should we use the default notification sound? (indicated either by
225                         // DEFAULT_SOUND or because notification.sound is pointing at
226                         // Settings.System.NOTIFICATION_SOUND)
227                         final boolean useDefaultSound =
228                                (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
229                                        Settings.System.DEFAULT_NOTIFICATION_URI
230                                                .equals(notification.sound);
231 
232                         Uri soundUri = null;
233                         boolean hasValidSound = false;
234 
235                         if (useDefaultSound) {
236                             soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
237 
238                             // check to see if the default notification sound is silent
239                             ContentResolver resolver = mContext.getContentResolver();
240                             hasValidSound = Settings.System.getString(resolver,
241                                    Settings.System.NOTIFICATION_SOUND) != null;
242                         } else if (notification.sound != null) {
243                             soundUri = notification.sound;
244                             hasValidSound = (soundUri != null);
245                         }
246 
247                         if (hasValidSound) {
248                             boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
249                             int audioStreamType;
250                             if (notification.audioStreamType >= 0) {
251                                 audioStreamType = notification.audioStreamType;
252                             } else {
253                                 audioStreamType = DEFAULT_STREAM_TYPE;
254                             }
255                             mSoundNotification = r;
256                             // do not play notifications if stream volume is 0 (typically because
257                             // ringer mode is silent) or if there is a user of exclusive audio focus
258                             if ((audioManager.getStreamVolume(audioStreamType) != 0)
259                                     && !audioManager.isAudioFocusExclusive()) {
260                                 final long identity = Binder.clearCallingIdentity();
261                                 try {
262                                     final IRingtonePlayer player = mAudioService.getRingtonePlayer();
263                                     if (player != null) {
264                                         player.playAsync(soundUri, user, looping, audioStreamType);
265                                     }
266                                 } catch (RemoteException e) {
267                                 } finally {
268                                     Binder.restoreCallingIdentity(identity);
269                                 }
270                             }
271                         }
272 
273                         // vibrate
274                         // Does the notification want to specify its own vibration?
275                         final boolean hasCustomVibrate = notification.vibrate != null;
276 
277                         // new in 4.2: if there was supposed to be a sound and we're in vibrate
278                         // mode, and no other vibration is specified, we fall back to vibration
279                         final boolean convertSoundToVibration =
280                                    !hasCustomVibrate
281                                 && hasValidSound
282                                 && (audioManager.getRingerMode()
283                                            == AudioManager.RINGER_MODE_VIBRATE);
284 
285                         // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
286                         final boolean useDefaultVibrate =
287                                 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
288 
289                         if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
290                                 && !(audioManager.getRingerMode()
291                                         == AudioManager.RINGER_MODE_SILENT)) {
292                             mVibrateNotification = r;
293 
294                             if (useDefaultVibrate || convertSoundToVibration) {
295                                 // Escalate privileges so we can use the vibrator even if the
296                                 // notifying app does not have the VIBRATE permission.
297                                 long identity = Binder.clearCallingIdentity();
298                                 try {
299                                     mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
300                                         useDefaultVibrate ? mDefaultVibrationPattern
301                                             : mFallbackVibrationPattern,
302                                         ((notification.flags & Notification.FLAG_INSISTENT) != 0)
303                                                 ? 0: -1);
304                                 } finally {
305                                     Binder.restoreCallingIdentity(identity);
306                                 }
307                             } else if (notification.vibrate.length > 1) {
308                                 // If you want your own vibration pattern, you need the VIBRATE
309                                 // permission
310                                 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
311                                         notification.vibrate,
312                                     ((notification.flags & Notification.FLAG_INSISTENT) != 0)
313                                             ? 0: -1);
314                             }
315                         }
316                     }
317 
318                     // light
319                     // the most recent thing gets the light
320                     mLights.remove(old);
321                     if (mLedNotification == old) {
322                         mLedNotification = null;
323                     }
324                     //Slog.i(TAG, "notification.lights="
325                     //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
326                     //                  != 0));
327                     if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
328                             && canInterrupt) {
329                         mLights.add(r);
330                         updateLightsLocked();
331                     } else {
332                         if (old != null
333                                 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
334                             updateLightsLocked();
335                         }
336                     }
337                 }
338             }
339         });
340 
341         idOut[0] = id;
342     }
View Code
原文地址:https://www.cnblogs.com/antoon/p/4276852.html