一直想要整理一下keyguard(锁屏)模块的相关流程,由于各种原因总是没真正着手开始做,一直拖也不是个办法,所以就索性开始了。
这篇内容会比较偏分析,所以闲话就少扯点了。
锁屏模块位于framework中,有别于一般的上层应用,它的逻辑代码和view视图,资源文件散布在framework的几处地方,这也给新手分析该模块带来也一点的麻烦。下面我会试着来捋捋这些散落的珠子。
1.文件目录:
a,逻辑是Frameworks/base/policy/src/com/android/internal/policy/ impl/目录下
b,视图view是在 Framework/base/core/java/com/android/internal/ widget/路径下:
文件夹multiwaveview就是从4.0开始加入的环形解锁视图及相应动画实现。
c,资源文件在Framework/base/core/res/res/下。
这个就不贴目录了,和一般android应用一样,图片资源,布局文件都很规矩的放在相应分辨率的文件夹下。大致了解了锁屏模块文件分布后,我们一般就要开始看源代码了。
2,keyguard锁屏流程图:
现在看不明白的没关系可以先跳过,先看下面,再更几遍源码就会发现该图就是个小菜了。
3,keyguard锁屏view层次图:
一般4.0真机都会有五种锁屏方式:
这里和2.3有明显区别了,2.3的view视图如下:
可以看出来以前的锁屏流程是分正常锁屏和图案锁屏方式。在选择图案解锁方式,是不会显示正常解锁slidingview的。但是选择密码解锁就会出现需要用户二次解锁的情况。三星2.2就对此进行了流程优化统一。本人也试着做了,有兴趣可以。。。
4.0以后该流程得到了官方的统一,选择任何锁屏方式,用户都只需要一次解锁。流程上也更直观了。相信Google,必须是越来越good。
4,keyguard锁屏重要类分析:
1,KeyguardScreenback.java和KeyguardViewCallback.java
说明:接口两个,等着被LockPatternKeyguardView.java实例化。注意Keyguard ViewCallback.java还偷偷捞外快,同时替KeyguardViewMediator.java服务。
public interface KeyguardScreenCallback extends KeyguardViewCallback { void goToLockScreen();//Transition to the lock screen. void goToUnlockScreen();//Transition to the unlock screen. void forgotPattern(boolean isForgotten);// The user forgot their pattern boolean isSecure();//Whether the keyguard requires some sort of PIN. /** * @return Whether we are in a mode where we only want to verify the * user can get past the keyguard. */ boolean isVerifyUnlockOnly(); /** * Stay on me, but recreate me (so I can use a different layout). */ void recreateMe(Configuration config); void takeEmergencyCallAction();//Take action to send an emergency call void reportFailedUnlockAttempt();//user had a failed attempt to unlock void reportSuccessfulUnlockAttempt();//successfully entered their password /** * Report whether we there's another way to unlock the device. * @return true */ boolean doesFallbackUnlockScreenExist(); }
public interface KeyguardViewCallback { /** * Request the wakelock to be poked for the default amount of time. */ void pokeWakelock();//使屏幕保持亮一段时间。 void pokeWakelock(int millis); void keyguardDone(boolean authenticated);// Report keyguard is done. /** * Report that the keyguard is done drawing. */ void keyguardDoneDrawing();//所有锁屏视图完成draw时调用该方法。 }
google的注释实在是太优雅了,都翻译成中文反而会有所误导且影响美观,单词看不明白的,有道去。实在懒的话,意会也行的;)
2. KeyguardScreen.java
说明:接口一个,坐等LockScreen.java等具体锁屏方式来实现它。
public interface KeyguardScreen { boolean needsInput();//是否需要键盘进行输入 void onPause();//view不在最上层时被系统调用。 void onResume();//view重新掌权时被系统调用 void cleanUp();//view被扫地出门。 }
3. KeyguardStatusViewManager.java
说明:这个类是4.0后新增加的,其实它是从2.3的LockScreen.java分离了出来,所以它还是摆脱不了命运的束缚,依然要为LockScreen.java服务,而且它比以前更累了,如果条件需要它还要服侍其他如密码解锁,图形解锁等方式。功能就是状态视图总管。
/*** * Manages a number of views inside of LockScreen layouts. See below for a list of widgets */ class KeyguardStatusViewManager implements OnClickListener { public KeyguardStatusViewManager(View view, KeyguardUpdateMonitor updateMonitor,LockPatternUtils lockPatternUtils, KeyguardScreenCallback callback,boolean showEmergencyButtonByDefault) { if (DEBUG) Log.v(TAG, "KeyguardStatusViewManager()"); mContainer = view; //视图容器 mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year);//格式日期 mLockPatternUtils = lockPatternUtils; mUpdateMonitor = updateMonitor; mCallback = callback; mCarrierView = (TextView) findViewById(R.id.carrier);//运营商标识 mDateView = (TextView) findViewById(R.id.date);//日期 mStatus1View = (TextView) findViewById(R.id.status1);//sim卡状态 mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);//闹铃状态 mOwnerInfoView = (TextView) findViewById(R.id.propertyOf); mTransportView = (TransportControlView) findViewById(R.id.transport); mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton); mShowEmergencyButtonByDefault = showEmergencyButtonByDefault; //紧急呼叫按钮 // Hide transport control view until we know we need to show it. if (mTransportView != null) { mTransportView.setVisibility(View.GONE); } if (mEmergencyCallButton != null) { mEmergencyCallButton.setText(R.string.lockscreen_emergency_call); mEmergencyCallButton.setOnClickListener(this); mEmergencyCallButton.setFocusable(false); // touch only! } mTransientTextManager = new TransientTextManager(mCarrierView); mUpdateMonitor.registerInfoCallback(mInfoCallback); mUpdateMonitor.registerSimStateCallback(mSimStateCallback); resetStatusInfo();//更新电池状态信息 refreshDate();//刷新时间 updateOwnerInfo();//更新所有者的信息 // Required to get Marquee to work. final View scrollableViews[] = { mCarrierView, mDateView, mStatus1View, mOwnerInfoView, mAlarmStatusView }; for (View v : scrollableViews) { if (v != null) { v.setSelected(true); } } }
4. LockScreen.java
说明:五种锁屏方式之一,为系统默认设置选用,名为滑动解锁,也就是4.0的那个带锁的圆。它继承于LinearLayout并实现了KeyguardScreen接口,所以他具备了接受视图的解锁事件并作出响应。
/** * The screen within {@link LockPatternKeyguardView} that shows general * information about the device depending on its state, and how to get * past it, as applicable. */ class LockScreen extends LinearLayout implements KeyguardScreen { class SlidingTabMethods implements SlidingTab.OnTriggerListener, UnlockWidgetCommonMethods { private final SlidingTab mSlidingTab; SlidingTabMethods(SlidingTab slidingTab) { mSlidingTab = slidingTab; } public void updateResources() { ....... } /** 解锁响应*/ public void onTrigger(View v, int whichHandle) { if (whichHandle == SlidingTab.OnTriggerListener.LEFT_HANDLE) { mCallback.goToUnlockScreen(); } else if (whichHandle == SlidingTab.OnTriggerListener.RIGHT_HANDLE) { toggleRingMode(); mCallback.pokeWakelock(); } } /** {@inheritDoc} */ public void onGrabbedStateChange(View v, int grabbedState) { ....... } public View getView() { return mSlidingTab; } public void reset(boolean animate) { mSlidingTab.reset(animate); } public void ping() { } } class WaveViewMethods implements WaveView.OnTriggerListener, UnlockWidgetCommonMethods { private final WaveView mWaveView; WaveViewMethods(WaveView waveView) { mWaveView = waveView; } /** {@inheritDoc} */ public void onTrigger(View v, int whichHandle) { if (whichHandle == WaveView.OnTriggerListener.CENTER_HANDLE) { requestUnlockScreen(); } } /** {@inheritDoc} */ public void onGrabbedStateChange(View v, int grabbedState) { // Don't poke the wake lock when returning to a state where the handle is // not grabbed since that can happen when the system (instead of the user) // cancels the grab. if (grabbedState == WaveView.OnTriggerListener.CENTER_HANDLE) { mCallback.pokeWakelock(STAY_ON_WHILE_GRABBED_TIMEOUT); } } public void updateResources() { } public View getView() { return mWaveView; } public void reset(boolean animate) { mWaveView.reset(); } public void ping() { } } class MultiWaveViewMethods implements MultiWaveView.OnTriggerListener, UnlockWidgetCommonMethods { private final MultiWaveView mMultiWaveView; private boolean mCameraDisabled; MultiWaveViewMethods(MultiWaveView multiWaveView) { mMultiWaveView = multiWaveView; final boolean cameraDisabled = mLockPatternUtils.getDevicePolicyManager() .getCameraDisabled(null); if (cameraDisabled) { Log.v(TAG, "Camera disabled by Device Policy"); mCameraDisabled = true; } else { // Camera is enabled if resource is initially defined for MultiWaveView // in the lockscreen layout file mCameraDisabled = mMultiWaveView.getTargetResourceId() != R.array.lockscreen_targets_with_camera; } }
这个类的主要作用就是提供了三种不同时期的滑动解锁方案重载,具体用哪种已经在keyguard_screen_tab_unlock.xml中配置好了。
5. KeyguardViewBase.java
说明:一个抽象类,里面封装了一些抽象方法,并完成对各种按键的监听。条件允许的话他还会拦截keyEvent,从中作梗。
public abstract class KeyguardViewBase extends FrameLayout { public boolean dispatchKeyEvent(KeyEvent event) { if (shouldEventKeepScreenOnWhileKeyguardShowing(event)) { mCallback.pokeWakelock(); } if (interceptMediaKey(event)) { return true; } return super.dispatchKeyEvent(event); } private boolean shouldEventKeepScreenOnWhileKeyguardShowing(KeyEvent event) { if (event.getAction() != KeyEvent.ACTION_DOWN) { return false; } switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_DOWN: case KeyEvent.KEYCODE_DPAD_LEFT: case KeyEvent.KEYCODE_DPAD_RIGHT: case KeyEvent.KEYCODE_DPAD_UP: return false; default: return true; } } private boolean interceptMediaKey(KeyEvent event) { final int keyCode = event.getKeyCode(); if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (keyCode) { case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: ...... }
6.LockPatternKeyguardView.java
说明:1-6的最终boss,上面的零碎都直接或间接为它工作,他的作用呢,可以参考上面的锁屏view层次图。一句话它是锁屏的最高权威,该整那样,它说了算,鉴于它太过NB,这里就不贴代码了,读者必须亲自膜拜三遍。
7. KeyguardViewManager.java
说明:封装了WindowManager,可以随性改变锁屏视图的创建,显示,隐藏及重新设定。
/** * Manages creating, showing, hiding and resetting the keyguard. Calls back * via {@link com.android.internal.policy.impl.KeyguardViewCallback} to poke * the wake lock and report that the keyguard is done, which is in turn, * reported to this class by the current {@link KeyguardViewBase}. */ public class KeyguardViewManager implements KeyguardWindowController { private final KeyguardViewProperties mKeyguardViewProperties; private final KeyguardUpdateMonitor mUpdateMonitor; private WindowManager.LayoutParams mWindowLayoutParams; private boolean mNeedsInput = false; private FrameLayout mKeyguardHost; private KeyguardViewBase mKeyguardView; ..... protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); mCallback.keyguardDoneDrawing(); } } /** * Show the keyguard. Will handle creating and attaching to the view manager * lazily. */ public synchronized void show() { ..... 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(); } public void setNeedsInput(boolean needsInput) { ... } /** * Reset the state of the view. */ public synchronized void reset() { } public synchronized void onScreenTurnedOff() { } public synchronized void onScreenTurnedOn( final KeyguardViewManager.ShowListener showListener) { // Caller should wait for this window to be shown before turning // on the screen. } public synchronized void verifyUnlock() { if (DEBUG) Log.d(TAG, "verifyUnlock()"); show(); mKeyguardView.verifyUnlock(); } /** * A key has woken the device. */ public boolean wakeWhenReadyTq(int keyCode) { ..... } /** * Hides the keyguard view */ public synchronized void hide() { ..... } /** * @return Whether the keyguard is showing */ public synchronized boolean isShowing() { return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE); }
8. KeyguardUpdateMonitor.java
说明:监听系统状态值的改变如时间、SIM卡状态、电池电量等,状态值的改变会回调监听了该状态信息的对象实例。如果只是关注功能的话只需要看hadle里面的每个消息调用的方法即可。
/** * Watches for updates that may be interesting to the keyguard, and provides * the up to date information as well as a registration for callbacks that care * to be updated. * * Note: under time crunch, this has been extended to include some stuff that * doesn't really belong here. see {@link #handleBatteryUpdate} where it shutdowns * the device, and {@link #getFailedAttempts()}, {@link #reportFailedAttempt()} * and {@link #clearFailedAttempts()}. Maybe we should rename this 'KeyguardContext'... */ public class KeyguardUpdateMonitor { private Handler mHandler; private ContentObserver mContentObserver; private int mRingMode; private int mPhoneState; ...... /** * SIM卡状态改变捕获赋值。 * the intent and provide a {@link SimCard.State} result. */ private static class SimArgs { public final IccCard.State simState; private SimArgs(Intent intent) { if (!TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) { throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED"); } String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE); if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { final String absentReason = intent .getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON); if (IccCard.INTENT_VALUE_ABSENT_ON_PERM_DISABLED.equals( absentReason)) { this.simState = IccCard.State.PERM_DISABLED; } else { this.simState = IccCard.State.ABSENT; } } else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) { this.simState = IccCard.State.READY; } else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { final String lockedReason = intent .getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON); if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { this.simState = IccCard.State.PIN_REQUIRED; } else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { this.simState = IccCard.State.PUK_REQUIRED; } else { this.simState = IccCard.State.UNKNOWN; } } else if (IccCard.INTENT_VALUE_LOCKED_NETWORK.equals(stateExtra)) { this.simState = IccCard.State.NETWORK_LOCKED; } else { this.simState = IccCard.State.UNKNOWN; } } public String toString() { return simState.toString(); } } public KeyguardUpdateMonitor(Context context) { mContext = context; mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_TIME_UPDATE: handleTimeUpdate(); break; case MSG_BATTERY_UPDATE: handleBatteryUpdate(msg.arg1, msg.arg2); break; case MSG_CARRIER_INFO_UPDATE: handleCarrierInfoUpdate(); break; case MSG_SIM_STATE_CHANGE: handleSimStateChange((SimArgs) msg.obj); break; case MSG_RINGER_MODE_CHANGED: handleRingerModeChange(msg.arg1); break; case MSG_PHONE_STATE_CHANGED: handlePhoneStateChanged((String)msg.obj); break; case MSG_CLOCK_VISIBILITY_CHANGED: handleClockVisibilityChanged(); break; case MSG_DEVICE_PROVISIONED: handleDeviceProvisioned(); break; } } };
9.KeyguardViewMediator.java
说明:也是boss级别的,虽然明面上不及LockPatternKeyguardView.java,但论实权,是个深藏不露的实力派。奈何可能有把柄在他人之手,所以他必须低调,任劳任怨,为PhoneWindowManager.java所各种差遣。看它的话先把开头的50来行英文注释整清楚,然后在跳到handle里面看一下每个消息对应的执行函数,这样对这个所谓调度者就有个大概的理解了。然后就可以具体功能流程具体分析了。
/** *有关键盘锁请求的调度者。包括键盘锁状态的查询,power management事件影响键盘锁是否应该被显示或者重置,特定的回调函数来 *通知window manager键盘锁是什么时候显示,以及接受view视图传过来的消息表明已经成功完成解锁。 *请注意键盘锁是在灭屏后立即被调用显示的。这样当你点亮屏幕,锁屏才能第一时间显示出来。 *例如外部事件调度锁屏视图流程: * *-灭屏动作-》重置锁屏并显示它为下次点亮屏幕做好准备。 *-锁屏很自然流畅的打开了-》如果他不是安全的,隐藏之。 * *来自于锁屏视图的事件: *-用户成功完成解锁条件-》隐藏锁屏视图,不再对输入事件进行拦截。 *请再注意:第三方应用通过条用power managment实例可以屏蔽系统的键盘锁。 * *线程和同步: *该类是由WindowManagerPolicy创建并运行在它的线程里,锁屏UI也是这个类的构造函数里面产生。这个apis也可以被其他线程所调用。 *然而,这个类的方法手势同步的,同时任何一个锁屏视图都会发消息到handle来保证它是在锁屏UI线程里面执行的。 */ public class KeyguardViewMediator implements KeyguardViewCallback, KeyguardUpdateMonitor.InfoCallback, KeyguardUpdateMonitor.SimStateCallback { private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000; /** * This handler will be associated with the policy thread, which will also * be the UI thread of the keyguard. Since the apis of the policy, and therefore * this class, can be called by other threads, any action that directly * interacts with the keyguard ui should be posted to this handler, rather * than called directly. */ private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case TIMEOUT: handleTimeout(msg.arg1); return ; case SHOW: handleShow(); return ; case HIDE: handleHide(); return ; case RESET: handleReset(); return ; case VERIFY_UNLOCK: handleVerifyUnlock(); return; case NOTIFY_SCREEN_OFF: handleNotifyScreenOff(); return; case NOTIFY_SCREEN_ON: handleNotifyScreenOn((KeyguardViewManager.ShowListener)msg.obj); return; case WAKE_WHEN_READY: handleWakeWhenReady(msg.arg1); return; case KEYGUARD_DONE: handleKeyguardDone(msg.arg1 != 0); return; case KEYGUARD_DONE_DRAWING: handleKeyguardDoneDrawing(); return; case KEYGUARD_DONE_AUTHENTICATING: keyguardDone(true); return; case SET_HIDDEN: handleSetHidden(msg.arg1 != 0); break; case KEYGUARD_TIMEOUT: synchronized (KeyguardViewMediator.this) { doKeyguardLocked(); } break; } } }; private void adjustStatusBarLocked() { ......//控制是否能在锁屏界面下拉状态栏。 } }
10. PhoneWindowManager.java
说明:在Android中的地位犹如封疆之王爷,此等人物,岂能一眼看透并妄加揣测。需时日翻阅各种资料假以研究才能得出个大概.......此乃后话,当另谋篇幅。
5,2.3keyguard锁屏解的几个小问题,贴上仅供娱乐:
后续待补充。
高清文档下载,请ClickMe!。
Android 4.4 keyguard 分析:Android4.4 锁屏流程梳理