当前位置: 代码迷 >> 综合 >> SystemUI之USB2(Framework UEvent - SystemUI)
  详细解决方案

SystemUI之USB2(Framework UEvent - SystemUI)

热度:5   发布时间:2024-01-10 22:24:23.0

第二节  Framework UEvent -> SystemUI

逻辑主要在Framework里面的NotificationManagerService.java

UsbDeviceManager.java 到framework的NotificationManagerService.java,这里面涉及到通知的分类和排序,通知的显示风格等等,然后通过NotificationListenerService来通知SystemUI.

在SystemUI注册服务,然后在framework层回调,贯通SystemUI以及Framework。

UsbDeviceManager.java中——》

mNotificationManager.notifyAsUser(null, id, notification,UserHandle.ALL);

调用NotificationManager的notifyAsUser()往上上传notification,跟到方法里面看下,

路径:framework/base/core/java/android/app/NotificationManager.java

 INotificationManager service = getService();//获取IBinder对象

我们看下Notification的addFieldsFromContext方法做了哪些事情:
路径:frameworks/base/core/java/android/app/Notification.java
public static void addFieldsFromContext(Context context, Notification notification) {  addFieldsFromContext(context.getApplicationInfo(), context.getUserId(), notification);  
}  /** * @hide */  
public static void addFieldsFromContext(ApplicationInfo ai, int userId,  Notification notification) {  notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);//"android.appinfo"  notification.extras.putInt(EXTRA_ORIGINATING_USERID, userId);//"android.originatingUserId"  
}  
addFieldsFromContext方法主要实现将当前应用的ApplicationInfo对象保存到“android.appinfo”字段,将当前的userId保存到“android.originatingUserId”字段中。
 
 
fixLegacySmalIcon,notify函数会判断notification是否有small icon,如果没有设icon或small icon,用notify方法时会抛出异常。fixLegacySmallIcon方法如下:
    private void fixLegacySmallIcon(Notification n, String pkg) {  if (n.getSmallIcon() == null && n.icon != 0) {  n.setSmallIcon(Icon.createWithResource(pkg, n.icon));  }  }  
完成上面的操作后,调用通知管理类NotificationManagerService的enqueueNotificationWithTag方法,@Override  public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,  Notification notification, int[] idOut, int userId) throws RemoteException {  enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),  Binder.getCallingPid(), tag, id, notification, idOut, userId);  } 
我们看到在enqueueNotificationWithTag方法中调用了enqueueNotificationInternal方法,这里就是通知处理的核心了。[java] view plain copy
在CODE上查看代码片派生到我的代码片void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,  final int callingPid, final String tag, final int id, final Notification notification,  int[] idOut, int incomingUserId) {  if (DBG) {  Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id  + " notification=" + notification);  }  checkCallerIsSystemOrSameApp(pkg);  // 校验UID  final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));  final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);  final int userId = ActivityManager.handleIncomingUser(callingPid,  callingUid, incomingUserId, true, false, "enqueueNotification", pkg);  final UserHandle user = new UserHandle(userId);  // Fix the notification as best we can.  try {  final ApplicationInfo ai = getContext().getPackageManager().getApplicationInfoAsUser(  pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,  (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);  Notification.addFieldsFromContext(ai, userId, notification);  } catch (NameNotFoundException e) {  Slog.e(TAG, "Cannot create a context for sending app", e);  return;  }  mUsageStats.registerEnqueuedByApp(pkg);  // Limit the number of notifications that any given package except the android  // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.  // 这里会做一个限制,除了系统级别的应用之外,其他应用的notification数量会做限制,用来防止DOS攻击导致的泄露  if (!isSystemNotification && !isNotificationFromListener) {  synchronized (mNotificationList) {  final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);  if (appEnqueueRate > mMaxPackageEnqueueRate) {  mUsageStats.registerOverRateQuota(pkg);  final long now = SystemClock.elapsedRealtime();  if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {  Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate  + ". Shedding events. package=" + pkg);  mLastOverRateLogTime = now;  }  return;  }  int count = 0;  final int N = mNotificationList.size();  for (int i=0; i<N; i++) {  final NotificationRecord r = mNotificationList.get(i);  if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {  if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {  break;  // Allow updating existing notification  }  count++;  if (count >= MAX_PACKAGE_NOTIFICATIONS) {// 同一个应用发送notification数量不能超过50  mUsageStats.registerOverCountQuota(pkg);  Slog.e(TAG, "Package has already posted " + count  + " notifications.  Not showing more.  package=" + pkg);  return;  }  }  }  }  }  if (pkg == null || notification == null) {//通知不能为空  throw new IllegalArgumentException("null not allowed: pkg=" + pkg  + " id=" + id + " notification=" + notification);  }  // Whitelist pending intents.  if (notification.allPendingIntents != null) {  final int intentCount = notification.allPendingIntents.size();  if (intentCount > 0) {  final ActivityManagerInternal am = LocalServices  .getService(ActivityManagerInternal.class);  final long duration = LocalServices.getService(  DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();  for (int i = 0; i < intentCount; i++) {  PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);  if (pendingIntent != null) {  am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);  }  }  }  }  // Sanitize inputs  notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,  Notification.PRIORITY_MAX);  // setup local book-keeping  // 验证完条件后,将前面传递进来的Notification封装成一个StatusBarNotification对象,  final StatusBarNotification n = new StatusBarNotification(  pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,  user);  // 封装NotificationRecord对象  final NotificationRecord r = new NotificationRecord(getContext(), n);  mHandler.post(new EnqueueNotificationRunnable(userId, r));  idOut[0] = id;  }  开启线程,异步处理。
    private class EnqueueNotificationRunnable implements Runnable {  private final NotificationRecord r;  private final int userId;  EnqueueNotificationRunnable(int userId, NotificationRecord r) {  this.userId = userId;  this.r = r;  };  @Override  public void run() {  synchronized (mNotificationList) {  final StatusBarNotification n = r.sbn;  if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());  NotificationRecord old = mNotificationsByKey.get(n.getKey());  if (old != null) {  // Retain ranking information from previous record  r.copyRankingInformation(old);  }  final int callingUid = n.getUid();  final int callingPid = n.getInitialPid();  final Notification notification = n.getNotification();  final String pkg = n.getPackageName();  final int id = n.getId();  final String tag = n.getTag();  final boolean isSystemNotification = isUidSystem(callingUid) ||  ("android".equals(pkg));  // Handle grouped notifications and bail out early if we  // can to avoid extracting signals.  handleGroupedNotificationLocked(r, old, callingUid, callingPid);  // This conditional is a dirty hack to limit the logging done on  //     behalf of the download manager without affecting other apps.  if (!pkg.equals("com.android.providers.downloads")  || Log.isLoggable("DownloadManager", Log.VERBOSE)) {  int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;  if (old != null) {  enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;  }  EventLogTags.writeNotificationEnqueue(callingUid, callingPid,  pkg, id, tag, userId, notification.toString(),  enqueueStatus);  }  mRankingHelper.extractSignals(r);  final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);  // blocked apps 判断pkg是否可以显示通知  if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE  || !noteNotificationOp(pkg, callingUid) || isPackageSuspended) {  if (!isSystemNotification) {//不拦截系统通知  if (isPackageSuspended) {  Slog.e(TAG, "Suppressing notification from package due to package "  + "suspended by administrator.");  mUsageStats.registerSuspendedByAdmin(r);  } else {  Slog.e(TAG, "Suppressing notification from package by user request.");  mUsageStats.registerBlocked(r);  }  return;  }  }  // tell the ranker service about the notification  if (mRankerServices.isEnabled()) {  mRankerServices.onNotificationEnqueued(r);  // TODO delay the code below here for 100ms or until there is an answer  }  // 获取是否已经发送过此notification  int index = indexOfNotificationLocked(n.getKey());  if (index < 0) {  // 如果是新发送的notification,就走新增流程.  mNotificationList.add(r);  mUsageStats.registerPostedByApp(r);  } else {  //如果有发送过,就获取oldNtificationRecord,后面走更新流程 mStatusBar.updateNotification(r.statusBarKey, n)  old = mNotificationList.get(index);  mNotificationList.set(index, r);  mUsageStats.registerUpdatedByApp(r, old);  // Make sure we don't lose the foreground service state.  notification.flags |=  old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;  r.isUpdate = true;  }  Slog.d(TAG, "NotificationRecord, r = "+r);  mNotificationsByKey.put(n.getKey(), r);  // Ensure if this is a foreground service that the proper additional  // flags are set.  if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {  notification.flags |= Notification.FLAG_ONGOING_EVENT  | Notification.FLAG_NO_CLEAR;  }  applyZenModeLocked(r);  mRankingHelper.sort(mNotificationList);//将mNotificationList排序  // 如果notification设置了smallIcon,调用所有NotificationListeners的notifyPostedLocked方法,  // 通知有新的notification,传入的参数为上面封装成的StatusBarNotification对象.  if (notification.getSmallIcon() != null) {  StatusBarNotification oldSbn = (old != null) ? old.sbn : null;  mListeners.notifyPostedLocked(n, oldSbn);  } else {  Slog.e(TAG, "Not posting notification without small icon: " + notification);  if (old != null && !old.isCanceled) {  mListeners.notifyRemovedLocked(n);  }  // ATTENTION: in a future release we will bail out here  // so that we do not play sounds, show lights, etc. for invalid  // notifications  Slog.e(TAG, "WARNING: In a future release this will crash the app: "  + n.getPackageName());  }  // buzzBeepBlinkLocked方法负责对消息进行处理。  // 通知status bar显示该notification,确认是否需要声音,震动和闪光,如果需要,那么就发出声音,震动和闪光  buzzBeepBlinkLocked(r);  }  }  }  

到这里,就开始进入SystemUI了。

// 如果notification设置了smallIcon,调用所有NotificationListeners的notifyPostedLocked方法,  // 通知有新的notification,传入的参数为上面封装成的StatusBarNotification对象.  if (notification.getSmallIcon() != null) {  StatusBarNotification oldSbn = (old != null) ? old.sbn : null;  mListeners.notifyPostedLocked(n, oldSbn);  } else {  Slog.e(TAG, "Not posting notification without small icon: " + notification);  if (old != null && !old.isCanceled) {  mListeners.notifyRemovedLocked(n);  }  // ATTENTION: in a future release we will bail out here  // so that we do not play sounds, show lights, etc. for invalid  // notifications  Slog.e(TAG, "WARNING: In a future release this will crash the app: "  + n.getPackageName());  }  

其中,mListeners为NotificationListeners,为内部类。查看方法notifyPostedLocked()

 /*** asynchronously notify all listeners about a new notification** <p>* Also takes care of removing a notification that has been visible to a listener before,* but isn't anymore.*/public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {// Lazily initialized snapshots of the notification.TrimCache trimCache = new TrimCache(sbn);通知所有的监听者关于这个新通知,for (final ManagedServiceInfo info : mServices) {boolean sbnVisible = isVisibleToListener(sbn, info);boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;// This notification hasn't been and still isn't visible -> ignore.if (!oldSbnVisible && !sbnVisible) {continue;}final NotificationRankingUpdate update = makeRankingUpdateLocked(info);// This notification became invisible -> remove the old one.if (oldSbnVisible && !sbnVisible) {final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();mHandler.post(new Runnable() {@Overridepublic void run() {notifyRemoved(info, oldSbnLightClone, update);}});continue;}final StatusBarNotification sbnToPost =  trimCache.ForListener(info);mHandler.post(new Runnable() {@Overridepublic void run() {notifyPosted(info, sbnToPost, update);}});}}

然后在notifyPosted()方法里面推送通知,我们来看下这个方法:

private void notifyPosted(final ManagedServiceInfo info,final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {final INotificationListener listener = (INotificationListener)info.service;StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);try {listener.onNotificationPosted(sbnHolder, rankingUpdate);} catch (RemoteException ex) {Log.e(TAG, "unable to notify listener (posted): " + listener, ex);}}

然后监听者调用onNotificationPosted()继续往上推送

路径:frameworks/base/core/java/android/service/notification/INotificationListener.aidl

INotificationManager.aidl  BaseStatusBar.java  

目录:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java

在BaseStatusBar.java 里面注册NotificationListenerService。

 try {mNotificationListener.registerAsSystemService(mContext,new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),UserHandle.USER_ALL);} catch (RemoteException e) {Log.e(TAG, "Unable to register notification listener", e);}
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


在registerAsSystemService中调用registerListener方法。这个方法在INotificationManager.stub中声明了,而INotificationManager.stub 跟NotificationManagerService进行了绑定。so 跟进了NotificationManagerService.java

frameworks/base/core/java/android/app/INotificationManager.aidl

framework/base/services/core/java/com/android/server/notification/NotificationManagerService.java

在NotificationManagerService.java里面

private final IBinder mService = new INotificationManager.Stub()

----> NotificationManagerService.java在实例化INotificationManager.stub 对象的时候,对registerListener 进行了override。在这个方法中,它首先去调用enforceSystemOrSystemUI,接着才去registerService。很显然,想要的答案就在enforceSystemOrSystemUI 中。so 跟进enforceSystemOrSystemUI。这个方法呢,也是在实例化INotificationManager.stub 对象mService的时候,对其 进行了override。

这样NotificationManagerService和NotificationListenerService连接上,可以吧消息传到systemUI进去。




  相关解决方案