本文簡單分析,android啟動之後,鎖屏界面啟動過程。
android系統開機後會運行PhoneWindowManage管理系統相關按鍵和事件,
看下PhoneWindowManager鎖屏相關
1.Pwm初始化KeyguardViewMediator類,用來接受PhoneWindowManager傳遞相關事件
[java]
<PRE class=java name="code"> /** {@inheritDoc} */
public void init(Context context, IWindowManager windowManager,
WindowManagerFuncs windowManagerFuncs,
LocalPowerManager powerManager) {
mContext = context;
mWindowManager = windowManager;
mWindowManagerFuncs = windowManagerFuncs;
mPowerManager = powerManager;
<SPAN style="COLOR: #ff0000"> mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager);
</SPAN> ........}</PRE><BR>
<PRE></PRE>
<P>2.系統啟動完成會調用SystemReady函數,進入鎖屏流程</P>
<PRE class=java name="code"> /** {@inheritDoc} */
public void systemReady() {
// tell the keyguard
<SPAN style="COLOR: #ff0000"> mKeyguardMediator.onSystemReady();
</SPAN> android.os.SystemProperties.set("dev.bootcomplete", "1");
synchronized (mLock) {
updateOrientationListenerLp();
mSystemReady = true;
mHandler.post(new Runnable() {
public void run() {
updateSettings();
}
});
}
}</PRE>
<P>3.KeyguradMediator中onSystemReady函數</P>
<PRE class=java name="code"> /**
* Let us know that the system is ready after startup.
*/
public void onSystemReady() {
synchronized (this) {
if (DBG_WAKE) Log.d(TAG, "onSystemReady");
mSystemReady = true;
doKeyguardLocked();
}
}</PRE>
<P><BR>
4.doKeyguardLocked進入鎖屏判斷,是否有第三方鎖屏應用文件。</P>
<PRE class=java name="code"> /**
* Enable the keyguard if the settings are appropriate. Return true if all
* work that will happen is done; returns false if the caller can wait for
* the keyguard to be shown.
*/
private void doKeyguardLocked() {
// if another app is disabling us, don't show
if (!mExternallyEnabled && !mUpdateMonitor.DM_IsLocked()) {
if (DEBUG) Xlog.d(TAG, "doKeyguard: not showing because externally disabled");
// note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
// for an occasional ugly flicker in this situation:
// 1) receive a call with the screen on (no keyguard) or make a call
// 2) screen times out
// 3) user hits key to turn screen back on
// instead, we reenable the keyguard when we know the screen is off and the call
// ends (see the broadcast receiver below)
// TODO: clean this up when we have better support at the window manager level
// for apps that wish to be on top of the keyguard
return;
}
// if the keyguard is already showing, don't bother
if (mKeyguardViewManager.isShowing()) {
if (DEBUG) Xlog.d(TAG, "doKeyguard: not showing because it is already showing");
return;
}
// if the setup wizard hasn't run yet, don't show
final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",
false);
final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();
final IccCard.State state = mUpdateMonitor.getSimState(Phone.GEMINI_SIM_1);
final IccCard.State stateGemini = mUpdateMonitor.getSimState(Phone.GEMINI_SIM_2);
boolean lockedOrMissing = state.isPinLocked()
|| state == IccCard.State.PERM_DISABLED
&& requireSim;
boolean lockedOrMissingGemini = stateGemini.isPinLocked()
|| stateGemini == IccCard.State.PERM_DISABLED
&& requireSim;
if (FeatureOption.MTK_GEMINI_SUPPORT){
lockedOrMissing = lockedOrMissing || lockedOrMissingGemini;
}
boolean keyguardisable = mLockPatternUtils.isLockScreenDisabled();
Log.i(TAG, "lockedOrMissing is "+lockedOrMissing+", requireSim="+requireSim
+", provisioned="+provisioned+", keyguardisable="+keyguardisable);
if (!lockedOrMissing && !provisioned) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
+ " and the sim is not locked or missing");
return;
}
if (keyguardisable && !lockedOrMissing) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
return;
}
if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
showLocked();
}</PRE>
<P>5.沒有第三方鎖屏應用或禁用鎖屏,則進入showLocked函數</P>
<PRE class=java name="code"> /**
* Send message to keyguard telling it to show itself
* @see #handleShow()
*/
private void showLocked() {
if (DEBUG) Xlog.d(TAG, "showLocked");
// ensure we stay awake until we are finished displaying the keyguard
mShowKeyguardWakeLock.acquire();
Message msg = mHandler.obtainMessage(SHOW);
if (isAlarmBoot() && !mChecked) {
mChecked = true;
Log.i(TAG, "it's alarm boot, delay 3s to show");
mHandler.sendMessageDelayed(msg, 5000);
} else {
mHandler.sendMessage(msg);
}
}</PRE>
<P><BR>
6.showLocked函數通過handler,發送顯示鎖屏信息或延時處理,handler接受消息,直接調用handleShow處理<BR>
</P>
<PRE class=java name="code"> /**
* Handle message sent by {@link #showLocked}.
* @see #SHOW
*/
private void handleShow() {
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Xlog.d(TAG, "handleShow");
if (!mSystemReady) return;
if (mShowing == true) return;
mShowCount++;
//avoid the int overflow
if (mShowCount == (int)Math.pow(2, 32)){
mShowCount=2;
}
Xlog.d(TAG, "handleShow, count="+mShowCount);
mKeyguardViewManager.show();
mShowing = true;
adjustUserActivityLocked();
adjustStatusBarLocked();
try {
ActivityManagerNative.getDefault().closeSystemDialogs("lock");
} catch (RemoteException e) {
}
// Do this at the end to not slow down display of the keyguard.
playSounds(true);
mShowKeyguardWakeLock.release();
}
}
</PRE>
<P>7.進入KeyguardViewManager中的show();KeyguardViewManager是鎖屏管理處理類,該類在KeyguraMeditor初始化</P>
<PRE class=java name="code"> /**
* Show the keyguard. Will handle creating and attaching to the view manager
* lazily.
*/
public synchronized void show() {
if (DEBUG) Xlog.d(TAG, "show(); mKeyguardView==" + mKeyguardView);
Resources res = mContext.getResources();
boolean enableScreenRotation =
SystemProperties.getBoolean("lockscreen.rot_override",false)
|| res.getBoolean(R.bool.config_enableLockScreenRotation);
if (mKeyguardHost == null) {
if (DEBUG) Xlog.d(TAG, "keyguard host is null, creating it...");
mKeyguardHost = new KeyguardViewHost(mContext, mCallback);
final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;
int flags = WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
| WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER
| WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
if (mUpdateMonitor.DM_IsLocked()) {//in the first created
flags &= ~WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
flags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
} else {
flags &= ~WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
flags &= ~WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
flags |= WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
}
if (!mNeedsInput) {
flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
if (ActivityManager.isHighEndGfx(((WindowManager)mContext.getSystemService(
Context.WINDOW_SERVICE)).getDefaultDisplay())) {
flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
stretch, stretch, WindowManager.LayoutParams.TYPE_KEYGUARD,
flags, PixelFormat.TRANSLUCENT);
lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
lp.windowAnimations = com.android.internal.R.style.Animation_LockScreen;
if (ActivityManager.isHighEndGfx(((WindowManager)mContext.getSystemService(
Context.WINDOW_SERVICE)).getDefaultDisplay())) {
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
lp.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
}
lp.setTitle("Keyguard");
mWindowLayoutParams = lp;
mViewManager.addView(mKeyguardHost, lp);
}
if (enableScreenRotation || FeatureOption.MTK_TB_APP_LANDSCAPE_SUPPORT) {
if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen On!");
mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;
} else {
if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen Off!");
mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
}
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
if (mKeyguardView == null) {
if (DEBUG) Xlog.d(TAG, "keyguard view is null, creating it...");
mKeyguardView = mKeyguardViewProperties.createKeyguardView(mContext, mUpdateMonitor, this);
mKeyguardView.setId(R.id.lock_screen);
mKeyguardView.setCallback(mCallback);
final ViewGroup.LayoutParams lp = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mKeyguardHost.addView(mKeyguardView, lp);
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();
}</PRE><PRE class=java name="code"> </PRE>
<P>show函數主要鎖屏界面添加到ViewManager中顯示,主要看下KeyguardView創建工程 </P>
<P>mKeyguardView = mKeyguardViewProperties.createKeyguardView(mContext, mUpdateMonitor, this);</P>
<P>看下mKeyguraViewProperties</P>
<P>通過源碼可知KeyViewMediator初始化創建,傳入KeyguardViewManager</P>
<P>mKeyguardViewProperties<BR>
= new LockPatternKeyguardViewProperties(mLockPatternUtils, mUpdateMonitor);</P>
<P>mKeyguardViewManager = new KeyguardViewManager(<BR>
context, WindowManagerImpl.getDefault(), this,<BR>
mKeyguardViewProperties, mUpdateMonitor);<BR>
8.LockPatternKeyguardViewProperties類中CreateKeyGuardView函數,初始化LockPatternKeyguardView,LockPatternKeyguardView便是我們的鎖屏界面</P>
<PRE class=java name="code">/**
* Knows how to create a lock pattern keyguard view, and answer questions about
* it (even if it hasn't been created, per the interface specs).
*/
public class LockPatternKeyguardViewProperties implements KeyguardViewProperties {
private final LockPatternUtils mLockPatternUtils;
private final KeyguardUpdateMonitor mUpdateMonitor;
/**
* @param lockPatternUtils Used to know whether the pattern enabled, and passed
* onto the keygaurd view when it is created.
* @param updateMonitor Used to know whether the sim pin is enabled, and passed
* onto the keyguard view when it is created.
*/
public LockPatternKeyguardViewProperties(LockPatternUtils lockPatternUtils,
KeyguardUpdateMonitor updateMonitor) {
mLockPatternUtils = lockPatternUtils;
mUpdateMonitor = updateMonitor;
}
public KeyguardViewBase createKeyguardView(Context context,
KeyguardUpdateMonitor updateMonitor,
KeyguardWindowController controller) {
return new LockPatternKeyguardView(context, updateMonitor,
mLockPatternUtils, controller);
}
public boolean isSecure() {
return mLockPatternUtils.isSecure() || isSimPinSecure();
}
private boolean isSimPinSecure() {
final IccCard.State simState = mUpdateMonitor.getSimState(Phone.GEMINI_SIM_1);
final IccCard.State sim2State = mUpdateMonitor.getSimState(Phone.GEMINI_SIM_2);
return (simState == IccCard.State.PIN_REQUIRED
|| simState == IccCard.State.PUK_REQUIRED
|| simState == IccCard.State.PERM_DISABLED
|| sim2State == IccCard.State.PIN_REQUIRED
|| sim2State == IccCard.State.PUK_REQUIRED
|| sim2State == IccCard.State.PERM_DISABLED
|| simState == IccCard.State.ABSENT
&& sim2State == IccCard.State.ABSENT);
}
}</PRE>
<P>9.看下LockPatternKeyguardView初始流程</P>
<PRE class=java name="code"> /**
* @param context Used to inflate, and create views.
* @param updateMonitor Knows the state of the world, and passed along to each
* screen so they can use the knowledge, and also register for callbacks
* on dynamic information.
* @param lockPatternUtils Used to look up state of lock pattern.
*/
public LockPatternKeyguardView(
Context context,
KeyguardUpdateMonitor updateMonitor,
LockPatternUtils lockPatternUtils,
KeyguardWindowController controller) {
super(context);
mHandler = new Handler(this);
mConfiguration = context.getResources().getConfiguration();
mEnableFallback = false;
mRequiresSim = TextUtils.isEmpty(SystemProperties.get("keyguard.no_require_sim"));
mUpdateMonitor = updateMonitor;
mLockPatternUtils = lockPatternUtils;
mWindowController = controller;
mHasOverlay = false;
mUpdateMonitor.registerDeviceInfoCallback(this);
mUpdateMonitor.registerPhoneStateCallback(this);
mKeyguardScreenCallback = new KeyguardScreenCallback() {
public void goToLockScreen() {
mForgotPattern = false;
if (mIsVerifyUnlockOnly) {
// navigating away from unlock screen during verify mode means
// we are done and the user failed to authenticate.
mIsVerifyUnlockOnly = false;
getCallback().keyguardDone(false);
} else {
updateScreen(Mode.LockScreen, false);
}
}
public void goToUnlockScreen() {
final IccCard.State simState = mUpdateMonitor.getSimState(Phone.GEMINI_SIM_1);
final IccCard.State sim2State = mUpdateMonitor.getSimState(Phone.GEMINI_SIM_2);
if (stuckOnLockScreenBecauseSimMissing()
|| (simState == IccCard.State.PUK_REQUIRED
&& !mLockPatternUtils.isPukUnlockScreenEnable())
|| (sim2State == IccCard.State.PUK_REQUIRED
&& !mLockPatternUtils.isPukUnlockScreenEnable())){
// stuck on lock screen when sim missing or
// puk'd but puk unlock screen is disabled
return;
}
if (!isSecure()) {
getCallback().keyguardDone(true);
} else {
updateScreen(Mode.UnlockScreen, false);
}
}
public void forgotPattern(boolean isForgotten) {
if (mEnableFallback) {
mForgotPattern = isForgotten;
updateScreen(Mode.UnlockScreen, false);
}
}
public boolean isSecure() {
return LockPatternKeyguardView.this.isSecure();
}
public boolean isVerifyUnlockOnly() {
return mIsVerifyUnlockOnly;
}
public void recreateMe(Configuration config) {
removeCallbacks(mRecreateRunnable);
post(mRecreateRunnable);
}
public void takeEmergencyCallAction() {
mHasOverlay = true;
// Continue showing FaceLock area until dialer comes up or call is resumed
if (mLockPatternUtils.usingBiometricWeak() &&
mLockPatternUtils.isBiometricWeakInstalled() && mFaceLockServiceRunning) {
showFaceLockAreaWithTimeout(FACELOCK_VIEW_AREA_EMERGENCY_DIALER_TIMEOUT);
}
// FaceLock must be stopped if it is running
stopAndUnbindFromFaceLock();
pokeWakelock(EMERGENCY_CALL_TIMEOUT);
if (TelephonyManager.getDefault().getCallState()
== TelephonyManager.CALL_STATE_OFFHOOK) {
mLockPatternUtils.resumeCall();
} else {
Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
getContext().startActivity(intent);
}
}
public void pokeWakelock() {
getCallback().pokeWakelock();
}
public void pokeWakelock(int millis) {
getCallback().pokeWakelock(millis);
}
public void keyguardDone(boolean authenticated) {
getCallback().keyguardDone(authenticated);
mSavedState = null; // clear state so we re-establish when locked again
}
public void keyguardDoneDrawing() {
// irrelevant to keyguard screen, they shouldn't be calling this
}
public void reportFailedUnlockAttempt() {
mUpdateMonitor.reportFailedAttempt();
final int failedAttempts = mUpdateMonitor.getFailedAttempts();
if (DEBUG) Xlog.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts +
" (enableFallback=" + mEnableFallback + ")");
final boolean usingPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality()
== DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager()
.getMaximumFailedPasswordsForWipe(null);
final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
- LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
(failedAttemptsBeforeWipe - failedAttempts)
: Integer.MAX_VALUE; // because DPM returns 0 if no restriction
if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
// If we reach this code, it means the user has installed a DevicePolicyManager
// that requests device wipe after N attempts. Once we get below the grace
// period, we'll post this dialog every time as a clear warning until the
// bombshell hits and the device is wiped.
if (remainingBeforeWipe > 0) {
showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
} else {
// Too many attempts. The device will be wiped shortly.
Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
showWipeDialog(failedAttempts);
}
} else {
boolean showTimeout =
(failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
if (usingPattern && mEnableFallback) {
if (failedAttempts == failedAttemptWarning) {
showAlmostAtAccountLoginDialog();
showTimeout = false; // don't show both dialogs
} else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
mLockPatternUtils.setPermanentlyLocked(true);
updateScreen(mMode, false);
// don't show timeout dialog because we show account unlock screen next
showTimeout = false;
}
}
if (showTimeout) {
showTimeoutDialog();
}
}
mLockPatternUtils.reportFailedPasswordAttempt();
}
public boolean doesFallbackUnlockScreenExist() {
return mEnableFallback;
}
public void reportSuccessfulUnlockAttempt() {
mFailedFaceUnlockAttempts = 0;
mLockPatternUtils.reportSuccessfulPasswordAttempt();
}
};
/**
* We'll get key events the current screen doesn't use. see
* {@link KeyguardViewBase#onKeyDown(int, android.view.KeyEvent)}
*/
setFocusableInTouchMode(true);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
<SPAN style="COLOR: #cc0000"> updateScreen(getInitialMode(), false);
</SPAN> maybeEnableFallback(context);
}</PRE><PRE class=java name="code">一些CallBack函數的實現,updateScreen函數才是初始更新鎖屏</PRE><PRE class=java name="code"> </PRE><PRE class=java name="code">10.updateScreen,根據當前鎖屏模式,更新顯示相應的鎖屏界面,</PRE><PRE class=java name="code" sizcache="0" sizset="16"><PRE class=java name="code"> private void updateScreen(Mode mode, boolean force) {
Log.v(TAG, "**** UPDATE SCREEN: mode=" + mode
+ " last mode=" + mMode + ", force = " + force);
mMode = mode;
// Re-create the lock screen if necessary
if (mode == Mode.LockScreen || mShowLockBeforeUnlock) {
if (force || mLockScreen == null) {
recreateLockScreen();
}
}
// Re-create the unlock screen if necessary. This is primarily required to properly handle
// SIM state changes. This typically happens when this method is called by reset()
if (mode == Mode.UnlockScreen) {
final UnlockMode unlockMode = getUnlockMode();
if (force || mUnlockScreen == null || unlockMode != mUnlockScreenMode) {
recreateUnlockScreen(unlockMode);
}
}
// visibleScreen should never be null
final View goneScreen = (mode == Mode.LockScreen) ? mUnlockScreen : mLockScreen;
final View visibleScreen = (mode == Mode.LockScreen) ? mLockScreen : mUnlockScreen;
// do this before changing visibility so focus isn't requested before the input
// flag is set
mWindowController.setNeedsInput(((KeyguardScreen)visibleScreen).needsInput());
if (DEBUG_CONFIGURATION) {
Xlog.v(TAG, "Gone=" + goneScreen);
Xlog.v(TAG, "Visible=" + visibleScreen);
}
if (mScreenOn) {
if (goneScreen != null && goneScreen.getVisibility() == View.VISIBLE) {
((KeyguardScreen) goneScreen).onPause();
}
if (visibleScreen.getVisibility() != View.VISIBLE) {
((KeyguardScreen) visibleScreen).onResume();
}
}
if (goneScreen != null) {
goneScreen.setVisibility(View.GONE);
}
visibleScreen.setVisibility(View.VISIBLE);
requestLayout();
if (!visibleScreen.requestFocus()) {
throw new IllegalStateException("keyguard screen must be able to take "
+ "focus when shown " + visibleScreen.getClass().getCanonicalName());
}
}</PRE><BR>
11.recreateLockScreen(),創建鎖屏
<PRE></PRE>
<PRE class=java name="code"><P> private void recreateLockScreen() {
Log.i(TAG, "recreateLockScreen");
if (mLockScreen != null) {
((KeyguardScreen) mLockScreen).onPause();
((KeyguardScreen) mLockScreen).cleanUp();
removeView(mLockScreen);
}</P><P> mLockScreen = createLockScreen();
mLockScreen.setVisibility(View.INVISIBLE);
addView(mLockScreen);
}
</P>這裡可以自己在framework中寫一個鎖屏文件,替換系統原理的鎖屏,</PRE><PRE class=java name="code">很簡單,最後調用show顯示鎖屏界面</PRE><PRE class=java name="code"> </PRE><PRE class=java name="code"> </PRE><PRE class=java name="code"> </PRE><PRE class=java name="code"> </PRE><PRE class=java name="code"> </PRE><PRE class=java name="code"> </PRE>
<PRE></PRE>
<PRE></PRE>
<PRE></PRE>
<PRE></PRE>
<PRE></PRE>
<PRE></PRE>
<PRE></PRE>
</PRE>