问题:
【指纹】灭屏下使用正确的指纹解锁,解锁成功背光亮但屏幕没亮
【偶现】滑动解锁后只显示壁纸,图标在4S后加载出来
抖音有个activity在灭屏的时候都会启动,如果出现如下情况:
06-25 13:08:14.489 980 2799 I am_create_activity: [0,150997160,80,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity,NULL,NULL,NULL,276824064]
06-25 13:08:14.545 980 2799 I am_restart_activity: [0,150997160,80,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity]
06-25 13:08:14.553 980 2799 I am_set_resumed_activity: [0,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity,minimalResumeActivityLocked]
06-25 13:08:14.585 980 2799 I am_pause_activity: [0,150997160,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity]
06-25 13:08:14.626 980 13170 I am_set_resumed_activity: [0,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity,resumeTopActivityInnerLocked]
06-25 13:08:14.642 980 13170 I am_resume_activity: [0,150997160,80,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity]
06-25 13:08:14.832 980 2973 I am_finish_activity: [0,150997160,80,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity,app-request]
06-25 13:08:14.836 980 2973 I am_focused_stack: [0,0,1,finishActivity adjustFocus]
06-25 13:08:14.853 980 2973 I am_pause_activity: [0,150997160,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity]
06-25 13:08:14.948 980 11678 I am_set_resumed_activity: [0,com.android.launcher3/com.android.searchlauncher.SearchLauncher,resumeTopActivityInnerLocked]
06-25 13:08:14.972 980 11678 I am_resume_activity: [0,257364849,3,com.android.launcher3/com.android.searchlauncher.SearchLauncher]
06-25 13:08:15.135 980 2973 I am_failed_to_pause: [0,150997160,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity,(none)]
06-25 13:08:15.278 3571 3571 I am_on_resume_called: [0,com.android.searchlauncher.SearchLauncher,RESUME_ACTIVITY]
06-25 13:08:20.379 980 1055 I am_destroy_activity: [0,150997160,80,com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity,finish-imm]
06-25 13:08:20.389 8315 8315 I am_on_stop_called: [0,com.ss.android.message.sswo.SswoActivity,destroy]
06-25 13:08:28.834 980 1054 I am_pss : [3571,10027,com.android.launcher3,106524672,89603072,5540864]
pause failed导致AMS这边会有一个超时等待的动作,需继续查找为啥会出现pause 超时。
导致问题流程如下:
1, 待机过程中,斗音会启动
com.ss.android.ugc.aweme/com.ss.android.message.sswo.SswoActivity,由于处于待机状态,该activity会立刻进入pause状态;后来得知SswoActivity这个类是抖音专门用来保活的一个透明类。
2,正常情况下,该activity在唤醒时会立刻finish(app request主动请求),然后在唤醒过程中Top Activity依然是Launcher,如果没有及时finish,则Top activity是该activity;
3,如果Top activity是 斗音activity,最终还是会主动请求activity finish,触发斗音activity--->Launcher activity的切换过程;
4,步骤3中的切换过程在 Keyguard lock情况下进行,这种情况下,在显示Launcher activity之前需要将将斗音的UI show过程走一次(WMS 在有keyguard locked情况下的APP 切换逻辑);
5,由于该斗音activity没有UI show过程(该activity没有UI),所以该等待会一直存在,直到5s超时,从而导致issue中的情况。
点亮解锁逻辑流程如下:
- 设备点亮流程
frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java
唤醒源 -> updatePowerStateLocked ->
updateDisplayPowerStateLocked -> requestPowerState
requestPowerState的实现在DisplayPowerController.java中
在这里有一个blockScreen的逻辑动作,为的就是当screen内容准备好后,才点亮屏幕。
相关接口及主要代码:
animateScreenStateChange -> setScreenState -> blockScreenOn
private void blockScreenOn() { if (mPendingScreenOnUnblocker == null) { Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0); mPendingScreenOnUnblocker = new ScreenOnUnblocker(); mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime(); Slog.i(TAG, "Blocking screen on until initial contents have been drawn."); } } |
ScreenOnUnblocker的作用是在回调触发的时候,发送一个消息MSG_SCREEN_ON_UNBLOCKED出去,执行unblock的动作。
private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener { @Override public void onScreenOn() { Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this); msg.setAsynchronous(true); mHandler.sendMessage(msg); } } |
回到setScreenState中,主要代码:
mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker);
screenTurningOn的实现在PhoneWindowmanager中,screenTurningOn回调结束后,屏点亮,unblock之前的逻辑,打印相关消息。
private void unblockScreenOn() { if (mPendingScreenOnUnblocker != null) { mPendingScreenOnUnblocker = null; long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime; Slog.i(TAG, "Unblocked screen on after " + delay + " ms"); //所以可以根据这里来判断屏亮耗时 Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0); } } |
// Called on the DisplayManager's DisplayPowerController thread. @Override public void screenTurningOn(final ScreenOnListener screenOnListener) { if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on..."); //所以可以根据这里来判断屏亮开始触发点亮 。。。 //这里走有锁屏的流程 if (mKeyguardDelegate != null) { mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT); mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, getKeyguardDrawnTimeout()); mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback); } else { if (DEBUG_WAKEUP) Slog.d(TAG, "null mKeyguardDelegate: setting mKeyguardDrawComplete."); finishKeyguardDrawn(); } } } |
从主要代码可以看到,这里触发PhoneWindowManager的screenTurningOn接口,等待Keyguard绘制完成,并设置了一个1s的timeout机制,超时系统就不会继续等待,强制执行finishKeyguardDrawn(),当keyguard正常绘制完成,回调到mKeyguardDrawnCallback,继续执行finishKeyguardDrawn()
case MSG_KEYGUARD_DRAWN_COMPLETE: if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mKeyguardDrawComplete"); finishKeyguardDrawn(); break; case MSG_KEYGUARD_DRAWN_TIMEOUT: Slog.w(TAG, "Keyguard drawn timeout. Setting mKeyguardDrawComplete"); finishKeyguardDrawn(); |
当keyguard绘制完成的时候,接着会等待后台所有visible的window绘制完成
private void finishKeyguardDrawn() { 。。。 // ... eventually calls finishWindowsDrawn which will finalize our screen turn on // as well as enabling the orientation change logic/sensor. mWindowManagerInternal.waitForAllWindowsDrawn(mWindowManagerDrawCallback, WAITING_FOR_DRAWN_TIMEOUT); } |
WAITING_FOR_DRAWN_TIMEOUT是一个1s的延时逻辑。
如果时间到了,还没有绘制完成,系统也不会一直等下去
@Override public void waitForAllWindowsDrawn(Runnable callback, long timeout) { boolean allWindowsDrawn = false; synchronized (mWindowMap) { mWaitingForDrawnCallback = callback; getDefaultDisplayContentLocked().waitForAllWindowsDrawn(); mWindowPlacerLocked.requestTraversal(); mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT); if (mWaitingForDrawn.isEmpty()) { allWindowsDrawn = true; } else { mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, timeout); checkDrawnWindowsLocked(); } } if (allWindowsDrawn) { callback.run(); } } |
超时后,会清掉mWaitingForDrawn,在执行回调的run方法
case WAITING_FOR_DRAWN_TIMEOUT: { Runnable callback = null; synchronized (mWindowMap) { Slog.w(TAG_WM, "Timeout waiting for drawn: undrawn=" + mWaitingForDrawn); mWaitingForDrawn.clear(); callback = mWaitingForDrawnCallback; mWaitingForDrawnCallback = null; } if (callback != null) { callback.run(); } break; } |
在看callback的实现,发送一个消息触发window绘制完成finishWindowsDrawn使能屏幕:
final Runnable mWindowManagerDrawCallback = new Runnable() { @Override public void run() { if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for display!"); mHandler.sendEmptyMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE); } }; |
private void finishWindowsDrawn() { synchronized (mLock) { if (!mScreenOnEarly || mWindowManagerDrawComplete) { return; // Screen is not turned on or we did already handle this case earlier. }
mWindowManagerDrawComplete = true; }
finishScreenTurningOn(); } |
所以,从这里可以看出,上层影响屏亮的因素:
- 锁屏绘制时长
- Visible的window绘制的时长
到此,背光点亮。
- Activity转场显示主要逻辑(锁屏状态下)
在锁屏显示状态下,当activityA->activityB切换的时候,会存在切换动画。
AMS startActivity会调用到ActivityStackSupervisor.java中的realStartActivityLocked,这里只看主要逻辑:
if (mKeyguardController.isKeyguardLocked()) { r.notifyUnknownVisibilityLaunched(); } |
notifyUnknownVisibilityLaunched的实现在ActivityRecord中
void notifyUnknownVisibilityLaunched() {
// No display activities never add a window, so there is no point in waiting them for // relayout. if (!noDisplay) { mWindowContainerController.notifyUnknownVisibilityLaunched(); } } |
接着看AppWindowContainerController中的实现:
public void notifyUnknownVisibilityLaunched() { synchronized(mWindowMap) { if (mContainer != null) { mService.mUnknownAppVisibilityController.notifyLaunched(mContainer); } } } |
最终实现在:
/** * Notifies that {@param appWindow} has been launched behind Keyguard, and we need to wait until * it is resumed and relaid out to resolve the visibility. */ void notifyLaunched(@NonNull AppWindowToken appWindow) { if (DEBUG_UNKNOWN_APP_VISIBILITY) { Slog.d(TAG, "App launched appWindow=" + appWindow); } mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_RESUME); } |
同样在AMS执行activity Resume生命周期的时候,会触发:
/** * Notifies that {@param appWindow} has finished resuming. */ void notifyAppResumedFinished(@NonNull AppWindowToken appWindow) { if (mUnknownApps.containsKey(appWindow) && mUnknownApps.get(appWindow) == UNKNOWN_STATE_WAITING_RESUME) { if (DEBUG_UNKNOWN_APP_VISIBILITY) { Slog.d(TAG, "App resume finished appWindow=" + appWindow); } mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_RELAYOUT); } } |
当relayout完成时,会清掉mUnknownApps
/** * Notifies that {@param appWindow} has relaid out. */ void notifyRelayouted(@NonNull AppWindowToken appWindow) { if (!mUnknownApps.containsKey(appWindow)) { return; } if (DEBUG_UNKNOWN_APP_VISIBILITY) { Slog.d(TAG, "App relayouted appWindow=" + appWindow); } int state = mUnknownApps.get(appWindow); if (state == UNKNOWN_STATE_WAITING_RELAYOUT) { mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_VISIBILITY_UPDATE); mService.notifyKeyguardFlagsChanged(this::notifyVisibilitiesUpdated); } } |
接着我们直接看AppTransition.java中,关于切换流程prepareAppTransitionLocked
mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT); mService.mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS); |
APP_TRANSITION_TIMEOUT_MS是一个5s的timeout机制,当超时了,系统也不会继续等待,而是强制继续执行。
case APP_TRANSITION_TIMEOUT: { synchronized (mWindowMap) { if (mAppTransition.isTransitionSet() || !mOpeningApps.isEmpty() || !mClosingApps.isEmpty()) { if (DEBUG_APP_TRANSITIONS || mWindowManagerDebugger.WMS_DEBUG_USER) Slog.v(TAG_WM, "*** APP TRANSITION TIMEOUT." + " isTransitionSet()=" + mAppTransition.isTransitionSet() + " mOpeningApps.size()=" + mOpeningApps.size() + " mClosingApps.size()=" + mClosingApps.size()); mAppTransition.setTimeout();//设置超时flag mWindowPlacerLocked.performSurfacePlacement();//强制绘制,不等待 } } |
直接看performSurfacePlacement的主要逻辑
// If we are ready to perform an app transition, check through all of the app tokens to be // shown and see if they are ready to go. if (mService.mAppTransition.isReady()) { defaultDisplay.pendingLayoutChanges |= surfacePlacer.handleAppTransitionReadyLocked(); if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("after handleAppTransitionReadyLocked", defaultDisplay.pendingLayoutChanges); } |
int handleAppTransitionReadyLocked() { int appsCount = mService.mOpeningApps.size(); //如果这里return了,后面就不会触发,界面就没内容 if (!transitionGoodToGo(appsCount, mTempTransitionReasons)) { return 0; } Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); int transit = mService.mAppTransition.getAppTransition(); if (mService.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) { transit = AppTransition.TRANSIT_UNSET; } mService.mSkipAppTransitionAnimation = false; mService.mNoAnimationNotifyOnTransitionFinished.clear(); //界面显示成功,转场完成,remove掉之前的timeout消息 mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT); } |
transitionGoodToGo的实现,这里只看与该问题相关的逻辑代码:
private boolean transitionGoodToGo(int appsCount, SparseIntArray outReasons) { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Checking " + appsCount + " opening apps (frozen=" + mService.mDisplayFrozen + " timeout=" + mService.mAppTransition.isTimeout() + ")..."); if (!mService.mAppTransition.isTimeout()) { ... //如果这里mUnknownApps不为空,那么说明relayout没有完成,直接return false 不会触发后面GOOD TO GO的逻辑显示 if (!mService.mUnknownAppVisibilityController.allResolved()) { if (DEBUG_APP_TRANSITIONS) { Slog.v(TAG, "unknownApps is not empty: " + mService.mUnknownAppVisibilityController.getDebugMessage()); } return false; } ... } return true; } |
到此该问题的相关逻辑已经梳理完成。