赞
踩
在 SystemServer 启动时: startBootstrapServices(); startCoreServices(); startOtherServices(); SystemUI 在 startOtherServices 中启动: 先启动与 SystemUI 的服务 statusBar = new StatusBarManagerService(context, wm); ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar); 再启动 SystemUI static final void startSystemUi(Context context, WindowManagerService windowManager) { Intent intent = new Intent(); intent.setComponent(new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService")); intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING); //Slog.d(TAG, "Starting service: " + intent); context.startServiceAsUser(intent, UserHandle.SYSTEM); windowManager.onSystemUiStarted(); }
在 SystemUI 模块中
启动组件如下:
<string-array name="config_systemUIServiceComponents" translatable="false"> <item>com.android.systemui.Dependency</item> <item>com.android.systemui.util.NotificationChannels</item> <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item> <item>com.android.systemui.keyguard.KeyguardViewMediator</item> <item>com.android.systemui.recents.Recents</item> <item>com.android.systemui.volume.VolumeUI</item> <item>com.android.systemui.stackdivider.Divider</item> <item>com.android.systemui.SystemBars</item> <item>com.android.systemui.usb.StorageNotification</item> <item>com.android.systemui.power.PowerUI</item> <item>com.android.systemui.media.RingtonePlayer</item> <item>com.android.systemui.keyboard.KeyboardUI</item> <item>com.android.systemui.pip.PipUI</item> <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item> <item>@string/config_systemUIVendorServiceComponent</item> <item>com.android.systemui.util.leak.GarbageMonitor$Service</item> <item>com.android.systemui.LatencyTester</item> <item>com.android.systemui.globalactions.GlobalActionsComponent</item> <item>com.android.systemui.ScreenDecorations</item> <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item> <item>com.android.systemui.SliceBroadcastRelayHandler</item> </string-array> 为单个用户启动的组件 (是全部组件的子类) <string-array name="config_systemUIServiceComponentsPerUser" translatable="false"> <item>com.android.systemui.Dependency</item> <item>com.android.systemui.util.NotificationChannels</item> <item>com.android.systemui.recents.Recents</item> </string-array>
所有组件都是 SystemUI 的子类, 通过列表来初始化启动组件
String clsName = services[i]; Class cls; try { cls = Class.forName(clsName); mServices[i] = (SystemUI) cls.newInstance(); } catch(ClassNotFoundException ex){ throw new RuntimeException(ex); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } catch (InstantiationException ex) { throw new RuntimeException(ex); } mServices[i].mContext = this; mServices[i].mComponents = mComponents; if (DEBUG) Log.d(TAG, "running: " + mServices[i]); mServices[i].start();
自此, SystemUI 完在启动和各个模块的初始化动作.
通知系统涉及内容较多, framework 部分单独成文介绍, 这里仅介绍 SystemUI部分.
通知注册监听:
创建一个 NotificationListenerService 子类, 主要用于 NotificationListenerService 的具体实现 public class NotificationListenerWithPlugins extends NotificationListenerService 继承自 NotificationListenerWithPlugins, 主要用于通知的分发 public class NotificationListener extends NotificationListenerWithPlugins 将通和相关的组件注册到 Dependency 组件中. providers.put(NotificationGroupManager.class, NotificationGroupManager::new); providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context)); providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context)); providers.put(NotificationBlockingHelperManager.class, () -> new NotificationBlockingHelperManager(context)); providers.put(NotificationRemoteInputManager.class, () -> new NotificationRemoteInputManager(context)); providers.put(SmartReplyConstants.class, () -> new SmartReplyConstants(Dependency.get(Dependency.MAIN_HANDLER), context)); providers.put(NotificationListener.class, () -> new NotificationListener(context));
将通知监听注册为系统服务
mNotificationListener = Dependency.get(NotificationListener.class);
mNotificationListener.setUpWithPresenter(this, mEntryManager);
到这里, 通知的注册监听就完在了.
那么当一条通知来的时候, SystemUI 是如何将它显示在界面上呢?
从前面的监听服务可以知道, 当一个通知将要显示出来, 源头便是监听服务
通过 onNotificationPosted 接口监听来到的通知
if (isUpdate) {
mEntryManager.updateNotification(sbn, rankingMap);// 已经在显地, 只需要更新一下
} else {
mEntryManager.addNotification(sbn, rankingMap); // 新增通知
}
以新增通知为例来看通知流程
1. 排序 mNotificationData.updateRanking(ranking); 在通知排序前需要对通知进行过滤 shouldFilterOut(Entry entry), 过滤规则来自于 StatusBar 实现的 Environment 接口 排序规则: 悬浮通知 > 媒体通知 > Rank排序 > 发送设置时间. private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() { private final Ranking mRankingA = new Ranking(); private final Ranking mRankingB = new Ranking(); @Override public int compare(Entry a, Entry b) { final StatusBarNotification na = a.notification; final StatusBarNotification nb = b.notification; int aImportance = NotificationManager.IMPORTANCE_DEFAULT; int bImportance = NotificationManager.IMPORTANCE_DEFAULT; int aRank = 0; int bRank = 0; if (mRankingMap != null) { // RankingMap as received from NoMan getRanking(a.key, mRankingA); getRanking(b.key, mRankingB); aImportance = mRankingA.getImportance(); bImportance = mRankingB.getImportance(); aRank = mRankingA.getRank(); bRank = mRankingB.getRank(); } String mediaNotification = mEnvironment.getCurrentMediaNotificationKey(); // IMPORTANCE_MIN media streams are allowed to drift to the bottom final boolean aMedia = a.key.equals(mediaNotification) && aImportance > NotificationManager.IMPORTANCE_MIN; final boolean bMedia = b.key.equals(mediaNotification) && bImportance > NotificationManager.IMPORTANCE_MIN; boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH && isSystemNotification(na); boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH && isSystemNotification(nb); boolean isHeadsUp = a.row.isHeadsUp(); if (isHeadsUp != b.row.isHeadsUp()) { return isHeadsUp ? -1 : 1; } else if (isHeadsUp) { // Provide consistent ranking with headsUpManager return mHeadsUpManager.compare(a, b); } else if (aMedia != bMedia) { // Upsort current media notification. return aMedia ? -1 : 1; } else if (aSystemMax != bSystemMax) { // Upsort PRIORITY_MAX system notifications return aSystemMax ? -1 : 1; } else if (aRank != bRank) { return aRank - bRank; } else { return Long.compare(nb.getNotification().when, na.getNotification().when); } } }; 2. 创建通知视图 createNotificationViews(StatusBarNotification sbn) 初始化加载视图: new RowInflaterTask().inflate(mContext, parent, entry, row -> { bindRow(entry, pmUser, sbn, row); updateNotification(entry, pmUser, sbn, row); }); ExpandableNotificationRow 负责视图显示逻辑
入口:
private void onRecentsClick(View v) {
if (LatencyTracker.isEnabled(getContext())) {
LatencyTracker.getInstance(getContext()).onActionStart(
LatencyTracker.ACTION_TOGGLE_RECENTS);
}
mStatusBar.awakenDreams();
mCommandQueue.toggleRecentApps();
}
获取系统有没有其它 Recents 支持
外部 Recents
IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
mOverviewProxyService.getProxy().onOverviewToggle();
内部Recents
mImpl.toggleRecents(growTarget);
Recents 本身是一个独立的 Activity, 与 SystemUI 本身关联性并不是很大, 这里以 SystemUI 内部的 recents 为例进行分析.
从SystemUI中启动 Recents
//toggleRecents protected void startRecentsActivityAndDismissKeyguardIfNeeded( final ActivityManager.RunningTaskInfo runningTask, final boolean isHomeStackVisible, final boolean animate, final int growTarget) { // Preload only if device for current user is unlocked final StatusBar statusBar = getStatusBar(); if (statusBar != null && statusBar.isKeyguardShowing()) { statusBar.executeRunnableDismissingKeyguard(() -> { // Flush trustmanager before checking device locked per user when preloading mTrustManager.reportKeyguardShowingChanged(); mHandler.post(() -> startRecentsActivity(runningTask, isHomeStackVisible, animate, growTarget)); }, null, true /* dismissShade */, false /* afterKeyguardGone */, true /* deferred */); } else { startRecentsActivity(runningTask, isHomeStackVisible, animate, growTarget); } }
加载近期作务
sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext);
mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
ActivityManager.getMaxRecentTasksStatic(), currentUserId);
获取近期任务图标
图片有缓存, 直接加载图片,图片没有缓存, 重新获取
Recents图片缓存路径:
/data/system_ce/0/snapshots
没有图片缓存
ThumbnailData thumbnailData = ActivityManagerWrapper.getInstance().getTaskThumbnail(
taskKey.id, true /* reducedResolution */);
getTaskThumbnail 流程
WindowManagerService.java public TaskSnapshot getTaskSnapshot(int taskId, int userId, boolean reducedResolution) { return mTaskSnapshotController.getSnapshot(taskId, userId, true /* restoreFromDisk */, reducedResolution); } TaskSnapShotCache.java @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk, boolean reducedResolution) { return mCache.getSnapshot(taskId, userId, restoreFromDisk, reducedResolution || DISABLE_FULL_SIZED_BITMAPS); } 获取并缓存图片 private TaskSnapshot tryRestoreFromDisk(int taskId, int userId, boolean reducedResolution) { final TaskSnapshot snapshot = mLoader.loadTask(taskId, userId, reducedResolution); if (snapshot == null) { return null; } return snapshot; }
状态栏显示, 在 SystemUI中占比了很大一部分, 其结构如下:
super_status_bar.xml:
其它 status_bar 通过 CollapsedStatusBarFragment 完成对 status_bar_container 的替换.
StatusBarWindowView 的加载:
StatusBar 组件启动时 createAndAddWindows(); 加载布局 protected void inflateStatusBarWindow(Context context) { mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_status_bar, null); } 将窗口加入到 PhoneWindow 中, 窗口类型为 TYPE_STATUS_BAR mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); mLp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, barHeight, WindowManager.LayoutParams.TYPE_STATUS_BAR, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, PixelFormat.TRANSLUCENT); mLp.token = new Binder(); mLp.gravity = Gravity.TOP; mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; mLp.setTitle("StatusBar"); mLp.packageName = mContext.getPackageName(); mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; mStatusBarView = statusBarView; mBarHeight = barHeight; mWindowManager.addView(mStatusBarView, mLp); mLpChanged = new WindowManager.LayoutParams(); mLpChanged.copyFrom(mLp); mStatusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
先来看一下,手机是如何锁屏的
手机的锁屏事件来源于 Power 的Notifier 线程的通知, 当按下电源键或自动灭屏会触发这个事件.
从上述源程中, 可以清楚的看到整个锁屏的结构图, 在 framework 层, 有一个 KeyguardService 的代理, 通过这个代理接口, 实现和 SystemUI 的交互
解锁流程:
android 提供的安全验证集在中 LockPatternUtils 中, 通过其中的接口来获取加密类型和解锁正确与否.
解锁成功
public void reportSuccessfulPasswordAttempt(int userId) {
if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
return;
}
getDevicePolicyManager().reportSuccessfulPasswordAttempt(userId);
getTrustManager().reportUnlockAttempt(true /* authenticated */, userId);
}
QS Panel 是 StatusBar 中的一个模块, 将它单独列出来, 主要是它有一个快捷入口的功能, 提供一些快捷事件.
一个自定义 TileService 例子
@Implements(TileService.class) public class ShadowTileService extends ShadowService { @RealObject TileService realService; private Tile mTile; public void __constructor__() { } @Implementation public final Tile getQsTile() { return mTile; } @Implementation public final void startActivityAndCollapse(Intent intent) { realService.startActivity(intent); } // Non-Android setter. public void setTile(Tile tile) { mTile = tile; } }
QS Panel的视图结构:
SystemUI 的下拉面板由两个部分构成, 一个是 QsPanel, 另一个是通知面板
QSTileHost 管理当前的Tile
加载默认Tiles: tileList = res.getString(R.string.quick_settings_tiles);
进入分屏模式
protected boolean toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) { if (mRecents == null) { return false; } int dockSide = WindowManagerProxy.getInstance().getDockSide(); if (dockSide == WindowManager.DOCKED_INVALID) { final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(); if (navbarPos == NAV_BAR_POS_INVALID) { return false; } int createMode = navbarPos == NAV_BAR_POS_LEFT ? ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT : ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; return mRecents.splitPrimaryTask(NavigationBarGestureHelper.DRAG_MODE_NONE, createMode, null, metricsDockAction); } else { Divider divider = getComponent(Divider.class); if (divider != null && divider.isMinimized() && !divider.isHomeStackResizable()) { // Undocking from the minimized state is not supported return false; } else { EventBus.getDefault().send(new UndockingTaskEvent()); if (metricsUndockAction != -1) { mMetricsLogger.action(metricsUndockAction); } } } return true; }
启动分屏
public void splitPrimaryTask(int taskId, int dragMode, int stackCreateMode,
Rect initialBounds) {
SystemServicesProxy ssp = Recents.getSystemServices();
// Make sure we inform DividerView before we actually start the activity so we can change
// the resize mode already.
if (ssp.setTaskWindowingModeSplitScreenPrimary(taskId, stackCreateMode, initialBounds)) {
EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds));
}
}
在窗口控制中, 将窗口属性改为 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
功能实现在 ActivityManagerService 中.
//TODO
// 未完, 仅记录了一个大体框架, 后续再充实.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。