Android框架浅析之锁屏(Keyguard)机制原理

来源:互联网 发布:python复制文件代码 编辑:程序博客网 时间:2024/05/21 06:17

最近终于成功的摆脱了FM收音机,迈向了新的模块:锁屏、状态栏、Launcher---姑且称之为“IDLE”小组,或许叫手机 美容小组,要是能施展下周星星同学的还我漂漂拳,岂不快哉。 OK,闲话打住,咱开始正文。         
本文主要内容:
          1、分析锁屏界面的组成 ;
          2、基于源代码分析锁屏相关类
          3、提出一种在框架取消锁屏的方法 。
      

花了一些时间研究Android原生的锁屏框架---Keyguard,今天就慢慢的讲解下我自己对这个模块的总结,因为目前还处于  理论学习的状况,很多细节以及功能上的实现有待后续的补充完整。      
本文分析适合Android2.2和2.3版本,Android4.0尚不清楚。整个锁屏源码基本上完全一样,只是改变了文件存放路径而已。     本文分析版本具体是Android2.3版本。
源文件路径主要有两个:frameworks\base\policy\src\com\android\internal\policy\impl\   ---->锁屏框架                                 
                                    frameworks\base\core\java\com\android\internal\widget\          ----> 提供了一些的自定义View.
  一、锁屏界面的组成      
通常 Android手机上大家常见的界面只有一种,成功后即可解锁进入界面了。其实在Android手机中,正常的锁屏界面由 两种不同性质的界面组成:
第一种界面称之为LockScreen界面(为了叙述方便,我们姑且称为“解锁界面),即我们通常所见到的界面,手机厂商一般定制该界面。界面如下所示:
1.jpg

该界面对应自定义View的是LockScreen.java类
路径位于:frameworkspoliciesasephonecomandroidinternalpolicyimplLockScreen.java
               
第二种界面称之为UnLockScreen(为了后文叙述方便,我们姑且称为“开锁界面”),一般由Android源码提供,有如下四种
①、图案开锁界面 ---- PatternUnlockScreen.java类     (自定义LinearLayout)
路径位于:frameworks\policies\base\phone\com\android\internal\policy\impl\PatternUnlockScreen.java
   界面显示为:
                      2.jpg
②、PIN开锁界面 ---- SimUnlockScreen.java 类           (自定义LinearLayout)
                      界面显示为:   (图片省略)
  ③、密码开锁界面 ---- PasswordUnlockScreen.java类   (自定义LinearLayout)
路径位于:frameworkspoliciesasephonecomandroidinternalpolicyimplPasswordUnlockScreen.java
                                       3.jpg

④、GoogleAccount 开锁界面 ,即Google账户开锁界面。一般用于当用户输入密码错误次数超过上限值时,系统会提示你输入Google账户去开锁。注意:开启它需要你手动设置账户与同步,否则该界面是不会出来的。对应的源文件是: AccountUnlockScreen.java类    (自定义LinearLayout)
路径位于:frameworks\policies\base\phone\com\android\internal\policy\impl\AccountUnlockScreen.java                    
界面显示为:
4.jpg
   可以按照如下办法选择开启哪一种开锁界面: 设置—>位置和安全—>设置屏幕锁定  ,具体选择那种开锁界面。
    显示规则:
            当然,这两种界面的组合也是有很多变化的,总的规则如下:首先显示LockScreen界面,接着判断是否开启了UnLockScreen界面,如果设置了UnLockScreen界面,则进入对应的UnLockScreen界面去解锁,才算成功解锁。但,存在一种特殊的情况,就是假如我们选择了图案  UnLockScreen界面,是不会显示LockScreen界面,而只会显示UnLockScreen界面。
   二、锁屏界面的实现
我们知道, 任何一种界面都是由各种View/ViewGroup(当然包括自定义的)组成的,然后根据系统对应的状态值的改变去更新
  这些View的显示状态,锁屏界面自然也是如此。锁屏界面的实现最顶层是采用了FrameLayout去控制的,当然内部也嵌套了很
  多层,内嵌层数的增多的一点好处就是我们可以分开而治,具体针对每层去做相应的更新。难处就是看代码看的很蛋疼。

       当界面复杂时,我不得不提Google为开发人员提供的一款优秀工具了---Hierarchy Viewer ,通过它,我们很清晰的弄明白整
  个View树的继承层次,一个布局结构,当然,看源代码也是必须的。
关于Hierarchy Viewer的使用请参Android 实用工具Hierarchy Viewer实战

整个锁屏界面的继承层次如下(部分以及设置了图案开锁界面),更加完整的图请使用Hierarchy Viewer 工具查看。
1.jpg
上图中比较重要的几个视图说明如下:
       LockPatternKeyguardView 继承至FrameLayout :作为LockScreen和UnLockScreen的载体,用来控制显示LockScreen还是UnLockScreen界面。
       LockScreen 继承至FrameLayout   
       PatterUnlockScreen    ViewGroup类型 : 图案解锁界面
       KeyguardViewHost继承至FrameLayout, 该ViewGroup作为顶层View,作为WindowManager的装饰对象添加至窗口。
        它和LockPatternKeyguardView关系相当于DecorView和我们Activity内设置的资源布局一样。  

三、锁屏机制的类结构说明

             看了几天代码,才稍微的理清了下头绪。看完后给我的感觉就是代码之间太BT了,几个类的唯一实例传来传去,太容易混
   乱了。接下来我们分析下一些主要的类及其重要的函数,更多函数实现,大家可以自己参考源代码。

       PS : 由于这些类的结构图比较简单,因此就没画类图了。主要是从源码角度来分析这些代码逻辑。

     1、 KeyguardScreen 类    接口   

       功能:该接口的主要功能是为每个需要显示的界面:LockScreen或者UnLockScreen定义了四个方法,使其在不同的状态能够
          得到相应处理。优点就是:   利用设计原则的面向接口编程,减少对具体对象的依赖。
      路径:\frameworks\base\policy\src\com\android\internal\policy\impl\KeyguardScreen.java
     其源代码释义如下:
  1. /**
  2. * Common interface of each {@link android.view.View} that is a screen of
  3. * {@link LockPatternKeyguardView}.
  4. */
  5. public interface KeyguardScreen {
  6.     /** Return true if your view needs input, so should allow the soft
  7.      * keyboard to be displayed. */
  8.     boolean needsInput(); //View是否需要输入数值,即该界面需要键盘输入数值
  9.     /** This screen is no longer in front of the user.*/
  10.     void onPause();//当该界面不处于前台界面时调用,包括处于GONE或者该界面即将被remove掉
  11.     /** This screen is going to be in front of the user. */
  12.     void onResume();//相对于onPause()方法,当该界面不处于前台界面时调用,处于VISIBLE状态时调用
  13.     /** This view is going away; a hook to do cleanup. */
  14.     void cleanUp();//该界面即将被remove掉 ,即不在需要
  15. }
复制代码
2、KeyguardScreenCallback类  接口

      功能:每个需要显示的界面:LockScreen或者UnLockScreen都保存了该对象的唯一实例,用来向控制界面汇报情况。
      路径:frameworks\base\policy\src\com\android\internal\policy\impl\KeyguardScreenCallback.java
       其源代码释义如下:
  1. /** Within a keyguard, there may be several screens that need a callback
  2. * to the host keyguard view.
  3. */
  4. public interface KeyguardScreenCallback extends KeyguardViewCallback {
  5.     /** Transition to the lock screen*/
  6.     void goToLockScreen();  //当前界面跳转为LockScreen ,而不是UnLockScreen
  7.     /** Transition to the unlock screen.*/
  8.     void goToUnlockScreen();//LockScreen成功开锁 ,是否需要显示UnLockScreen,否则,直接开锁成功。
  9.     //忘记了开锁图案,即我们需要跳转到Google 账户去开锁。
  10.     void forgotPattern(boolean isForgotten);
  11.     boolean isSecure();//当前机器是否安全,例如:设置了图案、密码开锁等   
  12.     //该函数还不太懂,可能是是否只需要验证UnlockScreen界面,即可成功开锁。
  13.     boolean isVerifyUnlockOnly();
  14.     /**Stay on me, but recreate me (so I can use a different layout).*/
  15.     void recreateMe(Configuration config); //重新根据手机当前状态,显示对应的Screen.
  16.     /** Take action to send an emergency call. */
  17.     void takeEmergencyCallAction();  //紧急呼叫时的处理行为.

  18.     /** Report that the user had a failed attempt to unlock with password or pattern.*/
  19.     void reportFailedUnlockAttempt(); //在UnLockScreen界面登陆失败时处理

  20.     /** Report that the user successfully entered their password or pattern.*/
  21.     void reportSuccessfulUnlockAttempt();//在UnLockScreen界面登陆成功时处理

  22.     /** Report whether we there's another way to unlock the device.
  23.      * @return true */
  24.     boolean doesFallbackUnlockScreenExist();
  25. }
复制代码
其唯一实现类位于LockPatternKeyguardView类的内部类(稍后讲到)。

   3、KeyguardViewCallback类  接口

         功能: 提供了一些接口用来接受用户操作Screen的结果。
         路径:frameworks\base\policy\src\com\android\internal\policy\impl\KeyguardViewCallback.java
         其源代码释义如下:
  1. /**
  2.   * The callback used by the keyguard view to tell the {@link KeyguardViewMediator}
  3.   * various things.
  4.   */
  5. public interface KeyguardViewCallback {

  6.      /** Request the wakelock to be poked for the default amount of time. */
  7.      void pokeWakelock();  //保存屏幕在一定时间内处于亮屏状况 , 默认时间为5s或者10s
  8.      /** Request the wakelock to be poked for a specific amount of time.  */
  9.      void pokeWakelock(int millis);//根据给定时间值,使屏幕在该事件段内保持亮屏状况

  10.      /** Report that the keyguard is done.
  11.       * @param authenticated Whether the user securely got past the keyguard.
  12.       *   the only reason for this to be false is if the keyguard was instructed
  13.       *   to appear temporarily to verify the user is supposed to get past the
  14.       *   keyguard, and the user fails to do so. */
  15.      //成功的完成开锁,可以进入手机界面了。参数为ture表示是否正大光明的开锁,例如:图案正确,密码输入正确。
  16.      void keyguardDone(boolean authenticated);
  17.      /**Report that the keyguard is done drawing. */
  18.      void keyguardDoneDrawing(); //整个锁屏界面draw()过程绘制完成时,回调该方法.
  19. }
复制代码
其唯一实现类是   KeyguardViewMediator类(稍后讲到)

    4、 KeyguardWindowController类 接口

         功能:提供通用 接口,判断该界面是否需要显示输入法窗口。
         其源代码释义如下:
  1. /**
  2. * Interface passed to the keyguard view, for it to call up to control
  3. * its containing window.
  4. */
  5. public interface KeyguardWindowController {
  6.     /** Control whether the window needs input -- that is if it has
  7.      * text fields and thus should allow input method interaction. */
  8.     void setNeedsInput(boolean needsInput);  //是否需要显示输入法,为true表示需要。该方法可以想上层报到是否需要显示输入法窗口
  9. }
复制代码
其唯一实现类是KeyguardViewManager类(稍后讲到)。
      
    5、KeyguardViewManager
        功能:包装了WindowManager功能了,提供了添加、删除锁屏界面的功能。
          其源代码释义如下:
  1. public class KeyguardViewManager implements KeyguardWindowController {
  2.          ...
  3.      private WindowManager.LayoutParams mWindowLayoutParams;
  4.      private boolean mNeedsInput = false; //是否需要输入法 , 默认不需要

  5.      private FrameLayout mKeyguardHost;   //该ViewGroup作为顶层View,作为WindowManager添加至窗口
  6.      private KeyguardViewBase mKeyguardView; //具体窗口内容。
  7.      //以上两种的关系相当于DecorView和我们Activity内设置的资源文件一样

  8.      private boolean mScreenOn = false; //是否处于亮屏状态
  9.      //构造函数,初始化各种属性
  10.      public KeyguardViewManager(Context context, ViewManager viewManager,
  11.              KeyguardViewCallback callback, KeyguardViewProperties keyguardViewProperties, KeyguardUpdateMonitor updateMonitor) {
  12.         ...
  13.      }
  14.      /**
  15.       * Helper class to host the keyguard view.
  16.       */
  17.      private static class KeyguardViewHost extends FrameLayout {
  18.          ... //KeyguardViewHost类
  19.      }

  20.      /**
  21.       * Show the keyguard.  Will handle creating and attaching to the view manager
  22.       * lazily.
  23.       */  //显示锁屏界面
  24.      public synchronized void show() {      
  25.          if (mKeyguardHost == null) {
  26.              ...
  27.              mViewManager.addView(mKeyguardHost, lp);
  28.          }
  29.          if (mKeyguardView == null) {
  30.              ...
  31.              mKeyguardHost.addView(mKeyguardView, lp);
  32.              if (mScreenOn) {
  33.                  mKeyguardView.onScreenTurnedOn();
  34.              }
  35.          }
  36.         ...
  37.      }
  38.      ...

  39.      /*** Hides the keyguard view */
  40.      public synchronized void hide() { //隐藏锁屏界面,也就是说我们成功的解锁了
  41.          if (mKeyguardHost != null) {
  42.              mKeyguardHost.setVisibility(View.GONE);
  43.                  ...
  44.              }
  45.      }
  46.      //锁屏界面是否处于显示状态
  47.      public synchronized boolean isShowing() {
  48.          return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
  49.      }
  50. }


  51. }
  52.       
复制代码
6、 KeyguardUpdateMonitor.java类

       功能:该类的主要功能就是根据监视系统状态值的改变(例如:时间、SIM卡状态、电池电量;使用广播监听),根据这种状态
           值的改变回调监听了该状态信息的对象实例。
       其源代码释义如下:
  1. public class KeyguardUpdateMonitor {
  2.     ...
  3.     private int mFailedAttempts = 0;  //当前登录事,已经失败的次数
  4.     private ArrayList<InfoCallback> mInfoCallbacks; //保存所有监听对象 InfoCallback
  5.     private ArrayList<SimStateCallback> mSimStateCallbacks ; //保存所有监听对象  SimStateCallback
  6.     private static class SimArgs {  //Sim状态信息
  7.       ...
  8.     }
  9.     /**
  10.      * Callback for general information relevant to lock screen.
  11.      */
  12.     interface InfoCallback {
  13.             //电池电量信息改变:参数含义分别如下:是否显示电量信息  、 是否插入电影充电、 当前电池电量值
  14.         void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel);
  15.         void onTimeChanged(); //时间发生了改变
  16.         //网络运营商状态发生了改变 ,例如从中国移动2G变为中国移动3G,或者无服务等 ;
  17.         void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn);
  18.         /** Called when the ringer mode changes. */
  19.         void onRingerModeChanged(int state);
  20.         /** 电话状态发生了改变  值可能为:EXTRA_STATE_IDLE、EXTRA_STATE_RINGING、EXTRA_STATE_OFFHOOK*/
  21.         void onPhoneStateChanged(String newState);
  22.     }
  23.     /** Callback to notify of sim state change. */
  24.     interface SimStateCallback {
  25.         void onSimStateChanged(IccCard.State simState); //Sim卡信息发生了改变,例如有正常状况变为ABSENT/MISSING状态
  26.     }

  27.     /*** Register to receive notifications about general keyguard information
  28.      * (see {@link InfoCallback}. */
  29.     public void registerInfoCallback(InfoCallback callback) {
  30.         if (!mInfoCallbacks.contains(callback)) {
  31.             mInfoCallbacks.add(callback);  //注册一个监听器
  32.         } ...
  33.     }
  34.    ...
  35. }
复制代码
7, LockPatternKeyguardView类  (自定义ViewGroup)
           功能:作为LockScreen和UnLockScreen界面的载体,控制显示哪个界面。
             其源代码释义如下:
  1. public class LockPatternKeyguardView extends KeyguardViewBase {
  2.      ...
  3.      private View mLockScreen;
  4.      private View mUnlockScreen;

  5.      private boolean mScreenOn = false;//是否亮屏

  6.      enum Mode {
  7.          //当前显示界面的Mode Lock 或者UnLock
  8.      }
  9.      enum UnlockMode {
  10.          ...//开锁界面的几种不同Mode
  11.      }
  12.      //构造函数
  13.      public LockPatternKeyguardView( ...) {     
  14.          //KeyguardScreenCallback的实现对象
  15.          mKeyguardScreenCallback = new KeyguardScreenCallback() {
  16.             ...
  17.          };
  18.          ...
  19.      }
  20.      public void reset() {
  21.         ...//重置显示界面
  22.      }
  23.      private void recreateLockScreen() {
  24.             ...//重新构建LockScreen
  25.      }
  26.      private void recreateUnlockScreen() {
  27.         ...//重新构建UnlockScreen
  28.      }
  29.      private void recreateScreens() {
  30.         ...//重新构建该视图
  31.      }
  32.      public void verifyUnlock() {
  33.         ...
  34.      }
  35.      public void cleanUp() {
  36.             ... //清理资源对象
  37.      }
  38.      private boolean isSecure() {
  39.         ...//手机设置是否处于安全状态
  40.      }
  41.      private void updateScreen(final Mode mode) {
  42.         ...//根据参数(Lock/unLock),判断显示为LockScreen或者UnlockScreen界面
  43.      }
  44.      View createLockScreen() {
  45.             ...//创建lockScreen
  46.      }
  47.      View createUnlockScreenFor(UnlockMode unlockMode) {
  48.         ...//根据不同的Unlock Mode , 创建不同的UnlockScreen
  49.      }
  50.      private Mode getInitialMode() {
  51.         ...//得到初始化的状态Mode (lock or unlock).
  52.      }
  53.      /** Given the current state of things, what should the unlock screen be? */
  54.      private UnlockMode getUnlockMode() {
  55.         ...//返回开锁的状态Unlock Mode
  56.      }
  57.      private void showTimeoutDialog() {
  58.          ... //输入密码超过一定次数时,提示30s后在登录的对话框
  59.      }
  60.      private void showAlmostAtAccountLoginDialog() {
  61.         ... //显示Google 账户登录对话框
  62.      }
  63. }
复制代码
8、KeyguardViewBase类 抽象类  (自定义ViewGroup)

            功能:为LockPatternKeyguardView提供了一组通用的方法 。需要值得注意的方法就是他对某些KeyEvent的监听,
      当他消费监听到这些KeyEvent,我们的App就监听不到这些KeyEvent了 。常用的有KEYEVENT_VOLUME_UP/DOWN等。
  1. public abstract class KeyguardViewBase extends FrameLayout {
  2.          ...
  3.   @Override
  4.     public boolean dispatchKeyEvent(KeyEvent event) {
  5.         ...
  6.         if (interceptMediaKey(event)) {
  7.             return true;
  8.         }
  9.         return super.dispatchKeyEvent(event);
  10.     }

  11.     private boolean interceptMediaKey(KeyEvent event) {
  12.         final int keyCode = event.getKeyCode();
  13.         if (event.getAction() == KeyEvent.ACTION_DOWN) {
  14.             switch (keyCode) {
  15.                 ...//more keyevent
  16.                 case KeyEvent.KEYCODE_VOLUME_UP:
  17.                 case KeyEvent.KEYCODE_VOLUME_DOWN: {
  18.                     ...
  19.                     // Don't execute default volume behavior
  20.                     return true; //直接返回不在向下传递处理
  21.                 }
  22.             }
  23.         }
  24.         return false;
  25.     }
  26. }
复制代码
9、 KeyguardViewProperties.java 接口
                 功能:提供了创建界面的通用方法。
  1.    public interface KeyguardViewProperties {  
  2.             //创建一个KeyguardViewBase实例 , 实际是指LockPatternKeyguardView实例
  3.             KeyguardViewBase createKeyguardView(Context context,
  4.                     KeyguardUpdateMonitor updateMonitor,
  5.                     KeyguardWindowController controller);

  6.             boolean isSecure();
  7.         }
复制代码
其唯一实现类是是LockPatternKeyguardViewProperties类(稍后讲到)。


  10、LockPatternKeyguardViewProperties
                源代码释义如下:
  1. public class LockPatternKeyguardViewProperties implements KeyguardViewProperties {
  2.     ...
  3.     //创建一个LockPatternKeyguardView对象
  4.     public KeyguardViewBase createKeyguardView(Context context,
  5.             KeyguardUpdateMonitor updateMonitor,
  6.             KeyguardWindowController controller) {
  7.         return new LockPatternKeyguardView(context, updateMonitor,
  8.                 mLockPatternUtils, controller);
  9.     }
  10. }
复制代码
11、KeyguardViewMediator核心类 ,该类是唯一实现了KeyguardViewCallback的类。
           功能:  功能:该类提供了一些接口,由PhoneWindowManager)去访问控制Keyguard....
           该类的初始化是在PolicyWindowManager的构造函数中创建的。如下:
  1. public class PhoneWindowManager implements WindowManagerPolicy {
  2.            ...
  3.           /** {@inheritDoc} */  //由SystemServer调用
  4.           public void init(Context context, IWindowManager windowManager,
  5.                   LocalPowerManager powerManager) {
  6.               ...//初始化该实例
  7.               mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager);
  8.           }
  9.   }
  10.   
复制代码
参照源代码,把一些重要的属性和方法的大意给分析下:
  1. public class KeyguardViewMediator implements KeyguardViewCallback, KeyguardUpdateMonitor.SimStateCallback {
  2.     private boolean mSystemReady;  //启动成功 由SystemServer调用

  3.     /**Used to keep the device awake while to ensure the keyguard finishes opening before
  4.      * we sleep.*/ //在需要显示锁屏界面时,保持屏幕在某个时间段内为暗屏状态  
  5.     private PowerManager.WakeLock mShowKeyguardWakeLock;
  6.     private KeyguardViewManager mKeyguardViewManager; //KeyguardViewManager实例
  7.     /**  * External apps (like the phone app) can tell us to disable the keygaurd.*/
  8.     //是否允许其他App禁止锁屏 , 例如来电时 禁止锁屏
  9.     private boolean mExternallyEnabled = true;
  10.     //处于锁屏状态 , 即显示锁屏
  11.     private boolean mShowing = false;
  12.     // true if the keyguard is hidden by another window
  13.     private boolean mHidden = false; //被其他窗口掩盖 , 例如来电时锁屏被掩盖
  14.     private boolean mScreenOn = false; // 是否亮屏

  15.     public KeyguardViewMediator(Context context, PhoneWindowManager callback,
  16.             LocalPowerManager powerManager) {
  17.         ...
  18.         //构造相关实例对象
  19.         mKeyguardViewProperties = new LockPatternKeyguardViewProperties(
  20.                 new LockPatternUtils(mContext), mUpdateMonitor);

  21.         mKeyguardViewManager = new KeyguardViewManager(
  22.                 context, WindowManagerImpl.getDefault(), this,
  23.                 mKeyguardViewProperties, mUpdateMonitor);
  24.         //解锁成功后发送的Intent
  25.         mUserPresentIntent = new Intent(Intent.ACTION_USER_PRESENT);
  26.         mUserPresentIntent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
  27.     }
  28.     /** Called to let us know the screen was turned off.
  29.      *   @param why either {@link WindowManagerPolicy#OFF_BECAUSE_OF_USER},
  30.      *   {@link WindowManagerPolicy#OFF_BECAUSE_OF_TIMEOUT} or
  31.      *   {@link WindowManagerPolicy#OFF_BECAUSE_OF_PROX_SENSOR}.
  32.      */
  33.     //屏幕变灰暗  , 原因有如下:以及对应的逻辑处理。
  34.     // 1、OFF_BECAUSE_OF_USER : 用户按下POWER键 , 当前是否处于锁屏界面,若是(mShowing)则重置显示界面,否则重新显示锁屏界面
  35.     // 2、OFF_BECAUSE_OF_TIMEOUT : 屏幕超时,常见情况就是一段时间没有操作屏幕,手机处于灰暗状态。          处理行为:   
  36.     //     发送Action值为DELAYED_KEYGUARD_ACTION的广播,因为该类注册了该Intent广播,接受到时会调用doKeyguard()方法锁屏
  37.     // 3、OFF_BECAUSE_OF_PROX_SENSOR:接打电话时,距离感应太近导致暗屏,此时由于PowerManager那儿已经处理了暗屏,不需要做任何事
  38.     // 最后,如果以上逻辑都不成立,调用 doKeyguard()方法显示屏幕                       
  39.     public void onScreenTurnedOff(int why) {
  40.        ...
  41.     }
  42.     /**
  43.      * Let's us know the screen was turned on.
  44.      */
  45.     public void onScreenTurnedOn() {
  46.         synchronized (this) {
  47.             ...
  48.             notifyScreenOnLocked();  //通知亮屏
  49.         }
  50.     }
  51.     /** Enable the keyguard if the settings are appropriate. */
  52.     private void doKeyguard() {
  53.         synchronized (this) {
  54.             ...
  55.             showLocked();//显示锁屏界面
  56.         }
  57.     }
  58.     //该方法的调用时机就是当按下POWER键时,系统会回调该方法 keyCode值一般为 26
  59.     public boolean onWakeKeyWhenKeyguardShowingTq(int keyCode) {
  60.         //操作按键是否能唤醒屏幕
  61.         if (isWakeKeyWhenKeyguardShowing(keyCode)) {
  62.             // give the keyguard view manager a chance to adjust the state of the
  63.             // keyguard based on the key that woke the device before poking
  64.             // the wake lock
  65.             wakeWhenReadyLocked(keyCode);//开始唤醒屏幕咯
  66.             return true;
  67.         } else {
  68.             return false;
  69.         }
  70.     }
  71.     /** {@inheritDoc} */  //在一定时间内保存屏幕为亮屏状态
  72.     public void pokeWakelock(int holdMs) {
  73.         ...
  74.     }
  75.     //表示成功得分完成了解锁操作
  76.     public void keyguardDone(boolean authenticated, boolean wakeup) {
  77.         synchronized (this) {
  78.             //发送给Handler 进行异步处理
  79.             Message msg = mHandler.obtainMessage(KEYGUARD_DONE);
  80.             msg.arg1 = wakeup ? 1 : 0;
  81.             mHandler.sendMessage(msg);
  82.             if (authenticated) {
  83.                 mUpdateMonitor.clearFailedAttempts(); //清除错误登录次数
  84.             }
  85.             ...
  86.         }
  87.     }
  88.     //Handler对象 , 异步处理
  89.     private Handler mHandler = new Handler() {
  90.         @Override
  91.         public void handleMessage(Message msg) {
  92.             switch (msg.what) {
  93.               ...  //异步处理
  94.             }
  95.         }
  96.     };
  97.     //异步处理完成开锁成功
  98.     private void handleKeyguardDone(boolean wakeup) {
  99.         handleHide(); //释放该Keyguard对应的窗口
  100.         mWakeLock.release();
  101.         mContext.sendBroadcast(mUserPresentIntent); //解锁成功,发送Intent信息
  102.     }
  103.     //显示锁屏界面
  104.     private void handleShow() {
  105.         synchronized (KeyguardViewMediator.this) {
  106.             ...
  107.             mKeyguardViewManager.show();
  108.             mShowing = true;
  109.             ...
  110.         }
  111.     }
  112.     private void handleHide() {
  113.         synchronized (KeyguardViewMediator.this) {
  114.             //去除锁屏界面对应的窗口
  115.             mKeyguardViewManager.hide();
  116.             mShowing = false;
  117.            ...
  118.         }
  119.     }
  120.     //设置状态栏enable状态 , 例如:能否被下拉等
  121.     private void adjustStatusBarLocked() {
  122.             ...
  123.             // if the keyguard is shown, allow the status bar to open
  124.             // only if the keyguard is insecure and is covered by another window
  125.             boolean enable = !mShowing || (mHidden && !isSecure());
  126.             mStatusBarManager.disable(enable ?StatusBarManager.DISABLE_NONE : StatusBarManager.DISABLE_EXPAND);
  127.         }
  128.     }
  129. }
复制代码
该类的很多方法都是由PhoneWindowManager调用访问的。

         基本上把一些重要的类及其相关的方法给走了一遍吧,对看源码的还是很有帮助的。因为我的理解还不是很深入,可能有偏颇的
  地方。希望以后能够组件完善起来。 阿门 !


  
  问题:如何在框架中, 解除锁屏 ?


       引入:该问题是在CSDN论坛上回答一位网友引发的,最开始由于我也只是简单看了下,因此也就事论事回答了一个小问题。
  随着看的越来越深入,慢慢的也在心里长生了涟漪。经过尝试、烧鸡验证,发现如下办法行的通,而且效果还比较好。风险应该
  比较小吧。 希望正在在框架修改的同学,慎重行事。


       基本思路:毫无疑问,我的想法就是每次需要显示Keyguard---锁屏界面时,我们并不真正的去锁屏,而只是提供了一个空的
  方法去给系统调用,让系统觉得我们“锁屏”了,同样也不去真正的隐藏“锁屏”界面,提供一个空壳给系统调用。由于可能涉及
  到其它问题,例如:能否下拉状态栏,按下POWER键后,屏幕很快休眠等。Come on ,我们需要统一做处理。


    所有步骤函数都发生在KeyguardViewMediator 类中,注释部分为我们所添加的。


      Step 1、  取消  真正的去锁屏实现
  1. //该方法会显示锁屏界面,我们使其成为一个空壳子
  2. private void handleShow() {
  3.     synchronized (KeyguardViewMediator.this) {
  4.         if (DEBUG) Log.d(TAG, "handleShow");
  5.         if (!mSystemReady) return;

  6.         playSounds(true);
  7.         //Begin : Modifid by qinjuning     
  8.         
  9.         //mKeyguardViewManager.show();  //   
  10.         //mShowing = true;              //
  11.         //adjustUserActivityLocked();   //
  12.         //adjustStatusBarLocked();     //取消对状态栏的控制
  13.         
  14.         //End
  15.         try {
  16.             ActivityManagerNative.getDefault().closeSystemDialogs("lock");
  17.         } catch (RemoteException e) {
  18.         }
  19.         mShowKeyguardWakeLock.release();
  20.     }
  21. }
复制代码
Step 2、  取消  真正的去隐藏锁屏实现
  1. //真正的隐藏屏幕实现
  2. private void handleHide() {
  3.     synchronized (KeyguardViewMediator.this) {
  4.         if (DEBUG) Log.d(TAG, "handleHide");
  5.         if (mWakeAndHandOff.isHeld()) {
  6.             Log.w(TAG, "attempt to hide the keyguard while waking, ignored");
  7.             return;
  8.         }
  9.         // only play "unlock" noises if not on a call (since the incall UI
  10.         // disables the keyguard)
  11.         if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
  12.             playSounds(false);
  13.         }
  14.         //Begin : Modifid by qinjuning  
  15.         
  16.         //mKeyguardViewManager.hide();
  17.         //mShowing = false;
  18.         //adjustUserActivityLocked();      
  19.         //adjustStatusBarLocked();         //取消对状态栏的控制
  20.         
  21.         //End
  22.     }
  23. }
复制代码
以上两步行动后,存在一个Bug(问题),就是唤醒屏幕后,会在指定的时间内屏幕由亮变暗,我们还需要做如下修改
   
     Step 3、按下POWER键时, 解除屏幕由亮变暗的Bug
  1. private void handleWakeWhenReady(int keyCode) {
  2.     synchronized (KeyguardViewMediator.this) {
  3.         if (DBG_WAKE) Log.d(TAG, "handleWakeWhenReady(" + keyCode + ")");

  4.         // this should result in a call to 'poke wakelock' which will set a timeout
  5.         // on releasing the wakelock
  6.         if (!mKeyguardViewManager.wakeWhenReadyTq(keyCode)) {
  7.             // poke wakelock ourselves if keyguard is no longer active
  8.             Log.w(TAG, "mKeyguardViewManager.wakeWhenReadyTq did not poke wake lock, so poke it ourselves");
  9.             //Begin : Modifid by qinjuning  
  10.             //pokeWakelock();  //按下POWER键时, 解除屏幕由亮变暗的Bug   
  11.             //End
  12.         }
  13.         /**
  14.          * Now that the keyguard is ready and has poked the wake lock, we can
  15.          * release the handoff wakelock
  16.          */
  17.         mWakeAndHandOff.release();

  18.         if (!mWakeLock.isHeld()) {
  19.             Log.w(TAG, "mWakeLock not held in mKeyguardViewManager.wakeWhenReadyTq");
  20.         }
  21.     }
  22. }
复制代码
经过真机测试是通过的,但其他风险并不清楚。 这个方法只是提供了一个学习的途径吧。大家慎重行事。

    上面Step 1、以及Step 2可以由如下方法代替:
           将属性mExternallyEnabled 设置为 false, 接下来需要显示界面时都不会继续走下去,如下函数:
  1. /**
  2. * Enable the keyguard if the settings are appropriate.
  3. */  //显示界面
  4. private void doKeyguard() {
  5.     synchronized (this) {
  6.         // if another app is disabling us, don't show
  7.         if (!mExternallyEnabled) {   //mExternallyEnabled 为false
  8.             if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");

  9.             // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
  10.             // for an occasional ugly flicker in this situation:
  11.             // 1) receive a call with the screen on (no keyguard) or make a call
  12.             // 2) screen times out
  13.             // 3) user hits key to turn screen back on
  14.             // instead, we reenable the keyguard when we know the screen is off and the call
  15.             // ends (see the broadcast receiver below)
  16.             // TODO: clean this up when we have better support at the window manager level
  17.             // for apps that wish to be on top of the keyguard
  18.             return;
  19.         }
  20.         ...
  21.     }
  22. }
复制代码
该方法的一个缺点就是,假如存在重新调用了setKeyguardEnabled()设置该值,一切都是白搭( 但从源码看,这点不可能
现,因为存在另一个判断依据:变量mNeedToReshowWhenReenabled , 其初始值为false,只有成功禁止锁屏之后才置为
true )。此,我们可以仿照这个方法,主动添加一个私有变量,禁止显示锁屏界面,即禁止doKeyguard()方法继续走下去。

     OK ,本文到此为止。讲的比较抽象。 大家看代码时多加理解才是王道 。

     显然易见,手机厂商,基于框架只需要修改LockScreen这个自定义ViewGroup即可,其他的一套Google已经为我们
   封装好了。


      在框架层修改肯定不是最好的,对于第三方的App而言,实现不了该功能。还好,SDK为我们提供了接口类去处理隐藏锁屏接口
的方法,该类是KeyguardManager类,关于该类的简介请参考该KeyguardManager简介

     我们可以通过KeyguardManager类实例获得一个KeyguardManager.KeyguardLock对象,进而调用相应方法去取消锁屏界面
示锁屏界面。
    KeyguardManager.KeyguardLock的两个方法说明如下:


          public void disableKeyguard ()
            功能:取消锁屏界面显示,同时禁止显示锁屏界面。除非你显示调用了reenableKeyguard()方法使能显示锁屏界面。

            public void reenableKeyguard ()            功能: 使能显示锁屏界面,如果你之前调用了disableKeyguard()方法取消锁屏界面,那么会马上显示锁屏界面。



     这两个方法最终都会调用到KeyguardViewMediator类的setKeyguardEnabled(boolean enable)方法。
            参数说明: enable = false  对应于disableKeyguard()方法,
                             enable = true  对应于reenableKeyguard()方法。

   该方法原型为: 位于KeyguardViewMediator类中
  1. /**
  2. * Same semantics as {@link WindowManagerPolicy#enableKeyguard}; provide
  3. * a way for external stuff to override normal keyguard behavior.  For instance
  4. * the phone app disables the keyguard when it receives incoming calls.
  5. */
  6. public void setKeyguardEnabled(boolean enabled) {
  7.     synchronized (this) {
  8.         mExternallyEnabled = enabled;  //保存值,该值会在doKeyguard()时用到,如果为false ,则不进行锁屏

  9.         if (!enabled && mShowing) {
  10.             if (mExitSecureCallback != null) {//该判断为false
  11.                 ...
  12.                 return ;
  13.             }
  14.             mNeedToReshowWhenReenabled = true;  //置为真,以便下次调用
  15.             hideLocked(); //已经显示了锁屏界面,则取消隐藏界面
  16.         } else if (enabled && mNeedToReshowWhenReenabled) { //重新显示锁屏界面
  17.             mNeedToReshowWhenReenabled = false;

  18.             if (mExitSecureCallback != null) {//该判断为false
  19.               
  20.             } else {
  21.                 showLocked(); //显示隐藏界面
  22.                 ...
  23.             }
  24.         }
  25.     }
  26. }
复制代码
使用这两个方法时,记得加上如下权限android.permission.DISABLE_KEYGUARD

   为了在亮屏时,达到取消显示界面的效果,我们还需要知道 一下两个广播:

         屏幕变暗以及屏幕点亮的广播


                    android.intent.action.SCREEN_ON  --- 屏幕变亮
                   android.intent.action.SCREEN_OFF ---- 屏幕点暗


        关于这两个广播的说明请参考:android 自定义锁屏

于是在监听到屏幕变暗/变亮时,通过KeyguardManager类实现即可。对与用户而言,就相当于解除了锁屏界面了。
   可能代码如下:
  1. //屏幕变暗/变亮的广播 , 我们要调用KeyguardManager类相应方法去解除屏幕锁定
  2.         private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver(){
  3.                 @Override
  4.                 public void onReceive(Context context , Intent intent) {
  5.                         String action = intent.getAction() ;
  6.                         
  7.                     Log.i(TAG, intent.toString());
  8.                     
  9.                         if(action.equals("android.intent.action.SCREEN_OFF")
  10.                                         || action.equals("android.intent.action.SCREEN_ON") ){
  11.                                 mKeyguardManager = (KeyguardManager)context.getSystemService(Context.KEYGUARD_SERVICE);
  12.                                 mKeyguardLock = mKeyguardManager.newKeyguardLock("zdLock 1");
  13.                                 mKeyguardLock.disableKeyguard();
  14.                                 startActivity(zdLockIntent);
  15.                         }
  16.                 }
  17.                
  18.         };
复制代码
  关于参考/设计一个好的解锁界面以及仿正点闹钟滑动解锁,请看我的这篇博客:
Android自定义锁屏实现----仿正点闹钟滑屏解锁


       最后,可能有些同学在做App时,可能想获取系统的登录次数等,例如:登录失败次数等 ; Android系统已经 为我们提供好
   了框架去处理,具体对应类是DevicePolicyManager类,关于该类的具体使用请参见该
【Android设备管理】 利用DevicePolicyManager执行屏幕锁定


我也不再罗嗦了 ,大家认真学习吧 。 后面我会仔细分析下锁屏框架的一些具体处理函数 。