目錄
目錄
鎖屏時序圖
開機啓動到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... 博文