zoukankan      html  css  js  c++  java
  • Android4.0 4.1Keyguard锁屏流程梳理

    一直想要整理一下keyguard(锁屏)模块的相关流程,由于各种原因总是没真正着手开始做,一直拖也不是个办法,所以就索性开始了。

    这篇内容会比较偏分析,所以闲话就少扯点了。

    锁屏模块位于framework中,有别于一般的上层应用,它的逻辑代码和view视图,资源文件散布在framework的几处地方,这也给新手分析该模块带来也一点的麻烦。下面我会试着来捋捋这些散落的珠子。

    1.文件目录:

    a,逻辑是Frameworks/base/policy/src/com/android/internal/policy/ impl/目录下

     

    b,视图view是在 Framework/base/core/java/com/android/internal/ widget/路径下:

     

    文件夹multiwaveview就是从4.0开始加入的环形解锁视图及相应动画实现。

    c,资源文件在Framework/base/core/res/res/下。

    这个就不贴目录了,和一般android应用一样,图片资源,布局文件都很规矩的放在相应分辨率的文件夹下。大致了解了锁屏模块文件分布后,我们一般就要开始看源代码了。

    2,keyguard锁屏流程图:

    现在看不明白的没关系可以先跳过,先看下面,再更几遍源码就会发现该图就是个小菜了。


    3,keyguard锁屏view层次图:

     


     

    一般4.0真机都会有五种锁屏方式:

     

    这里和2.3有明显区别了,2.3的view视图如下:


    可以看出来以前的锁屏流程是分正常锁屏和图案锁屏方式。在选择图案解锁方式,是不会显示正常解锁slidingview的。但是选择密码解锁就会出现需要用户二次解锁的情况。三星2.2就对此进行了流程优化统一。本人也试着做了,有兴趣可以。。。

    4.0以后该流程得到了官方的统一,选择任何锁屏方式,用户都只需要一次解锁。流程上也更直观了。相信Google,必须是越来越good。

    4,keyguard锁屏重要类分析:

    1,KeyguardScreenback.java和KeyguardViewCallback.java

    说明:接口两个,等着被LockPatternKeyguardView.java实例化。注意Keyguard ViewCallback.java还偷偷捞外快,同时替KeyguardViewMediator.java服务。

    public interface KeyguardScreenCallback extends KeyguardViewCallback {
        void goToLockScreen();//Transition to the lock screen.
        void goToUnlockScreen();//Transition to the unlock screen.
        void forgotPattern(boolean isForgotten);// The user forgot their pattern
        boolean isSecure();//Whether the keyguard requires some sort of PIN.
        /**
         * @return Whether we are in a mode where we only want to verify the
         *   user can get past the keyguard.
         */
        boolean isVerifyUnlockOnly();
        /**
         * Stay on me, but recreate me (so I can use a different layout).
         */
        void recreateMe(Configuration config);
        void takeEmergencyCallAction();//Take action to send an emergency call
        void reportFailedUnlockAttempt();//user had a failed attempt to unlock
        void reportSuccessfulUnlockAttempt();//successfully entered their password
        /**
         * Report whether we there's another way to unlock the device.
         * @return true
         */
        boolean doesFallbackUnlockScreenExist();
    }
    

    public interface KeyguardViewCallback {
        /**
         * Request the wakelock to be poked for the default amount of time.
         */
        void pokeWakelock();//使屏幕保持亮一段时间。
        void pokeWakelock(int millis);
        void keyguardDone(boolean authenticated);// Report keyguard is done.
        /**
         * Report that the keyguard is done drawing.
         */
        void keyguardDoneDrawing();//所有锁屏视图完成draw时调用该方法。
    }

    google的注释实在是太优雅了,都翻译成中文反而会有所误导且影响美观,单词看不明白的,有道去。实在懒的话,意会也行的;)

    2. KeyguardScreen.java

    说明:接口一个,坐等LockScreen.java等具体锁屏方式来实现它。

    public interface KeyguardScreen {
        boolean needsInput();//是否需要键盘进行输入
        void onPause();//view不在最上层时被系统调用。
        void onResume();//view重新掌权时被系统调用
        void cleanUp();//view被扫地出门。
    }

    3. KeyguardStatusViewManager.java

    说明:这个类是4.0后新增加的,其实它是从2.3的LockScreen.java分离了出来,所以它还是摆脱不了命运的束缚,依然要为LockScreen.java服务,而且它比以前更累了,如果条件需要它还要服侍其他如密码解锁,图形解锁等方式。功能就是状态视图总管。

    /***
     * Manages a number of views inside of LockScreen layouts. See below for a list of widgets
    */
    class KeyguardStatusViewManager implements OnClickListener {
     
        public KeyguardStatusViewManager(View view, KeyguardUpdateMonitor updateMonitor,LockPatternUtils lockPatternUtils, KeyguardScreenCallback callback,boolean showEmergencyButtonByDefault) {
            if (DEBUG) Log.v(TAG, "KeyguardStatusViewManager()");
            mContainer = view; //视图容器
            mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year);//格式日期
            mLockPatternUtils = lockPatternUtils;
            mUpdateMonitor = updateMonitor;
            mCallback = callback;
            mCarrierView = (TextView) findViewById(R.id.carrier);//运营商标识
            mDateView = (TextView) findViewById(R.id.date);//日期
            mStatus1View = (TextView) findViewById(R.id.status1);//sim卡状态
            mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);//闹铃状态
            mOwnerInfoView = (TextView) findViewById(R.id.propertyOf);
            mTransportView = (TransportControlView) findViewById(R.id.transport);
            mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
            mShowEmergencyButtonByDefault = showEmergencyButtonByDefault;
    //紧急呼叫按钮
            // Hide transport control view until we know we need to show it.
            if (mTransportView != null) {
                mTransportView.setVisibility(View.GONE);
            }
    
            if (mEmergencyCallButton != null) {
                mEmergencyCallButton.setText(R.string.lockscreen_emergency_call);
                mEmergencyCallButton.setOnClickListener(this);
                mEmergencyCallButton.setFocusable(false); // touch only!
            }
    
            mTransientTextManager = new TransientTextManager(mCarrierView);
    
            mUpdateMonitor.registerInfoCallback(mInfoCallback);
            mUpdateMonitor.registerSimStateCallback(mSimStateCallback);
    
            resetStatusInfo();//更新电池状态信息
            refreshDate();//刷新时间
            updateOwnerInfo();//更新所有者的信息
    
            // Required to get Marquee to work.
            final View scrollableViews[] = { mCarrierView, mDateView, mStatus1View, mOwnerInfoView,
                    mAlarmStatusView };
            for (View v : scrollableViews) {
                if (v != null) {
                    v.setSelected(true);
                }
            }
        }

    4. LockScreen.java

    说明:五种锁屏方式之一,为系统默认设置选用,名为滑动解锁,也就是4.0的那个带锁的圆。它继承于LinearLayout并实现了KeyguardScreen接口,所以他具备了接受视图的解锁事件并作出响应。

    /**
     * The screen within {@link LockPatternKeyguardView} that shows general
     * information about the device depending on its state, and how to get
     * past it, as applicable.
     */
    class LockScreen extends LinearLayout implements KeyguardScreen {
        class SlidingTabMethods implements SlidingTab.OnTriggerListener, UnlockWidgetCommonMethods {
            private final SlidingTab mSlidingTab;
    
            SlidingTabMethods(SlidingTab slidingTab) {
                mSlidingTab = slidingTab;
            }
    
            public void updateResources() {
               .......
            }
            /** 解锁响应*/
            public void onTrigger(View v, int whichHandle) {
                if (whichHandle == SlidingTab.OnTriggerListener.LEFT_HANDLE) {
                    mCallback.goToUnlockScreen();
                } else if (whichHandle == SlidingTab.OnTriggerListener.RIGHT_HANDLE) {
                    toggleRingMode();
                    mCallback.pokeWakelock();
                }
            }
    
            /** {@inheritDoc} */
            public void onGrabbedStateChange(View v, int grabbedState) {
               .......
            }
            public View getView() {
                return mSlidingTab;
            }
            public void reset(boolean animate) {
                mSlidingTab.reset(animate);
            }
            public void ping() {
            }
        }
        class WaveViewMethods implements WaveView.OnTriggerListener, UnlockWidgetCommonMethods {
            private final WaveView mWaveView;
            WaveViewMethods(WaveView waveView) {
                mWaveView = waveView;
            }
            /** {@inheritDoc} */
            public void onTrigger(View v, int whichHandle) {
                if (whichHandle == WaveView.OnTriggerListener.CENTER_HANDLE) {
                    requestUnlockScreen();
                }
            }
            /** {@inheritDoc} */
            public void onGrabbedStateChange(View v, int grabbedState) {
                // Don't poke the wake lock when returning to a state where the handle is
                // not grabbed since that can happen when the system (instead of the user)
                // cancels the grab.
                if (grabbedState == WaveView.OnTriggerListener.CENTER_HANDLE) {
                    mCallback.pokeWakelock(STAY_ON_WHILE_GRABBED_TIMEOUT);
                }
            }
    
            public void updateResources() {
            }
    
            public View getView() {
                return mWaveView;
            }
            public void reset(boolean animate) {
                mWaveView.reset();
            }
            public void ping() {
            }
        }
    
        class MultiWaveViewMethods implements MultiWaveView.OnTriggerListener,
                UnlockWidgetCommonMethods {
            private final MultiWaveView mMultiWaveView;
            private boolean mCameraDisabled;
            MultiWaveViewMethods(MultiWaveView multiWaveView) {
                mMultiWaveView = multiWaveView;
                final boolean cameraDisabled = mLockPatternUtils.getDevicePolicyManager()
                        .getCameraDisabled(null);
                if (cameraDisabled) {
                    Log.v(TAG, "Camera disabled by Device Policy");
                    mCameraDisabled = true;
                } else {
                    // Camera is enabled if resource is initially defined for MultiWaveView
                    // in the lockscreen layout file
                    mCameraDisabled = mMultiWaveView.getTargetResourceId()
                            != R.array.lockscreen_targets_with_camera;
                }
            }

    这个类的主要作用就是提供了三种不同时期的滑动解锁方案重载,具体用哪种已经在keyguard_screen_tab_unlock.xml中配置好了。

    5. KeyguardViewBase.java

    说明:一个抽象类,里面封装了一些抽象方法,并完成对各种按键的监听。条件允许的话他还会拦截keyEvent,从中作梗。

    public abstract class KeyguardViewBase extends FrameLayout {
        public boolean dispatchKeyEvent(KeyEvent event) {
            if (shouldEventKeepScreenOnWhileKeyguardShowing(event)) {
                mCallback.pokeWakelock();
            }
            if (interceptMediaKey(event)) {
                return true;
            }
            return super.dispatchKeyEvent(event);
        }
        private boolean shouldEventKeepScreenOnWhileKeyguardShowing(KeyEvent event) {
            if (event.getAction() != KeyEvent.ACTION_DOWN) {
                return false;
            }
            switch (event.getKeyCode()) {
                case KeyEvent.KEYCODE_DPAD_DOWN:
                case KeyEvent.KEYCODE_DPAD_LEFT:
                case KeyEvent.KEYCODE_DPAD_RIGHT:
                case KeyEvent.KEYCODE_DPAD_UP:
                    return false;
                default:
                    return true;
            }
        }
        private boolean interceptMediaKey(KeyEvent event) {
            final int keyCode = event.getKeyCode();
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                switch (keyCode) {
                    case KeyEvent.KEYCODE_MEDIA_PLAY:
                    case KeyEvent.KEYCODE_MEDIA_PAUSE:
                    case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                ......
        }

    6.LockPatternKeyguardView.java

    说明:1-6的最终boss,上面的零碎都直接或间接为它工作,他的作用呢,可以参考上面的锁屏view层次图。一句话它是锁屏的最高权威,该整那样,它说了算,鉴于它太过NB,这里就不贴代码了,读者必须亲自膜拜三遍。

    7. KeyguardViewManager.java

    说明:封装了WindowManager,可以随性改变锁屏视图的创建,显示,隐藏及重新设定。

    /**
     * Manages creating, showing, hiding and resetting the keyguard.  Calls back
     * via {@link com.android.internal.policy.impl.KeyguardViewCallback} to poke
     * the wake lock and report that the keyguard is done, which is in turn,
     * reported to this class by the current {@link KeyguardViewBase}.
     */
    public class KeyguardViewManager implements KeyguardWindowController {
        private final KeyguardViewProperties mKeyguardViewProperties;
        private final KeyguardUpdateMonitor mUpdateMonitor;
        private WindowManager.LayoutParams mWindowLayoutParams;
        private boolean mNeedsInput = false;
        private FrameLayout mKeyguardHost;
        private KeyguardViewBase mKeyguardView;
      .....
            protected void dispatchDraw(Canvas canvas) {
                super.dispatchDraw(canvas);
                mCallback.keyguardDoneDrawing();
            }
        }
        /**
         * Show the keyguard.  Will handle creating and attaching to the view manager
         * lazily.
         */
        public synchronized void show() {
           .....
                if (mScreenOn) {
                    mKeyguardView.show();
                }
            }
    
            // Disable aspects of the system/status/navigation bars that are not appropriate or
            // useful for the lockscreen but can be re-shown by dialogs or SHOW_WHEN_LOCKED activities.
            // Other disabled bits are handled by the KeyguardViewMediator talking directly to the
            // status bar service.
            int visFlags =
                    ( View.STATUS_BAR_DISABLE_BACK
                    | View.STATUS_BAR_DISABLE_HOME
                    );
            mKeyguardHost.setSystemUiVisibility(visFlags);
            mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
            mKeyguardHost.setVisibility(View.VISIBLE);
            mKeyguardView.requestFocus();
        }
    
        public void setNeedsInput(boolean needsInput) {
          ...
        }
        /**
         * Reset the state of the view.
         */
        public synchronized void reset() {
        }
        public synchronized void onScreenTurnedOff() {
        }
        public synchronized void onScreenTurnedOn(
                final KeyguardViewManager.ShowListener showListener) {
                // Caller should wait for this window to be shown before turning
                // on the screen.
        }
        public synchronized void verifyUnlock() {
            if (DEBUG) Log.d(TAG, "verifyUnlock()");
            show();
            mKeyguardView.verifyUnlock();
        }
        /**
         * A key has woken the device.  
         */
        public boolean wakeWhenReadyTq(int keyCode) {
          .....
        }
        /**
         * Hides the keyguard view
         */
        public synchronized void hide() {
           .....
        }
        /**
         * @return Whether the keyguard is showing
         */
        public synchronized boolean isShowing() {
            return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
        }

    8. KeyguardUpdateMonitor.java

    说明:监听系统状态值的改变如时间、SIM卡状态、电池电量等,状态值的改变会回调监听了该状态信息的对象实例。如果只是关注功能的话只需要看hadle里面的每个消息调用的方法即可。

    /**
     * Watches for updates that may be interesting to the keyguard, and provides
     * the up to date information as well as a registration for callbacks that care
     * to be updated.
     *
     * Note: under time crunch, this has been extended to include some stuff that
     * doesn't really belong here.  see {@link #handleBatteryUpdate} where it shutdowns
     * the device, and {@link #getFailedAttempts()}, {@link #reportFailedAttempt()}
     * and {@link #clearFailedAttempts()}.  Maybe we should rename this 'KeyguardContext'...
     */
    public class KeyguardUpdateMonitor {
        private Handler mHandler;
        private ContentObserver mContentObserver;
        private int mRingMode;
        private int mPhoneState;
    ......
      
        /**
         * SIM卡状态改变捕获赋值。
         * the intent and provide a {@link SimCard.State} result.
         */
        private static class SimArgs {
    
            public final IccCard.State simState;
    
            private SimArgs(Intent intent) {
                if (!TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
                    throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED");
                }
                String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
                if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
                    final String absentReason = intent
                        .getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
    
                    if (IccCard.INTENT_VALUE_ABSENT_ON_PERM_DISABLED.equals(
                            absentReason)) {
                        this.simState = IccCard.State.PERM_DISABLED;
                    } else {
                        this.simState = IccCard.State.ABSENT;
                    }
                } else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
                    this.simState = IccCard.State.READY;
                } else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
                    final String lockedReason = intent
                            .getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
                    if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
                        this.simState = IccCard.State.PIN_REQUIRED;
                    } else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
                        this.simState = IccCard.State.PUK_REQUIRED;
                    } else {
                        this.simState = IccCard.State.UNKNOWN;
                    }
                } else if (IccCard.INTENT_VALUE_LOCKED_NETWORK.equals(stateExtra)) {
                    this.simState = IccCard.State.NETWORK_LOCKED;
                } else {
                    this.simState = IccCard.State.UNKNOWN;
                }
            }
    
            public String toString() {
                return simState.toString();
            }
        }
    
        public KeyguardUpdateMonitor(Context context) {
            mContext = context;
    
            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case MSG_TIME_UPDATE:
                            handleTimeUpdate();
                            break;
                        case MSG_BATTERY_UPDATE:
                            handleBatteryUpdate(msg.arg1,  msg.arg2);
                            break;
                        case MSG_CARRIER_INFO_UPDATE:
                            handleCarrierInfoUpdate();
                            break;
                        case MSG_SIM_STATE_CHANGE:
                            handleSimStateChange((SimArgs) msg.obj);
                            break;
                        case MSG_RINGER_MODE_CHANGED:
                            handleRingerModeChange(msg.arg1);
                            break;
                        case MSG_PHONE_STATE_CHANGED:
                            handlePhoneStateChanged((String)msg.obj);
                            break;
                        case MSG_CLOCK_VISIBILITY_CHANGED:
                            handleClockVisibilityChanged();
                            break;
                        case MSG_DEVICE_PROVISIONED:
                            handleDeviceProvisioned();
                            break;
                    }
                }
            };

    9.KeyguardViewMediator.java

    说明:也是boss级别的,虽然明面上不及LockPatternKeyguardView.java,但论实权,是个深藏不露的实力派。奈何可能有把柄在他人之手,所以他必须低调,任劳任怨,为PhoneWindowManager.java所各种差遣。看它的话先把开头的50来行英文注释整清楚,然后在跳到handle里面看一下每个消息对应的执行函数,这样对这个所谓调度者就有个大概的理解了。然后就可以具体功能流程具体分析了。

     

    /**
     *有关键盘锁请求的调度者。包括键盘锁状态的查询,power management事件影响键盘锁是否应该被显示或者重置,特定的回调函数来
     *通知window manager键盘锁是什么时候显示,以及接受view视图传过来的消息表明已经成功完成解锁。
     *请注意键盘锁是在灭屏后立即被调用显示的。这样当你点亮屏幕,锁屏才能第一时间显示出来。
     *例如外部事件调度锁屏视图流程: 
     *
     *-灭屏动作-》重置锁屏并显示它为下次点亮屏幕做好准备。
     *-锁屏很自然流畅的打开了-》如果他不是安全的,隐藏之。
     *
     *来自于锁屏视图的事件:
     *-用户成功完成解锁条件-》隐藏锁屏视图,不再对输入事件进行拦截。
     *请再注意:第三方应用通过条用power managment实例可以屏蔽系统的键盘锁。
     *
     *线程和同步:
     *该类是由WindowManagerPolicy创建并运行在它的线程里,锁屏UI也是这个类的构造函数里面产生。这个apis也可以被其他线程所调用。
     *然而,这个类的方法手势同步的,同时任何一个锁屏视图都会发消息到handle来保证它是在锁屏UI线程里面执行的。
     */
    
    public class KeyguardViewMediator implements KeyguardViewCallback,
            KeyguardUpdateMonitor.InfoCallback, KeyguardUpdateMonitor.SimStateCallback {
    private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
    /**
         * This handler will be associated with the policy thread, which will also
         * be the UI thread of the keyguard.  Since the apis of the policy, and therefore
         * this class, can be called by other threads, any action that directly
         * interacts with the keyguard ui should be posted to this handler, rather
         * than called directly.
         */
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case TIMEOUT:
                        handleTimeout(msg.arg1);
                        return ;
                    case SHOW:
                        handleShow();
                        return ;
                    case HIDE:
                        handleHide();
                        return ;
                    case RESET:
                        handleReset();
                        return ;
                    case VERIFY_UNLOCK:
                        handleVerifyUnlock();
                        return;
                    case NOTIFY_SCREEN_OFF:
                        handleNotifyScreenOff();
                        return;
                    case NOTIFY_SCREEN_ON:
                        handleNotifyScreenOn((KeyguardViewManager.ShowListener)msg.obj);
                        return;
                    case WAKE_WHEN_READY:
                        handleWakeWhenReady(msg.arg1);
                        return;
                    case KEYGUARD_DONE:
                        handleKeyguardDone(msg.arg1 != 0);
                        return;
                    case KEYGUARD_DONE_DRAWING:
                        handleKeyguardDoneDrawing();
                        return;
                    case KEYGUARD_DONE_AUTHENTICATING:
                        keyguardDone(true);
                        return;
                    case SET_HIDDEN:
                        handleSetHidden(msg.arg1 != 0);
                        break;
                    case KEYGUARD_TIMEOUT:
                        synchronized (KeyguardViewMediator.this) {
                            doKeyguardLocked();
                        }
                        break;
                }
            }
        };
    private void adjustStatusBarLocked() {
           ......//控制是否能在锁屏界面下拉状态栏。
                }
    }

    10.        PhoneWindowManager.java

    说明:在Android中的地位犹如封疆之王爷,此等人物,岂能一眼看透并妄加揣测。需时日翻阅各种资料假以研究才能得出个大概.......此乃后话,当另谋篇幅。

    5,2.3keyguard锁屏解的几个小问题,贴上仅供娱乐:

    后续待补充。

    高清文档下载,请ClickMe!

  • 相关阅读:
    linux rz -e
    (转载)总结一下SQL语句中引号(')、quotedstr()、('')、format()在SQL语句中的用法
    Searching the Web论文要点
    搜索提示(search suggest)文献阅读
    C++常用数据结构(对照python)
    FM,FFM,GBDT推导
    Ranking relevance in yahoo search (2016)论文阅读
    荀子劝学篇
    不要尝试去锯木屑
    3服务器Java虚拟机配置
  • 原文地址:https://www.cnblogs.com/aaa2832/p/2594024.html
Copyright © 2011-2022 走看看