Android中Keyguard解析

概述

本文是基于Android版本:8.1.0来讨论Keyguard相关代码.

在Android 8.1.0版本中,将Keyguard模块的代码完全移到了SystemUI,将其和SystemUI进行了合并。那么为何要把SystemUI和Keyguard代码放在一起呢?估计基于以下几点考虑:
a) 虽然在之前的版本中,Keyguard模块和SystemUI模块的代码是分开放在两个目录,且单独参与编译,Keyguard模块编译生成keyguard.jar,SystemUI模块单独编译生成SystemUI.apk,且SystemUI.apk包含了Keyguard.jar. 两个模块都是公用一个进程com.android.systemui.
b) 两个模块有公用的小模块,比如StatusBar、NotificationView等等.
c) 两个模块都需要对滑动事件进行处理,而且大部分的处理逻辑很像.

Keyguard模块的重点是:
• 事件的处理
• LockPatternUtils
• 锁屏界面的显示
• 锁屏界面的隐藏
• 密码的认证和校验

"Android4.4 Keyguard架构"

Keyguard相关文件功能预分析

与Keyguard相关的代码有如下这些:
从最重要的framework中相关模块开始分析,对于每个模块里面的每个块代码的初步分析顺序并不是按照其重要性来的,是按照每个模块里面相关类的字母顺序来讲解.
在这个部分,我们主要任务是了解每个文件的功能和作用,因此,只是局限于单个文件眼前的功能,对Keyguard整体功能,并不能全面的理解.

core模块代码

  1. framework/base/core模块有KeyguardManager,LockPatternUtils,
    1
    2
    3
    4
    framework/base/core/java/android/app/KeyguardManager.java
    framework/base/core/java/com/android/internal/widget/LockPatternUtils.java
    frameworks/base/core/java/android/app/admin/DevicePolicyManager.java
    frameworks/base/core/java/android/os/TokenWatcher.java

首先要说明一下,从Android 6.0 开始屏幕解锁(即除了指纹解锁和面部解锁的PIN码、密码、图案解锁)的认证部分(Credential),都是通过Confirm Credential获取key的方式来完成,

KeyguardManager: 我们先来看看官方的注释

1
Class that can be used to lock and unlock the keyboard. The actual class to control the keyboard locking is {@link android.app.KeyguardManager.KeyguardLock}.

用于解锁屏,实际控制解锁的是其内部类KeyguardLock.接下来看看KeyguardManager有哪些实现.
• createConfirmDeviceCredentialIntent,有三个此方法的重载,分别创建
a)ACTION_CONFIRM_DEVICE_CREDENTIAL 常规intent
b)ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER 带用户ID的intent
c)ACTION_CONFIRM_FRP_CREDENTIAL 恢复出厂设置的FRP的intent

• 内部类KeyguardLock,看看KeyguardLock的代码发现,只有两个函数,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 /**
* @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
* and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
* instead; this allows you to seamlessly hide the keyguard as your application
* moves in and out of the foreground and does not require that any special
* permissions be requested.
*
* Handle returned by {@link KeyguardManager#newKeyguardLock} that allows
* you to disable / reenable the keyguard.
*/
@Deprecated
public class KeyguardLock {
private final IBinder mToken = new Binder();
private final String mTag;

KeyguardLock(String tag) {
mTag = tag;
}

/**
* Disable the keyguard from showing. If the keyguard is currently
* showing, hide it. The keyguard will be prevented from showing again
* until {@link #reenableKeyguard()} is called.
*
* A good place to call this is from {@link android.app.Activity#onResume()}
*
* Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager}
* is enabled that requires a password.
*
* @see #reenableKeyguard()
*/
@RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
public void disableKeyguard() {
try {
mWM.disableKeyguard(mToken, mTag);
} catch (RemoteException ex) {
}
}

/**
* Reenable the keyguard. The keyguard will reappear if the previous
* call to {@link #disableKeyguard()} caused it to be hidden.
*
* A good place to call this is from {@link android.app.Activity#onPause()}
*
* Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager}
* is enabled that requires a password.
*
* @see #disableKeyguard()
*/
@RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
public void reenableKeyguard() {
try {
mWM.reenableKeyguard(mToken);
} catch (RemoteException ex) {
}
}
}

我们看到在KeyguardLock类头部有个注解@deprecated,表明已经过时了,被弃用了.这种之前的Keyguard显示与隐藏,留给后面来跟踪.

• KeyguardDismissCallback 内部抽象类
• isKeyguardLocked() 判断当前是否处于锁屏状态,调用的是WindowManagerService的isKeyguardLocked()方法.继续调用 WindowManagerPolicy,最后调到PhoneWindowManager里面,

1
2
3
4
5
6
7
8
9
 /** {@inheritDoc} */
@Override
public boolean isKeyguardLocked() {
return keyguardOn();
}
...
boolean keyguardOn() {
return isKeyguardShowingAndNotOccluded() || inKeyguardRestrictedKeyInputMode();
}













接下来看看KeyguardLock内部类,

TokenWatcher: 抽象类,定义

services模块代码

  1. framework/base/services模块有PhoneWindowManager,KeyguardServiceDelegate,KeyguardServiceWrapper,KeyguardStateMonitor
    PhoneWindowManager.java
    KeyguardServiceDelegate Keyguard服务代理
    KeyguardServiceWrapper Keyguard服务封装
    KeyguardStateMonitor Keygurad状态监视
    1
    2
    3
    4
    5
    6
    frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
    frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
    frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
    frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
    frameworks/base/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
    frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

SystemUI模块代码

  1. SystemUI中的Keyguard模块代码在frameworks/base/packages/SystemUI/src/com/android/systemui/目录,其中处理Keyguard逻辑相关的代码有:

    1
    2


    KeyguardHostView 即为最终需要展示界面的基类
    KeyguardAbsKeyInputView
    作为基类实现了key input password类型(PIN, Sim PIN, Sim PUK, password)的大多数公用函数。
    KeyguardUpdateMonitor 注册了绝大多数的广播,负责处理界面的一些刷新流程处理
    使用了观察者模式
    KeyguardUpdateMonitorCallback
    KeyguardDisplayManager 作为中间类去控制keyguard的show与hide,主要用于双屏异显
    packages/Keyguard/src/com/android/keyguard/KeyguardDisplayManager.java
    此类中涉及到两个重要的类
    MediaRouter
    Presentation 这个类是用来控制双屏异显的
    LiftToActivateListener.java
    AccessibilityService 无障碍/辅助功能

KeyguardViewMediator 是整个待机解/锁屏业务的调度器,负责调度锁屏界面的相关动作及查询解锁屏状态。是一个服务
packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java

KeyguardService.java KeyguardViewMediator为其子类

KeyguardBouncer 管理是非滑动解锁还是滑动解锁,控制安全锁屏的显示与隐藏
packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java

SystemUI中Keyguard模块代码

  1. SystemUI中的Keyguard模块中处理Keyguard的UI显示相关的代码有:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    frameworks/base/packages/SystemUI/src/com/android/keyguard/AlphaOptimizedImageButton.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/AlphaOptimizedLinearLayout.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/AlphaOptimizedRelativeLayout.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/CarrierText.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/EmergencyCarrierArea.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardClockAccessibilityDelegate.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/LatencyTracker.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java
    frameworks/base/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java

    AlphaOptimizedImageButton:A frame layout which does not have overlapping renderings commands and therefore does not need a layer when alpha is changed. 用于PIN码解锁时,确认键Button时使用.

    AlphaOptimizedLinearLayout:A linear layout which does not have overlapping renderings commands and therefore does not need a layer when alpha is changed. 一个LinearLayout,但是hasOverlappingRendering方法返回为false,主要目的是防止过度绘制,提升性能. 关于hasOverlappingRendering方法请参考这里

    AlphaOptimizedRelativeLayout:同样的是一个相对布局的优化类.

    CarrierText: 继承自TextView,主要用来在锁屏界面,顶部StatusBar里面显示运营商信息以及当前的SIM卡状态和网络状态.

    EmergencyButton: 继承自Button, 锁屏界面的紧急拨号按钮

    EmergencyCarrierArea: 继承自AlphaOptimizedLinearLayout,主要处理紧急拨号按钮和这里的CarrierText的显示问题

    KeyguardAbsKeyInputView: 抽象类,继承自LinearLayout,实现了KeyguardSecurityView, EmergencyButton.EmergencyButtonCallback 三个类,主要的功能是:Base class for PIN and password unlock screens. 即 PIN码和密码解锁的基类,实现了对输入的密码处理的主要的三个函数verifyPasswordAndUnlock(), onPasswordChecked(), handleAttemptLockout().

    KeyguardClockAccessibilityDelegate:Replaces fancy colons with regular colons. Only works on TextViews.重新定义了一个冒号,继承自View.AccessibilityDelegate,主要用于锁屏界面时钟显示时的冒号

    KeyguardConstants: 定义Keyguard模块用到的三个Debug开关

    KeyguardDisplayManager:Support multiple external displays,支持多个扩展设备上显示. 控制Keyguard的显示和隐藏,主要有三个函数show(),hide(),updatedisplay(),还有一个KeyguardPresentation类,继承自Presentation,Presentation类就是Android支持双屏异显的类。还有一个MediaRouter.SimpleCallback,用于监视外接设备。

    KeyguardEsimArea:This button is used by the device with embedded SIM card to disable current carrier to unlock the device with no cellular service.
    继承自Button,按钮的主要作用是在eSIM卡(eSIM卡比日常见到的SIM卡基于硬件芯片的模块,安全级别可以做到最高,具体可以查下资料)的PIN码和PUK码解锁的时候,在无网络的情况下,diable掉carrier实现解锁设备.(eSIM卡的知识不懂,为啥要这样解锁设备,还不清楚)

    KeyguardHostView: 继承自FrameLayout,实现了SecurityCallback接口.Base class for keyguard view. {@link #reset} is where you should reset the state of your view. Use the {@link KeyguardViewCallback} via {@link #getCallback()} to send information back (such as poking the wake lock,or finishing the keyguard). Handles intercepting of media keys that still work when the keyguard is showing.是整个Keyguard显示的基类,可以reset(重置)Keyguard的显示,可以使用KeyguardViewCallback回调的方式,发信息 .在锁屏界面对多媒体相关的按键事件,比如播放音乐、暂停音乐、静音、音量+、音量-、耳机插入、上一首、下一首等事件进行拦截以及处理.
    SecurityCallback接口定义了四个函数,具体代码见com.android.keyguard.KeyguardSecurityContainer.SecurityCallback,分别是:
    • dismiss();
    • userActivity();
    • onSecurityModeChanged();
    • finish();
    • reset();

    KeyguardMessageArea: 继承自TextView,实现SecurityMessageDisplay.PIN码/图案/密码解锁界面时显示输入密码错误,以及输入次数等消息.在KeyguardPatternView中有使用.

    KeyguardPasswordView:Displays an alphanumeric (latin-1) key entry for the user to enter an unlock password.显示一个文本框,用来处理用户输入的密码字符,这里要讲一下锁屏界面的密码和PIN码(这里的PIN码不是SIM卡的PIN码),两者的区别是,密码可以是字母和数字的组合,但是PIN码只能是由数字组成. 继承了KeyguardAbsKeyInputView实现了 KeyguardSecurityView, OnEditorActionListener, TextWatcher. 此类完成了用户进入密码解锁界面后,密码输入,密码错误时的提示信息处理、密码校验等事情.

    KeyguardPatternView: 继承自LinearLayout,实现了KeyguardSecurityView, AppearAnimationCreator<LockPatternView.CellState>, EmergencyButton.EmergencyButtonCallback 接口,这里不是图案解锁中九宫格的View实现,九宫格的图案解锁实现是在com.android.internal.widget.LockPatternView中.
    此类中,完成了用户进入图案解锁界面后,图案密码输入、图案密码错误时的提示信息处理、密码校验等事情.

    KeyguardPinBasedInputView:A Pin based Keyguard input view.继承自KeyguardAbsKeyInputView,实现了View.OnKeyListener, View.OnTouchListener接口.是一个PIN码输入的基类,因为总共有三类PIN码,a)正常PIN码,b)SIM卡的PIN码,c)SIM卡的PUK码三种.由于PIN码全部是数字组成,所以在PIN输入的时候,可以不用调用输入法的输入面板,在此类中,实现了数字输入面板的处理逻辑.

    KeyguardPINView:Displays a PIN pad for unlocking.继承自KeyguardPinBasedInputView,显示一个PIN码解锁的界面.

    KeyguardSecurityCallback: 一个接口,定义了5个方法.
    • dismiss() //Dismiss the given security screen.
    • userActivity() //Manually report user activity to keep the device awake.
    • isVerifyUnlockOnly() //Checks if keyguard is in “verify credentials” mode.
    • reportUnlockAttempt(int userId, boolean success, int timeoutMs) //Call to report an unlock attempt.
    • reset() //Resets the keyguard view.
    这个接口和上面提到的SecurityCallback接口的区别在哪里呢?

    KeyguardSecurityContainer: 继承自FrameLayout,实现了如下功能:
    • 实现了KeyguardSecurityView接口
    • 定义了SecurityCallback接口.定义SecurityCallback接接口的作用是// Used to notify the container when something interesting happens.
    • 实现了showTimeoutDialog()函数,密码输入错误多次后的弹框提示
    • showAlmostAtWipeDialog()和showWipeDialog()函数,密码输入太多次后,擦除cache数据的提示
    • reportFailedUnlockAttempt()函数,上报错误次数
    • KeyguardSecurityCallback 接口的实现,整个Keyguard里面,KeyguardSecurityCallback接口都是直接当内部类的方式new出来的,具体实现,就在KeyguardSecurityContainer里面,从KeyguardSecurityCallback的实现来看,是连接 SecurityCallback和KeyguardSecurityContainer的.代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
    public void userActivity() {
    if (mSecurityCallback != null) {
    mSecurityCallback.userActivity();
    }
    }

    public void dismiss(boolean authenticated, int targetId) {
    mSecurityCallback.dismiss(authenticated, targetId);
    }

    public boolean isVerifyUnlockOnly() {
    return mIsVerifyUnlockOnly;
    }

    public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
    KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
    if (success) {
    monitor.clearFailedUnlockAttempts();
    mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
    } else {
    KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs);
    }
    }

    public void reset() {
    mSecurityCallback.reset();
    }
    };

    KeyguardSecurityView: 接口, 定义了如下方法:
    • setKeyguardCallback(KeyguardSecurityCallback callback);//Interface back to keyguard to tell it when security
    • setLockPatternUtils(LockPatternUtils utils); //Set {@link LockPatternUtils} object. Useful for providing a mock interface.
    • reset(); //Reset the view and prepare to take input. This should do things like clearing the password or pattern and clear error messages.
    • onPause(); //Emulate activity life cycle within the view. When called, the view should clean up and prepare to be removed.
    • onResume(int reason); //Emulate activity life cycle within this view. When called, the view should prepare itself
    • needsInput(); //Inquire whether this view requires IME (keyboard) interaction.
    • KeyguardSecurityCallback getCallback(); //Get {@link KeyguardSecurityCallback} for the given object
    • showPromptReason(int reason); //Show a string explaining why the security view needs to be solved.
    • showMessage(String message, int color); //Show a message on the security view with a specified color
    • showUsabilityHint(); //Instruct the view to show usability hints, if any.
    • startAppearAnimation(); //Starts the animation which should run when the security view appears. //Security View显示动画
    • startDisappearAnimation(Runnable finishRunnable); //Starts the animation which should run when the security view disappears.//Security View消失动画

    KeyguardSecurityModel: 定义了7种不同的锁屏模式(还有指纹解锁和面部解锁不在这里面),如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public enum SecurityMode {
    Invalid, // NULL state
    None, // No security enabled
    Pattern, // Unlock by drawing a pattern.
    Password, // Unlock by entering an alphanumeric password
    PIN, // Strictly numeric password
    SimPin, // Unlock by entering a sim pin.
    SimPuk // Unlock by entering a sim puk
    }

    • 通过getSecurityMode方法获取当前的锁屏模式,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    SecurityMode getSecurityMode(int userId) {
    KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);

    if (mIsPukScreenAvailable && SubscriptionManager.isValidSubscriptionId(
    monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED))) {
    return SecurityMode.SimPuk;
    }

    if (SubscriptionManager.isValidSubscriptionId(
    monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED))) {
    return SecurityMode.SimPin;
    }

    final int security = mLockPatternUtils.getActivePasswordQuality(userId);
    switch (security) {
    case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
    case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
    return SecurityMode.PIN;

    case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
    case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
    case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
    case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
    return SecurityMode.Password;

    case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
    return SecurityMode.Pattern;
    case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
    return SecurityMode.None;

    default:
    throw new IllegalStateException("Unknown security quality:" + security);
    }
    }

    这里面有两个很重要的类LockPatternUtils和DevicePolicyManager,代码路径如下,具体分析见上面的模块分析.
    frameworks/base/core/java/com/android/internal/widget/LockPatternUtils.java
    frameworks/base/core/java/android/app/admin/DevicePolicyManager.java

    KeyguardSecurityViewFlipper: 继承自ViewFlipper,实现了KeyguardSecurityView接口.Subclass of the current view flipper that allows us to overload dispatchTouchEvent() so we can emulate {@link WindowManager.LayoutParams#FLAG_SLIPPERY} within a view hierarchy. 用在keyguard_host_view布局中,嵌套与KeyguardSecurityContainer内部,具体见下面的keyguard_host_view.xml代码. 继承ViewFlipper的原因是,从滑动解锁界面上滑进入图案解锁、PIN码解锁、密码解锁等界面时有一个切换,实现视图翻转,另一方面,由于是KeyguardSecurityContainer的子视图,可以重写dispatchTouchEvent()函数,在各种解锁界面处理事件分发.具体可以看ViewFlipper.

    KeyguardSimPinView: 继承自KeyguardPinBasedInputView.用于显示SIM卡PIN码解锁界面逻辑的处理,实现了如下方法:
    • KeyguardUpdateMonitorCallback中的onSimStateChanged来监听SIM卡的状态
    • CheckSimPin 是个抽象类,继承自Thread,开启后台线程,用于验证PIN码正确性
    • verifyPasswordAndUnlock 验证密码并解锁
    • showDefaultMessage
    • getSimRemainingAttemptsDialog
    • getSimUnlockProgressDialog

    KeyguardSimPukView: 继承自KeyguardPinBasedInputView.用于显示SIM卡PUK码解锁界面逻辑的处理,实现了如下方法:
    • KeyguardUpdateMonitorCallback中的onSimStateChanged来监听SIM卡的状态,与SIM卡的PIN码一样
    • StateMachine 类,由于PUK码是在PIN码输入错三次都输入错误之后,才开启输入PUK码的,且PIN码要求是4-8位数字,PUK码必须是8位数字.此类用于此过程的检查
    • CheckSimPuk 是个抽象类,继承自Thread,开启后台线程,用于验证PUK码正确性
    • verifyPasswordAndUnlock 验证密码并解锁
    • showDefaultMessage
    • getSimRemainingAttemptsDialog
    • getSimUnlockProgressDialog

    KeyguardStatusView: 继承自GridLayout,布局文件是keyguard_status_view.xml,主要用于处理Keyguard界面的时钟,是否在充电,以及用户信息,并包含了keyguard_status_area的布局.整体来说,用于处理显示在Keyguard界面StatusBar以下,Notification以上的界面布局的逻辑.

    KeyguardUpdateMonitor: 实现了TrustManager.TrustListener接口,注册了绝大多数的广播,负责处理界面的一些刷新流程处理.
    到frameworks/base/core/java/android/app/trust/TrustManager.java里面看看TrustListener接口代码,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public interface TrustListener {

    /**
    * Reports that the trust state has changed.
    * @param enabled if true, the system believes the environment to be trusted.
    * @param userId the user, for which the trust changed.
    * @param flags flags specified by the trust agent when granting trust. See
    * {@link android.service.trust.TrustAgentService#grantTrust(CharSequence, long, int)
    * TrustAgentService.grantTrust(CharSequence, long, int)}.
    */
    void onTrustChanged(boolean enabled, int userId, int flags);

    /**
    * Reports that whether trust is managed has changed
    * @param enabled if true, at least one trust agent is managing trust.
    * @param userId the user, for which the state changed.
    */
    void onTrustManagedChanged(boolean enabled, int userId);
    }

    我们来看一下,哪些广播会刷新:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    private static final int MSG_TIME_UPDATE = 301;  //时间刷新
    private static final int MSG_BATTERY_UPDATE = 302; //电池状态刷新
    private static final int MSG_SIM_STATE_CHANGE = 304; //SIM卡状态改变
    private static final int MSG_RINGER_MODE_CHANGED = 305; //响铃模式改变
    private static final int MSG_PHONE_STATE_CHANGED = 306; //Phone状态改变
    private static final int MSG_DEVICE_PROVISIONED = 308; // 开机向导标记位
    private static final int MSG_DPM_STATE_CHANGED = 309; //DevicePolicyManage 设备授权
    private static final int MSG_USER_SWITCHING = 310; // 切换用户
    private static final int MSG_KEYGUARD_RESET = 312; //Keyguard重置
    private static final int MSG_BOOT_COMPLETED = 313; //开机完成
    private static final int MSG_USER_SWITCH_COMPLETE = 314; //用户切换完成
    private static final int MSG_USER_INFO_CHANGED = 317; // 用户信息变化
    private static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318; // 紧急拨号
    private static final int MSG_STARTED_WAKING_UP = 319; // 开始唤醒
    private static final int MSG_FINISHED_GOING_TO_SLEEP = 320; //休眠完成
    private static final int MSG_STARTED_GOING_TO_SLEEP = 321; //开始休眠
    private static final int MSG_KEYGUARD_BOUNCER_CHANGED = 322; //锁屏模式切换
    private static final int MSG_FACE_UNLOCK_STATE_CHANGED = 327; // 面部解锁状态改变
    private static final int MSG_SIM_SUBSCRIPTION_INFO_CHANGED = 328; //SIM卡SUBSCRIPTION信息变化
    private static final int MSG_AIRPLANE_MODE_CHANGED = 329; //飞行模式切换
    private static final int MSG_SERVICE_STATE_CHANGE = 330; // SIM卡服务状态切换
    private static final int MSG_SCREEN_TURNED_ON = 331; //亮屏
    private static final int MSG_SCREEN_TURNED_OFF = 332; //灭屏
    private static final int MSG_DREAMING_STATE_CHANGED = 333; //屏保模式
    private static final int MSG_USER_UNLOCKED = 334; //用户锁
    private static final int MSG_LOCALE_CHANGED = 500; //语言切换
    private static final int MSG_ASSISTANT_STACK_CHANGED = 335; // 指纹
    private static final int MSG_FINGERPRINT_AUTHENTICATION_CONTINUE = 336; //指纹认证

KeyguardUpdateMonitorCallback: 与KeyguardUpdateMonitor中广播消息对应的回调方法. Callback也是一种观察者模式,在Android的很多地方都有使用到.
现在下面要讲的是Android中经常用到的使用Callback来更新数据的一个实例,弄明白了,以后其他的使用也会很快理解到

在KeyguardUpdateMonitor中我们看看KeyguardUpdateMonitorCallback的使用.

1
2
private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
mCallbacks = Lists.newArrayList();

我们以MSG_TIME_UPDATE 时间的刷新为例,来看一下KeyguardUpdateMonitorCallback的用处.
第一步:接收到时间刷新的广播

1
2
3
4
5
6
7
8
9
10
11
12
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {

@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (DEBUG) Log.d(TAG, "received broadcast " + action);

if (Intent.ACTION_TIME_TICK.equals(action)
|| Intent.ACTION_TIME_CHANGED.equals(action)
|| Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
mHandler.sendEmptyMessage(MSG_TIME_UPDATE);
......

第二步:收到广播后,把消息发送给Handler,交由Handler来处理,我们来看看Handler收到MSG_TIME_UPDATE后的处理:

1
2
3
4
5
6
7
8
9
10
11
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_TIME_UPDATE:
handleTimeUpdate();
break;
case MSG_BATTERY_UPDATE:
handleBatteryUpdate((BatteryStatus) msg.obj);
break;
......

第三步:Hanlder收到MSG_TIME_UPDATE消息后,执行handleTimeUpdate函数,我们来看看handleTimeUpdate函数代码:

1
2
3
4
5
6
7
8
9
10
11
12
/**
* Handle {@link #MSG_TIME_UPDATE}
*/
private void handleTimeUpdate() {
if (DEBUG) Log.d(TAG, "handleTimeUpdate");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onTimeChanged();
}
}
}

第四步:在这里,我们看到了mCallbacks,从刚开始,我们看到mCallbacks的初始化,是空的,如下:

1
2
private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
mCallbacks = Lists.newArrayList();

接着,在registerCallback函数里面进行了赋值,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void registerCallback(KeyguardUpdateMonitorCallback callback) {
if (DEBUG) Log.v(TAG, "*** register callback for " + callback);
// Prevent adding duplicate callbacks
for (int i = 0; i < mCallbacks.size(); i++) {
if (mCallbacks.get(i).get() == callback) {
if (DEBUG) Log.e(TAG, "Object tried to add another callback",
new Exception("Called by"));
return;
}
}
mCallbacks.add(new WeakReference<KeyguardUpdateMonitorCallback>(callback));
removeCallback(null); // remove unused references
sendUpdates(callback);
}

那么registerCallback又在什么时候调用呢?我们以EmergencyButton为例,看看EmergencyButton里面的代码:

1
2
3
4
5
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
}

我们看到在EmergencyButton类中的onAttachedToWindow方法使用registerCallback方法,也就是说只要EmergencyButton可见,那么就开始注册这个Callback回调.
这里用EmergencyButton来举例好像不大合适,因为,在收到时间刷新的广播之后,第三步执行handleTimeUpdate方法,里面调用的Callback函数是cb.onTimeChanged();在EmergencyButton里面肯定是不需要关心时间的.

我们找个与时间有关的,找到KeyguardStatusView,在这里面看到registerCallback的调用如下:

1
2
3
4
5
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
}

和mergencyButton差不多,在KeyguardStatusView加载时候,就开始注册registerCallback方法.现在收到了时间更新的广播,执行到了第三步,现在要执行cb.onTimeChanged();我们看看KeyguardStatusView里面的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {

@Override
public void onTimeChanged() {
refresh();
}

@Override
public void onKeyguardVisibilityChanged(boolean showing) {
if (showing) {
if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing);
refresh();
updateOwnerInfo();
}
}

@Override
public void onStartedWakingUp() {
setEnableMarquee(true);
}

@Override
public void onFinishedGoingToSleep(int why) {
setEnableMarquee(false);
}

@Override
public void onUserSwitchComplete(int userId) {
refresh();
updateOwnerInfo();
}
};

从代码中可以看到,在KeyguardUpdateMonitor里面收到了时间刷新的广播之后,对应的KeyguardStatusView能立即执行refesh()方法,来刷新时间.
从上面代码中还可以看到KeyguardStatusView在KeyguardUpdateMonitor收到唤醒,休眠以及用户切换等广播时,也能进行相应的更新。

LatencyTracker: 我们先来看看关于这个类的官方注释

1
2
Class to track various latencies in SystemUI. It then outputs the latency to logcat so these latencies can be captured by tests and then used for dashboards.
This is currently only in Keyguard so it can be shared between SystemUI and Keyguard, but eventually we'd want to merge these two packages together so Keyguard can use common classes that are shared with SystemUI.

这个类主要的目的是用来跟踪SystemUI中各个小功能模块的耗时.方便后期性能分析和性能优化.这个类虽然在keyguard模块里面,由于Keyguard模块代码已经完全合入了SystemUI,所有他们两兄弟可以共用这个类.
我们来看看跟踪了哪几个模块的耗时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* Time it takes until the first frame of the notification panel to be displayed while expanding
*/
public static final int ACTION_EXPAND_PANEL = 0; // 通知面板下拉时间

/**
* Time it takes until the first frame of recents is drawn after invoking it with the button.
*/
public static final int ACTION_TOGGLE_RECENTS = 1; //Recent 最近任务栏显示时间

/**
* Time between we get a fingerprint acquired signal until we start with the unlock animation
*/
public static final int ACTION_FINGERPRINT_WAKE_AND_UNLOCK = 2; //指纹解锁时间

/**
* Time it takes to check PIN/Pattern/Password.
*/
public static final int ACTION_CHECK_CREDENTIAL = 3; // PIN码解锁、图案解锁、密码解锁认证时间

/**
* Time it takes to check fully PIN/Pattern/Password, i.e. that's the time spent including the
* actions to unlock a user.
*/
public static final int ACTION_CHECK_CREDENTIAL_UNLOCKED = 4; //PIN码解锁、图案解锁、密码解锁从开始到解锁整个认证时间

/**
* Time it takes to turn on the screen.
*/
public static final int ACTION_TURN_ON_SCREEN = 5; //亮屏时间

上述跟踪的几个时间模块,对应SystemUI来说的确非常重要,这几个模块的响应时间太长的话,给用户的体验就不好,因为这几个是每个用户最经常使用的地方.
从代码来看,这个功能开关是debug.systemui.latency_tracking这个prop属性
接下来看看具体如何进行耗时跟踪的,主要是onActionStart和onActionEnd两个方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* Notifies that an action is starting. This needs to be called from the main thread.
*
* @param action The action to start. One of the ACTION_* values.
*/
public void onActionStart(int action) {
if (!mEnabled) {
return;
}
Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, NAMES[action], 0);
mStartRtc.put(action, SystemClock.elapsedRealtime());
}

/**
* Notifies that an action has ended. This needs to be called from the main thread.
*
* @param action The action to end. One of the ACTION_* values.
*/
public void onActionEnd(int action) {
if (!mEnabled) {
return;
}
long endRtc = SystemClock.elapsedRealtime();
long startRtc = mStartRtc.get(action, -1);
if (startRtc == -1) {
return;
}
mStartRtc.delete(action);
Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, NAMES[action], 0);
long duration = endRtc - startRtc;
Log.i(TAG, "action=" + action + " latency=" + duration);
EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, (int) duration);
}

我们以跟踪ACTION_TOGGLE_RECENTS,最近历史任务为例,在NavigationBarFragment.java的onRecentsClick方法中代码如下:

1
2
3
4
5
6
7
8
private void onRecentsClick(View v) {
if (LatencyTracker.isEnabled(getContext())) {
LatencyTracker.getInstance(getContext()).onActionStart(
LatencyTracker.ACTION_TOGGLE_RECENTS);
}
mStatusBar.awakenDreams();
mCommandQueue.toggleRecentApps();
}

在RecentsActivity.java中,我们看到onActionEnd的调用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private final OnPreDrawListener mRecentsDrawnEventListener =
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
EventBus.getDefault().post(new RecentsDrawnEvent());
if (LatencyTracker.isEnabled(getApplicationContext())) {
DejankUtils.postAfterTraversal(() -> LatencyTracker.getInstance(
getApplicationContext()).onActionEnd(
LatencyTracker.ACTION_TOGGLE_RECENTS));
}
DejankUtils.postAfterTraversal(() -> {
Recents.getTaskLoader().startLoader(RecentsActivity.this);
Recents.getTaskLoader().getHighResThumbnailLoader().setVisible(true);
});
return true;
}
};

耗时计算的核心是onActionStart和onActionEnd两个方法的实现,需要追踪Trace.asyncTraceBegin和Trace.asyncTraceEnd两个方法,这里先不讲,偏题太远了.

LiftToActivateListener: 继承自View.OnHoverListener,我们来看看官方的注释:

1
Hover listener that implements lift-to-activate interaction for accessibility. May be added to multiple views.

通过继承View.OnHoverListener实现悬停监听,View.OnHoverListener是Android 4.0增加的对光标悬停事件、手写笔、鼠标按钮事件的支持。
主要用于ACCESSIBILITY_SERVICE,辅助服务

NumPadKey: 主要用于Keyguard模块中数字数码

PasswordTextView: 与TextView相似,但是属于密码输入,且有动画

SecurityMessageDisplay: 接口,定义了Keyguard中显示信息的几个方法.

ViewMediatorCallback: 接口,The callback used by the keyguard view to tell the {@link KeyguardViewMediator} various things.这个Callback 主要用于 KeyguardViewMediator中 更新Keyguard的View,和上面讲到的三个Callback实现起来不一样. ViewMediatorCallback接口是在KeyguardViewMediator中通过内部类来实现,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() {

@Override
public void userActivity() {
KeyguardViewMediator.this.userActivity();
}

@Override
public void keyguardDone(boolean strongAuth, int targetUserId) {
if (targetUserId != ActivityManager.getCurrentUser()) {
return;
}

tryKeyguardDone();
}

@Override
public void keyguardDoneDrawing() {
Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardDoneDrawing");
mHandler.sendEmptyMessage(KEYGUARD_DONE_DRAWING);
Trace.endSection();
}

@Override
public void setNeedsInput(boolean needsInput) {
mStatusBarKeyguardViewManager.setNeedsInput(needsInput);
}

@Override
public void keyguardDonePending(boolean strongAuth, int targetUserId) {
Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardDonePending");
if (targetUserId != ActivityManager.getCurrentUser()) {
Trace.endSection();
return;
}

mKeyguardDonePending = true;
mHideAnimationRun = true;
mHideAnimationRunning = true;
mStatusBarKeyguardViewManager.startPreHideAnimation(mHideAnimationFinishedRunnable);
mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_PENDING_TIMEOUT,
KEYGUARD_DONE_PENDING_TIMEOUT_MS);
Trace.endSection();
}

@Override
public void keyguardGone() {
Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardGone");
mKeyguardDisplayManager.hide();
Trace.endSection();
}

@Override
public void readyForKeyguardDone() {
Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#readyForKeyguardDone");
if (mKeyguardDonePending) {
mKeyguardDonePending = false;
tryKeyguardDone();
}
Trace.endSection();
}

@Override
public void resetKeyguard() {
resetStateLocked();
}

@Override
public void playTrustedSound() {
KeyguardViewMediator.this.playTrustedSound();
}

@Override
public boolean isScreenOn() {
return mDeviceInteractive;
}

@Override
public int getBouncerPromptReason() {
int currentUser = ActivityManager.getCurrentUser();
boolean trust = mTrustManager.isTrustUsuallyManaged(currentUser);
boolean fingerprint = mUpdateMonitor.isUnlockWithFingerprintPossible(currentUser);
boolean any = trust || fingerprint;
KeyguardUpdateMonitor.StrongAuthTracker strongAuthTracker =
mUpdateMonitor.getStrongAuthTracker();
int strongAuth = strongAuthTracker.getStrongAuthForUser(currentUser);

if (any && !strongAuthTracker.hasUserAuthenticatedSinceBoot()) {
return KeyguardSecurityView.PROMPT_REASON_RESTART;
} else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) != 0) {
return KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
} else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW) != 0) {
return KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
} else if (trust && (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) {
return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
} else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0) {
return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
}
return KeyguardSecurityView.PROMPT_REASON_NONE;
}

@Override
public void onSecondaryDisplayShowingChanged(int displayId) {
synchronized (KeyguardViewMediator.this) {
setShowingLocked(mShowing, displayId, false);
}
}
};

对于ViewMediatorCallback接口函数的作用,我们可以看到在KeyguardViewMediator的setupLocked()函数中,将ViewMediatorCallback作为一个参数传入了KeyguardDisplayManager类里面.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void setupLocked() {
mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);

mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
mShowKeyguardWakeLock.setReferenceCounted(false);

IntentFilter filter = new IntentFilter();
filter.addAction(DELAYED_KEYGUARD_ACTION);
filter.addAction(DELAYED_LOCK_PROFILE_ACTION);
filter.addAction(Intent.ACTION_SHUTDOWN);
mContext.registerReceiver(mBroadcastReceiver, filter);

mKeyguardDisplayManager = new KeyguardDisplayManager(mContext, mViewMediatorCallback);
...

那么再到KeyguardDisplayManager里面看看ViewMediatorCallback的使用,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public KeyguardDisplayManager(Context context, ViewMediatorCallback callback) {
mContext = context;
mCallback = callback;
mMediaRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);
}
....

protected void updateDisplays(boolean showing) {
Presentation originalPresentation = mPresentation;
...
// mPresentation is only updated when the display changes
if (mPresentation != originalPresentation) {
final int displayId = mPresentation != null
? mPresentation.getDisplay().getDisplayId() : INVALID_DISPLAY;
mCallback.onSecondaryDisplayShowingChanged(displayId);
}
}

从这里可以看到,KeyguardDisplayManager里面通过ViewMediatorCallback接口的onSecondaryDisplayShowingChanged,反过来通知KeyguardViewMediator做出相应的动作.
从之前的KeyguardViewMediator代码分析可知整个待机解/锁屏业务的调度器,负责调度锁屏界面的相关动作及查询解锁屏状态的一个服务.

至此,Keyguard模块的纯业务逻辑已经分析完毕.

SystemUI中Keyguard的UI显示模块代码

  1. SystemUI中的Keyguard模块中UI显示相关的代码有:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
    frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
    frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml
    frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
    frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_message_area_large.xml
    frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_message_area.xml
    frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml
    frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
    frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
    frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
    frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
    frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
    frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
    frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
    frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml

    keyguard_bouncer:是一个FrameLayout,include了keyguard_host_view文件.

    keyguard_emergency_carrier_area:锁屏上紧急拨号的UI处理,包含了显示当前运营商信息的CarrierText和一个接入紧急拨号界面的EmergencyButton.

    keyguard_host_view:This is the host view that generally contains two sub views: the widget view and the security view.
    这是Keyguard显示模块主要的布局文件,包含两个子布局:一个带小部件(时钟)的布局,还有一个是安全布局(指的是图案解锁/密码/PIN码解锁界面)
    功能由KeyguardHostView.java KeyguardSecurityContainer.java KeyguardSecurityViewFlipper.java实现
    具体代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    <com.android.keyguard.KeyguardHostView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:androidprv="http://schemas.android.com/apk/res-auto"
    android:id="@+id/keyguard_host_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:importantForAccessibility="yes"> <!-- Needed because TYPE_WINDOW_STATE_CHANGED is sent
    from this view when bouncer is shown -->

    <com.android.keyguard.KeyguardSecurityContainer
    android:id="@+id/keyguard_security_container"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    androidprv:layout_maxHeight="@dimen/keyguard_security_max_height"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:padding="0dp"
    android:layout_gravity="center">
    <com.android.keyguard.KeyguardSecurityViewFlipper
    android:id="@+id/view_flipper"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:paddingTop="@dimen/keyguard_security_view_top_margin"
    android:paddingStart="@dimen/keyguard_security_view_lateral_margin"
    android:paddingEnd="@dimen/keyguard_security_view_lateral_margin"
    android:gravity="center">
    </com.android.keyguard.KeyguardSecurityViewFlipper>
    </com.android.keyguard.KeyguardSecurityContainer>

    </com.android.keyguard.KeyguardHostView>

    keyguard_esim_area:This contains disable esim buttonas shared by sim_pin/sim_puk screens
    SIM卡的PIN码和PUK码解锁时,禁用esim卡的按钮,功能由KeyguardEsimArea.java来实现.

    keyguard_message_area_large:This contains emergency call button and carrier as shared by pin/pattern/password screens.
    PIN码/图案/密码解锁界面时显示输入密码错误,以及输入次数等消息,由KeyguardMessageArea.java来实现

    keyguard_message_area: 同上面的keyguard_message_area_large

    keyguard_num_pad_key:

    keyguard_password_view: 密码输入界面

    keyguard_pattern_view: 图案解锁界面

    keyguard_pin_view: PIN码解锁界面

    keyguard_presentation: This is a view that shows general status information in Keyguard.

    keyguard_sim_pin_view: SIM卡PIN码解锁界面

    keyguard_sim_puk_view: SIM卡PUK码解锁界面

    keyguard_status_area: 锁屏界面的日期和闹钟

    keyguard_status_view: This is a view that shows general status information in
    Keyguard.由KeyguardStatusView实现,主要显示Keyguard界面的时钟,是否在充电,以及用户信息,并包含了keyguard_status_area的布局.整体来说,用于显示Keyguard界面StatusBar以下,Notification以上的界面布局.

Keyguard里的一些属性配置

config_voice_capable
config_enable_emergency_call_while_sim_locked SIM卡锁的时候,是否能紧急拨号的开关
debug.systemui.latency_tracking true:且debug模式开启模块耗时跟踪 false:关闭模块耗时跟踪
velocity_tracker_impl 配置滑动速度跟踪是使用platform 还是 noisy

锁屏界面的布局

解锁界面的显示与隐藏

滑动解锁界面事件处理

为了理清楚滑动解锁的时候,滑动事件传递以及处理逻辑的流程,首先需要理清楚滑动解锁界面View的层级关系.
通过使用DDMS中的Hierarchy工具,查看滑动解锁界面布局,发现顶层布局是id为notification_panel,布局文件为res/layout/status_bar_expanded.xml 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<com.android.systemui.statusbar.phone.NotificationPanelView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/notification_panel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent" >

<include
layout="@layout/keyguard_status_view"
android:visibility="gone" /> //锁屏界面中时钟充电图标等显示

<com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
....
<include
layout="@layout/keyguard_status_bar"
android:visibility="invisible" />
.....
<include
layout="@layout/keyguard_bottom_area"
android:visibility="gone" /> //锁屏界面底部UI显示

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

从status_bar_expanded.xml的布局可以看到,正常情况下keyguard_status_view和keyguard_bottom_area以及keyguard_status_bar三个部分默认都是gone或者invisible,不显示,也就是说非Keyguard界面时,这两个显示Keyguard的头部信息和底部信息的布局是不显示.
在布局上,将Keyguard的头部和底部显示放到了通知面板NotificationPanelView中,则锁屏和非锁屏界面通知面板的滑动处理在NotificationPanelView中进行.
我们来看看 NotificationPanelView的继承关系:

1
2
3
4
5
6
public class NotificationPanelView extends PanelView implements
ExpandableView.OnHeightChangedListener,
View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener,
OnHeadsUpChangedListener, QS.HeightListener {
...

NotificationPanelView继承自PanelView: PanelView是一个抽象类,且其为抽象类PanelBar的内部类,PanelBar由PhoneStatusBarView实现.
实现了:
• ExpandableView.OnHeightChangedListener
• View.OnClickListener
• NotificationStackScrollLayout.OnOverscrollTopChangedListener
• KeyguardAffordanceHelper.Callback //Keyguard底部进入Phone和Camera的入口
• NotificationStackScrollLayout.OnEmptySpaceClickListener
• OnHeadsUpChangedListener
• QS.HeightListener

NotificationPanelView中与滑动事件有关的函数:
• onInterceptTouchEvent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mBlockTouches || mQs.isCustomizing()) {
return false;
}
initDownStates(event);
if (mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
mIsExpansionFromHeadsUp = true;
MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1);
MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1);
return true;
}

if (!isFullyCollapsed() && onQsIntercept(event)) {
return true;
}
return super.onInterceptTouchEvent(event);
}









MetricsLogger: frameworks/base/core/java/com/android/internal/logging/MetricsLogger.java
LockscreenGestureLogger.java

DozeLog.traceFling

解锁流程:

我们来看看实现解锁的方式有哪些:

• (1)当Keyguard被另外的窗口遮挡是,可以调用KeyguardService.java里的setOccluded方法,达到Hide Keyguard的目的,此时也可以实现解锁.
KeyguardViewMediator.java setOccluded() -> 收到 SET_OCCLUDED 消息-> handleSetOccluded() -> startKeyguardExitAnimation() 发送 START_KEYGUARD_EXIT_ANIM 消息

• (2)当用户手动按Power键灭屏或者灭屏时间到了自动灭屏,都可以调用KeyguardService.java里的onStartedGoingToSleep(int reason)方法来实现解锁
KeyguardViewMediator.java onStartedGoingToSleep() -> hideLocked() 发送HIDE 消息

• (3)对于APP侧可以调用KeyguardService.java里的setKeyguardEnabled方法来实现上锁和解锁,常用的情况是:锁屏时来电. KeyguardViewMediator.java setKeyguardEnabled() -> hideLocked() 发送HIDE 消息

• (4)KeyguardViewMediator.java doKeyguardLocked() -> hideLocked() 发送HIDE 消息

isGoingToNotificationShade mKeyguardGoingAwayRunnable 也是隐藏Keyguard的线程

KeyguardViewMediator.java 接到消息 HIDE 或者 START_KEYGUARD_EXIT_ANIM 则-> handleHide() -> handleStartKeyguardExitAnimation()
StatusBarKeyguardViewManager.java -> hide()
StatusBar.java -> hideKeyguard() -> updateIsKeyguard() -> hideKeyguardImpl() -> updateKeyguardState()
NotificationPanelView.java -> setBarState()

readyForKeyguardDone

解锁方式那么多,那么是如何实现滑动解锁的呢?

区别: onQsHeightChanged 执行了,解开锁

向上滑动–> 解锁
向下滑动–> 下拉通知栏

从事件分发角度来看滑动解锁事件的分发:
onInterceptTouchEvent dispatchTouchEvent onTouchEvent
PanelView 无
NotificationPanelView 无

Down事件开始
第一步: NotificationPanelView的 onInterceptTouchEvent
return ture,则交给自己的onTouchEvent方法处理
return false,则交给PanelView的onInterceptTouchEvent方法处理
此步:return false

第二步:从log来看,第一步return false,则交给PanelView的onInterceptTouchEvent方法处理
return ture,则交给自己的onTouchEvent方法处理
return false,则交给子类的onTouchEvent方法处理
此步:return false

第三步:交给NotificationPanelViewt 的 onTouchEvent处理

第四步:交给PanelView 的 onTouchEvent处理

第五步:交给StatusBarWindowView的onInterceptTouchEvent 处理

Move事件开始
第六步: 交给NotificationPanelView 的 onTouchEvent

第七步:交给PanelView 的 onTouchEvent

滑动解锁,滑不开可能的原因:
1) 滑动距离不够,此时需要修改滑动解锁距离的阈值,通过修改xml文件中unlock_falsing_threshold的值即可,Android默认是80dp.
或者修改PanelView.java里面的loadDimens函数的mUnlockFalsingThreshold值.
查看是否是由于滑动距离不够导致无法解锁的方法:
在PanelView.java的onTouchEvent函数中打印-h的值,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public boolean onTouchEvent(MotionEvent event) {
...
case MotionEvent.ACTION_MOVE:
log("onTouchEvent,ACTION_MOVE");
trackMovement(event);
float h = y - mInitialTouchY;
...
if (-h >= getFalsingThreshold()) {
mTouchAboveFalsingThreshold = true;
mUpwardsWhenTresholdReached = isDirectionUpwards(x, y);
}
Log.d(TAG+"eric", "-h="+(-h)+" getFalsingThreshold()="+getFalsingThreshold()+",mUpwardsWhenTresholdReached="+mUpwardsWhenTresholdReached);

打印上述log,假如当前mUnlockFalsingThreshold的值为80,我们收集解锁失败的log,如果从log中看到我们的-h的值都是小于80的话,可以通过将mUnlockFalsingThreshold的值改小的方式解决,但是如果,此时-h的值都大于等于mUnlockFalsingThreshold了,还是解锁失败呢?那就要看第二种情况了.

2)滑动距离满足条件,但是还是很大概率的解锁失败.
此时可以通过关闭xml中的config_lockscreenAntiFalsingClassifierEnabled值的方式来解决.具体看HumanInteractionClassifier.java中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private void updateConfiguration() {
boolean defaultValue = mContext.getResources().getBoolean(
R.bool.config_lockscreenAntiFalsingClassifierEnabled);

mEnableClassifier = 0 != Settings.Global.getInt(
mContext.getContentResolver(),
HIC_ENABLE, defaultValue ? 1 : 0);
}
...
public boolean isFalseTouch() {
if (mEnableClassifier) {
float evaluation = mHistoryEvaluator.getEvaluation();
boolean result = evaluation >= 5.0f;//这个5.0f的值具体是怎么定义的?
if (FalsingLog.ENABLED) {
FalsingLog.i("isFalseTouch", new StringBuilder()
.append("eval=").append(evaluation).append(" result=")
.append(result ? 1 : 0).toString());
}
return result;
}
return false;
}

通过设置config_lockscreenAntiFalsingClassifierEnabled为false或者,我们可以通过将isFalseTouch函数中的boolean result = evaluation >= 5.0f;中的5.0f调高到6.0f.

在KeguardDisplayManager的hide()函数里面打印调用堆栈如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
01-03 19:13:50.971  9378  9378 W eric    : java.lang.RuntimeException: here
01-03 19:13:50.971 9378 9378 W eric : at com.android.keyguard.KeyguardDisplayManager.hide(KeyguardDisplayManager.java:64)
01-03 19:13:50.971 9378 9378 W eric : at com.android.systemui.keyguard.KeyguardViewMediator$2.keyguardGone(KeyguardViewMediator.java:601)
01-03 19:13:50.971 9378 9378 W eric : at com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.hide(StatusBarKeyguardViewManager.java:440)
01-03 19:13:50.971 9378 9378 W eric : at com.android.systemui.keyguard.KeyguardViewMediator.handleStartKeyguardExitAnimation(KeyguardViewMediator.java:1847)
01-03 19:13:50.971 9378 9378 W eric : at com.android.systemui.keyguard.KeyguardViewMediator.-wrap16(Unknown Source:0)
01-03 19:13:50.971 9378 9378 W eric : at com.android.systemui.keyguard.KeyguardViewMediator$4.handleMessage(KeyguardViewMediator.java:1561)
01-03 19:13:50.971 9378 9378 W eric : at android.os.Handler.dispatchMessage(Handler.java:106)
01-03 19:13:50.971 9378 9378 W eric : at android.os.Looper.loop(Looper.java:164)
01-03 19:13:50.971 9378 9378 W eric : at android.app.ActivityThread.main(ActivityThread.java:6501)
01-03 19:13:50.971 9378 9378 W eric : at java.lang.reflect.Method.invoke(Native Method)
01-03 19:13:50.971 9378 9378 W eric : at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
01-03 19:13:50.971 9378 9378 W eric : at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
01-03 19:13:50.972 9378 9378 V KeyguardDisplayManager: hide

从上面的堆栈调用,可以看到hide()方法,是在收到START_KEYGUARD_EXIT_ANIM这个Message之后触发,START_KEYGUARD_EXIT_ANIM消息是在startKeyguardExitAnimation函数里面发出来的,继续查看startKeyguardExitAnimation函数的调用堆栈.

1
2
3
4
5
6
01-03 21:39:58.318 10956 11005 W eric    : Called: 
01-03 21:39:58.318 10956 11005 W eric : java.lang.RuntimeException: startKeyguardExitAnimation
01-03 21:39:58.318 10956 11005 W eric : at com.android.systemui.keyguard.KeyguardViewMediator.startKeyguardExitAnimation(KeyguardViewMediator.java:2020)
01-03 21:39:58.318 10956 11005 W eric : at com.android.systemui.keyguard.KeyguardService$1.startKeyguardExitAnimation(KeyguardService.java:222)
01-03 21:39:58.318 10956 11005 W eric : at com.android.internal.policy.IKeyguardService$Stub.onTransact(IKeyguardService.java:203)
01-03 21:39:58.318 10956 11005 W eric : at android.os.Binder.execTransact(Binder.java:697)

startKeyguardExitAnimation 在 handleStartTransitionForKeyguardLw

在前面的KeyguardManager代码分析里面讲到了KeyguardLock内部类,虽然已经过时,被弃用,接着往下看如何实现显示和隐藏锁屏界面的,我们从代码看到,会调用到WindowManagerService.java里面的disableKeyguard和reenableKeyguard方法.
继续跟到WindowManagerService.java里面看disableKeyguard的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Override
public void disableKeyguard(IBinder token, String tag) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
}
// If this isn't coming from the system then don't allow disabling the lockscreen
// to bypass security.
if (Binder.getCallingUid() != SYSTEM_UID && isKeyguardSecure()) {
Log.d(TAG_WM, "current mode is SecurityMode, ignore disableKeyguard");
return;
}

// If this isn't coming from the current profiles, ignore it.
if (!isCurrentProfileLocked(UserHandle.getCallingUserId())) {
Log.d(TAG_WM, "non-current profiles, ignore disableKeyguard");
return;
}

if (token == null) {
throw new IllegalArgumentException("token == null");
}

mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(
KeyguardDisableHandler.KEYGUARD_DISABLE, new Pair<IBinder, String>(token, tag)));
}

可以看到通过KeyguardDisableHandler,发送消息KEYGUARD_DISABLE,继续查阅KeyguardDisableHandler代码看到开始匹配密码.

1
2
3
4
5
6
7
8
9
10
11
12
@SuppressWarnings("unchecked")
@Override
public void handleMessage(Message msg) {
if (mKeyguardTokenWatcher == null) {
mKeyguardTokenWatcher = new KeyguardTokenWatcher(this);
}

switch (msg.what) {
case KEYGUARD_DISABLE:
final Pair<IBinder, String> pair = (Pair<IBinder, String>)msg.obj;
mKeyguardTokenWatcher.acquire(pair.first, pair.second);
break;

滑动实现解锁

滑动解锁涉及到的代码有:
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
PanelView是一个抽象类,继承自FrameLayout
NotificationPanelView 继承自 PanelView

KeyguardAffordanceHelper.java 锁屏界面滑动Phone和Camera图标,分别启动Phone和Camera的事件处理
A touch handler of the keyguard which is responsible for launching phone and camera affordances.
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java

PanelView.java中相关函数解析

onTrackingStarted

trackMovement()

onTouchEvent函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mInstantExpanding || mTouchDisabled
|| (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
return false;
}

// If dragging should not expand the notifications shade, then return false.
if (!mNotificationsDragEnabled) {
if (mTracking) {
// Turn off tracking if it's on or the shade can get stuck in the down position.
onTrackingStopped(true /* expand */);
}
return false;
}

// On expanding, single mouse click expands the panel instead of dragging.
if (isFullyCollapsed() && event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (event.getAction() == MotionEvent.ACTION_UP) {
expand(true);
}
return true;
}

/*
* We capture touch events here and update the expand height here in case according to
* the users fingers. This also handles multi-touch.
*
* If the user just clicks shortly, we show a quick peek of the shade.
*
* Flinging is also enabled in order to open or close the shade.
*/

int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
mTrackingPointer = event.getPointerId(pointerIndex);
}
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);

if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mGestureWaitForTouchSlop = isFullyCollapsed() || hasConflictingGestures();
mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
}

switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
mJustPeeked = false;
mMinExpandHeight = 0.0f;
mPanelClosedOnDown = isFullyCollapsed();
mHasLayoutedSinceDown = false;
mUpdateFlingOnLayout = false;
mMotionAborted = false;
mPeekTouching = mPanelClosedOnDown;
mDownTime = SystemClock.uptimeMillis();
mTouchAboveFalsingThreshold = false;
mCollapsedAndHeadsUpOnDown = isFullyCollapsed()
&& mHeadsUpManager.hasPinnedHeadsUp();
if (mVelocityTracker == null) {
initVelocityTracker();
}
trackMovement(event);
if (!mGestureWaitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning)
|| mPeekAnimator != null) {
mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning)
|| mPeekAnimator != null;
cancelHeightAnimator();
cancelPeek();
onTrackingStarted();
}
if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()) {
startOpening();
}
break;

case MotionEvent.ACTION_POINTER_UP:
final int upPointer = event.getPointerId(event.getActionIndex());
if (mTrackingPointer == upPointer) {
// gesture is ongoing, find a new pointer to track
final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
final float newY = event.getY(newIndex);
final float newX = event.getX(newIndex);
mTrackingPointer = event.getPointerId(newIndex);
startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
mMotionAborted = true;
endMotionEvent(event, x, y, true /* forceCancel */);
return false;
}
break;
case MotionEvent.ACTION_MOVE:
trackMovement(event);
float h = y - mInitialTouchY;

// If the panel was collapsed when touching, we only need to check for the
// y-component of the gesture, as we have no conflicting horizontal gesture.
if (Math.abs(h) > mTouchSlop
&& (Math.abs(h) > Math.abs(x - mInitialTouchX)
|| mIgnoreXTouchSlop)) {
mTouchSlopExceeded = true;
if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
h = 0;
}
cancelHeightAnimator();
onTrackingStarted();
}
}
float newHeight = Math.max(0, h + mInitialOffsetOnTouch);
if (newHeight > mPeekHeight) {
if (mPeekAnimator != null) {
mPeekAnimator.cancel();
}
mJustPeeked = false;
} else if (mPeekAnimator == null && mJustPeeked) {
// The initial peek has finished, but we haven't dragged as far yet, lets
// speed it up by starting at the peek height.
mInitialOffsetOnTouch = mExpandedHeight;
mInitialTouchY = y;
mMinExpandHeight = mExpandedHeight;
mJustPeeked = false;
}
newHeight = Math.max(newHeight, mMinExpandHeight);
if (-h >= getFalsingThreshold()) {
mTouchAboveFalsingThreshold = true;
mUpwardsWhenTresholdReached = isDirectionUpwards(x, y);
}
if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking) &&
!isTrackingBlocked()) {
setExpandedHeightInternal(newHeight);
}
break;

case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
trackMovement(event);
endMotionEvent(event, x, y, false /* forceCancel */);
break;
}
return !mGestureWaitForTouchSlop || mTracking;
}

endMotionEvent()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
mTrackingPointer = -1;
if ((mTracking && mTouchSlopExceeded)
|| Math.abs(x - mInitialTouchX) > mTouchSlop
|| Math.abs(y - mInitialTouchY) > mTouchSlop
|| event.getActionMasked() == MotionEvent.ACTION_CANCEL
|| forceCancel) {
float vel = 0f;
float vectorVel = 0f;
if (mVelocityTracker != null) {
mVelocityTracker.computeCurrentVelocity(1000);//设置计算速度的单位是1000ms,即1s
vel = mVelocityTracker.getYVelocity(); //计算竖直方向的速度
vectorVel = (float) Math.hypot(
mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity()); //计算加速度
}
boolean expand = flingExpands(vel, vectorVel, x, y)
|| event.getActionMasked() == MotionEvent.ACTION_CANCEL
|| forceCancel;
DozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
mStatusBar.isFalsingThresholdNeeded(),
mStatusBar.isWakeUpComingFromTouch());
// Log collapse gesture if on lock screen. // 如果未下拉通知面板,且处于锁屏界面,记录log
if (!expand && mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
float displayDensity = mStatusBar.getDisplayDensity();
int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity);
int velocityDp = (int) Math.abs(vel / displayDensity);
mLockscreenGestureLogger.write(
MetricsEvent.ACTION_LS_UNLOCK,
heightDp, velocityDp);
}
fling(vel, expand, isFalseTouch(x, y));
onTrackingStopped(expand);
mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
if (mUpdateFlingOnLayout) {
mUpdateFlingVelocity = vel;
}
} else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking) {
long timePassed = SystemClock.uptimeMillis() - mDownTime;
if (timePassed < ViewConfiguration.getLongPressTimeout()) {
// Lets show the user that he can actually expand the panel
runPeekAnimation(PEEK_ANIMATION_DURATION, getPeekHeight(), true /* collapseWhenFinished */);
} else {
// We need to collapse the panel since we peeked to the small height.
postOnAnimation(mPostCollapseRunnable);
}
} else {
boolean expands = onEmptySpaceClick(mInitialTouchX);
onTrackingStopped(expands);
}

if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
mPeekTouching = false;
}

• 通过对mVelocityTracker变量,VelocityTrackerInterface初始化时velocity_tracker_impl值为platform,确定使用的是PlatformVelocityTracker来计算滑动速度.

计算速度相关

速度跟踪接口
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/VelocityTrackerInterface.java

1
2
3
4
5
6
7
8
9
10
11
/**
* An interface for a velocity tracker to delegate. To be implemented by different velocity tracking
* algorithms.
*/
public interface VelocityTrackerInterface {
public void addMovement(MotionEvent event);
public void computeCurrentVelocity(int units);
public float getXVelocity();
public float getYVelocity();
public void recycle();
}

速度跟踪工厂类,速度跟踪有两个类
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/VelocityTrackerFactory.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.android.systemui.statusbar.phone;

import android.content.Context;

import com.android.systemui.R;

/**
* A class to generate {@link VelocityTrackerInterface}, depending on the configuration.
*/
public class VelocityTrackerFactory {

public static final String PLATFORM_IMPL = "platform";
public static final String NOISY_IMPL = "noisy";

public static VelocityTrackerInterface obtain(Context ctx) {
String tracker = ctx.getResources().getString(R.string.velocity_tracker_impl);
switch (tracker) {
case NOISY_IMPL:
return NoisyVelocityTracker.obtain();
case PLATFORM_IMPL:
return PlatformVelocityTracker.obtain();
default:
throw new IllegalStateException("Invalid tracker: " + tracker);
}
}
}

解锁认证

在接下来要讲的几种解锁方式:图案解锁、PIN码解锁、密码解锁、指纹解锁、面部解锁等等。均存在一个问题,那就是解锁校验问题.

图案解锁

九宫格解锁的实现

PIN码解锁

密码解锁

密码自动校验

在密码解锁界面,用户输入4位以及4位以上密码时,开始自动校验,如果输入正确,则直接解锁,否则让用户继续输入.

指纹解锁

面部解锁

参考文献

Android 6.0 ConfirmCredential

Android4.0 Keyguard解锁屏机制
Android 7.0 锁屏解锁之向上滑动显示解锁界面分析

Android Framework——之Keyguard 简单分析
Android 7.0 Keyguard流程分析

Android 7.0 Keyguard流程分析 GateKeeper模块的解析
Android窗口系统机制之KEYGUARD机制 Keyguard模块的框架
AndroidL 开机展示Keyguard锁屏机制初探
Android4.0 Keyguard解锁屏机制 带流程图