设置——>应用——>点击“已下载”列表中的任一APP,如图:
代码位置:Settings\src\com\android\settings\applications\InstalledAppDetails.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处理。android\frameworks\base\services\java\com\android\server\NotificationManagerService.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 }
以上处理中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 }
时间: 2024-10-27 05:14:52