Android SystemUI系统介绍

2022-08-08,,,

1. SystemUI 系统框架

1.1 启动流程

在 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 完在启动和各个模块的初始化动作.

2.系统模块分析

2.1 通知

通知系统涉及内容较多, 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 负责视图显示逻辑           

2.2 近期任务

入口:

    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;
    }
    

2.3 状态栏

状态栏显示, 在 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);

2.4 锁屏

先来看一下,手机是如何锁屏的
手机的锁屏事件来源于 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);
    } 

2.5 QS 面板

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);

2.6 分屏

进入分屏模式

    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 中.

3.杂项记录

//TODO
// 未完, 仅记录了一个大体框架, 后续再充实.

本文地址:https://blog.csdn.net/mzsyxiao/article/details/107033541

《Android SystemUI系统介绍.doc》

下载本文的Word格式文档,以方便收藏与打印。