Android 亮屏慢问题处理

问题:

【指纹】灭屏下使用正确的指纹解锁,解锁成功背光亮但屏幕没亮

【偶现】滑动解锁后只显示壁纸,图标在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中的情况。

点亮解锁逻辑流程如下:

  1. 设备点亮流程

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();
        }
    }
}

 

从主要代码可以看到,这里触发PhoneWindowManagerscreenTurningOn接口,等待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();
}

 

所以,从这里可以看出,上层影响屏亮的因素:

  1. 锁屏绘制时长
  2. Visible的window绘制的时长

到此,背光点亮。

 

  1. 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;
}

 

到此该问题的相关逻辑已经梳理完成。

 

猜你喜欢

转载自blog.csdn.net/chi_wy/article/details/81167163