Android horizontal and vertical screen switch stuck problem

Today, an android system is stuck when switching between horizontal and vertical screens. It can only turn around in about 3 seconds. Finally, it is pointed out that the function of the ScreenRotationAnimation class is called

SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
                        SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), sur);

Caused by screenshot.
The screenshot function is ultimately done by surfaceflinger. Add log debugging to the surface linker.

nsecs_t st = systemTime();
result = flinger->captureScreenImplLocked(hw,
        producer, reqWidth, reqHeight, minLayerZ, maxLayerZ);
nsecs_t et = systemTime();
ALOGI("surfaceflinger captureScreenImplLocked use time:%dms", (int)ns2ms(et - st));

Get the following output:

03-11 14:12:44.550 2259-2797/? W/ScreenRotationAnimation: SurfaceControl.screenshot use 3142ms
03-11 14:13:01.950 2259-2669/? W/ScreenRotationAnimation: SurfaceControl.screenshot use 3282ms
03-11 14:23:26.250 2259-2669/? W/ScreenRotationAnimation: SurfaceControl.screenshot use 3176ms
03-11 14:23:36.790 2259-2668/? W/ScreenRotationAnimation: SurfaceControl.screenshot use 3239ms

Why does it take so long?
Finally, the research found that this time has something to do with the number of surfaceviews. Every additional SurfaceView screenshot takes 500 to 600 milliseconds. My interface has added 3 surfaceviews in total, so the screenshot takes 3 seconds.
If a SurfaceView is not added, the screenshot only takes about 600 milliseconds.
Take a look at the implementation of the capturescreen impllocked function, and find that this function is very magical. Only a few GL functions are called, and the screenshot is completed.

status_t SurfaceFlinger::captureScreenImplLocked(
        const sp<const DisplayDevice>& hw,
        const sp<IGraphicBufferProducer>& producer,
        uint32_t reqWidth, uint32_t reqHeight,
        uint32_t minLayerZ, uint32_t maxLayerZ)
{
    ATRACE_CALL();

    // get screen geometry
    const uint32_t hw_w = hw->getWidth();
    const uint32_t hw_h = hw->getHeight();

    // if we have secure windows on this display, never allow the screen capture
    if (hw->getSecureLayerVisible()) {
        ALOGW("FB is protected: PERMISSION_DENIED");
        return PERMISSION_DENIED;
    }

    if ((reqWidth > hw_w) || (reqHeight > hw_h)) {
        ALOGE("size mismatch (%d, %d) > (%d, %d)",
                reqWidth, reqHeight, hw_w, hw_h);
        return BAD_VALUE;
    }

    reqWidth = (!reqWidth) ? hw_w : reqWidth;
    reqHeight = (!reqHeight) ? hw_h : reqHeight;

    // Create a surface to render into
    sp<Surface> surface = new Surface(producer);
    ANativeWindow* const window = surface.get();

    // set the buffer size to what the user requested
    native_window_set_buffers_user_dimensions(window, reqWidth, reqHeight);

    // and create the corresponding EGLSurface
    EGLSurface eglSurface = eglCreateWindowSurface(
            mEGLDisplay, mEGLConfig, window, NULL);
    if (eglSurface == EGL_NO_SURFACE) {
        ALOGE("captureScreenImplLocked: eglCreateWindowSurface() failed 0x%4x",
                eglGetError());
        return BAD_VALUE;
    }

    if (!eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext)) {
        ALOGE("captureScreenImplLocked: eglMakeCurrent() failed 0x%4x",
                eglGetError());
        eglDestroySurface(mEGLDisplay, eglSurface);
        return BAD_VALUE;
    }

    renderScreenImplLocked(hw, reqWidth, reqHeight, minLayerZ, maxLayerZ, false);

    // and finishing things up...
    if (eglSwapBuffers(mEGLDisplay, eglSurface) != EGL_TRUE) {
        ALOGE("captureScreenImplLocked: eglSwapBuffers() failed 0x%4x",
                eglGetError());
        eglDestroySurface(mEGLDisplay, eglSurface);
        getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
        return BAD_VALUE;
    }

    eglDestroySurface(mEGLDisplay, eglSurface);
    getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
    return NO_ERROR;
}

I don't know. I can't change this function. But fortunately, our device is ink screen, which is very slow to refresh. When rotating, we don't need animation. We can find a way to get rid of animation, so we don't need to call screenshot function.
First print the call stack to see who called the screenshot function.

 	try {
		throw new Exception();
     } catch(Exception e) {
		e.printStackTrace();
     }
	long st = System.currentTimeMillis();    SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
             SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), sur);				
	long et = System.currentTimeMillis();
	Slog.w(TAG, "SurfaceControl.screenshot use " + (et - st) + "ms");

Get the following output:

03-11 14:27:39.840 2259-2439/? W/System.err: java.lang.Exception
03-11 14:27:39.840 2259-2439/? W/System.err:     at com.android.server.wm.ScreenRotationAnimation.<init>(ScreenRotationAnimation.java:260)
03-11 14:27:39.840 2259-2439/? W/System.err:     at com.android.server.wm.WindowManagerService.startFreezingDisplayLocked(WindowManagerService.java:9945)
03-11 14:27:39.840 2259-2439/? W/System.err:     at com.android.server.wm.WindowManagerService.updateRotationUncheckedLocked(WindowManagerService.java:5960)
03-11 14:27:39.840 2259-2439/? W/System.err:     at com.android.server.wm.WindowManagerService.updateOrientationFromAppTokensLocked(WindowManagerService.java:3708)
03-11 14:27:39.840 2259-2439/? W/System.err:     at com.android.server.wm.WindowManagerService.updateOrientationFromAppTokensLocked(WindowManagerService.java:3645)
03-11 14:27:39.840 2259-2439/? W/System.err:     at com.android.server.wm.WindowManagerService.updateOrientationFromAppTokens(WindowManagerService.java:3633)
03-11 14:27:39.840 2259-2439/? W/System.err:     at com.android.server.am.ActivityManagerService.setRequestedOrientation(ActivityManagerService.java:3596)
03-11 14:27:39.840 2259-2439/? W/System.err:     at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:955)
03-11 14:27:39.840 2259-2439/? W/System.err:     at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2084)
03-11 14:27:39.840 2259-2439/? W/System.err:     at android.os.Binder.execTransact(Binder.java:404)
03-11 14:27:39.850 2259-2439/? W/System.err:     at dalvik.system.NativeStart.run(Native Method)

According to the log, see the following code near the line WindowManagerService.java 9945:

if (CUSTOM_SCREEN_ROTATION) {
	mExitAnimId = exitAnim;
	mEnterAnimId = enterAnim;
	final DisplayContent displayContent = getDefaultDisplayContentLocked();
	final int displayId = displayContent.getDisplayId();
	ScreenRotationAnimation screenRotationAnimation =
			mAnimator.getScreenRotationAnimationLocked(displayId);
	if (screenRotationAnimation != null) {
		screenRotationAnimation.kill();
	}

	// TODO(multidisplay): rotation on main screen only.
	screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
			mFxSession, inTransaction, mPolicy.isDefaultOrientationForced());
	mAnimator.setScreenRotationAnimationLocked(displayId, screenRotationAnimation);
}

The key point is if (custom menu menu rotation) {judgment condition, and then search the custom menu menu rotation variable. It is found that there is only one initial value, and there is no value assigned in the middle.

    /**
     * If true, the window manager will do its own custom freezing and general
     * management of the screen during rotation.
     */
    static final boolean CUSTOM_SCREEN_ROTATION = true;

Since this is the case, change the custom screen rotation to false to try the effect.
WOW! Sure enough, the effect is a little poor. You can see the black screen.

Published 3 original articles, won praise and 90 visitors
Private letter follow

Tags: Java Android SurfaceView Linker

Posted on Wed, 11 Mar 2020 03:30:01 -0400 by R0bb0b