Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9ee5dff8 authored by Adrian Roos's avatar Adrian Roos Committed by Michael Wright
Browse files

BootAnimation: Fix boot animation with hidden cutout

We do this by storing the masking inset in a persistent property.
The boot animation then animates itself to where it would be if that
masking inset were applied, then changes the viewport.

For this to work, we also need to make sure the DisplayManagerService
has the right overlay right at the start.

Bug: 112876936
Test: Hide cutout, then reboot. Verify boot animation is smooth.
Change-Id: I3e988b2340b2e0d2be3939bdc6878704c234ccc8
parent 2c9fd5fb
Loading
Loading
Loading
Loading
+48 −1
Original line number Diff line number Diff line
@@ -302,6 +302,7 @@ status_t BootAnimation::readyToRun() {
    mHeight = h;
    mFlingerSurfaceControl = control;
    mFlingerSurface = s;
    mTargetInset = -1;

    // If the device has encryption turned on or is in process
    // of being encrypted we show the encrypted boot animation.
@@ -942,6 +943,7 @@ bool BootAnimation::playAnimation(const Animation& animation)
                if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
                    drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
                }
                handleViewport(frameDuration);

                eglSwapBuffers(mDisplay, mSurface);

@@ -966,7 +968,7 @@ bool BootAnimation::playAnimation(const Animation& animation)
            usleep(part.pause * ns2us(frameDuration));

            // For infinite parts, we've now played them at least once, so perhaps exit
            if(exitPending() && !part.count)
            if(exitPending() && !part.count && mCurrentInset >= mTargetInset)
                break;
        }

@@ -986,6 +988,51 @@ bool BootAnimation::playAnimation(const Animation& animation)
    return true;
}

void BootAnimation::handleViewport(nsecs_t timestep) {
    if (mShuttingDown || !mFlingerSurfaceControl || mTargetInset == 0) {
        return;
    }
    if (mTargetInset < 0) {
        // Poll the amount for the top display inset. This will return -1 until persistent properties
        // have been loaded.
        mTargetInset = android::base::GetIntProperty("persist.sys.displayinset.top",
                -1 /* default */, -1 /* min */, mHeight / 2 /* max */);
    }
    if (mTargetInset <= 0) {
        return;
    }

    if (mCurrentInset < mTargetInset) {
        // After the device boots, the inset will effectively be cropped away. We animate this here.
        float fraction = static_cast<float>(mCurrentInset) / mTargetInset;
        int interpolatedInset = (cosf((fraction + 1) * M_PI) / 2.0f + 0.5f) * mTargetInset;

        SurfaceComposerClient::Transaction()
                .setCrop(mFlingerSurfaceControl, Rect(0, interpolatedInset, mWidth, mHeight))
                .apply();
    } else {
        // At the end of the animation, we switch to the viewport that DisplayManager will apply
        // later. This changes the coordinate system, and means we must move the surface up by
        // the inset amount.
        sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
                ISurfaceComposer::eDisplayIdMain));

        Rect layerStackRect(0, 0, mWidth, mHeight - mTargetInset);
        Rect displayRect(0, mTargetInset, mWidth, mHeight);

        SurfaceComposerClient::Transaction t;
        t.setPosition(mFlingerSurfaceControl, 0, -mTargetInset)
                .setCrop(mFlingerSurfaceControl, Rect(0, mTargetInset, mWidth, mHeight));
        t.setDisplayProjection(dtoken, 0 /* orientation */, layerStackRect, displayRect);
        t.apply();

        mTargetInset = mCurrentInset = 0;
    }

    int delta = timestep * mTargetInset / ms2ns(200);
    mCurrentInset += delta;
}

void BootAnimation::releaseAnimation(Animation* animation) const
{
    for (Vector<Animation::Part>::iterator it = animation->parts.begin(),
+4 −0
Original line number Diff line number Diff line
@@ -157,11 +157,15 @@ private:

    void checkExit();

    void handleViewport(nsecs_t timestep);

    sp<SurfaceComposerClient>       mSession;
    AssetManager mAssets;
    Texture     mAndroid[2];
    int         mWidth;
    int         mHeight;
    int         mCurrentInset;
    int         mTargetInset;
    bool        mUseNpotTextures = false;
    EGLDisplay  mDisplay;
    EGLDisplay  mContext;
+11 −0
Original line number Diff line number Diff line
@@ -146,6 +146,7 @@ import com.android.internal.os.RuntimeInit;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.org.conscrypt.OpenSSLSocketImpl;
import com.android.org.conscrypt.TrustedCertificateStore;
@@ -5144,6 +5145,16 @@ public final class ActivityThread extends ClientTransactionHandler {
        }
    }

    /**
     * Updates the application info.
     *
     * This only works in the system process. Must be called on the main thread.
     */
    public void handleSystemApplicationInfoChanged(@NonNull ApplicationInfo ai) {
        Preconditions.checkState(mSystemThread, "Must only be called in the system process");
        handleApplicationInfoChanged(ai);
    }

    void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) {
        // Updates triggered by package installation go through a package update
        // receiver. Here we try to capture ApplicationInfo changes that are
+43 −2
Original line number Diff line number Diff line
@@ -151,6 +151,8 @@ public final class DisplayManagerService extends SystemService {
    // Otherwise WFD is enabled according to the value of config_enableWifiDisplay.
    private static final String FORCE_WIFI_DISPLAY_ENABLE = "persist.debug.wfd.enable";

    private static final String PROP_DEFAULT_DISPLAY_TOP_INSET = "persist.sys.displayinset.top";

    private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;

    private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
@@ -243,6 +245,15 @@ public final class DisplayManagerService extends SystemService {
    // device).
    private Point mStableDisplaySize = new Point();

    // Whether the system has finished booting or not.
    private boolean mSystemReady;

    // The top inset of the default display.
    // This gets persisted so that the boot animation knows how to transition from the display's
    // full size to the size configured by the user. Right now we only persist and animate the top
    // inset, but theoretically we could do it for all of them.
    private int mDefaultDisplayTopInset;

    // Viewports of the default display and the display that should receive touch
    // input from an external source.  Used by the input system.
    private final DisplayViewport mDefaultViewport = new DisplayViewport();
@@ -301,6 +312,7 @@ public final class DisplayManagerService extends SystemService {
        Resources resources = mContext.getResources();
        mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_defaultDisplayDefaultColorMode);
        mDefaultDisplayTopInset = SystemProperties.getInt(PROP_DEFAULT_DISPLAY_TOP_INSET, -1);
        float[] lux = getFloatArray(resources.obtainTypedArray(
                com.android.internal.R.array.config_minimumBrightnessCurveLux));
        float[] nits = getFloatArray(resources.obtainTypedArray(
@@ -311,6 +323,8 @@ public final class DisplayManagerService extends SystemService {
        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
        mCurrentUserId = UserHandle.USER_SYSTEM;

        mSystemReady = false;
    }

    public void setupSchedulerPolicies() {
@@ -400,6 +414,10 @@ public final class DisplayManagerService extends SystemService {
        synchronized (mSyncRoot) {
            mSafeMode = safeMode;
            mOnlyCore = onlyCore;
            mSystemReady = true;
            // Just in case the top inset changed before the system was ready. At this point, any
            // relevant configuration should be in place.
            recordTopInsetLocked(mLogicalDisplays.get(Display.DEFAULT_DISPLAY));
        }

        mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
@@ -457,7 +475,7 @@ public final class DisplayManagerService extends SystemService {
            LogicalDisplay display = mLogicalDisplays.get(displayId);
            if (display != null) {
                if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) {
                    sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
                    handleLogicalDisplayChanged(displayId, display);
                    scheduleTraversalLocked(false);
                }
            }
@@ -938,6 +956,13 @@ public final class DisplayManagerService extends SystemService {
        scheduleTraversalLocked(false);
    }

    private void handleLogicalDisplayChanged(int displayId, @NonNull LogicalDisplay display) {
        if (displayId == Display.DEFAULT_DISPLAY) {
            recordTopInsetLocked(display);
        }
        sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
    }

    private void applyGlobalDisplayStateLocked(List<Runnable> workQueue) {
        final int count = mDisplayDevices.size();
        for (int i = 0; i < count; i++) {
@@ -991,6 +1016,7 @@ public final class DisplayManagerService extends SystemService {
        configureColorModeLocked(display, device);
        if (isDefault) {
            recordStableDisplayStatsIfNeededLocked(display);
            recordTopInsetLocked(display);
        }

        mLogicalDisplays.put(displayId, display);
@@ -1039,6 +1065,21 @@ public final class DisplayManagerService extends SystemService {
        }
    }

    private void recordTopInsetLocked(@Nullable LogicalDisplay d) {
        // We must only persist the inset after boot has completed, otherwise we will end up
        // overwriting the persisted value before the masking flag has been loaded from the
        // resource overlay.
        if (!mSystemReady || d == null) {
            return;
        }
        int topInset = d.getInsets().top;
        if (topInset == mDefaultDisplayTopInset) {
            return;
        }
        mDefaultDisplayTopInset = topInset;
        SystemProperties.set(PROP_DEFAULT_DISPLAY_TOP_INSET, Integer.toString(topInset));
    }

    private void setStableDisplaySizeLocked(int width, int height) {
        mStableDisplaySize = new Point(width, height);
        try {
@@ -1118,7 +1159,7 @@ public final class DisplayManagerService extends SystemService {
                sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
                changed = true;
            } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
                sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
                handleLogicalDisplayChanged(displayId, display);
                changed = true;
            }
        }
+14 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.display;

import android.graphics.Rect;
import android.hardware.display.DisplayManagerInternal;
import android.os.SystemProperties;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -57,6 +58,8 @@ import java.util.Objects;
 * </p>
 */
final class LogicalDisplay {
    private static final String PROP_MASKING_INSET_TOP = "persist.sys.displayinset.top";

    private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();

    // The layer stack we use when the display has been blanked to prevent any
@@ -296,6 +299,17 @@ final class LogicalDisplay {
        }
    }

    /**
     * Return the insets currently applied to the display.
     *
     * Note that the base DisplayInfo already takes these insets into account, so if you want to
     * find out the <b>true</b> size of the display, you need to add them back to the logical
     * dimensions.
     */
    public Rect getInsets() {
        return getMaskingInsets(mPrimaryDisplayDeviceInfo);
    }

    /**
     * Returns insets in ROTATION_0 for areas that are masked.
     */
Loading