锁屏相关的文件在android对应package:com.android.internal.policy.impl。
phoneWindowManager.java可以派发各种物理key,如Power,Home,Vol+和Vol-等等。那么,这个类是由谁启动的?
在package下Policy.java中
public PhoneWindowManager makeNewWindowManager() {
return new PhoneWindowManager();
}
这个方法实现创建这个类。而在PolicyManager.java下调用这个方法实现对PhoneWindowManager的创建。
但是,PhoneWindowManager.java中有一个init()方法,这个是这个类的初始化方法,它调用PowerManager、windowManager、KeyguardViewMediator等等实现对PhoneWindowManager。这个Init()是在WindowManagerService.java下 的PolicyThread类下得Run方法中调用mPolicy.init(mContext, mService, mPM)。
PhoneWindowManager.java这个类是非常重要的,这里注册了一个Orientation变化的监听类:
MyOrientationListener extends WindowOrientationListener,但是,它不能够自动进行更新,它的更新主要是依据接收广播进行的,此处大概就是JNI根据驱动获取相关Sensor消息然后发送消息。这里仅仅粗略说明在这个文件中有屏幕旋转的处理方法。
下面是锁屏的处理流程:
在PhoneWindowManager.java中:
public void systemReady() {
// tell the keyguard
mKeyguardMediator.onSystemReady();
android.os.SystemProperties.set("dev.bootcomplete", "1");
synchronized (mLock) {
updateOrientationListenerLp();
mSystemReady = true;
mHandler.post(new Runnable() {
public void run() {
updateSettings();
}
});
}
}
这个方法在开机时启动调用,然后mKeyguardMediator.onSystemReady()
在KeyGuardViewMeditor.java是一个非常重要的类,它用来响应电源键的处理(黑亮屏),查询解锁屏等等。
public void onSystemReady() {
synchronized (this) {
if (DEBUG) Log.d(TAG, "onSystemReady");
mSystemReady = true; //系统准备好了
doKeyguard(); //判断是否设定了锁
}
}
private void doKeyguard()主要功能:Enable the keyguard if the settings are appropriate.意思就是如果在Setting里面设置了密码就激活keyguard。
1.如果设置了屏幕锁,黑屏后,在onScreenTurnedOff中doKeyguard()调用一次;
2.如果有SIM卡,且设置了PIN或PUK锁,那么调用doKeyguard();
3.TIMEROUT时调用doKeyguard();
4.接收到消息;DELAYED_KEYGUARD_ACTION或者TelephonyManager.ACTION_PHONE_STATE_CHANGED可能会调用
doKeyguard().
doKeyguard()方法最后调用了showLocked();
private void showLocked() {
if (DEBUG) Log.d(TAG, "showLocked");
// ensure we stay awake until we are finished displaying the keyguard
mShowKeyguardWakeLock.acquire();
Message msg = mHandler.obtainMessage(SHOW);
mHandler.sendMessage(msg);
}
实际上就是发送消息显示SHOW
当Handler接收到SHOW这个Message时,进行相关处理:
进入KeyguardViewManager.java:
在public synchronized void show()方法中:
if (mKeyguardView == null) {
if (DEBUG) Log.d(TAG, "keyguard view is null, creating it...");
mKeyguardView = mKeyguardViewProperties.createKeyguardView(mContext, mUpdateMonitor, this);
mKeyguardView.setId(R.id.lock_screen);
……
}
mKeyguardView = mKeyguardViewProperties.createKeyguardView(mContext, mUpdateMonitor, this)这个就是KeyguardViewBase创建一个KeyguardViewBase的对象,也就是管理Lock、Unlock画面的类实例。
在Setting里面设置各种锁屏之后,在按power键重新开启时会产生lock或者unlock界面,需要输入密码或者绘出Pattern等操作后才能进入界面。这个锁定和解锁界面是由LockPatternKeyguardView这个类实现的。下面便是这个class的构造函数。
public LockPatternKeyguardView(
Context context,
KeyguardUpdateMonitor updateMonitor,
LockPatternUtils lockPatternUtils,
KeyguardWindowController controller)
重要实现:
mConfiguration = context.getResources().getConfiguration();
这个函数是用来获取系统的一些配置,在此用来决定是横屏还是竖屏。
mMode = getInitialMode();
这个则是获取系统的mode了。
mMode是这样的类型:
enum Mode {
LockScreen,
UnlockScreen
}
可以初步判断是lock还是unlock状态的flag枚举。获取这个状态是非常重要的,它决定初始化时究竟是显示unlock 界面还是lock界面。
private Mode getInitialMode() {
boolean isPukRequired = false;
for (int i = 0; i < TelephonyManager.getPhoneCount(); i++) {
isPukRequired = isPukRequired || isSimPukLocked(i);
if (isPukRequired) break;
}
//这里是判断sim是否加入PUK锁或者sim卡不存在
if (stuckOnLockScreenBecauseSimMissing() || isPukRequired) {
return Mode.LockScreen;
} else {
// Show LockScreen first for any screen other than Pattern unlock.
final boolean usingLockPattern =
mLockPatternUtils.getKeyguardStoredPasswordQuality()== DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
if (isSecure() && usingLockPattern) {
return Mode.UnlockScreen;
} else {
return Mode.LockScreen;
}
}
}
对于isSecure这个函数,定义如下:
private boolean isSecure() {
UnlockMode unlockMode = getUnlockMode();
boolean secure = false;
switch (unlockMode) {
case Pattern:
secure = mLockPatternUtils.isLockPatternEnabled();
break;
case SimPin:
for (int i = 0; i < TelephonyManager.getPhoneCount(); i++) {
// Check if subscription is PIN/PUK locked.
// isPinLocked returns true if the state is PIN_REQUIRED/PUK_REQUIRED.
secure = secure || getSimState(i).isPinLocked();
if (secure) break;
}
break;
case Account:
secure = true;
break;
case Password:
secure = mLockPatternUtils.isLockPasswordEnabled();
break;
default:
throw new IllegalStateException("unknown unlock mode " + unlockMode);
}
return secure;
很显然是判断,是否设置了解锁屏方式。是则返回true,否则false。
而usingLockPattern是判断是否是用pattern模式的。具体判断后面研究。
在获取相关初始状态后,就是一些回调函数的实现:
mKeyguardScreenCallback = new KeyguardScreenCallback(),这个是十分容易解决的。
在之后是:
// create both the lock and unlock screen so they are quickly available
// when the screen turns on
mLockScreen = createLockScreen();
addView(mLockScreen);
final UnlockMode unlockMode = getUnlockMode();
if (DEBUG) Log.d(TAG,
"LockPatternKeyguardView ctor: about to createUnlockScreenFor; mEnableFallback="
+ mEnableFallback);
mUnlockScreen = createUnlockScreenFor(unlockMode);
mUnlockScreenMode = unlockMode;
maybeEnableFallback(context);
addView(mUnlockScreen);
updateScreen(mMode);
由注释可知:其实就是创建lock和unlock界面,这样切换时就可以直接显示而不必先创建在切换,造成时间的缓冲。那么究竟显示lock还是unlock界面就是最后那个调用。具体函数:
private void updateScreen(final Mode mode) {
if (DEBUG_CONFIGURATION) Log.v(TAG, "**** UPDATE SCREEN: mode=" + mode
+ " last mode=" + mMode, new RuntimeException());
mMode = mode;
// 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 && mCurrentUnlockMode != getUnlockMode()) ||
(getUnlockMode() == UnlockMode.SimPin)) {
recreateUnlockScreen();
}
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) {
Log.v(TAG, "Gone=" + goneScreen);
Log.v(TAG, "Visible=" + visibleScreen);
}
if (mScreenOn) {
if (goneScreen.getVisibility() == View.VISIBLE) {
((KeyguardScreen) goneScreen).onPause();
}
if (visibleScreen.getVisibility() != View.VISIBLE) {
((KeyguardScreen) visibleScreen).onResume();
}
}
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());
}
}
这里mMode = mode;之后
final View goneScreen = (mode == Mode.LockScreen) ? mUnlockScreen : mLockScreen;
final View visibleScreen = (mode == Mode.LockScreen) ? mLockScreen : mUnlockScreen;
很显然,这是根据mode决定显示lock还是unlock的。
关于在Unlock界面输入错误密码的提示流程:
当用户输入密码时,若不正确,此时就会报告错误:
在PasswordUnlockScreen.java中有一个函数verifyPasswordAndUnlock,如下:
private void verifyPasswordAndUnlock() {
String entry = mPasswordEntry.getText().toString();
if (mLockPatternUtils.checkPassword(entry)) {
mCallback.keyguardDone(true);
mCallback.reportSuccessfulUnlockAttempt();
} else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
mCallback.reportFailedUnlockAttempt();
//在这里,也就是输入次数是5的整数倍时,弹出报错对话框:
if (0 == (mUpdateMonitor.getFailedAttempts()
% LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
handleAttemptLockout(deadline);
}
}
mPasswordEntry.setText("");
}
那么这个错误如何显示的?
上面代码有一处: mCallback.reportFailedUnlockAttempt()
reportFailedUnlockAttempt()函数是在LockPatternKeyguardView.java中,
public void reportFailedUnlockAttempt() {
mUpdateMonitor.reportFailedAttempt();
final int failedAttempts = mUpdateMonitor.getFailedAttempts();
final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality()
== DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
if (usingLockPattern && mEnableFallback && failedAttempts ==
(LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
- LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
showAlmostAtAccountLoginDialog();
} else if (usingLockPattern && mEnableFallback
&& failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
mLockPatternUtils.setPermanentlyLocked(true);
updateScreen(mMode);
} else if ((failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)
== 0) {
showTimeoutDialog();
}
mLockPatternUtils.reportFailedPasswordAttempt();
}
showAlmostAtAccountLoginDialog和showTimeoutDialog()决定显示什么样的Message:
private void showTimeoutDialog() {
//Get time to lock screen
int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
//Announce a message
String message = "";
//Get unlock mode
UnlockMode unlockMode = getUnlockMode();
if (unlockMode == UnlockMode.password) {
message = mContext.getString(
R.string.lockscreen_too_many_failed_attempts_dialog_message,
mUpdateMonitor.getFailedAttempts(),
timeoutInSeconds);
}
else {
message = mContext.getString(
R.string.lockscreen_too_many_failed_attempts_dialog_message,
mUpdateMonitor.getFailedAttempts(),
timeoutInSeconds);
}
final AlertDialog dialog = new AlertDialog.Builder(mContext)
.setTitle(null)
.setMessage(message)
.setNeutralButton(R.string.ok, null)
.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
if (!mContext.getResources().getBoolean(
com.android.internal.R.bool.config_sf_slowBlur)) {
dialog.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
}
dialog.show();
}