当前位置:   article > 正文

什么是SystemUI?简单分析

systemui

SystemUI概览

SystemUI属于系统级的apk,位置在frameworks\base\packages\SystemUI,主要功能有:

  • 状态栏信息显示,比如电池,wifi信号,3G/4G等icon显示
  • 通知面板,比如系统消息,第三方应用消息
  • 近期任务栏显示面板,比如长按近期任务快捷键,显示近期使用的应用
  • 截图服务
  • 壁纸服务
  • ……

SystemUI的启动流程

SystemServer启动后,会在Main Thread启动ActivityManagerService,当ActivityManagerService systemReady后,会去启动SystemUIService。
SystemServer路径:frameworks/base/services/java/com/android/server/SystemServer.java

  1. mActivityManagerService.systemReady(new Runnable() {
  2. @Override
  3. public void run() {
  4. Slog.i(TAG, "Making services ready");
  5. ......
  6. Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartSystemUI");
  7. try {
  8. startSystemUi(context);
  9. } catch (Throwable e) {
  10. reportWtf("starting System UI", e);
  11. }
  12. Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
  13. ......
  14. }
  15. });

在这个方法里启动一个SystemUIService服务

  1. static final void startSystemUi(Context context) {
  2. Intent intent = new Intent();
  3. intent.setComponent(new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService"));
  4. intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
  5. //Slog.d(TAG, "Starting service: " + intent);
  6. context.startServiceAsUser(intent, UserHandle.SYSTEM);
  7. }

通过startServiceAsUser,SystemUIService就启动了,即SystemUI进程开机启动。

  1. public class SystemUIService extends Service {
  2. @Override
  3. public void onCreate() {
  4. super.onCreate();
  5. ((SystemUIApplication) getApplication()).startServicesIfNeeded();
  6. }
  7. ......

在SystemUIService的onCreate方法中会调用SystemUIApplication的startServicesIfNeeded方法,这个方法会调用 startServicesIfNeeded(SERVICES)方法启动一系列服务(并不是真正的service,都继承自SystemUI)

  1. public class SystemUIApplication extends Application {
  2. ......
  3. /**
  4. * The classes of the stuff to start.
  5. */
  6. private final Class<?>[] SERVICES = new Class[] {
  7. com.android.systemui.tuner.TunerService.class,
  8. com.android.systemui.keyguard.KeyguardViewMediator.class,
  9. com.android.systemui.recents.Recents.class,
  10. com.android.systemui.volume.VolumeUI.class,
  11. Divider.class,
  12. com.android.systemui.statusbar.SystemBars.class,
  13. com.android.systemui.usb.StorageNotification.class,
  14. com.android.systemui.power.PowerUI.class,
  15. com.android.systemui.media.RingtonePlayer.class,
  16. com.android.systemui.keyboard.KeyboardUI.class,
  17. com.android.systemui.tv.pip.PipUI.class,
  18. com.android.systemui.shortcut.ShortcutKeyDispatcher.class,
  19. com.android.systemui.VendorServices.class
  20. };
  21. ......
  22. public void startServicesIfNeeded() {
  23. startServicesIfNeeded(SERVICES);
  24. }
  25. }

所有SERVICES统一继承了SystemUI类:

  1. public abstract class SystemUI {
  2. ......
  3. public abstract void start();
  4. protected void onConfigurationChanged(Configuration newConfig) {
  5. }
  6. public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
  7. }
  8. protected void onBootCompleted() {
  9. }
  10. ......
  11. }

startServicesIfNeeded方法会遍历SERVICES 这个数组,依次调用service的start方法启动服务。

  1. private void startServicesIfNeeded(Class<?>[] services) {
  2. ......
  3. final int N = services.length;
  4. for (int i=0; i<N; i++) {
  5. Class<?> cl = services[i];
  6. if (DEBUG) Log.d(TAG, "loading: " + cl);
  7. try {
  8. Object newService = SystemUIFactory.getInstance().createInstance(cl);
  9. mServices[i] = (SystemUI) ((newService == null) ? cl.newInstance() : newService);
  10. } catch (IllegalAccessException ex) {
  11. throw new RuntimeException(ex);
  12. } catch (InstantiationException ex) {
  13. throw new RuntimeException(ex);
  14. }
  15. mServices[i].mContext = this;
  16. mServices[i].mComponents = mComponents;
  17. if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
  18. mServices[i].start();
  19. if (mBootCompleted) {
  20. mServices[i].onBootCompleted();
  21. }
  22. }
  23. ......
  24. }

状态栏

状态栏(SystemBars)service是SystemUI中最重要的service,代码量最多,最复杂的,界面结构也复杂。根据前面的内容可知,启动SystemBars是通过调用start()方法,如下图:

  1. public void start() {
  2. if (DEBUG) Log.d(TAG, "start");
  3. mServiceMonitor = new ServiceMonitor(TAG, DEBUG,
  4. mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);
  5. mServiceMonitor.start(); // will call onNoService if no remote service is found
  6. }

这里实质是回调到到SystemBars的onNoService()方法,最后是调用SystemBars的createStatusBarFromConfig()方法:

  1. private void createStatusBarFromConfig() {
  2. ......
  3. String clsName = mContext.getString(R.string.config_statusBarComponent);
  4. ......
  5. try {
  6. cls = mContext.getClassLoader().loadClass(clsName);
  7. } catch (Throwable t) {
  8. throw andLog("Error loading status bar component: " + clsName, t);
  9. }
  10. try {
  11. mStatusBar = (BaseStatusBar) cls.newInstance();
  12. } catch (Throwable t) {
  13. throw andLog("Error creating status bar component: " + clsName, t);
  14. }
  15. ......
  16. mStatusBar.start();
  17. ......
  18. }

上图可以看到,从string资源文件里面读取class name,通过java的映射机制实例化对象,然后调用start()方法启动,class name的值如下图:

  1. <!-- Component to be used as the status bar service. Must implement the IStatusBar
  2. interface. This name is in the ComponentName flattened format (package/class) -->
  3. <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string>

该配置文件在SystemUI/res/values/config.xml中。所以实质是PhoneStatusBar调用了start()方法。
SystemBars模块的初始化过程主要涉及的类有:

SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
SystemUI/src/com/android/systemui/statusbar/CommandQueue.java

PhoneStatusBar的父类是BaseStatusBar继承于SystemUI,SystemBars调用PhoneStatusBar中的start()方法,类关系图如下:

PhoneStatusBar的start()

  1. public void start() {
  2. ......
  3. super.start(); // calls createAndAddWindows()
  4. ......
  5. addNavigationBar();
  6. ......
  7. }

如上图,调用父类中的start()方法,即BaseStatsuBar中的start()方法。然后调用addNavigationBar()方法实例化导航条。状态栏的布局层次结构如下图:

继续看BaseStatsuBar中的方法。

  1. public void start() {
  2. ......
  3. mBarService = IStatusBarService.Stub.asInterface(
  4. ServiceManager.getService(Context.STATUS_BAR_SERVICE));
  5. ......
  6. // Connect in to the status bar manager service
  7. mCommandQueue = new CommandQueue(this);
  8. int[] switches = new int[9];
  9. ArrayList<IBinder> binders = new ArrayList<IBinder>();
  10. ArrayList<String> iconSlots = new ArrayList<>();
  11. ArrayList<StatusBarIcon> icons = new ArrayList<>();
  12. Rect fullscreenStackBounds = new Rect();
  13. Rect dockedStackBounds = new Rect();
  14. try {
  15. mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
  16. fullscreenStackBounds, dockedStackBounds);
  17. } catch (RemoteException ex) {
  18. // If the system process isn't there we're doomed anyway.
  19. }
  20. createAndAddWindows();
  21. ......
  22. // Set up the initial icon state
  23. int N = iconSlots.size();
  24. int viewIndex = 0;
  25. for (int i=0; i < N; i++) {
  26. setIcon(iconSlots.get(i), icons.get(i));
  27. }
  28. // Set up the initial notification state.
  29. try {
  30. mNotificationListener.registerAsSystemService(mContext,
  31. new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
  32. UserHandle.USER_ALL);
  33. } catch (RemoteException e) {
  34. Log.e(TAG, "Unable to register notification listener", e);
  35. }
  36. ......
  37. }

如上面BaseStatsuBar中的start()方法,实例化一些对象,此处的对象都是“空值”,然后通过IStatusBarService的实例mBarService对象注册到StatusBarManagerService。

mCommandQueue是CommandQueue的实例,在StatusBarManagerService的远程回调,实现StatusBarManagerService和SystemUI的通信。

然后调用createAndAddWindows()方法,该方法初始化status bar,notification,quick settings等的View控件。

在这里,还需要注意NotificationListenerService的实例mNotificationListener的registerAsSystemService()方法,该方法主要实现StatusBarManagerService和SystemUI的notification的控制通道,也就是说,StatusBarManagerService收到notification变化时,通过此通道通知SystemUI显示notification的变化。

通知显示过程

一个APP需要显示notification首先需要实例化一个NotificationManager的对象,然后调用NotificationManager的方法notify()方法把创建好的Notification对象作为参数传进去。

  1. public void notify(int id, Notification notification){
  2. notify(null, id, notification);
  3. }
  1. public void notify(String tag, int id, Notification notification){
  2. notifyAsUser(tag, id, notification, new UserHandle(UserHandle.myUserId()));
  3. }
  1. public void notifyAsUser(String tag, int id, Notification notification, UserHandle user){
  2. ......
  3. INotificationManager service = getService();
  4. ......
  5. try {
  6. service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
  7. copy, idOut, user.getIdentifier());
  8. ......
  9. } catch (RemoteException e) {
  10. throw e.rethrowFromSystemServer();
  11. }
  12. }

上图中可以看到一个service的对象调用了enqueueNotificationWithTag()方法,该方法实质是远程调用NotificationManagerService中的enqueueNotificationWithTag()方法,该方法又直接调用enqueueNotificationInternal(),该方法如下:

  1. void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
  2. final int callingPid, final String tag, final int id, final Notification notification,
  3. int[] idOut, int incomingUserId) {
  4. ......
  5. final StatusBarNotification n = new StatusBarNotification(
  6. pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
  7. user);
  8. ......
  9. final NotificationRecord r = new NotificationRecord(getContext(), n);
  10. mHandler.post(new EnqueueNotificationRunnable(userId, r));
  11. ......
  12. }

这里会把NotificationManager传递过来的Notification对象进行很多处理,比如变换成NotificationRecord,实质就是把Notification缓存下来。在上图的这个过程,还有一些其它的处理逻辑,在这里就不详细说明。最后把这个NotificationRecord传递给EnqueueNotificationRunnable线程来处理:

  1. private class EnqueueNotificationRunnable implements Runnable {
  2. private final NotificationRecord r;
  3. private final int userId;
  4. EnqueueNotificationRunnable(int userId, NotificationRecord r) {
  5. this.userId = userId;
  6. this.r = r;
  7. };
  8. @Override
  9. public void run() {
  10. synchronized (mNotificationList) {
  11. final StatusBarNotification n = r.sbn;
  12. ......
  13. if (notification.getSmallIcon() != null) {
  14. StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
  15. mListeners.notifyPostedLocked(n, oldSbn);
  16. } else {
  17. ......
  18. }
  19. buzzBeepBlinkLocked(r);
  20. }
  21. }
  22. }

代码的末尾调用了buzzBeepBlinkLocked()方法,该方法主要处理Notification的声音和震动的逻辑。mListeners调用了notifyPostedLocked()方法,此方法最终会执行到如下图的代码:

  1. private void notifyPosted(final ManagedServiceInfo info,
  2. final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
  3. final INotificationListener listener = (INotificationListener)info.service;
  4. StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
  5. try {
  6. listener.onNotificationPosted(sbnHolder, rankingUpdate);
  7. } catch (RemoteException ex) {
  8. Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
  9. }
  10. }

info.service返回一个INotificationListener的实例对象,该对象在上文中的mNotificationListener.registerAsSystemService()方法进行设置,所以listener.onNotificationPosted()方法实质是远程回调SystemUI中的方法:

  1. private final NotificationListenerService mNotificationListener =
  2. new NotificationListenerService() {
  3. ......
  4. @Override
  5. public void onNotificationPosted(final StatusBarNotification sbn,
  6. final RankingMap rankingMap) {
  7. ......
  8. if (sbn != null) {
  9. mHandler.post(new Runnable() {
  10. @Override
  11. public void run() {
  12. ......
  13. if (isUpdate) {
  14. updateNotification(sbn, rankingMap);
  15. } else {
  16. addNotification(sbn, rankingMap, null /* oldEntry */);
  17. }
  18. }
  19. });
  20. }
  21. }
  22. }

代码运行又回到了BaseStatusBar.java类中,从APP调用NotificationManager的notify()方法到BaseStatusBar的addNotification()或updateNotification()方法,经历了一个复杂的过程。就不再往下详情说明Notification到达SystemUI的处理过程了,之后有机会我们继续分析。

锁屏

锁屏(Keyguard)service在SystemUI是一个比较特殊的模块,特殊在于SystemUI启动的service只是一个信息传递者,也就是KeyguardViewMediator,并没有做锁屏或解屏的实质操作。在这里,涉及到三个比较关键的类是:

SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java

KeyguardViewMediator和KeyguardService在源码中位于SystemUI中,而KeyguardUpdateMonitor则位于KeyGuard中。在KeyguardViewMediator的初始化中主要做了三件事,如图:

  1. public void start() {
  2. synchronized (this) {
  3. setupLocked();
  4. }
  5. putComponent(KeyguardViewMediator.class, this);
  6. }
  1. private void setupLocked() {
  2. ......
  3. mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
  4. ......
  5. mStatusBarKeyguardViewManager =
  6. SystemUIFactory.getInstance().createStatusBarKeyguardViewManager(mContext,
  7. mViewMediatorCallback, mLockPatternUtils);
  8. final ContentResolver cr = mContext.getContentResolver();
  9. mDeviceInteractive = mPM.isInteractive();
  10. ......
  11. if (soundPath != null) {
  12. mLockSoundId = mLockSounds.load(soundPath, 1);
  13. }
  14. if (soundPath == null || mLockSoundId == 0) {
  15. Log.w(TAG, "failed to load lock sound from " + soundPath);
  16. }
  17. soundPath = Settings.Global.getString(cr, Settings.Global.UNLOCK_SOUND);
  18. if (soundPath != null) {
  19. mUnlockSoundId = mLockSounds.load(soundPath, 1);
  20. }
  21. if (soundPath == null || mUnlockSoundId == 0) {
  22. Log.w(TAG, "failed to load unlock sound from " + soundPath);
  23. }
  24. soundPath = Settings.Global.getString(cr, Settings.Global.TRUSTED_SOUND);
  25. if (soundPath != null) {
  26. mTrustedSoundId = mLockSounds.load(soundPath, 1);
  27. }
  28. if (soundPath == null || mTrustedSoundId == 0) {
  29. Log.w(TAG, "failed to load trusted sound from " + soundPath);
  30. }
  31. ......
  32. }

实例化KeyguardUpdateMonitor的实例mUpdateMonitor,KeyguardUpdateMonitor负责更新已经锁屏界面上的内容(如时间)。当然,KeyguardUpdateMonitor只是一个信息传递者,实际去刷新界面的是StatusBar模块。Keyguard模块通知StatusBar刷新解密是通过KeyguardUpdateMonitorCallback这个类进行远程回调,该类的实例在StatusBar模块启动时通过KeyguardService获取到IKeyguardService的远端实例,通过IKeyguardService远程调用IKeyguardService的addStateMonitorCallback()方法实例化KeyguardUpdateMonitorCallback对象,SystemUI/src/com/android/systemui/keyguard/KeyguardService.java

  1. private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
  2. @Override // Binder interface
  3. public void addStateMonitorCallback(IKeyguardStateCallback callback) {
  4. checkPermission();
  5. mKeyguardViewMediator.addStateMonitorCallback(callback);
  6. }
  7. ......
  8. @Override // Binder interface
  9. public void onScreenTurnedOn() {
  10. Trace.beginSection("KeyguardService.mBinder#onScreenTurningOn");
  11. checkPermission();
  12. mKeyguardViewMediator.onScreenTurnedOn();
  13. Trace.endSection();
  14. }
  15. ......
  16. };

SystemUI启动的Keyguard模块并没有真正的去操作锁屏界面,而是作为一个信息传递者把信息传递给StatusBar模块。
KeyguardService在KeyguardServiceDelegate中绑定调用。
frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java:

  1. public void bindService(Context context) {
  2. Intent intent = new Intent();
  3. final Resources resources = context.getApplicationContext().getResources();
  4. final ComponentName keyguardComponent = ComponentName.unflattenFromString(
  5. resources.getString(com.android.internal.R.string.config_keyguardComponent));
  6. intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
  7. intent.setComponent(keyguardComponent);
  8. boolean isBox = isBox();
  9. if (isBox || !context.bindServiceAsUser(intent, mKeyguardConnection,
  10. Context.BIND_AUTO_CREATE, mScrimHandler, UserHandle.SYSTEM)) {
  11. Log.v(TAG, "*** Keyguard: can't bind to " + keyguardComponent);
  12. mKeyguardState.showing = false;
  13. mKeyguardState.showingAndNotOccluded = false;
  14. mKeyguardState.secure = false;
  15. synchronized (mKeyguardState) {
  16. // TODO: Fix synchronisation model in this class. The other state in this class
  17. // is at least self-healing but a race condition here can lead to the scrim being
  18. // stuck on keyguard-less devices.
  19. mKeyguardState.deviceHasKeyguard = false;
  20. hideScrim();
  21. }
  22. } else {
  23. if (DEBUG) Log.v(TAG, "*** Keyguard started");
  24. }
  25. }
  26. private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
  27. @Override
  28. public void onServiceConnected(ComponentName name, IBinder service) {
  29. if (DEBUG) Log.v(TAG, "*** Keyguard connected (yay!)");
  30. mKeyguardService = new KeyguardServiceWrapper(mContext,
  31. IKeyguardService.Stub.asInterface(service), mShowingStateChangedCallback);
  32. if (mKeyguardState.systemIsReady) {
  33. // If the system is ready, it means keyguard crashed and restarted.
  34. mKeyguardService.onSystemReady();
  35. if (mKeyguardState.currentUser != UserHandle.USER_NULL) {
  36. // There has been a user switch earlier
  37. mKeyguardService.setCurrentUser(mKeyguardState.currentUser);
  38. }
  39. // This is used to hide the scrim once keyguard displays.
  40. if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE) {
  41. mKeyguardService.onStartedWakingUp();
  42. }
  43. if (mKeyguardState.screenState == SCREEN_STATE_ON
  44. || mKeyguardState.screenState == SCREEN_STATE_TURNING_ON) {
  45. mKeyguardService.onScreenTurningOn(
  46. new KeyguardShowDelegate(mDrawnListenerWhenConnect));
  47. }
  48. if (mKeyguardState.screenState == SCREEN_STATE_ON) {
  49. mKeyguardService.onScreenTurnedOn();
  50. }
  51. mDrawnListenerWhenConnect = null;
  52. }
  53. if (mKeyguardState.bootCompleted) {
  54. mKeyguardService.onBootCompleted();
  55. }
  56. if (mKeyguardState.occluded) {
  57. mKeyguardService.setOccluded(mKeyguardState.occluded, false /* animate */);
  58. }
  59. }
  60. @Override
  61. public void onServiceDisconnected(ComponentName name) {
  62. if (DEBUG) Log.v(TAG, "*** Keyguard disconnected (boo!)");
  63. mKeyguardService = null;
  64. }
  65. };

com.android.internal.R.string.config_keyguardComponent的默认配置值:

  1. <!-- Keyguard component -->
  2. <string name="config_keyguardComponent" translatable="false">com.android.systemui/com.android.systemui.keyguard.KeyguardService</string>

KeyguardViewMediator启动的流程图如下:

总结

先大概看一遍流程,这个apk比较复杂,之后还会分析。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/633935
推荐阅读
相关标签
  

闽ICP备14008679号