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!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM