SystemUI之滑动锁屏的建立

前面几篇文章大体介绍了SystemUI的两个模块,StatusBar和QuickSetting,这篇文章开始分析Keyguard模块。java

对于锁屏呢,须要有个基本认知,它分为两类,一是滑动锁屏,一是安全锁屏。滑动锁屏是指经过手指滑动便可解锁的锁屏,安全锁屏是指密码锁,图案锁,PIN码锁等等。这两种锁屏是在不一样的地方建立的,不可一律而论,而本文只分析滑动锁屏。android

滑动锁屏视图

根据SystemUI之StatusBar建立可知,整个SystemUI视图是由super_status_bar.xml建立的数据库

<!--根布局FrameLayout-->
<com.android.systemui.statusbar.phone.StatusBarWindowView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:sysui="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true">

    <com.android.systemui.statusbar.BackDropView android:id="@+id/backdrop">
        <ImageView android:id="@+id/backdrop_back" />
        <ImageView android:id="@+id/backdrop_front"/>
    </com.android.systemui.statusbar.BackDropView>

    <com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_behind" />

    <!--状态栏容器-->
    <FrameLayout android:id="@+id/status_bar_container" />

    <!--整个下拉通知面版,包括滑动锁屏界面-->
    <include layout="@layout/status_bar_expanded" />

    <!--这里亮度调节bar-->
    <include layout="@layout/brightness_mirror" />

    <com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_in_front"/>

    <!--在锁屏界面,显示在状态栏之下,显示在时间之上-->
    <LinearLayout android:id="@+id/lock_icon_container">
        <com.android.systemui.statusbar.phone.LockIcon android:id="@+id/lock_icon" />
        <com.android.keyguard.KeyguardMessageArea android:id="@+id/keyguard_message_area" />
    </LinearLayout>
</com.android.systemui.statusbar.phone.StatusBarWindowView>
复制代码

在这个布局中,ID为lock_icon_container的布局是在锁屏界面上显示锁图标和一些信息的。这个布局include了一个status_bar_expanded.xml布局,这是整个下拉通知面版,包括滑动锁屏的各类控件,来看看这个布局安全

<com.android.systemui.statusbar.phone.NotificationPanelView android:id="@+id/notification_panel" >

    <FrameLayout android:id="@+id/big_clock_container" />

    <!--滑动锁屏界面状态视图: 时间,日期-->
    <include layout="@layout/keyguard_status_view" android:visibility="gone" />

    <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer android:id="@+id/notification_container_parent">

        <include layout="@layout/dock_info_overlay" />
        
        <!--QS界面-->
        <FrameLayout android:id="@+id/qs_frame" android:layout="@layout/qs_panel" />
            
        <!--显示通知的容器-->
        <com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout android:id="@+id/notification_stack_scroller"/>

        <include layout="@layout/ambient_indication" android:id="@+id/ambient_indication_container" />

        <ViewStub android:id="@+id/keyguard_user_switcher" />

        <!--滑动锁屏状态栏-->
        <include layout="@layout/keyguard_status_bar" android:visibility="invisible" />

        <Button android:id="@+id/report_rejected_touch" />

    </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>

    <!--这里锁屏底部图标, 填充整个父布局,例如左下角的图标,右下角的图标Camera-->
    <include layout="@layout/keyguard_bottom_area" android:visibility="gone" />

    <com.android.systemui.statusbar.AlphaOptimizedView android:id="@+id/qs_navbar_scrim"/>

</com.android.systemui.statusbar.phone.NotificationPanelView>
复制代码

能够看到,滑动锁屏的各个部分比较分散,并非在同一容器中集中建立的。app

滑动锁屏的显示

通常咱们经过电源键的开关来锁屏的,本文来分析下,从开机到滑动锁屏显示的过程。ide

在开机的过程当中,当ActivityManagerService启动完毕后,会建立SystemUIoop

// frameworks/base/services/java/com/android/server/SystemServer.java
    
    private static void startSystemUi(Context context, WindowManagerService windowManager) {
        Intent intent = new Intent();
        // 1. 启动 SystemUIService
        intent.setComponent(new ComponentName("com.android.systemui",
                "com.android.systemui.SystemUIService"));
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        context.startServiceAsUser(intent, UserHandle.SYSTEM);
        
        // 2. 通知WMS,SystemUI已经启动
        windowManager.onSystemUiStarted();
    }
复制代码

前面的文章已经分析了SystemUI的启动,这个过程建立了整个SystemUI的视图,包括滑动锁屏的视图。如今来看看WindowManagerService在SystemUI启动后,作了什么布局

// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
    
    public void onSystemUiStarted() {
        mPolicy.onSystemUiStarted();
    }
复制代码

Window Manager 通知了 PhoneWindowManager , SystemUI 已经启动post

// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
    
    public void onSystemUiStarted() {
    	// 绑定 KeyguardService
        bindKeyguard();
    }
    
    private void bindKeyguard() {
        synchronized (mLock) {
            if (mKeyguardBound) {
                return;
            }
            mKeyguardBound = true;
        }
		// 绑定 KeyguardService 的操做交给了代理类 KeyguardServiceDelegate
        mKeyguardDelegate.bindService(mContext);
    }    
复制代码

策略类 PhoneWindowManager 经过一个代理类 KeyguardServiceDelegate 来绑定了 KeyguardService。动画

KeyguardService 是一个标准的 Service,在绑定它的时候会返回一个 IBinder 对象,也就是服务端接口。咱们在后面直接称 KeyguardService 为锁屏服务端。

// frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
    
    public void bindService(Context context) {
        Intent intent = new Intent();
        final Resources resources = context.getApplicationContext().getResources();
        // KeyguardService
        final ComponentName keyguardComponent = ComponentName.unflattenFromString(
                resources.getString(com.android.internal.R.string.config_keyguardComponent));
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        intent.setComponent(keyguardComponent);
   
        if (!context.bindServiceAsUser(intent, mKeyguardConnection,
                Context.BIND_AUTO_CREATE, mHandler, UserHandle.SYSTEM)) {
            // ...
        }
    }
复制代码

这就是一个标准的绑定 Service 流程,经过绑定时传入的参数 mKeyguardConnection 能够查当作功绑定后的操做

// frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
    
    private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            mKeyguardService = new KeyguardServiceWrapper(mContext,
                    IKeyguardService.Stub.asInterface(service), mCallback);

            if (mKeyguardState.systemIsReady) {
            
                mKeyguardService.onSystemReady();
                
                if (mKeyguardState.currentUser != UserHandle.USER_NULL) {
                    mKeyguardService.setCurrentUser(mKeyguardState.currentUser);
                }

                if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE
                        || mKeyguardState.interactiveState == INTERACTIVE_STATE_WAKING) {
                    mKeyguardService.onStartedWakingUp();
                }
                
                if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE) {
                    mKeyguardService.onFinishedWakingUp();
                }
                
                if (mKeyguardState.screenState == SCREEN_STATE_ON
                        || mKeyguardState.screenState == SCREEN_STATE_TURNING_ON) {
                    mKeyguardService.onScreenTurningOn(
                            new KeyguardShowDelegate(mDrawnListenerWhenConnect));
                }
                
                if (mKeyguardState.screenState == SCREEN_STATE_ON) {
                    mKeyguardService.onScreenTurnedOn();
                }
                
                mDrawnListenerWhenConnect = null;
            }

            if (mKeyguardState.bootCompleted) {
                mKeyguardService.onBootCompleted();
            }
            
            if (mKeyguardState.occluded) {
                mKeyguardService.setOccluded(mKeyguardState.occluded, false /* animate */);
            }

            if (!mKeyguardState.enabled) {
                mKeyguardService.setKeyguardEnabled(mKeyguardState.enabled);
            }
        }
    }
复制代码

经过查询各类状态,而后按顺序向锁屏服务端发送指令。而我只分析服务端的onSystemReady()的实现

代理类 KeyguardServiceDelegate 内部经过 KeyguardState 对象保存锁屏的状态,从而控制 KeyguardService 的行为。

// frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
    
    private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
        public void onSystemReady() {
            // SYSTEM 用户或者有 android.Manifest.permission.CONTROL_KEYGUARD 权限
            checkPermission();
            mKeyguardViewMediator.onSystemReady();
        }
    }
复制代码

锁屏服务端又展转通知了 KeyguardViewMediator,KeyguardViewMediator 向 Handler 发送了一个 SYSTEM_READY 事件来处理,最终会调用 handleSystemReady()

  1. KeyguardViewMediator 是典型的中介者模式的应用,它综合了各方面的信息来控制锁屏。
  2. KeyguardViewMediator 有一个与主线程 Looper 关联的 Handlder,事件和处理都是经过这个 Handler,所以保证了事件处理的有序性,这样就不会致使界面刷新混乱。
// frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
    
    private void handleSystemReady() {
        synchronized (this) {
            if (DEBUG) Log.d(TAG, "onSystemReady");
            mSystemReady = true;
            
            // 开启锁屏, 这里参数为 null
            doKeyguardLocked(null);

            // KeyguardUpdateMonitor 经过监听数据库Uri, 注册广播接收器,向各类服务注册监听,从而获取到与锁屏有关的更新
            // KeyguardViewMediator 这个中介者关心的回调以下
            // 1. onClockVisibilityChanged
            // 2. onDeviceProvisioned
            // 3. onSimStateChanged
            // 4. onBiometricAuthFailed, onBiometricAuthenticated
            // 5. onHasLockscreenWallpaperChanged
            mUpdateMonitor.registerCallback(mUpdateCallback);
        }
        // Most services aren't available until the system reaches the ready state, so we
        // send it here when the device first boots.
        maybeSendUserPresentBroadcast();
    }
复制代码

KeyguardMediator 经过 doKeyguardLocked() 来开启锁屏,这里也是经过主线程的 Handler 来处理的,最终调用 handleShow()

// frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
    
    private void handleShow(Bundle options) {

        // ...

        synchronized (KeyguardViewMediator.this) {
            if (!mSystemReady) {
                return;
            }
            // 通知监听都,锁屏状态改变了
            setShowingLocked(true, mAodShowing);
            // 显示锁屏屏界面
            mStatusBarKeyguardViewManager.show(options);
            mHiding = false;
            mWakeAndUnlocking = false;
            resetKeyguardDonePendingLocked();
            mHideAnimationRun = false;
            // 经过StatusBarManager调整状态栏
            adjustStatusBarLocked();
            userActivity();
            // KeyguardUpdateMonitor 保存锁屏状态
            mUpdateMonitor.setKeyguardGoingAway(false /* away */);
            mShowKeyguardWakeLock.release();
        }
        // 更新显示什么
        mKeyguardDisplayManager.show();
    }
复制代码

锁屏的中介者 KeyguardViewMediator 最终把显示锁屏这个任务交给了 StatusBarKeyguardViewManager。

StatusBarKeyguardViewManager 管理锁屏的建立,显示,隐藏,重置。

// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
    
    public void show(Bundle options) {
        mShowing = true;
        // 更新锁屏状态
        mStatusBarWindowController.setKeyguardShowing(true);

        // 通知各类关心锁屏状态的组件,锁屏状态改变了
        mKeyguardMonitor.notifyKeyguardState(
                mShowing, mKeyguardMonitor.isSecure(), mKeyguardMonitor.isOccluded());

        // 重置锁屏界面。参数为true,就表示当显示滑动锁屏时,隐藏安全锁界面
        reset(true /* hideBouncerWhenShowing */);
    }

复制代码

KeyguardMonitorImpl 介绍

  1. 它是一个单例。
  2. 它接收注册回调。
  3. 它利用了 KeyguardUpdateMonitor 只监听了 onTrustedChanged() 回调,而且在事件发生时,会调用各类已经注册的回调。
  4. 它暴露了一些 public 方法,经过这些方法设置锁屏状态,而后经过已经注册的回调。
  5. 它的做用就是供 SystemUI 其它组件获取最新的锁屏状态。

锁屏管理者 StatusBarKeyguardViewManager 先通知各路人马,锁屏状态改变了,而后重置了锁屏界面

// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
    
    public void reset(boolean hideBouncerWhenShowing) {
        if (mShowing) {
            if (mOccluded && !mDozing) {
                // ...
            } else {
                // 参数为true, 表示隐藏安全锁界面,显示滑动锁界面
                showBouncerOrKeyguard(hideBouncerWhenShowing);
            }
            // 调用 KeyguardUpdateMonitor#handleKeyguardReset()
            // 更新指纹解锁,面部解锁状态
            KeyguardUpdateMonitor.getInstance(mContext).sendKeyguardReset();
            updateStates();
        }
    }  
    
    protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
        /** * 注意: needsFullscreenBouncer() 会加载安全锁视图,并添加到SystemUI的根视图中 * 可是只有显示SIM PIN/PUK锁时,这个方法才会返回true, 表示须要全屏显示 */
        if (mBouncer.needsFullscreenBouncer() && !mDozing) {
            // ...
        } else {
            // 显示滑动锁屏
            mStatusBar.showKeyguard();
            // 隐藏安全锁
            if (hideBouncerWhenShowing) {
                hideBouncer(shouldDestroyViewOnReset() /* destroyView */);
                mBouncer.prepare();
            }
        }
        // 更新各方状态
        updateStates();
    }    
复制代码

reset() 这个方法的名字起的不是很直观,它作了不少事,主要有

  1. 经过 KeyguardBouncer#needsFullScreenBouncer() 加载了安全锁视图到 SystemUI 根视图中。
  2. 决定滑动锁,安全锁的显示或隐藏。
  3. 通知各方,锁屏状态改变了。

加载安全锁视图的过程就不分析了,很简单,就是加载布局,而后添加到 SystemUI 根视图中。

本该只关心滑动锁,从代码能够看到,锁屏管理者 StatusBarKeyguardViewManager 把显示滑动锁的任务交给了 StatusBar。

SystemUI之StatusBar建立可知,StatusBar 建立并管理了整个 SystemUI 视图,所以最终由它来显示滑动锁是瓜熟蒂落的。

StatusBar#showKeyguard() 的调用链很长,这里就不展现代码了,直接给出调用链。

showKeyguard() -> updateIsKeyguard() -> showKeyguardImpl() -> updatePanelExpansionForKeyguard() -> instantExpandNotificationsPanel()

// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
    
    public void showKeyguard() {
        // 保存状态
        mStatusBarStateController.setKeyguardRequested(true);
        mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
        mPendingRemoteInputView = null;
        
        // 更新滑动锁屏,最终调用了 instantExpandNotificationsPanel()
        updateIsKeyguard();
        
        mAssistManager.onLockscreenShown();
    }
    
    
    @Override
    public void instantExpandNotificationsPanel() {
        // Make our window larger and the panel expanded.
        // 设置通知面版可见
        makeExpandedVisible(true);
        // 展开,参数为false表示不须要动画&emsp;
        mNotificationPanel.expand(false /* animate */);
        mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */);
    }    
复制代码

NotificationPanelView mNotificationPanel是整个下拉通知面版的父容器,在文章开头,经过查看布局能够发现,滑动锁屏的大部分控件都是在下拉通知面版里,所以这里的操做就不奇怪了。

// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
    
    @Override
    public void expand(boolean animate) {
        // 经过父类PanelView实现展开操做,并最终调用 requestLayout() 从新布局
        super.expand(animate);
        // 让各类控件监听它感兴趣的事情,例如 KeyguardStatusBarView 监听电池事件
        setListening(true);
    }
复制代码

这里就属于Android控件的各类操做了,这里就不展开了,最终它会调用 requestLayout() 从新布局,所以它会调用 onLayout() 来更新布局

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        
        // ...
        
        // 显示 Clock 和 Notification
        positionClockAndNotifications();

        // ...
        
        // 这里更新了锁屏大的时钟,以及底部图标区域
        updateExpandedHeight(getExpandedHeight());

        // 显示时间,日期的状态视图
        updateHeader();

        // ...
    }
复制代码

能够看到各类锁屏的控件显示出来了。

结束

从系统启动到显示滑动锁屏,这个过程其实并不复杂,有了这个基础,就能够分析安全锁界面的显示过程了。

另外,咱们应该对电源键控制锁屏的显示与隐藏很感兴趣,其实本文也给出了实现,例如点亮屏幕显示锁屏的过程是

DisplayManagerService -> DisplayPowerController -> PhoneWindowManager#screenTurnedOff() -> KeyguardServiceDelegate#onScreenTurnedOff() -> KeyguardService#onScreenTurnedOff()

SystemUI 还有不少模块,下篇挑个简单点的吧,就分析 VolumeUI 吧,让咱们继续为 SystemUI 窒息。

相关文章
相关标签/搜索