AndroidL 開機展現Keyguard鎖屏機制初探

目錄
目錄
鎖屏時序圖
開機啓動到PhoneWindowManager的systemReady方法
鎖屏加載流程
PhoneWindowManager
KeyguardServiceDelegate
KeyguardServiceWrapper
KeyguardService
KeyguardViewMediator
onSystemReady
onKeyguardLocked
showLocked
handleShow
重點一
重點二
StatusBarKeyguardViewManager
show
reset
showBouncerOrKeyguard
鎖屏時序圖
研究了將近兩天的Android5.1 Keyguard鎖屏機制,不得不說,各類饒。這裏先把鎖屏流程時序圖貢獻給你們: java


使用的是線編輯工具ProcessOn,用來編輯時序圖效果看起來不是太好。不過沒有太大關係,這個時序圖只是爲了方便咱們能清晰的對鎖屏流程有個大體的瞭解,接下來,我會詳細的分析每一個類的具體流程。android

聲明:本文基於AndroidLollipop 5.1.1_r6版本進行的源碼分析。安全

開機啓動到PhoneWindowManager的systemReady方法
準備先從開機啓動到PhoneWindowManager類的systemReady方法調用開始介紹。開機啓動流程其實也很複雜,可是本文重點在於Keyguard鎖屏的展現,因此這裏只是大致列出如何從開機啓動調用到PhoneWindowManager的systemReady()方法。具體流程以下: 
init進程->zygote進程(java世界)->system server進程。 
而在system server進程中,它的SystemServer.java中main函數會調用startOtherServices()方法,相關源碼以下:app

private void startOtherServices() {
    WindowManagerService wm = null;
    wm = WindowManagerService.main(context, inputManager,
            mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
            !mFirstBoot, mOnlyCore);
    try {
        wm.systemReady();
    } catch (Throwable e) {
        reportWtf("makeing Window Manager Service ready", e);
    }
}

既然wm是WindowManagerServer類的實例,那就須要繼續看一下這個類關於systemReady()方法的實現:async

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();
    public void systemReady() {
        mPolicy.systemReady();
    }
}

其中,mPolicy實例的生成依賴於Java的反射機制,具體流程以下:ide

public final class PolicyManager {
    private static final String POLICY_IMPL_CLASS_NAME = 
        "com.android.internal.policy.impl.Policy";
    private static final IPolicy sPolicy;
    static {
        try {
            // 獲取了Policy的類類型
            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
            // 經過Policy的類類型獲取Policy的對象實例
            sPolicy = (IPolicy)policyClass.newInstance();
        } catch (Exception e) {
        }
    }
    public static Window makeNewWindowManager(Context context) {
        return sPolicy.makeNewWindowManager(context);
    }
}函數

// Policy中繼續調用反射機制進行預加載
public class Policy implements IPolicy {
    public Window makeNewWindowManager(Context context) {
        return new PhoneWindowManager();
    }
}

經過上述代碼的跟蹤,終於來到了咱們時序圖的第一個類PhonewWindowManager的systemReady()方法了。工具

鎖屏加載流程
既然上述分析到了PhoneWindowManager,咱們也就是按照時序圖,根據時序圖上的每一個類,進行相關源碼分析(重點難點的源碼我會中文註釋)。oop

PhoneWindowManager
路徑源碼分析

frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java

源碼:

public class PhoneWindowManager implements WindowManagerPolicy {
    public void systemReady() {
        // 調用Keygurad代理類的onSystemReady方法
        mKeyguardDelegate = new KeyguardServiceDelegate(mContext);
        mKeyguardDelegate.onSystemReady();
        // ... 省略不相關源碼 
    }
}

KeyguardServiceDelegate
路徑:

frameworks/base/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java

源碼:

public class KeyguardServiceDelegate {
    protected KeyguardServiceWrapper mKeyguardService;

    public void onSystemReady() {
        if (mKeyguardService != null) {
            mKeyguardService.onSystemReady();
        } else {
            mKeyguardState.systemIsReady = true;
        }   
    } 
}

KeyguardServiceWrapper
路徑:

frameworks/base/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java

源碼:

public class KeyguardServiceWrapper implements IKeyguardService {
    private IkeyguardService mService;
    public KeyguardServiceWrapper(Context context, IKeyguardService service) {
        // 構造函數中對mService進行了初始化
        mService = service;
    }
    public void onSystemReady() {
        try {
            mService.onSystemReady();
        } catch (RemoteException e) {
            Slog.w(TAG , "Remote Exception", e); 
        }   
    }
}

因爲mService是IKeyguardService接口實現類的實例,而且mService又是在KeyguardServiceWrapper的構造函數中傳遞進來初始化的。因此,咱們又須要回到KeyguardServiceDelegate類,去看一下KeyguardServiceWrapper初始化的過程,相關代碼以下:

public class KeyguardServiceDelegate {
    public static final String KEYGUARD_PACKAGE = "com.android.systemui";
    public static final String KEYGUARD_CLASS = "com.android.systemui.keyguard.KeyguardService";

    /**
     * 使用bindService的方式來綁定服務。利用bindService的方式:
     * 調用者與服務綁定在一塊兒,調用者退出,服務即終止。
     * ps => bind方式綁定服務,服務的執行順序爲:
     * onCreate()->onBind()->onUnbind()->onDestroy()
     */
    public void bindService(Context context) {
        Intent intent = new Intent();
        intent.setClassName(KEYGUARD_PACKAGE, KEYGUARD_CLASS);
        if (!context.bindServiceAsUser(intent, mKeyguardConnection,
                Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
            Log.v(TAG, "*** Keyguard: can't bind to " + KEYGUARD_CLASS);
            mKeyguardState.showing = false;
            mKeyguardState.showingAndNotOccluded = false;
            mKeyguardState.secure = false;
            mKeyguardState.deviceHasKeyguard = false;
            hideScrim();
        } else {
            if (DEBUG) Log.v(TAG, "*** Keyguard started");
        }   
    }   

    private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            if (DEBUG) Log.v(TAG, "*** Keyguard connected (yay!)");
            // 當和服務綁定後,這IKeyguardService.Stub.asInterface(service)獲取的就是KeyguardService的類實例
            mKeyguardService = new KeyguardServiceWrapper(mContext,
                    IKeyguardService.Stub.asInterface(service));
            if (mKeyguardState.systemIsReady) {
                // If the system is ready, it means keyguard crashed and restarted.
                mKeyguardService.onSystemReady();
                // This is used to hide the scrim once keyguard displays.
                mKeyguardService.onScreenTurnedOn(new KeyguardShowDelegate(
                        mShowListenerWhenConnect));
                mShowListenerWhenConnect = null;
            }
            if (mKeyguardState.bootCompleted) {
                mKeyguardService.onBootCompleted();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if (DEBUG) Log.v(TAG, "*** Keyguard disconnected (boo!)");
            mKeyguardService = null;
        }
    };
}

接下來,就須要去看一下KeyguardService類的onSystemReady方法了。

KeyguardService
路徑:

/frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java

源碼:

public class KeyguardService extends Service {
    private KeyguardViewMediator mKeyguardViewMediator;

    @Override // Binder interface
    public void onSystemReady() {
        // 檢查調用進程是否具體SYSTEM權限
        checkPermission();
        // 真正的鎖屏入口
        mKeyguardViewMediator.onSystemReady();
    }
}

Wow,終於到了咱們的主角KeyguardViewMediator登場了。

KeyguardViewMediator
路徑:

frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java

雖然KeyguardViewMediator是鎖屏的入口,可是從這裏到鎖屏的真正展示還有很長一段路。接下來,爲了方便,咱們都是基於當前類的函數進行分析。

onSystemReady
public class KeyguardViewMediator extends SystemUI {
    // 開機顯示鎖屏入口函數   
    public void onSystemReady() {
        synchronized (this) {
            if (DEBUG) Log.d(TAG, "onSystemReady");
            mSystemReady = true;            

            // 判斷是否使用生物識別解鎖(相似:人臉識別、聲音識別等)
            if (mLockPatternUtils.usingBiometricWeak()
                    && mLockPatternUtils.isBiometricWeakInstalled()) {
                if (DEBUG) Log.d(TAG, "suppressing biometric unlock during boot");
                mUpdateMonitor.setAlternateUnlockEnabled(false);
            } else {
                mUpdateMonitor.setAlternateUnlockEnabled(true);
            }
            // 進行鎖屏預處理判斷等操做
            doKeyguardLocked(null);
        }
    }
}

onKeyguardLocked()
其中,doKeyguardLocked是來作啓動鎖屏界面的預處理方法,咱們來看一下這個函數的具體實現:

public class KeyguardViewMediator extends SystemUI {
    private void doKeyguardLocked(Bundle options) {
        if (!mExternallyEnabled) {
            // 其餘應用禁止鎖屏呈現,例如接電話等操做.
            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
            return;
        }

        // 判斷鎖屏是否正在展現
        if (mStatusBarKeyguardViewManager.isShowing()) {
            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
            resetStateLocked();
            return;
        }

        // 判斷是否無sim卡也可以使用手機
        final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", false);
        // 獲取sim卡狀態
        final boolean absent = SubscriptionManager.isValidSubscriptionId(
            mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.ABSENT));
        final boolean disabled = SubscriptionManager.isValidSubscriptionId(
            mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.PERM_DISABLED));
        final boolean lockedOrMissing = mUpdateMonitor.isSimPinSecure()
                || ((absent || disabled) && requireSim);

        if (!lockedOrMissing && shouldWaitForProvisioning()) {
            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
                    + " and the sim is not locked or missing");
            return;
        }

        if (mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) {
            // Settings中沒有啓用鎖屏
            return;
        }

        if (mLockPatternUtils.checkVoldPassword()) {
            setShowingLocked(false);
            hideLocked();
            return;
        }

        // 通過上述判斷後,去展現鎖屏
        showLocked(options);
    }
}

注意showLocked(options)方法調用,這個是啓動鎖屏的關鍵方法。這裏的options傳遞的值爲null。

showLocked()
public class KeyguardViewMediator extends SystemUI {
   private void showLocked(Bundle options) {
        if (DEBUG) Log.d(TAG, "showLocked");
        // 獲取PARTIAL_WAKE_LOCK,不受電源鍵影響,不讓CPU進入休眠狀態 
        mShowKeyguardWakeLock.acquire();
        // 發送msg.what爲SHOW類型的message
        Message msg = mHandler.obtainMessage(SHOW, options);
        mHandler.sendMessage(msg);
    }
}

注意:

mShowKeyguardWakeLock.acquire(); ⇒ 獲取以後是沒法讓CPU休眠,不要忘記釋放,不讓會增長系統功耗。

跟mShowKeyguardWakeLock相關的代碼以下:

public class KeyguardViewMediator extends SystemUI {
    private PowerManager.WakeLock mShowKeyguardWakeLock;
    private void setupLocked() {
        // 獲取了PARTIAL_WAKE_LOCK鎖,即不受電源鍵控制,即便按下電源鍵也不能使系統進入休眠狀態
        mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
        mShowKeyguardWakeLock.setReferenceCounted(false);
    }

    private void showLocked(Bundle options) {
        // 獲取PARTIAL_WAKE_LOCK
        mShowKeyguardWakeLock.acquire();
    }

    private void handleShow(Bundle options) {
        synchronized (KeyguardViewMediator.this) {
            // 釋放PARTIAL_WAKE_LOCK
            mShowKeyguardWakeLock.release();
        }
    }
}

handleShow()
既然是Handler Message機制,那咱們就要去看一下mHandler類實例是如何處理SHOW類型的消息了。mHandle處理SHOW類型消息的方法以下:

public class KeyguardViewMediator extends SystemUI {
    private Handler mHandler = new Handler(Looper.myLooper(), null /*callback*/, true /*async*/) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SHOW:
                    handleShow((Bundle) msg.obj);
            }
        }
    }

    private void handleShow(Bundle options) {
        synchronized (KeyguardViewMediator.this) {
            if (!mSystemReady) {
                // 系統未Ready,則不呈現鎖屏
                return;
            } else {
                if (DEBUG) Log.d(TAG, "handleShow");
            }

            setShowingLocked(true);
            // 展現鎖屏界面
            mStatusBarKeyguardViewManager.show(options);
            mHiding = false;
            resetKeyguardDonePendingLocked();
            mHideAnimationRun = false;
            updateActivityLockScreenState();
            adjustStatusBarLocked();
            userActivity();

            // Do this at the end to not slow down display of the keyguard.
            playSounds(true);

            mShowKeyguardWakeLock.release();
        }
        mKeyguardDisplayManager.show();
    }
}

針對這個方法,有幾個重點須要強調一下。

重點一
Android5.1和Android4.4鎖屏機制展現的區別?

解答:在Android5.1中,keyguard自己再也不是一個獨立的apk,而是跟SystemUI進行了合併,做爲SystemUI的靜態庫進行調用。對比Android5.1和Android4.4的SystemUI模塊的Android.mk文件能夠更加直觀的對比。

Android5.1 SystemUI Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java--files-under, src)

# 指定Keyguard做爲靜態庫
LOCAL_STATIC_JAVA_LIBRARIES := Keyguard

LOCAL_JAVA_LIBRATIES := telephony-common
# 指定名稱
LOCAL_PACKAGE_NAME := SystemUI
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
LOCAL_RESOURCE_DIR := \
    frameworks/base/packages/Keyguard/res \
    $(LOCAL_PATH)/res
LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages com.android.keyguard
# 編譯成apk
include $(BUILD_PACKAGE)

Android4.4 SystemUI Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
    src/com/android/systemui/EventLogTags.logtags
LOCAL_JAVA_LIBRARIES := telephony-common
LOCAL_PACKAGE_NAME := SystemUI
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))

因此,系統調試鎖屏,只須要單獨編譯SystemUI模塊,而後替換SystemUI.apk便可。

重點二
如何替換系統鎖屏,改成咱們的鎖屏應用?

解答:我的認爲,若是可以修改SystemUI方法,那最好就是重載handleShow()方法,在這個方法中實現咱們本身的鎖屏界面。例如經過Activity跳轉,別忘了釋放PARTIAL_WEAK_LOCK。

重載這個方法,能夠最大程度的不影響Android系統邏輯(ps:我的意見,你們有更好的辦法能夠指點我)。

StatusBarKeyguardViewManager
位置:

framework/base/package/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java

分析這個類,咱們確定是先從show方法開始入手。

show()
show方法的源碼以下:

public class StatusBarKeyguardViewManager {
    public void show(Bundle options) {
        // 設置keguard是否顯示的標誌
        mShowing = true;
        mStatusBarWindowManager.setKeyguardShowing(true);
        // 重置view的狀態,進行keyguard鎖屏顯示
        reset();
    }
}

reset()
源碼以下:

public class StatusBarKeyguardViewManager {
    public void reset() {
        Log.e("TAG", "mShowing:" + mShowing + ", mOccluded:" + mOccluded);
        if (mShowing) {
            if (mOccluded) {
                mPhoneStatusBar.hideKeyguard();
                mBouncer.hide(false /* destroyView */);
            } else {
                // 判斷是調用安全鎖屏仍是調用滑動鎖屏
                showBouncerOrKeyguard();
            }
            updateStates();
        }
    }
}


showBouncerOrKeyguard()
public class StatusBarKeyguardViewManager {
    private void showBouncerOrKeyguard() {
        if (mBouncer.needsFullscreenBouncer()) {

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

接下來,就是view展現的過程了。其中,mBouncer是用來顯示安全鎖屏,例如圖案、密碼、PIN碼等。有興趣的同窗能夠繼續跟蹤一下mBouncer的展現或者mPhoneStatusBar的展現過程。

設置開機默認無keyguard
閱讀數 58

設置開機默認無keyguard文章目錄設置開機默認無keyguard1.Description2.Analysis3.solution4.summary1.Description默認狀況下keygua... 博文  

相關文章
相關標籤/搜索