gatekeeper_gatekeeper理论 - CSDN
精华内容
参与话题
  • gatekeeper学习概述

    2017-10-12 16:59:00
    1、概述  该产品部署在网络隔离装置两端,以代理程序的身份,完成两侧设备连接维护,数据转发的功能。场景简化如图所示:  软件核心是一个基于Netty的网络应用程序,考虑到系统的可维可测性,集成了web化的...

    1、概述

      该产品部署在网络隔离装置两端,以代理程序的身份,完成两侧设备连接维护,数据转发的功能。场景简化如图所示:

      软件核心是一个基于Netty的网络应用程序,考虑到系统的可维可测性,集成了web化的配置、监控和调试功能。

    2、启动类是GateKeeperWebMain

    public static void main(String[] args) throws Exception {
            startJetty(PORT);
        }

    private static void startJetty(int port) throws Exception { LOGGER.debug("Starting server at port {}", port); Server server = new Server(port); Configuration.ClassList classlist = Configuration.ClassList .setServerDefault(server); classlist.clear(); classlist.add("org.eclipse.jetty.webapp.WebXmlConfiguration"); classlist.add("org.eclipse.jetty.annotations.AnnotationConfiguration"); server.setHandler(getServletContextHandler()); addRuntimeShutdownHook(server); server.start(); LOGGER.info("Server started at port {}", port); server.join(); }

    3. web容器是jetty在web服务启动过程中,通过spring初始化后台相关bean,并在初始化完成后启动后台监听服务。

    4. 发布维护过程中的一些收获

      1、功能的可用性

        产品部署在全国各地多个地方,生产环境掉电重启,网络不稳定等多种异常情况都会出现。

        - 软件需要保证掉电重启自动恢复等。

        - 通过代理程序的心跳机制及重连机制,保证网络正常或网络断开恢复后能正常提供服务。

      2、服务的可靠性

        - 第三方服务端处理速度不一定快,如果是同步阻塞的话,就会加大系统自身负担,被外部拖累。

        - 设备不可达等情况,应有快速失败机制。(一旦出现一定的超时,如果有多次重试的机制,可能会使得网络负担更大,造成雪崩效应。)

      3、系统的维护性

        - 提供版本一键安装升级工具,操作简单快速。

        - 提供一键日志采集功能。获取服务器核心的操作系统日志、业务日志,便于有问题时采集分析。

        - 现场人员可以通过web监控调试页面,查看当前系统状态及网络情况。便于简单问题的现场自行处理。

     

    转载于:https://www.cnblogs.com/eaglediao/p/7656979.html

    展开全文
  • Android 7.0 Keyguard流程分析

    万次阅读 2017-01-16 10:25:17
    在android 6.0 上Keyguard作为了SystemUI的一个库文件被引用,所以编译的时候不会出现Keyguard.apk这个文件,Keyguard也伴随着SystemUI的启动而启动,其中最重要的一个文件就是KeyguardViewMediator,这个文件负责...

    在android 6.0 上Keyguard作为了SystemUI的一个库文件被引用,所以编译的时候不会出现Keyguard.apk这个文件,Keyguard也伴随着SystemUI的启动而启动,其中最重要的一个文件就是KeyguardViewMediator,这个文件负责SystemUI与Keyguard的交互,我们来看一下这个文件的启动。


    一.KeyguardViewMediator的启动

    KeyguardViewMediator.java作为SystemUIApplication.java中SERVICES[]中的一个元素,我们用一个流程图来说明这个文件的启动过程,这个数组中的其它元素启动顺序与这个类似,只是功能不同而已。


    从上面的流程图中可以看到KeyguardViewMediator.java的启动分为两部分。

    1.SystemUIApplication.java的onstart()

    SystemUIApplication.java的onstart()调用,这个调用会初始化锁屏相关的工具类,但是需要注意这个步骤中没没有初始化任何锁屏界面相关的东西,只是进行一些Intent的注册以及一些必要的初始化工作。其中KeyguardDisplayManager.java会作为中间类去控制keyguard的show与hide,KeyguardUpdateMonitor.java中注册了绝大多数的广播,负责处理界面的一些刷新流程处理,还有一个需要特别注意一下就是FingerManager.java,这个类是android 6.0 新添加的,负责处理一些指纹相关的东西。

    2.SystemUIApplication的onBootCompleted()

    这个函数会去发送锁屏加载完成的广播,USER_PRESENT_INTENT,但是需要注意这个函数的调用时机。这个函数的调用是received到系统发送的ACTION_BOOT_COMPLETED,根据实际工作中的经验,ACTION_BOOT_COMPLETED的发送比较偏后,在系统绝大多数的界面处理完成以后才会发送,在看到锁屏之后,也就是说,如果我们监听到USER_PRESENT_INTENT,那么锁屏一般是肯定加载完成了的。

    备注:锁屏界面的初始化是在SystemBar.java的onStart()方法中处理的,这个方法比较复杂,我们在SystemUI的分析中在细说。初始化是在onStart(),但是锁屏的加载需要经过一些特殊条件的触发与判断,由于开机第一次锁屏的加载是需要经过ActivityManagerService.java中界面的判断与WindowMnaagerService.java中界面绘制的处理才决定是否显示,牵扯的比较负责,暂时不分析,后续专门分析这部分。

    二.SystemUI的启动

    Keyguard是SystemUI的一个库文件,所以分析Keyguard我们需要先看一下SystemUI的启动。流程图简单分析如下:

    PhoneStatusBar.java中的createAndAddWindows()会初始化并且加载SystemUI的绝大多数界面,包括statusbar与navigationbar,以及初始化QSHostView。这部分代码需要仔细阅读代码,注意细节,用到这些细节的时候我们再仔细分析。

    至此,keyguard以及systemUI启动初始化加载完毕,SystemService.java中的startSystemUI()函数执行完毕。

    三.POWER键灭屏时Keyguard的加载

    我们知道用power键灭屏的时候会加载keyguard,这样可以保证亮屏的时候终端用户可以第一时间看到锁屏,保证良好的用户体验,我们用这个场景来分析锁屏的加载过程。

    1.PowerManagerService亮屏流程


    上述流程图从power按键的分发到PowerManagerService.java的处理,再到keyguard相关的代码处理,这条线路传到KeyguardViewMediator.java中以后就要开始锁屏界面的绘制了。稍后专门对锁屏界面的绘制进行说明。

    备注:
    亮屏过程中涉及到两条关键的线程,分别是PowerManager Notifier Thread 与DisplayManager Thread。
    1.PowerManager Notifier Thread:power按键事件传到PowerManagerService.java中以后会分为两条线路进行事件处理,一条就是我们上面流程图上标注的,这条Thread(Notifier.java)主要负责亮屏或者灭屏广播的发送,以及锁屏界面的加载。
    2.DisplayManager Thread:这条Thread会传送到DisplayManagerService.java中去,会负责进行屏幕状态的设置以及屏幕的熄灭与点亮(真正的屏幕操作者),此外跟屏幕相关的一些可见性操作都是这条Thread(DisplayPowerController.java)进行处理,例如屏幕亮度的变化、屏幕亮度的设置、屏幕的显示时机等等,非常重要,有时间可以研究研究。

    2.KeyguardManager.java的说明

    APP侧开发锁屏应用,调用的接口是KeyguardManager.java或者内部类KeyguardManager.KeyguardLock,用这个manager来获取或者设置锁屏的一些状态,这样就可以控制锁屏的显示。需要添加对于的权限到AndroidMainfest.xml文件中,android.permission.DISABLE_KEYGUARD。
    对这个类的详细介绍以及用法介绍请参考如下网址:http://blog.csdn.net/hudashi/article/details/7073373
    我们简单的来分析一下这个调用过程,也用流程图来说明一下

    从这个流程图不难发现,不管APP侧怎么调用KeyguardManager.java的接口,最终的实现都是在KeyguardViewMediator.java中。KeyguardService.java是Binder的服务器端,客户端是KeyguardServiceDelegate.java,KeyguardServiceDelegate.java这个文件在调用过程中只起到过渡的左右,在这里并没有深入研究。KeyguardService.java会将所有的调用传递到KeyguardViewMediator.java中。
    备注:
    Android 6.0 的Keyguard是在SystemUI中控制的,SystemUI是一个系统级的APK,要操作锁屏还是需要使用上述这些东西。所以如果需要改变锁屏的一些流程,我们可以在合适的位置调用KeyguardManager.java中接口,或者添加一些客制化的东西,直接在KeyguardViewMediator.java中进行操作,都是可以实现的。

    3.锁屏的加载绘制过程

    KeyguardViewMediator.java的onFinishedGoingToSleep()是加载锁屏的入口,接下来对这个过程进行分析。
    这个过程是开机以后正常使用过程中,用power键灭屏的时候加载锁屏的流程,并且存在锁屏方式。还有其它方式的灭屏跟这个略有区别,例如亮屏超时的熄屏,但是大同小异,根本是一样的,有时间再仔细分析。


    这样经过上述的流程以后,keyguard就在锁屏界面显示了,需要注意的是Android 6.0 上看到的锁屏实际上是一层通知panelview,也就是流程图上的17,如果去掉这个函数,那么界面上是不会出现通知界面的,但是锁屏也是不能用的,因为在这个函数执行之前已经通知了系统要加载锁屏,所以结果就是看上去去掉了notificationpanelview,但是实际上系统中的状态还是存在这个界面的,所以如果要从keyguard中用代码去掉这个界面,还需要其它的一些修改,需要保持界面状态的一致。

    需要注意这个过程中的26:updateState(),这个函数会去更新keyguard以及systemUI的一些标志状态,保持系统状态的统一性,27会去设置更新后的这些状态值。

    备注:

    在这个过程中,使用了几次Handle,Handle的使用使得从log上看流程跟诡异,这是由于handle消息处理机制导致的,这个流程处理下来加了大量的状态判断,所以并不会对结果造成什么影响,只是分析问题查看log的时候可能需要注意一下这个情况。

    四.锁屏方式的保存

    系统在加载keyguard的时候需要先判断当前系统中是否有锁屏方式,那么锁屏方式又是如何保存的了?需要分析一下:

    研究锁屏方式的保存可以从Settings 里的设置锁屏方式作为入口,ChooseLockGeneric.java。在这个文件中我们找到了锁屏的工具了,LockPathernUtils.java,锁屏方式的获取以及锁屏方式的保存都依靠这个类来进行,所以如果需要修改默认锁屏方式或者初始化锁屏方式有什么特殊的需求,可以直接改LockPathernUtils.java中对应的默认方式,本地实验过,都是可行的,副作用最小。

    锁屏方式的保存与检测最后都会调用到framework层去,这部分比较复杂,后续会有专门的部分来说明framework中的锁屏方式。

    五.锁屏界面解锁分析

    1.onInterceptTouchEvent和onTouchEvent调用时序

    解锁动作是由手势触发的,在研究解锁流程之前需要明白onInterceptTouchEvent与onTouchEvent的区别与使用。

    onInterceptTouchEvent()是ViewGroup的一个方法,目的是在系统向该ViewGroup及其各个childView触发onTouchEvent()之前对相关事件进行一次拦截,Android这么设计的想法也很好理解,由于ViewGroup会包含若干childView,因此需要能够统一监控各种touch事件的机会,因此纯粹的不能包含子view的控件是没有这个方法的,如LinearLayout就有,TextView就没有。
    onInterceptTouchEvent()使用也很简单,如果在ViewGroup里覆写了该方法,那么就可以对各种touch事件加以拦截。但是如何拦截,是否所有的touch事件都需要拦截则是比较复杂的,touch事件在onInterceptTouchEvent()和onTouchEvent以及各个childView间的传递机制完全取决于onInterceptTouchEvent()和onTouchEvent()的返回值。并且,针对down事件处理的返回值直接影响到后续move和up事件的接收和传递。
    关于返回值的问题,基本规则很清楚,如果return true,那么表示该方法消费了此次事件,如果return false,那么表示该方法并未处理完全,该事件仍然需要以某种方式传递下去继续等待处理。

    由于onInterceptTouchEvent()的机制比较复杂,总结一下,基本的规则是:
    1.down事件首先会传递到onInterceptTouchEvent()方法
    2.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。
    3.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。
    4.如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。
    5.如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。

    下面用一个简单的实验说明上述复杂的规则。视图自底向上共3层,其中LayoutView1和LayoutView2就是LinearLayout,MyTextView就是TextView:

    通常外围的layoutview1,layoutview2,只是布局的容器不需要响应触屏的点击事件,仅仅Mytextview需要相应点击。但这只是一般情况,一些特殊的布局可能外围容器也要响应,甚至不让里面的mytextview去响应。更有特殊的情况是,动态更换响应对象。
    那么首先看一下默认的触屏事件的在两个函数之间的传递流程。如下图:


    如果仅仅想让MyTextView来响应触屏事件,让MyTextView的OnTouchEvent返回true,那么事件流就变成如下图,可以看到layoutview1,layoutview2已经不能进入OnTouchEvent:


    另外一种情况,就是外围容器想独自处理触屏事件,那么就应该在相应的onInterceptTouchEvent函数中返回true,表示要截获触屏事件,比如layoutview1作截获处理,处理流变成如下图:


    以此类推,我们可以得到各种具体的情况,整个layout的view类层次中都有机会截获,而且能看出来外围的容器view具有优先截获权。
    当我们去做一些相对来讲具有更复杂的触屏交互效果的应用时候,经常需要动态变更touch event的处理对象,比如launcher待机桌面和主菜单(见下图),从滑动屏幕开始到停止滑动过程当中,只有外围的容器view才可以处理touch event,否则就会误点击上面的应用图标或者widget.反之在静止不动的状态下则需要能够响应图标(子view)的touch事件

    2.解锁流程

    (1)notification界面的滑动

    Android L上, 锁屏分为两个界面, 一个是可以显示notification的界面(称为Notification Keyguard), 另一个是在Notification Keyguard界面向上滑之后出现输入密码的界面(称为bouncer). 只有在设置为安全锁(非滑动锁)的情况下, 才会显示bouncer界面.
    在Notification Keyguard界面上滑动时, 它有去检查滑动的Y-距离长度, 只有大于一定的阈值才会触发解锁.
    这个阈值是在如下文件中定义的, 它是用下面的值乘以一个因子(1.5)之后得到这个Y-距离.

    1.alps/frameworks/base/packages/SystemUI/res/values/Dimens.xml
    2.<dimen name="unlock_falsing_threshold">80dp</dimen>
    对于在Notification Keyguard界面向上滑动没有解锁或者没有显示出bouncer界面的情况, 可如下加log验证是不是因为滑动距离小于阈值引起.
    添加如下log后, 在问题时间点搜索log "onTrackingStarted begin" 和 "onTrackingStopped begin", 这两句log是成对出现的, 在手指按下在屏幕上开始滑时会打印"onTrackingStarted begin", 松开离开屏幕时会打印"onTrackingStopped begin", 搜索到这对log后, 寻找离"onTrackingStopped begin"最近的关于"-h="的log, "-h"表示的就是滑动的高度, 检查下打印的这个高度和阈值的比较. 如果在正常情况下, 向上滑动解锁体验不是很好的原因是由于这个高度小于阈值所致, 可适当修改将这个阈值改小.

    PhoneStatusBar.java
    public void onTrackingStarted(){
        Log.d(TAG, "onTrackingStarted begin"); //添加这行
     ...
    }
    
    public void onTrackingStopped(){
        Log.d(TAG, "onTrackingStopped begin"); //添加这行
     ...
    }
    
    
    PanelView.java
    public boolean onTouchEvent(MotionEvent event){
        ...
     case MotionEvent.ACTION_MOVE:
          ...
       Log.d(TAG, "-h="+(-h)+" getFalsingThreshold()="+getFalsingThreshold());         //添加这行
       if(-h >= getFalsingThreshold()){
            mTouchAboveFalsingThreshold = true;
       }
       ...
     ...
    }

    onTrackingStarted()的调用流程如下:

    09-20 09:47:15.896 W/yangjie ( 1276): java.lang.RuntimeException: here
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at com.android.systemui.statusbar.phone.PanelView.onTrackingStarted(PanelView.java:438)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at com.android.systemui.statusbar.phone.NotificationPanelView.onTrackingStarted(NotificationPanelView.java:1868)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at com.android.systemui.statusbar.phone.PanelView.onTouchEvent(PanelView.java:311)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at com.android.systemui.statusbar.phone.NotificationPanelView.onTouchEvent(NotificationPanelView.java:774)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.View.dispatchTouchEvent(View.java:9415)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2660)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2304)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2666)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2318)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2666)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2318)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at com.android.systemui.statusbar.phone.StatusBarWindowView.dispatchTouchEvent(StatusBarWindowView.java:208)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.View.dispatchPointerEvent(View.java:9646)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4738)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4596)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4101)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4154)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4120)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4266)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4128)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4323)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4101)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4154)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4120)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4128)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4101)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6555)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6529)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6472)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6727)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:176)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:6686)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:6753)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:894)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.Choreographer.doCallbacks(Choreographer.java:696)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.Choreographer.doFrame(Choreographer.java:625)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:880)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.os.Handler.handleCallback(Handler.java:815)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.os.Handler.dispatchMessage(Handler.java:104)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.os.Looper.loop(Looper.java:207)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at android.app.ActivityThread.main(ActivityThread.java:5692)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at java.lang.reflect.Method.invoke(Native Method)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905)
    
    09-20 09:47:15.896 W/yangjie ( 1276): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:766)

    onTrackingStopped()的调用流程如下:

    09-20 09:47:16.014 W/yangjie ( 1276): java.lang.RuntimeException: here
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at com.android.systemui.statusbar.phone.PanelView.onTrackingStopped(PanelView.java:426)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at com.android.systemui.statusbar.phone.NotificationPanelView.onTrackingStopped(NotificationPanelView.java:1881)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at com.android.systemui.statusbar.phone.PanelView.endMotionEvent(PanelView.java:396)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at com.android.systemui.statusbar.phone.PanelView.onTouchEvent(PanelView.java:335)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at com.android.systemui.statusbar.phone.NotificationPanelView.onTouchEvent(NotificationPanelView.java:774)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.View.dispatchTouchEvent(View.java:9415)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2660)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2304)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2666)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2318)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2666)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2318)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at com.android.systemui.statusbar.phone.StatusBarWindowView.dispatchTouchEvent(StatusBarWindowView.java:208)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.View.dispatchPointerEvent(View.java:9646)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4738)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4596)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4101)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4154)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4120)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4266)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4128)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4323)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4101)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4154)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4120)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4128)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4101)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6555)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6529)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6472)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6727)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.os.MessageQueue.nativePollOnce(Native Method)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.os.MessageQueue.next(MessageQueue.java:328)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.os.Looper.loop(Looper.java:164)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at android.app.ActivityThread.main(ActivityThread.java:5692)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at java.lang.reflect.Method.invoke(Native Method)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905)
    
    09-20 09:47:16.014 W/yangjie ( 1276): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:766)
    
    09-20 09:47:16.015 D/StatusBarKeyguardViewManager( 1276): dismiss(authenticated = false) is called. mScreenOn = true
    
    09-20 09:47:16.015 D/KeyguardBouncer( 1276): show(resetSecuritySelection = false
    
    09-20 09:47:16.016 D/KeyguardSecurityView( 1276): showNextSecurityScreenOrFinish(false)
    
    09-20 09:47:16.016 D/KeyguardSecurityView( 1276): showNext.. mCurrentSecuritySelection = Pattern
    
    09-20 09:47:16.016 D/KeyguardViewMediator( 1276): updateNavbarStatus() is called.
    
    09-20 09:47:16.016 D/KeyguardSecurityView( 1276): showNextSecurityScreenOrFinish() - return finish = false
    
    09-20 09:47:16.016 D/KeyguardBouncer( 1276): show() - try to dismiss "Bouncer" directly.

    从上面的调用栈可以看到,调用从PanelView.java的onTouchEvent()开始做的区分,所以如果我们有什么问题是关于触摸后的一些情况的,可以在这个文件的这个函数中打log以及一些调用栈来确定问题。
    从上面的onTrackingStopped()的最后log可以看到,这个函数调用结束以后会调用keyguard的show()来展示具体的锁屏界面。

    (2)锁屏界面的解锁

    对应不同的锁屏界面有不同的方式去处理,包括KeyguardPatternView.java、KeyguardPasswordView.java、KeyguardPINView.java等等方式,都有各自的处理方式,但是如果解锁输入正确,那么都会去调用KeyguardBouncer.java中的hide()与removeView()函数,我们在这两个方法中添加对应的异常调用栈来定位调用过程。
    解锁失败等情况下下面的framework层的分析,根据这个过程添加log可以定位问题原因,具体的调用过程就不再分析了。

    (3)根据异常栈来定位调用顺序

    具体这两个方法会在什么时候调用,可以使用打印异常调用栈的方式来确定这个调用过程:

    六.framework层Keyguard 的保存与读取

    1.背景资料

    (1)Android M 之前锁屏密码的存储

    在 Android M 之前,锁屏密码的存储格式很简单,其使用了 64 位随机数作为 salt 值,此 salt 值被存储在 SQLite 数据库 /data/system/locksettings.db 中。密码在存储的时候,会将输入的密码加上此随机数组成新的字符串。然后对新的字符串分别进行 SHA-1 和 MD5 加密,将加密后的密文通过 MD5 + SHA-1 的方式进行字符串拼接,组成新的密文存储在 /data/system/password.key 中,共有 72 位。其加密后的形式如下:
    /data/system # cat password.keyB40C2F6FE4E89F3386D4E689B135304410D64951914FB35770FDAC58B694177B29297A80

    而密码的详细信息,存储在 /data/system/device_policies.xml 中,内容类似如下:

    /data/system # cat device_policies.xml
    


    其中主要用到的两个字段 quality 是密码的类型,简单密码和复杂密码的值不同,length 是密码的长度,其他字段存储密码中各种字符的数量。

    (2)Android M 中锁屏密码的存储

    在 Android M 中,锁屏密码的存储格式发生了变化,其默认的存储格式在/system/gatekeeper/include/gatekeeper/password_handle.h 中描述如下:
    typedef uint64_t secure_id_t;
    typedef uint64_t salt_t;
    /** 
    * structure for easy serialization 
    * and deserialization of password handles. 
    */
    static const uint8_t HANDLE_VERSION = 2;
    struct __attribute__ ((__packed__)) 
    password_handle_t {    
    // fields included in signature    
    uint8_t version;    
    secure_id_t user_id;    
    uint64_t flags;    
    // fields not included in signature    
    salt_t salt;    
    uint8_t signature[32];    
    bool hardware_backed;
    };
    其中 version 默认是 2,user_id 是 Android 用户 id ,signature 存储的便是密文,hardware_backed 存储的是加密方式,0 表示密文是软件加密,而 1 表示密文是通过 TEE 环境进行加密得到的。
    密码加密后默认以 password_handle_t 格式存储在/data/system/gatekeeper.password.key 中。密码的生成和校验,在 HAL 层是通过 system/core/gatekeeperd/gatekeeperd.cpp 中的函数实现的。其在系统启动时被注册为 gatekeeperd 服务,服务在启动的时候会调用 GateKeeperProxy()对象,此类的构造函数会去查找 TEE module,如果找到,则通过 TEE 设备进行加解密,如果没有找到,则通过一个软件设备进行加解密。

    Android M引入了GateKeeper,锁屏的密码都存储在此模块中,GateKeeper分software和hardware两种,同时只能使用一种。


    Keyguard/choose lock,使用lockSettings的模块。
    LockSetings:这个模块包括LockPatternUtils.java, LockSettingsStorage.java, LockSettingsService.kava
    Native层,gatekeeper相关类的关系图及其路径:

    常见Hardware gatekeeper的代码路径:
    /vendor/mediatek/proprietary/trustzone/trustonic/source/bsp/platform/mt6752/gatekeeper/TlcTeeGatekeeper/
    /vendor/mediatek/proprietary/trustzone/trustonic/source/external/gatekeeper/common/302c/
    /vendor/mediatek/proprietary/trustzone/trustonic/source/bsp/platform/mt6735/gatekeeper/TlcTeeGatekeeper/

    2.无法解锁、解锁较慢或者注册锁慢问题

    从上述的背景,知道注册密码,验证都是经过LockSettingsService,再到GateKeeper。遇到无法解锁、解锁较慢或者注册锁慢的,可以在这条逻辑路径上加入debug log,在分析是属于哪个模块的问题。
    以下log红色部分与解锁相关,橙色部分与设置锁相关。依据你的问题来决定加哪一种,或者两者都加。

    (1) java层分析

    /frameworks/base/services/core/java/com/android/server/LockSettingsService.java
    public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException {
    
    +      Slog.d(TAg, "checkPattern"); //这里是slog,在吐sys log文件中return doVerifyPattern(pattern, false, 0, userId);
    }
       private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge,long challenge, int userId) throws RemoteException {
           checkPasswordReadPermission(userId);
    
    +      Slog.d(TAg, "doVerifyPattern start");CredentialHash storedHash = mStorage.readPatternHash(userId);
    
    +      Slog.d(TAg, "read pattern hash");boolean shouldReEnrollBaseZero = storedHash != null && storedHash.isBaseZeroPattern;
    
           ...
    
           VerifyCredentialResponse response = verifyCredential(userId, storedHash, patternToVerify,hasChallenge, challenge,new CredentialUtil() {...}
           );
    +      Slog.d(TAg, "verifyCredential response code: response.getResponseCode()");if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK
                   && shouldReEnrollBaseZero) {
               setLockPattern(pattern, patternToVerify, userId);
           }
    +      Slog.d(TAg, "doVerifyPattern end");return response;
    
        }
     private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
                String credential, boolean hasChallenge, long challenge, CredentialUtil credentialUtil)
                    throws RemoteException {
            ...
    +       Slog.d(TAG, "hasChallenge: " + hasChallenge);if (hasChallenge) {
                byte[] token = null;
                GateKeeperResponse gateKeeperResponse = getGateKeeperService()
                        .verifyChallenge(userId, challenge, storedHash.hash, credential.getBytes());
    
                ...
    +           Slog.d(TAG, "verify response " + response);
            } else {
                GateKeeperResponse gateKeeperResponse = getGateKeeperService().verify(
                        userId, storedHash.hash, credential.getBytes()); 
    
           ...
    +           Slog.d(TAG, "verify response " + response);
            }
    
            if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
                // credential has matchedunlockKeystore(credential, userId);
                if (shouldReEnroll) {
                    credentialUtil.setCredential(credential, credential, userId);
                }
    
    +           Slog.d(TAG, "verify ok");
            } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
                if (response.getTimeout() > 0) {
                    requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
                }
    
    +           Slog.d(TAG, "verify fail, retry ");
            }
    
            return response;
        }
    public void setLockPattern(String pattern, String savedCredential, int userId)
                throws RemoteException {
            checkWritePermission(userId);
            byte[] currentHandle = getCurrentHandle(userId);
    +       Slog.d(TAG, "setLockPattern");if (pattern == null) {
                getGateKeeperService().clearSecureUserId(userId);
                mStorage.writePatternHash(null, userId);
                setKeystorePassword(null, userId);
                return;
            }
            ...
            byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
            if (enrolledHandle != null) {
    
    +           Slog.d(TAG, "writePatternHash ");mStorage.writePatternHash(enrolledHandle, userId);
            } else {
                Slog.e(TAG, "Failed to enroll pattern");
            }
    
    +       Slog.d(TAG, "setLockPattern done!");
        }
        @Overridepublic void setLockPassword(String password, String savedCredential, int userId)
                throws RemoteException {
           checkWritePermission(userId);
            byte[] currentHandle = getCurrentHandle(userId);
    +       Slog.d(TAG, "setLockPassword");if (password == null) {
                getGateKeeperService().clearSecureUserId(userId);
                mStorage.writePasswordHash(null, userId);
                setKeystorePassword(null, userId);
                return;
            }
    
            ...
            byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
            if (enrolledHandle != null) {
    
    +           Slog.d(TAG, "writePatternHash ");mStorage.writePasswordHash(enrolledHandle, userId);
            } else {
                Slog.e(TAG, "Failed to enroll password");
            }
    
    +       Slog.d(TAG, "setLockPassword done!");
        }
        private byte[] enrollCredential(byte[] enrolledHandle,
                String enrolledCredential, String toEnroll, int userId)
                throws RemoteException {
            ...
    
    +       Slog.d(TAG, "enroll start.");GateKeeperResponse response = getGateKeeperService().enroll(userId, enrolledHandle,
                    enrolledCredentialBytes, toEnrollBytes);
    +       Slog.d(TAG, "enroll finish.");
    
    if (response == null) {
                return null;
            }
    
            byte[] hash = response.getPayload();
            if (hash != null) {
    
    +           Slog.d(TAG, "setKeystorePassword.");setKeystorePassword(toEnroll, userId);
            } else {
                // Should not happenSlog.e(TAG, "Throttled while enrolling a password");
            }
    
    +       Slog.d(TAG, "enrollCredential done.");return hash;
        }

    /frameworks/base/core/java/com/android/internal/widget/LockPatternUtils.java

        public void saveLockPattern(List<LockPatternView.Cell> pattern, String savedPattern,
                boolean isFallback, String fallbackFor, int userId) {
            try {
                if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
                    throw new IllegalArgumentException("pattern must not be null and at least "
                            + MIN_LOCK_PATTERN_SIZE + " dots long.");
                }
    +           Log.d(TAG, "saveLockPattern begin. ");
    
    getLockSettings().setLockPattern(patternToString(pattern), savedPattern, userId);
    
    +           Log.d(TAG, "saveLockPattern end. ");DevicePolicyManager dpm = getDevicePolicyManager();
    
                // Update the device encryption password.if (userId == UserHandle.USER_OWNER
                        && LockPatternUtils.isDeviceEncryptionEnabled()) {
                    if (!shouldEncryptWithCredentials(true)) {
                        clearEncryptionPassword();
                    } else {
                        String stringPattern = patternToString(pattern);
                        updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern);
                    }
                }
    +           Log.d(TAG, "update encrypt password ");
    
    setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId);
    
                ...
    
    +           Log.d(TAG, set pawword type finish.");onAfterChangingPassword(userId);
            } catch (RemoteException re) {
                Log.e(TAG, "Couldn't save lock pattern " + re);
            }
    
    +       Log.d(TAG, "saveLockPattern end. ");
        }
      public void saveLockPassword(String password, String savedPassword, int quality,
                boolean isFallback, String fallbackFor, int userHandle) {
            try {
                 ...
    
    
    +           Log.d(TAG, "saveLockPassowrd start.");getLockSettings().setLockPassword(password, savedPassword, userHandle);
    
    +           Log.d(TAG, "saveLockPassowrd start.");int computedQuality = computePasswordQuality(password);
    
                ...
    +           Log.d(TAG, "is fallback: " + isFallback);if (!isFallback) {
                   ...
                } else {
                   ...
                }
               ...
    
    +          Log.d(TAG, "saveLockPassword done.");
            } catch (RemoteException re) {
                // Cant do muchLog.e(TAG, "Unable to save lock password " + re);
            }
        }

    (2)native层分析

    /system/gatekeeper/Android.mk
     LOCAL_CFLAGS = -Wall -Werror -g LOCAL_MODULE_TAGS := optional
    
    +LOCAL_SHARED_LIBRARIES := liblog

    /system/gatekeeper/gatekeeper.cpp

    + #define LOG_TAG "gatekeeperd"+ #include <cutils/log.h>+ #include <utils/Log.h>
     void GateKeeper::Verify(const VerifyRequest &request, VerifyResponse *response) {

        if (response == NULL) return;
    
        ...
    
    
    +   ALOGI("Verify: version %d", password_handle->version);if (password_handle->version > HANDLE_VERSION) {
            response->error = ERROR_INVALID;
            return;
        }
    
        secure_id_t user_id = password_handle->user_id;
        secure_id_t authenticator_id = 0;
        uint32_t uid = request.user_id;
    
        uint64_t timestamp = GetMillisecondsSinceBoot();
    
        uint32_t timeout = 0;
        bool throttle = (password_handle->version >= HANDLE_VERSION_THROTTLE);
        bool throttle_secure = password_handle->flags & HANDLE_FLAG_THROTTLE_SECURE;
    
    
    
    +   ALOGI("throttle: %d, throttle_secure: %d", (throttle? 1:0), (throttle_secure? 1:0));if (throttle) {
            failure_record_t record;
    
    +       ALOGI("GetFailureRecord"); //GetFailtureRecord具体有soft或hard的gatekeeper实现if (!GetFailureRecord(uid, user_id, &record, throttle_secure)) {
                response->error = ERROR_UNKNOWN;
                return;
            }
    
    
    +       ALOGI("ThrottleRequest");if (ThrottleRequest(uid, timestamp, &record, throttle_secure, response)) return;
    
    
    +       ALOGI("IncrementFailureRecord");if (!IncrementFailureRecord(uid, user_id, timestamp, &record, throttle_secure)) {
                response->error = ERROR_UNKNOWN;
                return;
            }
    
            timeout = ComputeRetryTimeout(&record);
    
    +       ALOGI("ComputeRetryTimeout %d", timeout);
        } else {
            response->request_reenroll = true;
    
    +       ALOGI("reenroll");
        }
    
        if (DoVerify(password_handle, request.provided_password)) {//校验函数
            // Signature matchesUniquePtr<uint8_t> auth_token_buffer;
            uint32_t auth_token_len;
            MintAuthToken(&auth_token_buffer, &auth_token_len, timestamp,
                    user_id, authenticator_id, request.challenge);
    
            SizedBuffer auth_token(auth_token_len);
            memcpy(auth_token.buffer.get(), auth_token_buffer.get(), auth_token_len);
            response->SetVerificationToken(&auth_token);
            if (throttle) ClearFailureRecord(uid, user_id, throttle_secure);
        } else {
            // compute the new timeout given the incremented recordif (throttle && timeout > 0) {
                response->SetRetryTimeout(timeout);
            } else {
                response->error = ERROR_INVALID;
            }
        }
    
    
    
    +   ALOGI("Verify: error is %d", response->error); //校验结束
    }
    bool GateKeeper::CreatePasswordHandle(SizedBuffer *password_handle_buffer, salt_t salt,
            secure_id_t user_id, uint64_t flags, uint8_t handle_version, const uint8_t *password,
            uint32_t password_length) {
        ...
    +   ALOGI("CreatePasswordHandle");const uint8_t *password_key = NULL;
        uint32_t password_key_length = 0;
        GetPasswordKey(&password_key, &password_key_length);
    +   ALOGI("password lenght: %d", password_key_length);if (!password_key || password_key_length == 0) {
            return false;
        }
    
        ComputePasswordSignature(password_handle->signature, sizeof(password_handle->signature), //ComputePasswordSignature由具体的soft或者hard gatekeeper实现。
                password_key, password_key_length, to_sign, sizeof(to_sign), salt);
    
    +   ALOGI("CreatePasswordHandle end");return true;
    }

    /system/core/gatekeeperd/gatekeeperd.cpp

        virtual int enroll(uint32_t uid,
                const uint8_t *current_password_handle, uint32_t current_password_handle_length,
                const uint8_t *current_password, uint32_t current_password_length,
                const uint8_t *desired_password, uint32_t desired_password_length,
                uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
            ...
    
    +       ALOGI("enroll");
            if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
                return PERMISSION_DENIED;
            }
    
            // need a desired password to enrollif (desired_password_length == 0) return -EINVAL;
    
            int ret;
            if (device) {
                   ...
    +           ALOGI("hardware gatekeeper enroll begin.");ret = device->enroll(device, uid, current_password_handle, current_password_handle_length,
                        current_password, current_password_length,
                        desired_password, desired_password_length,
                        enrolled_password_handle, enrolled_password_handle_length);
    
    
            } else {
    
    +           ALOGI("soft gatekeeper enroll begin.");ret = soft_device->enroll(uid,
                        current_password_handle, current_password_handle_length,
                        current_password, current_password_length,
                        desired_password, desired_password_length,
                        enrolled_password_handle, enrolled_password_handle_length);
            }
    
    +       ALOGI("enroll end. ret: %d", ret);
    
            if (ret == 0) {
                gatekeeper::password_handle_t *handle =
                        reinterpret_cast<gatekeeper::password_handle_t *>(*enrolled_password_handle);
                store_sid(uid, handle->user_id);
                bool rr;
    
    
    +           ALOGI("internal verify begin. ");// immediately verify this password so we don't ask the user to enter it again// if they just created it.verify(uid, *enrolled_password_handle, sizeof(password_handle_t), desired_password,
                        desired_password_length, &rr);
    
    +           ALOGI("internal verify end. ");
            }
    
            return ret;
        }

        virtual int verifyChallenge(uint32_t uid, uint64_t challenge,
                const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
                const uint8_t *provided_password, uint32_t provided_password_length,
                uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) {
            IPCThreadState* ipc = IPCThreadState::self();
            ...
    
    +       ALOGI("verifyChallenge start. ");if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
                return PERMISSION_DENIED;
            }
    
            // can't verify if we're missing either paramif ((enrolled_password_handle_length | provided_password_length) == 0)
                return -EINVAL;
    
            int ret;
            if (device) {
                const gatekeeper::password_handle_t *handle =
                        reinterpret_cast<const gatekeeper::password_handle_t *>(enrolled_password_handle);
                // handle version 0 does not have hardware backed flag, and thus cannot be upgraded to// a HAL if there was none beforeif (handle->version == 0 || handle->hardware_backed) {
    
    +               ALOGI("hardware gatekeeper verify begin. ");ret = device->verify(device, uid, challenge,
                        enrolled_password_handle, enrolled_password_handle_length,
                        provided_password, provided_password_length, auth_token, auth_token_length,
                        request_reenroll);
                } else {
                    // upgrade scenario, a HAL has been added to this device where there was none beforeSoftGateKeeperDevice soft_dev;
    
    +               ALOGI("software gatekeeper verify begin. ");ret = soft_dev.verify(uid, challenge,
                        enrolled_password_handle, enrolled_password_handle_length,
                        provided_password, provided_password_length, auth_token, auth_token_length,
                        request_reenroll);
    
                    if (ret == 0) {
                        // success! re-enroll with HAL
                        *request_reenroll = true;
                    }
                }
            } else {
    
    +           ALOGI("software gatekeeper verify begin. ");ret = soft_device->verify(uid, challenge,
                    enrolled_password_handle, enrolled_password_handle_length,
                    provided_password, provided_password_length, auth_token, auth_token_length,
                    request_reenroll);
            }
    
    +       ALOGI("gatekeeper verify end. ret: %d", ret);
    
    
            ...
            return ret;
        }

    通过上面的log,应该可以初步定位无法解锁和解锁慢的问题,然后在依据结果具体问题具体分析。

    (3)如何通过log知道创建的是software还是hardware gatekeeper?

    答: 在开机时候创建gatekeeper daemon的时候通过/system/core/gatekeeperd/gatekeeperd.cpp中的main函数启动daemon,

    会创建GateKeeperProxy实例, 在GateKeeperProxy的构造函数,会决定创建的是software还是hardware gatekeeper:

        GateKeeperProxy() {
            int ret = hw_get_module_by_class(GATEKEEPER_HARDWARE_MODULE_ID, NULL, &module);//尝试获取HW gatekpper
            device = NULL;
    
            if (ret < 0) {
                ALOGW("falling back to software GateKeeper"); //如果有这条log出来,就表示用的是sw。否则就是hwsoft_device.reset(new SoftGateKeeperDevice());//使用sw gatekeeper
            } else {
                ret = gatekeeper_open(module, &device); //尝试打开hw gatekeeper
                if (ret < 0)
                    LOG_ALWAYS_FATAL_IF(ret < 0, "Unable to open GateKeeper HAL");
            }
    
            if (mark_cold_boot()) {
                ALOGI("cold boot: clearing state");
                if (device != NULL && device->delete_all_users != NULL) {
                    device->delete_all_users(device);
                }
            }
        }


    展开全文
  • Gatekeeper

    2016-04-20 13:44:00
    Gatekeeper Overview The Gatekeeper subsystem performs device pattern/password authentication in a Trusted Execution Environment (TEE). Gatekeeper enrolls and verifies passwords via an HMAC with a ...

    Gatekeeper

    Overview


    The Gatekeeper subsystem performs device pattern/password authentication in a Trusted Execution Environment (TEE). Gatekeeper enrolls and verifies passwords via an HMAC with a hardware-backed secret key. Additionally, Gatekeeper throttles consecutive failed verification attempts and must refuse to service requests based on a given timeout and a given number of consecutive failed attempts.

    When users verify their passwords, Gatekeeper uses the TEE-derived shared secret to sign an authentication attestation to send to the hardware-backed Keystore. That is, a Gatekeeper attestation notifies Keystore that authentication-bound keys (for example, keys that apps have created) can be released for use by apps.

    Architecture


    Gatekeeper involves three main components:

    • gatekeeperd (Gatekeeper daemon). A C++ binder service containing platform-independent logic and corresponding to the GateKeeperService Java interface.
    • Gatekeeper Hardware Abstraction Layer (HAL). The HAL interface in hardware/libhardware/include/hardware/gatekeeper.h, and the implementing module.
    • Gatekeeper (TEE). The TEE counterpart of gatekeeperd. A TEE-based implementation of Gatekeeper.

    To implement Gatekeeper:

    • Implement the Gatekeeper HAL, specifically the functions in gatekeeper.h (hardware/libhardware/include/hardware/gatekeeper.h). See HAL Implementation.
    • Implement the TEE-specific Gatekeeper component, in part based on the following header file: system/gatekeeper/include/gatekeeper/gatekeeper.h. This header file includes pure virtual functions for creating and accessing keys, as well as for computing signatures. See Trusty and other implementations.

    As shown in the following diagram, the LockSettingsService makes a request (via Binder) that reaches the gatekeeperd daemon in the Android OS. The gatekeeperd daemon makes a request that reaches its counterpart (Gatekeeper) in the TEE.

    Figure 1. High-level data flow for authentication by GateKeeper

    The gatekeeperd daemon gives the Android framework APIs access to the HAL, and participates in reporting device authentications to Keystore. The gatekeeperd daemon runs in its own process, separate from the system server.

    HAL Implementation


    The gatekeeperd daemon uses the HAL to interact with the gatekeeperd daemon's TEE counterpart for password authentication. The HAL implementation must be able to sign (enroll) and verify blobs. All implementations are expected to adhere to the standard format for the authentication token (AuthToken) generated on each successful password verification. The contents and semantics of the AuthToken are described in Authentication.

    Specifically, an implementation of the gatekeeper.h header file (in the hardware/libhardware/include/hardware folder) needs to implement the enroll and verify functions.

    The enroll function takes a password blob, signs it, and returns the signature as a handle. The returned blob (from a call to enroll) must have the structure shown in system/gatekeeper/include/gatekeeper/password_handle.h.

    The verify function needs to compare the signature produced by the provided password and ensure that it matches the enrolled password handle.

    The key used to enroll and verify must never change, and should be re-derivable at every device boot.

    Trusty and other implementations


    The Trusty operating system is Google's open source trusted OS for TEE environments. Trusty contains an approved implementation of GateKeeper. However, any TEE OS can be used for the implementation of Gatekeeper. The TEE must have access to a hardware-backed key as well as a secure, monotonic clock that ticks in suspend.

    Trusty uses an internal IPC system to communicate a shared secret directly between Keymaster and the Trusty implementation of Gatekeeper ("Trusty Gatekeeper"). This shared secret is used for signing AuthTokens that will be sent to Keystore, providing attestations of password verification. Trusty Gatekeeper requests the key from Keymaster for each use and does not persist or cache the value. Implementations are free to share this secret in any way that does not compromise security.

    The HMAC key, used to enroll and verify passwords, is derived and kept solely in GateKeeper.

    The Android tree provides a generic C++ implementation of GateKeeper, requiring only the addition of device-specific routines to be complete. To implement a TEE Gatekeeper with device-specific code for your TEE, please refer to the functions and comments in the following file:

    system/gatekeeper/include/gatekeeper/gatekeeper.h

    For the TEE GateKeeper, the primary responsibilities of a compliant implementation are:

    • Adherence to the Gatekeeper HAL
    • Returned AuthTokens must be formatted according to the AuthToken specification (described in Authentication)
    • The TEE Gatekeeper must be able to share an HMAC key with Keymaster, either by requesting the key through a TEE IPC on demand or maintaining a valid cache of the value at all times

    User SIDs


    A User Secure ID (User SID) is the TEE representation of a user. The User SID has no strong connection to an Android user ID.

    A User SID is generated with a cryptographic PRNG whenever a user enrolls a new password without providing a previous one. This is known as an "untrusted" re-enroll. A "trusted" re-enroll occurs when a user provides a valid, previous password. In this case, the User SID is migrated to the new password handle, conserving the keys that were bound to it. The Android framework does not allow for an "untrusted" re-enroll under regular circumstances.

    The User SID is HMAC'ed along with the password in the password handle when the password is enrolled.

    User SIDs are written into the AuthToken returned by the verify function and associated to all authentication-bound Keystore keys. For information about the AuthToken format and Keystore, see Authentication. Since an untrusted call to the enroll function will change the User SID, the call will render the keys bound to that password useless.

    Attackers can change the password for the device if they control the Android OS, but they will destroy root-protected, sensitive keys in the process.

    Request throttling


    GateKeeper must be able to securely throttle brute-force attempts on a user credential. As shown in the gatekeeper.h file (in hardware/libhardware/include/hardware), the HAL provides for returning a timeout in milliseconds. The timeout informs the client not to call GateKeeper again until after the timeout has elapsed. GateKeeper should not service requests if there is a pending timeout.

    GateKeeper must write a failure counter before verifying a user password. If the password verification succeeds, the failure counter should be cleared. This prevents attacks that prevent throttling by disabling the embedded MMC (eMMC) after issuing a verify call. The enroll function also verifies the user password (if provided) and so must be throttled in the same way.

    If supported by the device, it is highly recommended that the failure counter be written to secure storage. If the device does not support file-based encryption, or if secure storage is too slow, implementations may use RPMB directly.

    展开全文
  • TEE normal world侧实现分析

    千次阅读 2016-12-28 11:02:56
    TEE GateKeeper Keystore

    1 TEE 验证操作流程

    这里写图片描述
    1.用户提供PIN/pattern/password/fingerprint,在Android侧,LockSettingsService 和FingerprintService 产生通过binder给Gatekeeperd ,fingerprintd 发送一个请求。
    2.这一步涉及到PIN/pattern/password和fingerprint,这取决于PIN/pattern/password和fingerprint是否提供。

    • GateKeeperd发送PIN/pattern/password的哈希值到TEE侧的GateKeeper,如果TEE侧验证成功,GateKeeper会发送一个包含AuthToken HMAC密钥签名的相应的User SID到android下的GateKeeperd。
    • 或者监听指纹事件的Fingerprintd发送数据到TEE侧的Fingerprint,如果TEE侧验证成功,Fingerprint会发送一个包含AuthToken HMAC密钥签名的相应的AuthToken到android下的Fingerprintd。

    3.GateKeeperd和Fingerprintd接收到签名的AuthToken,并且通过拓展的Keystore服务binder接口将其发送给Keystore服务。另外,当设备密码改变或设备再次锁住时,GateKeeperd会通知Keystore服务。
    4.Keystore服务将从GateKeeperd和Fingerprintd获取的AuthTokens发送给Keymaster,使用GateKeeper和Fingerprint共享的密钥验证AuthTokens。 Keymaster 将token中的时间戳信任为最后一次认证时间, 并基于时间戳的密钥发布决策。

    2 GateKeeper

    2.1 GateKeeper简介

    Gatekeeper子系统在可信执行环境(TEE)中执行设备pattern/PIN/password认证。 GateKeeper通过具有硬件支持的密钥的HMAC注册和验证密码。 此外,Gatekeeper抑制连续失败的验证尝试,并且必须拒绝那些基于给定的超时和给定数量的连续失败尝试的服务请求。
    当用户验证密码时,GateKeeper使用TEE派生的共享密钥签名认证证书发送个硬件支持的Keystore。也就是说,GateKeeper通知Keystore,认证绑定密钥(例如app创建的密钥)可以发布供app使用。

    2.2 GateKeeper 结构

    GateKeeper主要包含三个部分:

    • GateKeeperd(GateKeeper daemon): 一个c++的binder service,包括平台无关的逻辑和对 GateKeeperService Java接口的响应
    • Gatekeeper HAL:hardware/libhardware/include/hardware/gatekeeper.h中的HAL接口,以及相应的HAL实现模块
    • Gatekeeper (TEE):TEE环境下对应的GateKeeperd,一个基于TEE环境实现的GateKeeper

    GateKepper 认证流程:
    这里写图片描述
    摘自https://source.android.com/security/authentication/gatekeeper.html#architecture

    2.3 GateKeeper 实现

    GateKeeper的实现分为两种:
    1.基于软件的实现(无硬件TEE环境,安全性较低);
    2.基于硬件的实现(存在硬件TEE环境,安全性较高);
    GateKeeper HAL实现:
    GateKeeperd使用HAL与TEE下的GateKeeper进行密码验证。HAL的实现必须能够注册和验证blobs,所有实现都应遵循在每次成功密码验证生成的AuthTokens的标准格式。AuthToken的内容和语法都有相应的专门描述。
    尤其是,gatekeeper.h的实现需要实现enroll和verify这两个函数。
    enroll函数接收password blob,并对其签名,返回一个签名的句柄,返回的blob必须遵循system/gatekeeper/include/gatekeeper/password_handle.h中规范的结构。
    verify函数需要比较给定password的签名,并确保与enroll的密码句柄匹配。
    enroll和verify使用的key不能改变,并且应该在每次设备启动时重新生成。

    3 Fingerprint HAL

    3.1 简介

    如果设备有用指纹传感器,那么用户能够注册一个或多个指纹来解锁设备或执行其他任务,android使用Fingerprint HAL链接供应商特定库和指纹硬件,要实现Fingerprint HAL,你必须在供应商特定库中实现fingerprint.h (/hardware/libhardware/include/hardware/fingerprint.h) 中的函数。
    指纹对比流程:
    假设设备上已经注册过一个指纹,指纹设备通常是空闲的,但是因为要响应注册和认证函数的调用,指纹会监听触碰事件(并且有可能指纹触碰时,屏幕会被唤醒)。
    1.用户将手指放到指纹传感器上,供应商特定库基于当前注册模板集合决定是否匹配;
    2.上一步的结果将被发送到Fingerprint HAL,Fingerprint HAL通知Fingerprintd指纹认证。

    3.2 Fingerprint 结构

    Fingerprint认证流程:

    这里写图片描述
    摘自https://source.android.com/security/authentication/fingerprint-hal.html

    • FingerprintManager API: 直接和APP进程交互
      • 每个app都有FingerprintManager的实例;
      • FingerprintManager 是与FingerprintService通信的封装;
    • FingerprintService.:操作系统进程的单一服务,主要处理和fingerprintd的通信
    • fingerprintd (Fingerprint daemon): FingerprintService中binder接口的c++实现, fingerprintd 在其自己的进程中操作,并且封装Fingerprint HAL的供应商特定库
    • Fingerprint HAL供应商特定库:Fingerprint HAL硬件供应商的实现,其主要和特定的硬件设备通信
    • Keystore API and Keymaster:这些组件提供在TEE环境下安全密钥存储的硬件支持加密

    具体供应商HAL的实现需要遵循TEE通信协议的要求。

    指纹传感器采集的原始图像和处理的指纹特征数据不能发送到不可信的内存空间中,所有生物识别数据都需要被放到可信内存和传感器硬件中保护(在TEE中的内存被认为是可信的,反之,不可信)。
    fingerprintd通过供应商特定库调用去注册指纹或执行其它操作。
    这里写图片描述

    3.3 Fingerprint HAL 实现

    本节的指南旨在确保一下内容:

    • 指纹数据不泄露
    • 当用户在设备中被删除时,指纹数据被移除

    以下是指南:
    1.原始指纹数据或者派生产物(例如指纹模板),必须不能用传感器驱动和TEE环境外访问。如果硬件支持访问,则访问必须被TEE限制,并且应该被SElinux策略保护。也就是说SPI通道只能由TEE访问,并且所有设备文件必须有明确的SELinux策略。
    2.指纹的获取,注册,识别都必须在TEE内部。
    3.只有加密形式的指纹数据可以存储在文件系统上,即使文件系统是加密的。
    4.指纹模板必须使用专用的设备特定的密钥签名(例如使用AES),其中至少包含文件路径,group和指纹ID,以使指纹模板文件不能被其他设备操作以及除用户以外的其他人在同一台设备上注册。比如在同一台设备上把指纹数据复制到其他用户,或者复制到另一台设备,都不能工作。
    5.实现既不能使用文件系统提供的set_active_group()函数,也不能提供当删除用户时删除所有用户模板的方法。强烈建议将指纹模板文件加密存储在提供的路径中,如果因为TEE要求导致这不可行,则实现必须添加钩子函数以使删除用户时删除指纹数据。

    3.4 Fingerprint HAL主要函数

    主要的函数都已经在/hardware/libhardware/include/hardware/fingerprint.h中列出,一下是对一些函数的简要说明:

    • enroll:切换HAL状态机开始收集和存储指纹模板,一旦注册完成或超时,状态机将会返回到空闲状态。
    • pre_enroll:产生一个唯一的token,一开始指纹注册。为enroll函数提供token以确保已经有一个先前的认证,例如密码。token会被封装,并且例如,一旦设备认证确认,HMAC就会防止篡改。注册期间,必须检查token已验证token是否可用。
    • get_authenticator_id:返回一个与token相连的当前指纹集。
    • cancel:取消任何挂起的注册和认证操作,HAL状态机返回到空闲状态。
    • enumerate:枚举所有指纹模板的同步调用。
    • remove:删除指纹模板。
    • set_active_group:将指纹操作限定为属于特定组(由GID标志或组标识符标志)的一组指纹中。
    • authenticate:认证指纹相关操作。
    • set_notify:注册一个从HAL获取通知的用户函数,如果HAL状态机处在忙状态,该函数将会被阻塞直到状态机解除忙状态。

    4 Keystore

    4.1 硬件支持的Keystore

    SOC TEE环境的能力为android设备的系统,平台服务甚至第三方应用程序提供了硬件支持的强安全服务的机会。
    Keystore在Android 6.0中得到显着增强,增加了对称加密原语,AES和HMAC,以及为硬件支持的密钥添加了访问控制系统。在密钥生成期间指定访问控制,并在密钥生命期内强制执行。密钥可被限制为仅在用户被认证之后才可使用,并且仅用于指定目的和指定加密参数。
    android 6.0之前,android已经有一个简单的,硬件支持的加密服务API,提供了0.2,0.3版本的Keymaster HAL。keystore提供了数字签名和认证操作,加上生成和导入非对称签名密钥对。这已经在很多设备上实现了,但是还有很多安全目标是不能仅靠一个签名API能简单达到的。android 6.0的Keystore拓展了Keystore API 已提供更广泛的功能。

    4.2 目标

    Android 6.0 Keystore API和底层Keymaster 1.0 HAL的目标是提供一组基本但足够的加密原语,以允许使用访问控制的硬件支持密钥实现协议。
    除了拓展加密原语的范围外,android6.0的Keystore还添加了一下内容:
    - 允许限制密钥使用的使用控制方案,以减轻由于密钥的滥用而导致的安全损害的风险
    - 允许将密钥限制到指定的用户,客户端和定义的时间范围的访问控制方案

    4.3 架构

    Keymaster HAL是一个OEM提供的,可动态加载的库,由Keystore服务使用,以提供硬件支持的加密服务。HAL的实现不能在用户空间甚至内核空间执行任何敏感操作。敏感操作被委派给通过某些内核接口到达的安全处理器。
    在android设备中,keymaster HAL 认为”客户端”是多层的(例如app,framework,Keystore daemon),但由于本文档的目的,可以忽略。这就意味着所描述的Keymaster HAL API是底层的,用于平台内部组件,并不暴露给应用开发者。
    Keymaster HAL的目的不是实现安全敏感算法,而是实现对安全世界的编排和解排请求。

    展开全文
  • gatekeeper实践

    2019-10-27 18:49:39
    gatekeeper是基于k8s的 admission controller webhooks实现的,需要k8s的 kube-apiserve开启相应的准入控制,也就是 validating admission webhook和mutating admissionwebhook. 关于admission controller webhooks...
  • 关闭 Gatekeeper

    千次阅读 2017-04-06 10:45:26
    关闭 GatekeeperMacOS “安全与隐私” 设置项中的 允许“任何来源”应用安装项(打开,即关闭Gatekeeper): 终端命令行: sudo spctl –master-disablePS: Gatekeeper 是 Mountain Lion 和 OS X Lion v10.7.5 ...
  • AndroidQ 锁屏密码验证流程之GateKeeper解析

    千次阅读 热门讨论 2020-04-29 16:38:31
    本篇文章分析一下GateKeeper这个模块,官网对GateKeeper的描述如下: Gatekeeper Gatekeeper 子系统会在可信执行环境 (TEE) 中执行设备解锁图案/密码身份验证。Gatekeeper 会使用由硬件支持的密钥通过 HMAC 注册和...
  • 安卓底层用于锁屏密码加密的一个接口IGateKeeperService的具体实现在什么地方,只找到了aidl文件,却没有找到实现这个接口的文件
  • Symmetrix Gatekeeper device 详解

    千次阅读 2013-06-04 16:16:06
    1. What are the most important things to know about Gatekeepers?...If the system selects and uses a data or system device as a Gatekeeper, applications on the application host can be impacted! EMC ST
  • 关注两篇gatekeeper的论坛

    千次阅读 2016-01-30 23:19:34
    http://www.kaixin001.com/repaste/71054051_10279938583.html#0.22388237123733 ... 灰度发布 和 A/B testing Facebook使用的一大法宝就是灰度发布和A/B testing。这一利器像宙斯
  • OpenH323 Gatekeeper - The GNU Gatekeeper 使用手册作者:黄志伟 v2.0.3, 20 February 2003 这是 OpenH323 Gatekeeper - The GNU Gatekeeper 的中文版使用手册。说明如何编译、安装、设定与监看 OpenH323 ...
  • Android Gatekeeper

    2020-02-29 11:16:38
    https://source.android.google.cn/security/authentication/gatekeeper
  • (service) frameworks/base/services/...LockSettingsService.java LockSettingsStorage.java PasswordSlotManager.java SyntheticPasswordCrypto.java recoverablekeystore LockSettingsShellCommand.java LockSettin
  • 转自:http://blog.51cto.com/yoke88/1972096背景信息由于内部使用Polycom的视频会议,在北、上、深各有办事处也用polycom ,偶尔需要和外部的视频会议互联,发现直接把视频会议设备在防火墙上开端口实在是太困难,...
  • Protect applications and services by using a dedicated host instance that acts as a broker between clients and the application or service, validate...
  • 在Android M上,google改变了之前在java层进行加密的方式, 引入了Gatekeeper ,将密码校验以及相关事务通过HAL层的相关服务进行处理, 完成了对TEE的支持,用以支撑指纹识别等硬件安全特性,于此同时 普通的锁屏加密方式...
  • 1、先来张整体的软件框图 2、enroll和verify的调用流程 4、关键函数的介绍 (1)、writeCredentialHash //将enroll_handle保存到文件 如果是password,保存到passwordFilename, patterFilename写入空 ...
  • Intro Keycloak支持OpenID Connect以及SAML 2.0,对于不同的编程语言有不同的适配,比如对于Java,Spring Boot和Spring ...Keycloak Gatekeeper是用Go语言编写的使用OIDC的代理服务,主要应用是方便的把Keycloak同时
  • 文章目录1、authToken是什么?2、authToken的填充3、authToken的保存 1、authToken是什么? ... 以下是gatekeeper的verify产生...在gatekeeper TA的verify()通过后,对填充authToken结构体,然后再返回。 (hardware/l
  • gatekeeper调研

    2019-10-27 18:34:18
    动机及简介 如果你的组织在运行 Kubernetes,那么你可能一直在寻找控制终端用户可以在集群上做什 么,以及确保集群符合公司或组织政策的方法。这些政策可能是用来满足治理和法律需求,或者 执行最佳实践和组织约定。...
1 2 3 4 5 ... 20
收藏数 2,714
精华内容 1,085
关键字:

gatekeeper