keyguard分析之二:息屏與亮屏流程篇
息屏與亮屏時, Keyguard繪製的基本流程
手機的Power鍵在滅屏是會載入keyguard, 保證使用者在亮屏時,第一時間看到鎖屏介面,以保證使用者的良好體驗.
在亮屏過程中涉及到了兩條主要的執行緒: PowerManager Thread 和DisplayManager Thread
> PowerManager: 主要是Power的按鍵事件(PhoneWindowManager.java)傳遞到PowerManagerService.java, 然後進行一系列的操作.
> DisplayManager: 會負責進行螢幕狀態的設定以及螢幕的熄滅與點亮(與螢幕有關),此外跟螢幕相關的一些可見性操作. 顯示操作.
根據上面所述, 找到power按鍵的事件, 手機的實體按鍵基本都是在PhoneWindowManaer.java 被處理的.找到入口:
# PhoneWindowManger private void powerPress(long eventTime, boolean interactive, int count) { ... if (interactive && !mBeganFromNonInteractive) { switch (mShortPressOnPowerBehavior) { ... case SHORT_PRESS_POWER_GO_TO_SLEEP: mPowerManager.goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0); break; case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP: mPowerManager.goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); break; case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME: mPowerManager.goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); launchHomeFromHotKey(); break; case SHORT_PRESS_POWER_GO_HOME://進入home介面,應該是亮屏操作 launchHomeFromHotKey(true /* awakenFromDreams */, false /*respectKeyguard*/); break; } } }
息屏操作的入口在: mPowerManager.goToSleep(), 這是強制手機進入休眠狀態.
# PowerManager public void goToSleep(long time, int reason, int flags) { try { mService.goToSleep(time, reason, flags); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
mService是一個Ipowermanager物件, 其使用的aidl機制進行資料共享, 其AIDL Service為 PowerMangerService.java
# PowerManagerService$BinderService
public void goToSleep(long eventTime, int reason, int flags) {
...
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
goToSleepInternal(eventTime, reason, flags, uid);
} finally {
Binder.restoreCallingIdentity(ident);//恢復遠端呼叫端的uid和pid資訊
}
}
private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
synchronized (mLock) {
...//判斷與sensor有關的息屏
if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
updatePowerStateLocked();
}
}
}
//全域性更新power狀態. 主要告知手機即將息屏.
private void updatePowerStateLocked() {
...
try {
...
for (;;) {
...
if (!updateWakefulnessLocked(dirtyPhase1)) {
break;
}
}
...
}
}
private boolean updateWakefulnessLocked(int dirty) {
...//多重判斷
if (shouldNapAtBedTimeLocked()) {
changed = napNoUpdateLocked(time, Process.SYSTEM_UID);
} else {
changed = goToSleepNoUpdateLocked(time,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
}
//多重判斷結束
return changed;
}
通過分析: goToSleepNoUpdateLocked() 和 napNoUpdateLocked() 最終會呼叫:
# PowerManagerService.java
private void setWakefulnessLocked(int wakefulness, int reason) {
if (mWakefulness != wakefulness) {
mWakefulness = wakefulness;
mWakefulnessChanging = true;
mDirty |= DIRTY_WAKEFULNESS;
mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
}
}
只是這兩個方法的區別是: 是否是自動去息屏操作.
Notifier 主要是用來發送 Power 狀態的廣播. 在這裡邊經過一些列的判斷操作, 直接呼叫了 handleEarlyInteractiveChange() -> mPolicy.startedGoingToSleep(why), 此時PhoneWindowManager開始事件分發:
# PhoneWindowManager.java
public void startedGoingToSleep(int why) {
...
mGoingToSleep = true;
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onStartedGoingToSleep(why);
}
}
這裡就不看KeyguardServiceDelegate.java 與 KeyguardService.java, 只要瞭解他們之間的aidl的跨程序服務. Keyguard.java 是 服務端. KeyguardServiceWrapper.java 是客戶端.
息屏操作, 此時進入到了SystemUI, 通知SystemUI的手機進入息屏狀態.
# KeyguardViewMediator.java
public void onStartedGoingToSleep(int why) {
...
synchronized (this) {
...
if (mShowing) {
mPendingReset = true;
} else if ((why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT && timeout > 0)
|| (why == WindowManagerPolicy.OFF_BECAUSE_OF_USER && !lockImmediately/*keyguard的狀態,非wipe狀態*/)
&& !mIsIPOShutDown/*非關機*/) {
doKeyguardLaterLocked(timeout);
mLockLater = true;
}
...
}
KeyguardUpdateMonitor.getInstance(mContext).dispatchStartedGoingToSleep(why);//稍後分析
notifyStartedGoingToSleep(); //通知息屏結束,鎖屏了.
}
private void doKeyguardLaterLocked(long timeout) {
long when = SystemClock.elapsedRealtime() + timeout;
Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
intent.putExtra("seq", mDelayedShowingSequence);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
//傳送一個廣播,呼叫鎖屏函式.
PendingIntent sender = PendingIntent.getBroadcast(mContext,
0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender);
doKeyguardLaterForChildProfilesLocked();
}
//DELAYED_KEYGUARD_ACTION 作為廣播資訊
private final BroadcastReceiver mBroadcastReceiver = new roadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (DELAYED_KEYGUARD_ACTION.equals(action)) {
final int sequence = intent.getIntExtra("seq", 0);
...
synchronized (KeyguardViewMediator.this) {
if (mDelayedShowingSequence == sequence) {
mSuppressNextLockSound = true;
doKeyguardLocked(null); //鎖屏路徑
}
}
}
...
}
};
總這裡看出: 其實在息屏操作中, KeyguardViewMediator最終通過廣播呼叫了 doKeyguardLocked(null), 這個函式應該有印象, 在開機的時候就被呼叫過. 不過呼叫它的函式為 onSystemReady(). 這樣,我們就不用再通過這條路徑往下走了, 其結果應該差不多. 也就證明了keyguard 是在息屏時, 被建立的.
> 在開機狀態中, Keyguar是在息屏時被建立.
> 息屏下建立Keyguard, 與是否是主動息屏這個行為無關.
<br/>
螢幕的亮與滅
在點選 Power 按鍵進行亮屏時:
# PhoneWindowManager
void launchHomeFromHotKey(final boolean awakenFromDreams, final boolean respectKeyguard) {
if (respectKeyguard) {
if (isKeyguardShowingAndNotOccluded()) {
// don't launch home if keyguard showing
return;
}
...
}
...
}
這裡僅僅是判斷是有 keyguard 這個介面. 如果有, 將停留在這個 keyguard 的介面,; 如果沒有, 將進行後續其他的處理.
螢幕的亮起與熄滅,主要在 DisplayPowerController 中驅動. 之前已經說了 Power 的 的控制主要在 Powermanager.
# PowerMnagerService
private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
synchronized (mLock) {
if (mProximityPositive && reason == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON) {
...
updatePowerStateLocked();
return;
}
if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
updatePowerStateLocked();
}
}
}
private void updatePowerStateLocked() {
...
try {
...
// Phase 2: Update display power state.
boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
...
}
}
private boolean updateDisplayPowerStateLocked(int dirty) {
final boolean oldDisplayReady = mDisplayReady;
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
| DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
| DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_BOOT_IPO)) != 0) {
...
mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
mRequestWaitForNegativeProximity);
...
}
return mDisplayReady && !oldDisplayReady;
}
上述可以看出, 在息屏的時候, PowerManager服務通過一定的操作, 呼叫了 DisplayPowerController, 來進行顯示操作. DisplayManagerInternal 和DisplayManagerService$LocalService作為中間類來進行處理.
首先從requestPowerState()方法開始分析.
# DisplayPowerController.java
public boolean requestPowerState(DisplayPowerRequest request,
boolean waitForNegativeProximity) {
synchronized (mLock) {
boolean changed = false;
...
if (changed && !mPendingRequestChangedLocked) {
mPendingRequestChangedLocked = true;
sendUpdatePowerStateLocked(); //螢幕發生變化
}
...
return mDisplayReadyLocked;
}
}
private void sendUpdatePowerStateLocked() {
if (!mPendingUpdatePowerStateLocked) {
...
Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);//傳送到Handler子執行緒去執行.
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}
private void updatePowerState() {//一系列的更新操作.
...
//驅動螢幕狀態變化.
animateScreenStateChange(state, performScreenOffTransition);
state = mPowerState.getScreenState();
}
}
private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
...
//息屏操作, 指向了setScreenState();
if (mPendingScreenOff && target != Display.STATE_OFF) {
setScreenState(Display.STATE_OFF);
mPendingScreenOff = false;
...
}
//亮屏操作, 指向了setScreenState()
if (target == Display.STATE_ON) {
if (!setScreenState(Display.STATE_ON)) {
return; // screen on blocked
}
}
...
}
//從上述可以看出,亮屏操作與息屏操作都是需要走setScreenState()方法的.
private boolean setScreenState(int state) {
...
//告訴窗體管理WindowManagerPolicy的螢幕狀態.
final boolean isOff = (state == Display.STATE_OFF);
if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF
&& !mScreenOffBecauseOfProximity) {
unblockScreenOn();
mWindowManagerPolicy.screenTurnedOff();
} else if (!isOff && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF) {
if (mPowerState.getColorFadeLevel() == 0.0f) {//控制顯示狀態
blockScreenOn(); //阻止螢幕亮
} else {
unblockScreenOn();//解除阻止螢幕狀態.
}
mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker);
}
// Return true if the screen isn't blocked.
return mPendingScreenOnUnblocker == null;
}
可以看出, 息屏與亮屏過程都會觸發WindowManagerPolicy進行一系列事件的操作. 在 PhoneWindowManagerPolicy.java 中, 亮屏與息屏這個訊息都會觸發 keyguard 代理進行處理.
# PhoneWindowManagerPolicy
//息屏時
public void screenTurnedOff() {
...
synchronized (mLock) {
...
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onScreenTurnedOff();
}
}
}
//亮屏時
public void screenTurningOn(final ScreenOnListener screenOnListener) {
...
synchronized (mLock) {
...
if (mKeyguardDelegate != null) {
mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, 1000);
mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);
} else {
...
finishKeyguardDrawn();
}
}
}
接著對 KeyguardServiceDelegate 的物件進行判斷.如果為空, 即表示未進行keyguard的設定;如果為非空,即表明:keyguard設定了某一 model 形式.
# KeyguardServiceDelegate.java
//息屏
public void onScreenTurnedOff() {
if (mKeyguardService != null) {
mKeyguardService.onScreenTurnedOff();
}
mKeyguardState.screenState = SCREEN_STATE_OFF;
}
//亮屏
public void onScreenTurningOn(final DrawnListener drawnListener) {
if (mKeyguardService != null) {
//當手機在息狀態時,必然是開啟了keyguard的服務.如果設定了keyguard.
mKeyguardService.onScreenTurningOn(new KeyguardShowDelegate(drawnListener));
} else {
mDrawnListenerWhenConnect = drawnListener;
showScrim();
}
mKeyguardState.screenState = SCREEN_STATE_TURNING_ON;
}
public void screenTurnedOn() {
synchronized (mLock) {
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onScreenTurnedOn();
}
}
}
接著進入到SystemUI,開啟Keyguard服務,KeyguardService.java.我們可以發現KeyguardServiceWrapper這個類物件是啟動KeyguardService的一箇中間類,代理分派任務到Systemui.
# KeyguardService.java
private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
//息屏
public void onScreenTurnedOff() {
checkPermission();
mKeyguardViewMediator.onScreenTurnedOff();
}
//亮屏
...
@Override // Binder interface
public void onScreenTurningOn(IKeyguardDrawnCallback callback) {
checkPermission();
mKeyguardViewMediator.onScreenTurningOn(callback);
}
...
}
Keyguard服務開啟後,也是先檢查許可權,然後再將事件分派出去.
#KeyguardViewMediator.java
//息屏
public void onScreenTurnedOff() {
notifyScreenTurnedOff();
mUpdateMonitor.dispatchScreenTurnedOff();
}
//> 息屏做了兩件事:
//1. notifyScreenTurnedOff()將事件交由StatusBarKeyguardViewManager處理
//2. dispatchScreenTurnedOff()將事件交於KeyguardUpdateMonitor處理.
//亮屏
public void onScreenTurningOn(IKeyguardDrawnCallback callback) {
notifyScreenOn(callback);
}
先看看息屏狀態時, 流程走向
notifyScreenTurnedOff(): 通知StatusBarKeyguardViewManager去進行處理
# StatusBarKeyguardViewManager.java
public void onScreenTurnedOff() {
mScreenTurnedOn = false;
mPhoneStatusBar.onScreenTurnedOff();
}
# PhoneStatusBar.java
public void onScreenTurnedOff() {
mFalsingManager.onScreenOff();
}
# FalsingManager.jva
public void onScreenOff() {
mDataCollector.onScreenOff();
mScreenOn = false;
sessionExitpoint(false /* force */); //感測器接觸註冊, 停止感測器的使用
}
# DataCollector.java
public void onScreenOff() {
addEvent(PhoneEvent.ON_SCREEN_OFF); //感測器log日誌手機
sessionExitpoint(Session.FAILURE); //touch事件收集器
}
dispatchScreenTurnedOff() 通知 KeyguardUpdateMonitor進行處理
# KeyguardUpdateMonitor.java
private void handleScreenTurnedOff() {
final int count = mCallbacks.size();
for (int i = 0; i < count; i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onScreenTurnedOff();
}
}
}
看看cb的實現體
# KeyguardHostView.java
public void onScreenTurnedOff() {
mSecurityContainer.onScreenTurnedOff(); //空實現
}
對於亮屏的這個過程, 其餘息屏的方式是一樣的.都是需要經過
# KeyguardViewMediator.java
private void handleNotifyScreenTurningOn(IKeyguardDrawnCallback callback) {
synchronized (KeyguardViewMediator.this) {
//更新notification的時間和感測器註冊, 以及事件的收集.
mStatusBarKeyguardViewManager.onScreenTurningOn();
if (callback != null) {
if (mWakeAndUnlocking) {//判斷鎖的形式.
mDrawnCallback = callback;
} else {
//回撥一個介面物件進行回撥繪製.
notifyDrawn(callback);
}
}
}
}
可以看出, "息屏"與"正在亮屏"最後都會呼叫介面物件實現其息屏與亮屏的操作.
,那麼也存在"完成開機"這個實現, 再次回到原點.直接看 StatusBarKeyguardViewManager.java
# StatusBarKeyguardViewManager.java
public void onScreenTurnedOn() {
mScreenTurnedOn = true;
if (mDeferScrimFadeOut) {
mDeferScrimFadeOut = false;
animateScrimControllerKeyguardFadingOut(0, WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS,
true /* skipFirstFrame */);
updateStates();
}
if (mPhoneStatusBar != null) {
mPhoneStatusBar.onScreenTurnedOn();//後續操作, 也是一層一層的通知
}
}
---
> 在亮屏與滅屏階段, keyguard的繪製其實在滅屏階段.
> 螢幕的顯示亮屏與滅屏, 與keyguard的並無直接關係.