基于android7.1.1鎖屏模塊分析

<h2>基于Android 7.1.1鎖屏模塊分析</h2>

1.代碼基于Android7.1.1可以從 https://android.googlesource.com/ 中獲取代碼,主要關(guān)注兩個package椿猎,
一個是frameworks/base/package/Keyguard主要為系統(tǒng)中鎖屏模塊的代碼,例如我們看到的鎖屏界面宴合,以及畫開鎖屏界面會有密碼,圖案等不同的解鎖界面等,主要是和鎖屏的view相關(guān)的代碼澎迎。
二是frameworks/base/package/SystemUI這個代表的是系統(tǒng)UI稼虎,狀態(tài)欄衅檀,通知中心顯示,最近任務(wù)列表霎俩,鎖屏的都在這里面控制哀军,只看鎖屏模塊的話,keyguard相當(dāng)于處理顯示view打却,而SystemUI是屬于對keyguard進(jìn)行管理的杉适,控制其顯示邏輯的。

2.從SystemUI目錄下的Android.mk也可以看出來這兩個模塊的關(guān)系学密。
LOCAL_STATIC_JAVA_LIBRARIES := \Keyguard淘衙,LOCAL_RESOURCE_DIR := \frameworks/base/packages/Keyguard/res \
SystemUI編譯需要是要依賴Keyguard模塊的。
修改Keyguard模塊的代碼后編譯此模塊不會產(chǎn)生相應(yīng)的apk而是要在繼續(xù)編譯SystemUI模塊的代碼產(chǎn)生的SystemUI.apk放到手機(jī)里才會起作用腻暮,也就是說鎖屏不會產(chǎn)生一個單獨(dú)的apk文件彤守,而是由SystemUI.apk包含了現(xiàn)有手機(jī)的鎖屏模塊。

3.鎖屏分為兩部分:

  • 鎖屏界面稱為Keyguard(常見為鎖屏界面時鐘哭靖,通知界面)
  • 解鎖界面稱為Bouncer (常見為滑開鎖屏界面具垫,顯示密碼,圖案等解鎖方式界面)
Paste_Image.png

上圖為keyguard界面的時序調(diào)用關(guān)系,這只是展示了鎖屏界面试幽,還有鎖屏界面后面的解鎖界面還需要加載和展示
StatusBarKeyguardViewManager.java中的showBouncerOrKeyguard()方法中筝蚕,從上圖看,調(diào)用此方法加載鎖屏界面:

protected void showBouncerOrKeyguard() {
        if (mBouncer.needsFullscreenBouncer()) {

            // The keyguard might be showing (already). So we need to hide it.
            mPhoneStatusBar.hideKeyguard();
            mBouncer.show(true /* resetSecuritySelection */);
        } else {
            mPhoneStatusBar.showKeyguard();
            mBouncer.hide(false /* destroyView */);
            mBouncer.prepare();
        }
    }

此方法中就是對于顯示Bouncer還是Keyguard的區(qū)分,先執(zhí)行了KeyguardBouncer.java中的needsFullscreenBouncer()方法:

public boolean needsFullscreenBouncer() {
        ensureView();
        if (mKeyguardView != null) {
            SecurityMode mode = mKeyguardView.getSecurityMode();
            return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk;
        }
        return false;
    }

從此方法可以看出當(dāng)解鎖方式為SimPinSimPuk時才會返回為true起宽,對應(yīng)著sim卡的兩種狀態(tài)洲胖,這個時候要顯示一個全屏的界面,必須要輸入正確相應(yīng)的密碼才能進(jìn)入坯沪,否則不能使用手機(jī)绿映。

4.KeyguardSystemUI的交互

既然Keyguard是作為SystemUI的library存在的,那么在SystemUI中調(diào)用Keyguard中的類腐晾,就很方便了叉弦,只需要直接導(dǎo)入類即可,但是在Keyguard中如何和SystemUI通信呢藻糖?主要關(guān)注三個類:

  • KeyguardViewMediator.java此類是在SystemUI中做統(tǒng)一調(diào)度的淹冰,也就是像我們長熟悉的熄屏,亮屏巨柒,鎖屏等的處理都是在這里面的做的樱拴,它是一個對Keyguard的調(diào)度者。
  • KeyguardUpdateMonitor.java潘拱,從此類的說明中就可以看的出來疹鳄,它是來處理鎖屏更新操作的類,KeyguardViewMediator.java等對于鎖屏更新的額相關(guān)處理都是在這里面進(jìn)行的操作芦岂。
  • KeyguardUpdateMonitorCallback.java瘪弓,作為更新之后的回調(diào)類,當(dāng)我們跟新一些狀態(tài)之后禽最,還需要更新之后反饋一些狀態(tài)時就會需要用到此類腺怯,就像我們進(jìn)行網(wǎng)絡(luò)請求,不能就發(fā)送網(wǎng)絡(luò)請求后就不管了川无,需要反饋結(jié)果是連接成功了呛占,還是失敗了,需要有明確的信息幫助我們進(jìn)行下一步操作懦趋。

這樣我們就清楚了整個SystemUIKeyguard的流程:

調(diào)用關(guān)系.png

這樣我們就知道了整體鎖屏是如何通信交流的晾虑,關(guān)鍵的兩個類就是KeyguardUpdateMonitor.java,
KeyguardUpdateMonitorCallback.java,再明確一下這兩個類的作用:

  • KeyguardUpdateMonitor.java做鎖屏狀態(tài)變更等代碼的處理
  • KeyguardUpdateMonitorCallback.java當(dāng)KeyguardSystemUI中有需要回調(diào)的類仅叫,就需要先注冊此Callback然后再有具體實(shí)現(xiàn)帜篇。

當(dāng)我們需要新加處理代碼時就知道了,在KeyguardUpdateMonitorCallback.java接口中聲明诫咱,在KeyguardUpdateMonitor.java中調(diào)用生命函數(shù)笙隙,在所要更新的類中注冊,當(dāng)接收到回調(diào)借口函數(shù)是坎缭,執(zhí)行方法即可竟痰。

5.如何通過回調(diào)函數(shù)通知多個類(借鑒源碼額外分析理解)

我們都會使用回調(diào)函數(shù)签钩,但是一般寫法的回調(diào)函數(shù)只能是1對1的,也就是說A坏快,和B類之間通過接口回調(diào)铅檩,但是如果a類需要發(fā)送消息,b類莽鸿,c類等其他類都需要執(zhí)行回調(diào)函數(shù)時柠并,我們需要怎么做呢?查看此處源碼富拗,我們就會使用了這個方法,通過在b類鸣戴,c類中注冊相應(yīng)的接口啃沪,這樣在a類中遍歷所注冊的接口,然后即可調(diào)用b,c類中的方法實(shí)現(xiàn)窄锅,這樣也就是a類實(shí)現(xiàn)狀態(tài)變化创千,同時跟新了b,c類等。

示例代碼:
我們定義了三個類分別為:
CallbackUpdateMonitor.java

public class CallbackUpdateMonitor {

    private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();

    private volatile static CallbackUpdateMonitor sInstance;

    public static CallbackUpdateMonitor getInstance() {
        if (sInstance == null) {
            synchronized (CallbackUpdateMonitor.class) {
                if (sInstance == null) {
                    sInstance = new CallbackUpdateMonitor();
                }
            }
        }
        return sInstance;
    }

    void udpateCurrentState() {
        for (int i = 0; i < mCallbacks.size(); i++) {
            Callback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.updateOtherState();
            }
        }
    }

    void registerCallback(Callback callback) {
        for (int i = 0; i < mCallbacks.size(); i++) {
            if (mCallbacks.get(i).get() == callback) {
                Log.d("callbacktest", "already add callback = " + callback, new Exception("Called by"));
                return;
            }
        }
        mCallbacks.add(new WeakReference<Callback>(callback));
        removeCallback(null); // remove unused references
    }

    void removeCallback(Callback callback) {
        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
            if (mCallbacks.get(i).get() == callback) {
                mCallbacks.remove(i);
            }
        }
    }

    public interface Callback {
        void updateOtherState();
    }

}

CallbackTestB

public class CallbackTestB {

    CallbackUpdateMonitor.Callback callback = new CallbackUpdateMonitor.Callback() {
        @Override
        public void updateOtherState() {
            Log.d("callbacktest", "you update b");
        }
    };

    CallbackTestB() {
        CallbackUpdateMonitor.getInstance().registerCallback(callback);
    }

}

CallbackTestC

public class CallBackTestC {

    CallbackUpdateMonitor.Callback callback = new CallbackUpdateMonitor.Callback() {
        @Override
        public void updateOtherState() {
            Log.d("callbacktest", "you update c");
        }
    };

    CallBackTestC() {
        CallbackUpdateMonitor.getInstance().registerCallback(callback);
    }
}

然后在一個Activity中調(diào)用更新方法即可

public class MainActivity extends AppCompatActivity {

    Button mButton;
    CallbackUpdateMonitor callbackTestA;
    CallbackTestB callbackTestB;
    CallBackTestC callBackTestC;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        callbackTestA = CallbackUpdateMonitor.getInstance();
        callbackTestB = new CallbackTestB();
        callBackTestC = new CallBackTestC();

        mButton = (Button) findViewById(R.id.button);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                callbackTestA.udpateCurrentState();
            }
        });
    }
}
log截圖.png

從上述log截圖可以看出來入偷,當(dāng)我們點(diǎn)擊了button時追驴,即可遍歷代碼更新所注冊兩個類的方法。
我們將接口和管理分發(fā)的中間代碼都放到CallbackUpdateMonitor.java類中進(jìn)行疏之,當(dāng)我們需要在添加其他類的時候殿雪,只需要通過注冊之后通過CallbackUpdateMonitor來就可以進(jìn)行通信了。

6.接下來我們再看一下鎖屏方式和設(shè)置的關(guān)聯(lián)

當(dāng)我們在設(shè)置中選擇不同的鎖屏方式锋爪,為什么就可以設(shè)置不同的解鎖密碼丙曙,這個是由什么控制的?
這里只列出關(guān)鍵代碼:
Settings應(yīng)用包中的ChooseLockGeneric.java中的

    private boolean setUnlockMethod(String unlockMethod) {
            EventLog.writeEvent(EventLogTags.LOCK_SCREEN_TYPE, unlockMethod);
            if (KEY_UNLOCK_SET_OFF.equals(unlockMethod)) {
                updateUnlockMethodAndFinish(
                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, true /* disabled */ );
            } else if (KEY_UNLOCK_SET_NONE.equals(unlockMethod)) {
                updateUnlockMethodAndFinish(
                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, false /* disabled */ );
            } else if (KEY_UNLOCK_SET_MANAGED.equals(unlockMethod)) {
                maybeEnableEncryption(DevicePolicyManager.PASSWORD_QUALITY_MANAGED, false);
            } else if (KEY_UNLOCK_SET_PATTERN.equals(unlockMethod)) {
                maybeEnableEncryption(
                        DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false);
            } else if (KEY_UNLOCK_SET_PIN.equals(unlockMethod)) {
                maybeEnableEncryption(
                        DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, false);
            } else if (KEY_UNLOCK_SET_PASSWORD.equals(unlockMethod)) {
                maybeEnableEncryption(
                        DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, false);
            } else {
                Log.e(TAG, "Encountered unknown unlock method to set: " + unlockMethod);
                return false;
            }
            return true;
        }

一個關(guān)鍵類為LockPatternUtils.java它是用來設(shè)置和鎖屏交互的類其骄,設(shè)置亏镰,更新解鎖方式和加密等操作都是通過此類來完成的。我們看到它設(shè)置鎖屏方式就是設(shè)置了不同的DevicePolicyManager拯爽,這樣的話我們就需要看在鎖屏中的代碼索抓,根據(jù)搜關(guān)鍵字DevicePolicyManager我們就可以發(fā)現(xiàn)使如何設(shè)置當(dāng)前的鎖屏方式的。
keyguard包中的代碼KeyguardSecurityModel.java

    public enum SecurityMode {
        Invalid, // NULL state
        None, // No security enabled
        Pattern, // Unlock by drawing a pattern.
        Password, // Unlock by entering an alphanumeric password
        PIN, // Strictly numeric password
        SimPin, // Unlock by entering a sim pin.
        SimPuk // Unlock by entering a sim puk
    }
    SecurityMode getSecurityMode() {
        KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);

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

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

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

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

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

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

從而可以看出設(shè)置和鎖屏中通過LockPatternUtils設(shè)置和保存不同的密碼等來區(qū)分當(dāng)前的解鎖方式毯炮,然后再看到它的調(diào)用出的代碼KeyguardSecurityContainer類逼肯,此類是屏幕 解鎖界面的父view,同過此view跟新當(dāng)前解鎖方式view:

    private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
        KeyguardSecurityView view = null;
        final int children = mSecurityViewFlipper.getChildCount();
        for (int child = 0; child < children; child++) {
            if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
                view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));
                break;
            }
        }
        int layoutId = getLayoutIdFor(securityMode);
        if (view == null && layoutId != 0) {
            final LayoutInflater inflater = LayoutInflater.from(mContext);
            if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
            View v = inflater.inflate(layoutId, mSecurityViewFlipper, false);
            mSecurityViewFlipper.addView(v);
            updateSecurityView(v);
            view = (KeyguardSecurityView)v;
        }

        return view;
    }
    protected int getLayoutIdFor(SecurityMode securityMode) {
        switch (securityMode) {
            case Pattern: return R.layout.keyguard_pattern_view;
            case PIN: return R.layout.keyguard_pin_view;
            case Password: return R.layout.keyguard_password_view;
            case SimPin: return R.layout.keyguard_sim_pin_view;
            case SimPuk: return R.layout.keyguard_sim_puk_view;
            default:
                return 0;
        }
    }

這樣就可以看到不同的解鎖方式對應(yīng)者不同的自定義布局例如:
密碼布局為:KeyguardPasswordView.java
Pin碼為:KeyguardPINView.java
圖案為:KeyguardPatternView
等等.
這樣能夠我們就從SystemUI和Keyguard來分析了鎖屏的邏輯和相關(guān)操作否副,主體就是這樣汉矿。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市备禀,隨后出現(xiàn)的幾起案子洲拇,更是在濱河造成了極大的恐慌奈揍,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,496評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赋续,死亡現(xiàn)場離奇詭異男翰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)纽乱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,187評論 3 385
  • 文/潘曉璐 我一進(jìn)店門蛾绎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鸦列,你說我怎么就攤上這事租冠。” “怎么了薯嗤?”我有些...
    開封第一講書人閱讀 157,091評論 0 348
  • 文/不壞的土叔 我叫張陵顽爹,是天一觀的道長。 經(jīng)常有香客問我骆姐,道長镜粤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,458評論 1 283
  • 正文 為了忘掉前任玻褪,我火速辦了婚禮肉渴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘带射。我一直安慰自己同规,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,542評論 6 385
  • 文/花漫 我一把揭開白布庸诱。 她就那樣靜靜地躺著捻浦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪桥爽。 梳的紋絲不亂的頭發(fā)上朱灿,一...
    開封第一講書人閱讀 49,802評論 1 290
  • 那天,我揣著相機(jī)與錄音钠四,去河邊找鬼盗扒。 笑死,一個胖子當(dāng)著我的面吹牛缀去,可吹牛的內(nèi)容都是我干的侣灶。 我是一名探鬼主播,決...
    沈念sama閱讀 38,945評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼缕碎,長吁一口氣:“原來是場噩夢啊……” “哼褥影!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起咏雌,我...
    開封第一講書人閱讀 37,709評論 0 266
  • 序言:老撾萬榮一對情侶失蹤凡怎,失蹤者是張志新(化名)和其女友劉穎校焦,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體统倒,經(jīng)...
    沈念sama閱讀 44,158評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡寨典,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,502評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了房匆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耸成。...
    茶點(diǎn)故事閱讀 38,637評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖浴鸿,靈堂內(nèi)的尸體忽然破棺而出井氢,到底是詐尸還是另有隱情,我是刑警寧澤岳链,帶...
    沈念sama閱讀 34,300評論 4 329
  • 正文 年R本政府宣布毙沾,位于F島的核電站,受9級特大地震影響宠页,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜寇仓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,911評論 3 313
  • 文/蒙蒙 一举户、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧遍烦,春花似錦俭嘁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,744評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至罢猪,卻和暖如春近她,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背膳帕。 一陣腳步聲響...
    開封第一講書人閱讀 31,982評論 1 266
  • 我被黑心中介騙來泰國打工粘捎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人危彩。 一個月前我還...
    沈念sama閱讀 46,344評論 2 360
  • 正文 我出身青樓攒磨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親汤徽。 傳聞我的和親對象是個殘疾皇子娩缰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,500評論 2 348

推薦閱讀更多精彩內(nèi)容