All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.wm.DisplayPolicy Maven / Gradle / Ivy

/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.wm;

import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.view.Display.TYPE_BUILT_IN;
import static android.view.InsetsState.TYPE_TOP_BAR;
import static android.view.InsetsState.TYPE_TOP_GESTURES;
import static android.view.InsetsState.TYPE_TOP_TAPPABLE_ELEMENT;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED;
import static android.view.WindowManagerPolicyConstants.EXTRA_HDMI_PLUGGED_STATE;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;

import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_HIDE;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_SHOW;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
import static com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.localLOGV;

import android.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Px;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.LoadedApk;
import android.app.ResourcesManager;
import android.app.StatusBarManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
import android.hardware.power.V1_0.PowerHint;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.IApplicationToken;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.Surface;
import android.view.View;
import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants;
import android.view.accessibility.AccessibilityManager;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.util.function.TriConsumer;
import com.android.internal.widget.PointerLocationView;
import com.android.server.LocalServices;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.policy.WindowManagerPolicy.InputConsumer;
import com.android.server.policy.WindowManagerPolicy.NavigationBarPosition;
import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
import com.android.server.policy.WindowOrientationListener;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.utils.InsetUtils;

import java.io.PrintWriter;

/**
 * The policy that provides the basic behaviors and states of a display to show UI.
 */
public class DisplayPolicy {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayPolicy" : TAG_WM;
    private static final boolean DEBUG = false;

    private static final boolean ALTERNATE_CAR_MODE_NAV_SIZE = false;

    // The panic gesture may become active only after the keyguard is dismissed and the immersive
    // app shows again. If that doesn't happen for 30s we drop the gesture.
    private static final long PANIC_GESTURE_EXPIRATION = 30000;

    // Controls navigation bar opacity depending on which workspace stacks are currently
    // visible.
    // Nav bar is always opaque when either the freeform stack or docked stack is visible.
    private static final int NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED = 0;
    // Nav bar is always translucent when the freeform stack is visible, otherwise always opaque.
    private static final int NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE = 1;
    // Nav bar is never forced opaque.
    private static final int NAV_BAR_FORCE_TRANSPARENT = 2;

    /**
     * These are the system UI flags that, when changing, can cause the layout
     * of the screen to change.
     */
    private static final int SYSTEM_UI_CHANGING_LAYOUT =
            View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.STATUS_BAR_TRANSLUCENT
                    | View.NAVIGATION_BAR_TRANSLUCENT
                    | View.STATUS_BAR_TRANSPARENT
                    | View.NAVIGATION_BAR_TRANSPARENT;

    private final WindowManagerService mService;
    private final Context mContext;
    private final DisplayContent mDisplayContent;
    private final Object mLock;
    private final Handler mHandler;

    private Resources mCurrentUserResources;

    private final boolean mCarDockEnablesAccelerometer;
    private final boolean mDeskDockEnablesAccelerometer;
    private final AccessibilityManager mAccessibilityManager;
    private final ImmersiveModeConfirmation mImmersiveModeConfirmation;
    private final ScreenshotHelper mScreenshotHelper;

    private final Object mServiceAcquireLock = new Object();
    private StatusBarManagerInternal mStatusBarManagerInternal;

    @Px
    private int mBottomGestureAdditionalInset;
    @Px
    private int mSideGestureInset;

    private StatusBarManagerInternal getStatusBarManagerInternal() {
        synchronized (mServiceAcquireLock) {
            if (mStatusBarManagerInternal == null) {
                mStatusBarManagerInternal =
                        LocalServices.getService(StatusBarManagerInternal.class);
            }
            return mStatusBarManagerInternal;
        }
    }

    private final SystemGesturesPointerEventListener mSystemGestures;

    private volatile int mLidState = LID_ABSENT;
    private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
    private volatile boolean mHdmiPlugged;

    private volatile boolean mHasStatusBar;
    private volatile boolean mHasNavigationBar;
    // Can the navigation bar ever move to the side?
    private volatile boolean mNavigationBarCanMove;
    private volatile boolean mNavigationBarLetsThroughTaps;
    private volatile boolean mNavigationBarAlwaysShowOnSideGesture;
    private volatile boolean mAllowSeamlessRotationDespiteNavBarMoving;

    // Written by vr manager thread, only read in this class.
    private volatile boolean mPersistentVrModeEnabled;

    private volatile boolean mAwake;
    private volatile boolean mScreenOnEarly;
    private volatile boolean mScreenOnFully;
    private volatile ScreenOnListener mScreenOnListener;

    private volatile boolean mKeyguardDrawComplete;
    private volatile boolean mWindowManagerDrawComplete;

    private final ArraySet mScreenDecorWindows = new ArraySet<>();
    private WindowState mStatusBar = null;
    private final int[] mStatusBarHeightForRotation = new int[4];
    private WindowState mNavigationBar = null;
    @NavigationBarPosition
    private int mNavigationBarPosition = NAV_BAR_BOTTOM;
    private int[] mNavigationBarHeightForRotationDefault = new int[4];
    private int[] mNavigationBarWidthForRotationDefault = new int[4];
    private int[] mNavigationBarHeightForRotationInCarMode = new int[4];
    private int[] mNavigationBarWidthForRotationInCarMode = new int[4];

    /** See {@link #getNavigationBarFrameHeight} */
    private int[] mNavigationBarFrameHeightForRotationDefault = new int[4];

    /** Cached value of {@link ScreenShapeHelper#getWindowOutsetBottomPx} */
    @Px private int mWindowOutsetBottom;

    private final StatusBarController mStatusBarController;

    private final BarController mNavigationBarController;

    private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener =
            new BarController.OnBarVisibilityChangedListener() {
                @Override
                public void onBarVisibilityChanged(boolean visible) {
                    if (mAccessibilityManager == null) {
                        return;
                    }
                    mAccessibilityManager.notifyAccessibilityButtonVisibilityChanged(visible);
                }
            };

    @GuardedBy("mHandler")
    private SleepToken mDreamingSleepToken;

    @GuardedBy("mHandler")
    private SleepToken mWindowSleepToken;

    private final Runnable mAcquireSleepTokenRunnable;
    private final Runnable mReleaseSleepTokenRunnable;

    // The windows we were told about in focusChanged.
    private WindowState mFocusedWindow;
    private WindowState mLastFocusedWindow;

    IApplicationToken mFocusedApp;

    int mLastSystemUiFlags;
    // Bits that we are in the process of clearing, so we want to prevent
    // them from being set by applications until everything has been updated
    // to have them clear.
    private int mResettingSystemUiFlags = 0;
    // Bits that we are currently always keeping cleared.
    private int mForceClearedSystemUiFlags = 0;
    private int mLastFullscreenStackSysUiFlags;
    private int mLastDockedStackSysUiFlags;
    private final Rect mNonDockedStackBounds = new Rect();
    private final Rect mDockedStackBounds = new Rect();
    private final Rect mLastNonDockedStackBounds = new Rect();
    private final Rect mLastDockedStackBounds = new Rect();

    // What we last reported to system UI about whether the compatibility
    // menu needs to be displayed.
    private boolean mLastFocusNeedsMenu = false;
    // If nonzero, a panic gesture was performed at that time in uptime millis and is still pending.
    private long mPendingPanicGestureUptime;

    private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
    private static final Rect sTmpRect = new Rect();
    private static final Rect sTmpDockedFrame = new Rect();
    private static final Rect sTmpNavFrame = new Rect();
    private static final Rect sTmpLastParentFrame = new Rect();

    private WindowState mTopFullscreenOpaqueWindowState;
    private WindowState mTopFullscreenOpaqueOrDimmingWindowState;
    private WindowState mTopDockedOpaqueWindowState;
    private WindowState mTopDockedOpaqueOrDimmingWindowState;
    private boolean mTopIsFullscreen;
    private boolean mForceStatusBar;
    private boolean mForceStatusBarFromKeyguard;
    private boolean mForceStatusBarTransparent;
    private int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED;
    private boolean mForcingShowNavBar;
    private int mForcingShowNavBarLayer;
    private boolean mForceShowSystemBars;

    /**
     * Force the display of system bars regardless of other settings.
     */
    private boolean mForceShowSystemBarsFromExternal;

    private boolean mShowingDream;
    private boolean mLastShowingDream;
    private boolean mDreamingLockscreen;
    private boolean mDreamingSleepTokenNeeded;
    private boolean mWindowSleepTokenNeeded;
    private boolean mLastWindowSleepTokenNeeded;
    private boolean mAllowLockscreenWhenOn;

    private InputConsumer mInputConsumer = null;

    private PointerLocationView mPointerLocationView;

    /**
     * The area covered by system windows which belong to another display. Forwarded insets is set
     * in case this is a virtual display, this is displayed on another display that has insets, and
     * the bounds of this display is overlapping with the insets of the host display (e.g. IME is
     * displayed on the host display, and it covers a part of this virtual display.)
     * The forwarded insets is used to compute display frames of this virtual display, which will
     * be then used to layout windows in the virtual display.
     */
    @NonNull private Insets mForwardedInsets = Insets.NONE;

    private RefreshRatePolicy mRefreshRatePolicy;

    // -------- PolicyHandler --------
    private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 1;
    private static final int MSG_REQUEST_TRANSIENT_BARS = 2;
    private static final int MSG_DISPOSE_INPUT_CONSUMER = 3;
    private static final int MSG_ENABLE_POINTER_LOCATION = 4;
    private static final int MSG_DISABLE_POINTER_LOCATION = 5;

    private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
    private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;

    private class PolicyHandler extends Handler {

        PolicyHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_UPDATE_DREAMING_SLEEP_TOKEN:
                    updateDreamingSleepToken(msg.arg1 != 0);
                    break;
                case MSG_REQUEST_TRANSIENT_BARS:
                    WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS)
                            ? mStatusBar : mNavigationBar;
                    if (targetBar != null) {
                        requestTransientBars(targetBar);
                    }
                    break;
                case MSG_DISPOSE_INPUT_CONSUMER:
                    disposeInputConsumer((InputConsumer) msg.obj);
                    break;
                case MSG_ENABLE_POINTER_LOCATION:
                    enablePointerLocation();
                    break;
                case MSG_DISABLE_POINTER_LOCATION:
                    disablePointerLocation();
                    break;
            }
        }
    }

    DisplayPolicy(WindowManagerService service, DisplayContent displayContent) {
        mService = service;
        mContext = displayContent.isDefaultDisplay ? service.mContext
                : service.mContext.createDisplayContext(displayContent.getDisplay());
        mDisplayContent = displayContent;
        mLock = service.getWindowManagerLock();

        final int displayId = displayContent.getDisplayId();
        mStatusBarController = new StatusBarController(displayId);
        mNavigationBarController = new BarController("NavigationBar",
                displayId,
                View.NAVIGATION_BAR_TRANSIENT,
                View.NAVIGATION_BAR_UNHIDE,
                View.NAVIGATION_BAR_TRANSLUCENT,
                StatusBarManager.WINDOW_NAVIGATION_BAR,
                FLAG_TRANSLUCENT_NAVIGATION,
                View.NAVIGATION_BAR_TRANSPARENT);

        final Resources r = mContext.getResources();
        mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
        mDeskDockEnablesAccelerometer = r.getBoolean(R.bool.config_deskDockEnablesAccelerometer);
        mForceShowSystemBarsFromExternal = r.getBoolean(R.bool.config_forceShowSystemBars);

        mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(
                Context.ACCESSIBILITY_SERVICE);
        if (!displayContent.isDefaultDisplay) {
            mAwake = true;
            mScreenOnEarly = true;
            mScreenOnFully = true;
        }

        final Looper looper = UiThread.getHandler().getLooper();
        mHandler = new PolicyHandler(looper);
        mSystemGestures = new SystemGesturesPointerEventListener(mContext, mHandler,
                new SystemGesturesPointerEventListener.Callbacks() {
                    @Override
                    public void onSwipeFromTop() {
                        if (mStatusBar != null) {
                            requestTransientBars(mStatusBar);
                        }
                    }

                    @Override
                    public void onSwipeFromBottom() {
                        if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_BOTTOM) {
                            requestTransientBars(mNavigationBar);
                        }
                    }

                    @Override
                    public void onSwipeFromRight() {
                        final Region excludedRegion;
                        synchronized (mLock) {
                            excludedRegion = mDisplayContent.calculateSystemGestureExclusion();
                        }
                        final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture
                                || mNavigationBarPosition == NAV_BAR_RIGHT;
                        if (mNavigationBar != null && sideAllowed
                                && !mSystemGestures.currentGestureStartedInRegion(excludedRegion)) {
                            requestTransientBars(mNavigationBar);
                        }
                    }

                    @Override
                    public void onSwipeFromLeft() {
                        final Region excludedRegion;
                        synchronized (mLock) {
                            excludedRegion = mDisplayContent.calculateSystemGestureExclusion();
                        }
                        final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture
                                || mNavigationBarPosition == NAV_BAR_LEFT;
                        if (mNavigationBar != null && sideAllowed
                                && !mSystemGestures.currentGestureStartedInRegion(excludedRegion)) {
                            requestTransientBars(mNavigationBar);
                        }
                    }

                    @Override
                    public void onFling(int duration) {
                        if (mService.mPowerManagerInternal != null) {
                            mService.mPowerManagerInternal.powerHint(
                                    PowerHint.INTERACTION, duration);
                        }
                    }

                    @Override
                    public void onDebug() {
                        // no-op
                    }

                    private WindowOrientationListener getOrientationListener() {
                        final DisplayRotation rotation = mDisplayContent.getDisplayRotation();
                        return rotation != null ? rotation.getOrientationListener() : null;
                    }

                    @Override
                    public void onDown() {
                        final WindowOrientationListener listener = getOrientationListener();
                        if (listener != null) {
                            listener.onTouchStart();
                        }
                    }

                    @Override
                    public void onUpOrCancel() {
                        final WindowOrientationListener listener = getOrientationListener();
                        if (listener != null) {
                            listener.onTouchEnd();
                        }
                    }

                    @Override
                    public void onMouseHoverAtTop() {
                        mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
                        Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
                        msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS;
                        mHandler.sendMessageDelayed(msg, 500 /* delayMillis */);
                    }

                    @Override
                    public void onMouseHoverAtBottom() {
                        mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
                        Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
                        msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION;
                        mHandler.sendMessageDelayed(msg, 500 /* delayMillis */);
                    }

                    @Override
                    public void onMouseLeaveFromEdge() {
                        mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
                    }
                });
        displayContent.registerPointerEventListener(mSystemGestures);
        displayContent.mAppTransition.registerListenerLocked(
                mStatusBarController.getAppTransitionListener());
        mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper,
                mService.mVrModeEnabled);
        mAcquireSleepTokenRunnable = () -> {
            if (mWindowSleepToken != null) {
                return;
            }
            mWindowSleepToken = service.mAtmInternal.acquireSleepToken(
                    "WindowSleepTokenOnDisplay" + displayId, displayId);
        };
        mReleaseSleepTokenRunnable = () -> {
            if (mWindowSleepToken == null) {
                return;
            }
            mWindowSleepToken.release();
            mWindowSleepToken = null;
        };

        // TODO: Make it can take screenshot on external display
        mScreenshotHelper = displayContent.isDefaultDisplay
                ? new ScreenshotHelper(mContext) : null;

        if (mDisplayContent.isDefaultDisplay) {
            mHasStatusBar = true;
            mHasNavigationBar = mContext.getResources().getBoolean(R.bool.config_showNavigationBar);

            // Allow a system property to override this. Used by the emulator.
            // See also hasNavigationBar().
            String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
            if ("1".equals(navBarOverride)) {
                mHasNavigationBar = false;
            } else if ("0".equals(navBarOverride)) {
                mHasNavigationBar = true;
            }
        } else {
            mHasStatusBar = false;
            mHasNavigationBar = mDisplayContent.supportsSystemDecorations();
        }

        mRefreshRatePolicy = new RefreshRatePolicy(mService,
                mDisplayContent.getDisplayInfo(),
                mService.mHighRefreshRateBlacklist);
    }

    void systemReady() {
        mSystemGestures.systemReady();
        if (mService.mPointerLocationEnabled) {
            setPointerLocationEnabled(true);
        }
    }

    private int getDisplayId() {
        return mDisplayContent.getDisplayId();
    }

    public void setHdmiPlugged(boolean plugged) {
        setHdmiPlugged(plugged, false /* force */);
    }

    public void setHdmiPlugged(boolean plugged, boolean force) {
        if (force || mHdmiPlugged != plugged) {
            mHdmiPlugged = plugged;
            mService.updateRotation(true /* alwaysSendConfiguration */, true /* forceRelayout */);
            final Intent intent = new Intent(ACTION_HDMI_PLUGGED);
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
            intent.putExtra(EXTRA_HDMI_PLUGGED_STATE, plugged);
            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
        }
    }

    boolean isHdmiPlugged() {
        return mHdmiPlugged;
    }

    boolean isCarDockEnablesAccelerometer() {
        return mCarDockEnablesAccelerometer;
    }

    boolean isDeskDockEnablesAccelerometer() {
        return mDeskDockEnablesAccelerometer;
    }

    public void setPersistentVrModeEnabled(boolean persistentVrModeEnabled) {
        mPersistentVrModeEnabled = persistentVrModeEnabled;
    }

    public boolean isPersistentVrModeEnabled() {
        return mPersistentVrModeEnabled;
    }

    public void setDockMode(int dockMode) {
        mDockMode = dockMode;
    }

    public int getDockMode() {
        return mDockMode;
    }

    /**
     * @see WindowManagerService.setForceShowSystemBars
     */
    void setForceShowSystemBars(boolean forceShowSystemBars) {
        mForceShowSystemBarsFromExternal = forceShowSystemBars;
    }

    public boolean hasNavigationBar() {
        return mHasNavigationBar;
    }

    public boolean hasStatusBar() {
        return mHasStatusBar;
    }

    public boolean navigationBarCanMove() {
        return mNavigationBarCanMove;
    }

    public void setLidState(int lidState) {
        mLidState = lidState;
    }

    public int getLidState() {
        return mLidState;
    }

    public void setAwake(boolean awake) {
        mAwake = awake;
    }

    public boolean isAwake() {
        return mAwake;
    }

    public boolean isScreenOnEarly() {
        return mScreenOnEarly;
    }

    public boolean isScreenOnFully() {
        return mScreenOnFully;
    }

    public boolean isKeyguardDrawComplete() {
        return mKeyguardDrawComplete;
    }

    public boolean isWindowManagerDrawComplete() {
        return mWindowManagerDrawComplete;
    }

    public ScreenOnListener getScreenOnListener() {
        return mScreenOnListener;
    }

    public void screenTurnedOn(ScreenOnListener screenOnListener) {
        synchronized (mLock) {
            mScreenOnEarly = true;
            mScreenOnFully = false;
            mKeyguardDrawComplete = false;
            mWindowManagerDrawComplete = false;
            mScreenOnListener = screenOnListener;
        }
    }

    public void screenTurnedOff() {
        synchronized (mLock) {
            mScreenOnEarly = false;
            mScreenOnFully = false;
            mKeyguardDrawComplete = false;
            mWindowManagerDrawComplete = false;
            mScreenOnListener = null;
        }
    }

    /** Return false if we are not awake yet or we have already informed of this event. */
    public boolean finishKeyguardDrawn() {
        synchronized (mLock) {
            if (!mScreenOnEarly || mKeyguardDrawComplete) {
                return false;
            }

            mKeyguardDrawComplete = true;
            mWindowManagerDrawComplete = false;
        }
        return true;
    }

    /** Return false if screen is not turned on or we did already handle this case earlier. */
    public boolean finishWindowsDrawn() {
        synchronized (mLock) {
            if (!mScreenOnEarly || mWindowManagerDrawComplete) {
                return false;
            }

            mWindowManagerDrawComplete = true;
        }
        return true;
    }

    /** Return false if it is not ready to turn on. */
    public boolean finishScreenTurningOn() {
        synchronized (mLock) {
            if (DEBUG_SCREEN_ON) Slog.d(TAG,
                    "finishScreenTurningOn: mAwake=" + mAwake
                            + ", mScreenOnEarly=" + mScreenOnEarly
                            + ", mScreenOnFully=" + mScreenOnFully
                            + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete
                            + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);

            if (mScreenOnFully || !mScreenOnEarly || !mWindowManagerDrawComplete
                    || (mAwake && !mKeyguardDrawComplete)) {
                return false;
            }

            if (DEBUG_SCREEN_ON) Slog.i(TAG, "Finished screen turning on...");
            mScreenOnListener = null;
            mScreenOnFully = true;
        }
        return true;
    }

    private boolean hasStatusBarServicePermission(int pid, int uid) {
        return mContext.checkPermission(permission.STATUS_BAR_SERVICE, pid, uid)
                == PackageManager.PERMISSION_GRANTED;
    }

    /**
     * Sanitize the layout parameters coming from a client.  Allows the policy
     * to do things like ensure that windows of a specific type can't take
     * input focus.
     *
     * @param attrs The window layout parameters to be modified.  These values
     * are modified in-place.
     */
    public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
            int callingPid, int callingUid) {

        final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
        if (mScreenDecorWindows.contains(win)) {
            if (!isScreenDecor) {
                // No longer has the flag set, so remove from the set.
                mScreenDecorWindows.remove(win);
            }
        } else if (isScreenDecor && hasStatusBarServicePermission(callingPid, callingUid)) {
            mScreenDecorWindows.add(win);
        }

        switch (attrs.type) {
            case TYPE_SYSTEM_OVERLAY:
            case TYPE_SECURE_SYSTEM_OVERLAY:
                // These types of windows can't receive input events.
                attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
                attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
                break;
            case TYPE_DREAM:
            case TYPE_WALLPAPER:
                // Dreams and wallpapers don't have an app window token and can thus not be
                // letterboxed. Hence always let them extend under the cutout.
                attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
                break;
            case TYPE_STATUS_BAR:

                // If the Keyguard is in a hidden state (occluded by another window), we force to
                // remove the wallpaper and keyguard flag so that any change in-flight after setting
                // the keyguard as occluded wouldn't set these flags again.
                // See {@link #processKeyguardSetHiddenResultLw}.
                if (mService.mPolicy.isKeyguardOccluded()) {
                    attrs.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
                    attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
                }
                break;

            case TYPE_SCREENSHOT:
                attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
                break;

            case TYPE_TOAST:
                // While apps should use the dedicated toast APIs to add such windows
                // it possible legacy apps to add the window directly. Therefore, we
                // make windows added directly by the app behave as a toast as much
                // as possible in terms of timeout and animation.
                if (attrs.hideTimeoutMilliseconds < 0
                        || attrs.hideTimeoutMilliseconds > TOAST_WINDOW_TIMEOUT) {
                    attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT;
                }
                // Accessibility users may need longer timeout duration. This api compares
                // original timeout with user's preference and return longer one. It returns
                // original timeout if there's no preference.
                attrs.hideTimeoutMilliseconds = mAccessibilityManager.getRecommendedTimeoutMillis(
                        (int) attrs.hideTimeoutMilliseconds,
                        AccessibilityManager.FLAG_CONTENT_TEXT);
                attrs.windowAnimations = com.android.internal.R.style.Animation_Toast;
                // Toast can show with below conditions when the screen is locked.
                if (canToastShowWhenLocked(callingPid)) {
                    attrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
                }
                break;
        }

        if (attrs.type != TYPE_STATUS_BAR) {
            // The status bar is the only window allowed to exhibit keyguard behavior.
            attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
        }
    }

    /**
     * @return {@code true} if the calling activity initiate toast and is visible with
     * {@link WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} flag.
     */
    boolean canToastShowWhenLocked(int callingPid) {
        return mDisplayContent.forAllWindows(w -> {
            return callingPid == w.mSession.mPid && w.isVisible() && w.canShowWhenLocked();
        }, true /* traverseTopToBottom */);
    }

    /**
     * Preflight adding a window to the system.
     *
     * Currently enforces that three window types are singletons per display:
     * 
    *
  • {@link WindowManager.LayoutParams#TYPE_STATUS_BAR}
  • *
  • {@link WindowManager.LayoutParams#TYPE_NAVIGATION_BAR}
  • *
* * @param win The window to be added * @param attrs Information about the window to be added * * @return If ok, WindowManagerImpl.ADD_OKAY. If too many singletons, * WindowManagerImpl.ADD_MULTIPLE_SINGLETON */ public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) { if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.STATUS_BAR_SERVICE, "DisplayPolicy"); mScreenDecorWindows.add(win); } switch (attrs.type) { case TYPE_STATUS_BAR: mContext.enforceCallingOrSelfPermission( android.Manifest.permission.STATUS_BAR_SERVICE, "DisplayPolicy"); if (mStatusBar != null) { if (mStatusBar.isAlive()) { return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; } } mStatusBar = win; mStatusBarController.setWindow(win); if (mDisplayContent.isDefaultDisplay) { mService.mPolicy.setKeyguardCandidateLw(win); } final TriConsumer frameProvider = (displayFrames, windowState, rect) -> { rect.top = 0; rect.bottom = getStatusBarHeight(displayFrames); }; mDisplayContent.setInsetProvider(TYPE_TOP_BAR, win, frameProvider); mDisplayContent.setInsetProvider(TYPE_TOP_GESTURES, win, frameProvider); mDisplayContent.setInsetProvider(TYPE_TOP_TAPPABLE_ELEMENT, win, frameProvider); break; case TYPE_NAVIGATION_BAR: mContext.enforceCallingOrSelfPermission( android.Manifest.permission.STATUS_BAR_SERVICE, "DisplayPolicy"); if (mNavigationBar != null) { if (mNavigationBar.isAlive()) { return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; } } mNavigationBar = win; mNavigationBarController.setWindow(win); mNavigationBarController.setOnBarVisibilityChangedListener( mNavBarVisibilityListener, true); mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR, win, null /* frameProvider */); mDisplayContent.setInsetProvider(InsetsState.TYPE_BOTTOM_GESTURES, win, (displayFrames, windowState, inOutFrame) -> { inOutFrame.top -= mBottomGestureAdditionalInset; }); mDisplayContent.setInsetProvider(InsetsState.TYPE_LEFT_GESTURES, win, (displayFrames, windowState, inOutFrame) -> { inOutFrame.left = 0; inOutFrame.top = 0; inOutFrame.bottom = displayFrames.mDisplayHeight; inOutFrame.right = displayFrames.mUnrestricted.left + mSideGestureInset; }); mDisplayContent.setInsetProvider(InsetsState.TYPE_RIGHT_GESTURES, win, (displayFrames, windowState, inOutFrame) -> { inOutFrame.left = displayFrames.mUnrestricted.right - mSideGestureInset; inOutFrame.top = 0; inOutFrame.bottom = displayFrames.mDisplayHeight; inOutFrame.right = displayFrames.mDisplayWidth; }); mDisplayContent.setInsetProvider(InsetsState.TYPE_BOTTOM_TAPPABLE_ELEMENT, win, (displayFrames, windowState, inOutFrame) -> { if ((windowState.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0 || mNavigationBarLetsThroughTaps) { inOutFrame.setEmpty(); } }); if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar); break; case TYPE_NAVIGATION_BAR_PANEL: case TYPE_STATUS_BAR_PANEL: case TYPE_STATUS_BAR_SUB_PANEL: case TYPE_VOICE_INTERACTION_STARTING: mContext.enforceCallingOrSelfPermission( android.Manifest.permission.STATUS_BAR_SERVICE, "DisplayPolicy"); break; } return ADD_OKAY; } /** * Called when a window is being removed from a window manager. Must not * throw an exception -- clean up as much as possible. * * @param win The window being removed. */ public void removeWindowLw(WindowState win) { if (mStatusBar == win) { mStatusBar = null; mStatusBarController.setWindow(null); if (mDisplayContent.isDefaultDisplay) { mService.mPolicy.setKeyguardCandidateLw(null); } mDisplayContent.setInsetProvider(TYPE_TOP_BAR, null, null); } else if (mNavigationBar == win) { mNavigationBar = null; mNavigationBarController.setWindow(null); mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR, null, null); } if (mLastFocusedWindow == win) { mLastFocusedWindow = null; } mScreenDecorWindows.remove(win); } private int getStatusBarHeight(DisplayFrames displayFrames) { return Math.max(mStatusBarHeightForRotation[displayFrames.mRotation], displayFrames.mDisplayCutoutSafe.top); } /** * Control the animation to run when a window's state changes. Return a * non-0 number to force the animation to a specific resource ID, or 0 * to use the default animation. * * @param win The window that is changing. * @param transit What is happening to the window: * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_ENTER}, * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_EXIT}, * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_SHOW}, or * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_HIDE}. * * @return Resource ID of the actual animation to use, or 0 for none. */ public int selectAnimationLw(WindowState win, int transit) { if (DEBUG_ANIM) Slog.i(TAG, "selectAnimation in " + win + ": transit=" + transit); if (win == mStatusBar) { final boolean isKeyguard = (win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0; final boolean expanded = win.getAttrs().height == MATCH_PARENT && win.getAttrs().width == MATCH_PARENT; if (isKeyguard || expanded) { return -1; } if (transit == TRANSIT_EXIT || transit == TRANSIT_HIDE) { return R.anim.dock_top_exit; } else if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) { return R.anim.dock_top_enter; } } else if (win == mNavigationBar) { if (win.getAttrs().windowAnimations != 0) { return 0; } // This can be on either the bottom or the right or the left. if (mNavigationBarPosition == NAV_BAR_BOTTOM) { if (transit == TRANSIT_EXIT || transit == TRANSIT_HIDE) { if (mService.mPolicy.isKeyguardShowingAndNotOccluded()) { return R.anim.dock_bottom_exit_keyguard; } else { return R.anim.dock_bottom_exit; } } else if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) { return R.anim.dock_bottom_enter; } } else if (mNavigationBarPosition == NAV_BAR_RIGHT) { if (transit == TRANSIT_EXIT || transit == TRANSIT_HIDE) { return R.anim.dock_right_exit; } else if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) { return R.anim.dock_right_enter; } } else if (mNavigationBarPosition == NAV_BAR_LEFT) { if (transit == TRANSIT_EXIT || transit == TRANSIT_HIDE) { return R.anim.dock_left_exit; } else if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) { return R.anim.dock_left_enter; } } } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) { return selectDockedDividerAnimationLw(win, transit); } if (transit == TRANSIT_PREVIEW_DONE) { if (win.hasAppShownWindows()) { if (DEBUG_ANIM) Slog.i(TAG, "**** STARTING EXIT"); return R.anim.app_starting_exit; } } else if (win.getAttrs().type == TYPE_DREAM && mDreamingLockscreen && transit == TRANSIT_ENTER) { // Special case: we are animating in a dream, while the keyguard // is shown. We don't want an animation on the dream, because // we need it shown immediately with the keyguard animating away // to reveal it. return -1; } return 0; } private int selectDockedDividerAnimationLw(WindowState win, int transit) { int insets = mDisplayContent.getDockedDividerController().getContentInsets(); // If the divider is behind the navigation bar, don't animate. final Rect frame = win.getFrameLw(); final boolean behindNavBar = mNavigationBar != null && ((mNavigationBarPosition == NAV_BAR_BOTTOM && frame.top + insets >= mNavigationBar.getFrameLw().top) || (mNavigationBarPosition == NAV_BAR_RIGHT && frame.left + insets >= mNavigationBar.getFrameLw().left) || (mNavigationBarPosition == NAV_BAR_LEFT && frame.right - insets <= mNavigationBar.getFrameLw().right)); final boolean landscape = frame.height() > frame.width(); final boolean offscreenLandscape = landscape && (frame.right - insets <= 0 || frame.left + insets >= win.getDisplayFrameLw().right); final boolean offscreenPortrait = !landscape && (frame.top - insets <= 0 || frame.bottom + insets >= win.getDisplayFrameLw().bottom); final boolean offscreen = offscreenLandscape || offscreenPortrait; if (behindNavBar || offscreen) { return 0; } if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) { return R.anim.fade_in; } else if (transit == TRANSIT_EXIT) { return R.anim.fade_out; } else { return 0; } } /** * Determine the animation to run for a rotation transition based on the * top fullscreen windows {@link WindowManager.LayoutParams#rotationAnimation} * and whether it is currently fullscreen and frontmost. * * @param anim The exiting animation resource id is stored in anim[0], the * entering animation resource id is stored in anim[1]. */ public void selectRotationAnimationLw(int anim[]) { // If the screen is off or non-interactive, force a jumpcut. final boolean forceJumpcut = !mScreenOnFully || !mService.mPolicy.okToAnimate(); if (DEBUG_ANIM) Slog.i(TAG, "selectRotationAnimation mTopFullscreen=" + mTopFullscreenOpaqueWindowState + " rotationAnimation=" + (mTopFullscreenOpaqueWindowState == null ? "0" : mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation) + " forceJumpcut=" + forceJumpcut); if (forceJumpcut) { anim[0] = R.anim.rotation_animation_jump_exit; anim[1] = R.anim.rotation_animation_enter; return; } if (mTopFullscreenOpaqueWindowState != null) { int animationHint = mTopFullscreenOpaqueWindowState.getRotationAnimationHint(); if (animationHint < 0 && mTopIsFullscreen) { animationHint = mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation; } switch (animationHint) { case ROTATION_ANIMATION_CROSSFADE: case ROTATION_ANIMATION_SEAMLESS: // Crossfade is fallback for seamless. anim[0] = R.anim.rotation_animation_xfade_exit; anim[1] = R.anim.rotation_animation_enter; break; case ROTATION_ANIMATION_JUMPCUT: anim[0] = R.anim.rotation_animation_jump_exit; anim[1] = R.anim.rotation_animation_enter; break; case ROTATION_ANIMATION_ROTATE: default: anim[0] = anim[1] = 0; break; } } else { anim[0] = anim[1] = 0; } } /** * Validate whether the current top fullscreen has specified the same * {@link WindowManager.LayoutParams#rotationAnimation} value as that * being passed in from the previous top fullscreen window. * * @param exitAnimId exiting resource id from the previous window. * @param enterAnimId entering resource id from the previous window. * @param forceDefault For rotation animations only, if true ignore the * animation values and just return false. * @return true if the previous values are still valid, false if they * should be replaced with the default. */ public boolean validateRotationAnimationLw(int exitAnimId, int enterAnimId, boolean forceDefault) { switch (exitAnimId) { case R.anim.rotation_animation_xfade_exit: case R.anim.rotation_animation_jump_exit: // These are the only cases that matter. if (forceDefault) { return false; } int anim[] = new int[2]; selectRotationAnimationLw(anim); return (exitAnimId == anim[0] && enterAnimId == anim[1]); default: return true; } } /** * Called when a new system UI visibility is being reported, allowing * the policy to adjust what is actually reported. * @param visibility The raw visibility reported by the status bar. * @return The new desired visibility. */ public int adjustSystemUiVisibilityLw(int visibility) { mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); // Reset any bits in mForceClearingStatusBarVisibility that // are now clear. mResettingSystemUiFlags &= visibility; // Clear any bits in the new visibility that are currently being // force cleared, before reporting it. return visibility & ~mResettingSystemUiFlags & ~mForceClearedSystemUiFlags; } /** * @return true if the system bars are forced to stay visible */ public boolean areSystemBarsForcedShownLw(WindowState windowState) { return mForceShowSystemBars; } // TODO: Should probably be moved into DisplayFrames. /** * Return the layout hints for a newly added window. These values are computed on the * most recent layout, so they are not guaranteed to be correct. * * @param attrs The LayoutParams of the window. * @param taskBounds The bounds of the task this window is on or {@code null} if no task is * associated with the window. * @param displayFrames display frames. * @param floatingStack Whether the window's stack is floating. * @param outFrame The frame of the window. * @param outContentInsets The areas covered by system windows, expressed as positive insets. * @param outStableInsets The areas covered by stable system windows irrespective of their * current visibility. Expressed as positive insets. * @param outOutsets The areas that are not real display, but we would like to treat as such. * @param outDisplayCutout The area that has been cut away from the display. * @return Whether to always consume the system bars. * See {@link #areSystemBarsForcedShownLw(WindowState)}. */ public boolean getLayoutHintLw(LayoutParams attrs, Rect taskBounds, DisplayFrames displayFrames, boolean floatingStack, Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) { final int fl = PolicyControl.getWindowFlags(null, attrs); final int pfl = attrs.privateFlags; final int requestedSysUiVis = PolicyControl.getSystemUiVisibility(null, attrs); final int sysUiVis = requestedSysUiVis | getImpliedSysUiFlagsForLayout(attrs); final int displayRotation = displayFrames.mRotation; final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl); if (useOutsets) { int outset = mWindowOutsetBottom; if (outset > 0) { if (displayRotation == Surface.ROTATION_0) { outOutsets.bottom += outset; } else if (displayRotation == Surface.ROTATION_90) { outOutsets.right += outset; } else if (displayRotation == Surface.ROTATION_180) { outOutsets.top += outset; } else if (displayRotation == Surface.ROTATION_270) { outOutsets.left += outset; } } } final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0; final boolean layoutInScreenAndInsetDecor = layoutInScreen && (fl & FLAG_LAYOUT_INSET_DECOR) != 0; final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; if (layoutInScreenAndInsetDecor && !screenDecor) { if ((sysUiVis & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) { outFrame.set(displayFrames.mUnrestricted); } else { outFrame.set(displayFrames.mRestricted); } final Rect sf; if (floatingStack) { sf = null; } else { sf = displayFrames.mStable; } final Rect cf; if (floatingStack) { cf = null; } else if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { if ((fl & FLAG_FULLSCREEN) != 0) { cf = displayFrames.mStableFullscreen; } else { cf = displayFrames.mStable; } } else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) { cf = displayFrames.mOverscan; } else { cf = displayFrames.mCurrent; } if (taskBounds != null) { outFrame.intersect(taskBounds); } InsetUtils.insetsBetweenFrames(outFrame, cf, outContentInsets); InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets); outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame) .getDisplayCutout()); return mForceShowSystemBars; } else { if (layoutInScreen) { outFrame.set(displayFrames.mUnrestricted); } else { outFrame.set(displayFrames.mStable); } if (taskBounds != null) { outFrame.intersect(taskBounds); } outContentInsets.setEmpty(); outStableInsets.setEmpty(); outDisplayCutout.set(DisplayCutout.NO_CUTOUT); return mForceShowSystemBars; } } private static int getImpliedSysUiFlagsForLayout(LayoutParams attrs) { int impliedFlags = 0; final boolean forceWindowDrawsBarBackgrounds = (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0 && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT; if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 || forceWindowDrawsBarBackgrounds) { impliedFlags |= SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; } return impliedFlags; } private static boolean shouldUseOutsets(WindowManager.LayoutParams attrs, int fl) { return attrs.type == TYPE_WALLPAPER || (fl & (WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN)) != 0; } private final Runnable mClearHideNavigationFlag = new Runnable() { @Override public void run() { synchronized (mLock) { mForceClearedSystemUiFlags &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; mDisplayContent.reevaluateStatusBarVisibility(); } } }; /** * Input handler used while nav bar is hidden. Captures any touch on the screen, * to determine when the nav bar should be shown and prevent applications from * receiving those touches. */ private final class HideNavInputEventReceiver extends InputEventReceiver { HideNavInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); } @Override public void onInputEvent(InputEvent event) { try { if (event instanceof MotionEvent && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { final MotionEvent motionEvent = (MotionEvent) event; if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { // When the user taps down, we re-show the nav bar. boolean changed = false; synchronized (mLock) { if (mInputConsumer == null) { return; } // Any user activity always causes us to show the // navigation controls, if they had been hidden. // We also clear the low profile and only content // flags so that tapping on the screen will atomically // restore all currently hidden screen decorations. int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN; if (mResettingSystemUiFlags != newVal) { mResettingSystemUiFlags = newVal; changed = true; } // We don't allow the system's nav bar to be hidden // again for 1 second, to prevent applications from // spamming us and keeping it from being shown. newVal = mForceClearedSystemUiFlags | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; if (mForceClearedSystemUiFlags != newVal) { mForceClearedSystemUiFlags = newVal; changed = true; mHandler.postDelayed(mClearHideNavigationFlag, 1000); } if (changed) { mDisplayContent.reevaluateStatusBarVisibility(); } } } } } finally { finishInputEvent(event, false /* handled */); } } } /** * Called when layout of the windows is about to start. * * @param displayFrames frames of the display we are doing layout on. * @param uiMode The current uiMode in configuration. */ public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) { displayFrames.onBeginLayout(); mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); // For purposes of putting out fake window up to steal focus, we will // drive nav being hidden only by whether it is requested. final int sysui = mLastSystemUiFlags; boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; boolean navTranslucent = (sysui & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0; boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; boolean navAllowedHidden = immersive || immersiveSticky; navTranslucent &= !immersiveSticky; // transient trumps translucent boolean isKeyguardShowing = isStatusBarKeyguard() && !mService.mPolicy.isKeyguardOccluded(); boolean statusBarForcesShowingNavigation = !isKeyguardShowing && mStatusBar != null && (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; // When the navigation bar isn't visible, we put up a fake input window to catch all // touch events. This way we can detect when the user presses anywhere to bring back the // nav bar and ensure the application doesn't see the event. if (navVisible || navAllowedHidden) { if (mInputConsumer != null) { mHandler.sendMessage( mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer)); mInputConsumer = null; } } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) { mInputConsumer = mService.createInputConsumer(mHandler.getLooper(), INPUT_CONSUMER_NAVIGATION, HideNavInputEventReceiver::new, displayFrames.mDisplayId); // As long as mInputConsumer is active, hover events are not dispatched to the app // and the pointer icon is likely to become stale. Hide it to avoid confusion. InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL); } // For purposes of positioning and showing the nav bar, if we have decided that it can't // be hidden (because of the screen aspect ratio), then take that into account. navVisible |= !canHideNavigationBar(); boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible, navTranslucent, navAllowedHidden, statusBarForcesShowingNavigation); if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock); updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui, isKeyguardShowing); if (updateSysUiVisibility) { updateSystemUiVisibilityLw(); } layoutScreenDecorWindows(displayFrames); if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) { // Make sure that the zone we're avoiding for the cutout is at least as tall as the // status bar; otherwise fullscreen apps will end up cutting halfway into the status // bar. displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top, displayFrames.mStable.top); } // In case this is a virtual display, and the host display has insets that overlap this // virtual display, apply the insets of the overlapped area onto the current and content // frame of this virtual display. This let us layout windows in the virtual display as // expected when the window needs to avoid overlap with the system windows. // TODO: Generalize the forwarded insets, so that we can handle system windows other than // IME. displayFrames.mCurrent.inset(mForwardedInsets); displayFrames.mContent.inset(mForwardedInsets); } private void layoutScreenDecorWindows(DisplayFrames displayFrames) { if (mScreenDecorWindows.isEmpty()) { return; } sTmpRect.setEmpty(); final int displayId = displayFrames.mDisplayId; final Rect dockFrame = displayFrames.mDock; final int displayHeight = displayFrames.mDisplayHeight; final int displayWidth = displayFrames.mDisplayWidth; for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) { final WindowState w = mScreenDecorWindows.valueAt(i); if (w.getDisplayId() != displayId || !w.isVisibleLw()) { // Skip if not on the same display or not visible. continue; } w.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */, displayFrames.mUnrestricted /* displayFrame */, displayFrames.mUnrestricted /* overscanFrame */, displayFrames.mUnrestricted /* contentFrame */, displayFrames.mUnrestricted /* visibleFrame */, sTmpRect /* decorFrame */, displayFrames.mUnrestricted /* stableFrame */, displayFrames.mUnrestricted /* outsetFrame */); w.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout); w.computeFrameLw(); final Rect frame = w.getFrameLw(); if (frame.left <= 0 && frame.top <= 0) { // Docked at left or top. if (frame.bottom >= displayHeight) { // Docked left. dockFrame.left = Math.max(frame.right, dockFrame.left); } else if (frame.right >= displayWidth) { // Docked top. dockFrame.top = Math.max(frame.bottom, dockFrame.top); } else { Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w + " not docked on left or top of display. frame=" + frame + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight); } } else if (frame.right >= displayWidth && frame.bottom >= displayHeight) { // Docked at right or bottom. if (frame.top <= 0) { // Docked right. dockFrame.right = Math.min(frame.left, dockFrame.right); } else if (frame.left <= 0) { // Docked bottom. dockFrame.bottom = Math.min(frame.top, dockFrame.bottom); } else { Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w + " not docked on right or bottom" + " of display. frame=" + frame + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight); } } else { // Screen decor windows are required to be docked on one of the sides of the screen. Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w + " not docked on one of the sides of the display. frame=" + frame + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight); } } displayFrames.mRestricted.set(dockFrame); displayFrames.mCurrent.set(dockFrame); displayFrames.mVoiceContent.set(dockFrame); displayFrames.mSystem.set(dockFrame); displayFrames.mContent.set(dockFrame); displayFrames.mRestrictedOverscan.set(dockFrame); } private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui, boolean isKeyguardShowing) { // decide where the status bar goes ahead of time if (mStatusBar == null) { return false; } // apply any navigation bar insets sTmpRect.setEmpty(); final WindowFrames windowFrames = mStatusBar.getWindowFrames(); windowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */, displayFrames.mUnrestricted /* displayFrame */, displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */, displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */, displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */); windowFrames.setDisplayCutout(displayFrames.mDisplayCutout); // Let the status bar determine its size. mStatusBar.computeFrameLw(); // For layout, the status bar is always at the top with our fixed height. displayFrames.mStable.top = displayFrames.mUnrestricted.top + mStatusBarHeightForRotation[displayFrames.mRotation]; // Make sure the status bar covers the entire cutout height displayFrames.mStable.top = Math.max(displayFrames.mStable.top, displayFrames.mDisplayCutoutSafe.top); // Tell the bar controller where the collapsed status bar content is sTmpRect.set(mStatusBar.getContentFrameLw()); sTmpRect.intersect(displayFrames.mDisplayCutoutSafe); sTmpRect.top = mStatusBar.getContentFrameLw().top; // Ignore top display cutout inset sTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size mStatusBarController.setContentFrame(sTmpRect); boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0; boolean statusBarTranslucent = (sysui & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0; // If the status bar is hidden, we don't want to cause windows behind it to scroll. if (mStatusBar.isVisibleLw() && !statusBarTransient) { // Status bar may go away, so the screen area it occupies is available to apps but just // covering them when the status bar is visible. final Rect dockFrame = displayFrames.mDock; dockFrame.top = displayFrames.mStable.top; displayFrames.mContent.set(dockFrame); displayFrames.mVoiceContent.set(dockFrame); displayFrames.mCurrent.set(dockFrame); if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format( "dock=%s content=%s cur=%s", dockFrame.toString(), displayFrames.mContent.toString(), displayFrames.mCurrent.toString())); if (!statusBarTranslucent && !mStatusBarController.wasRecentlyTranslucent() && !mStatusBar.isAnimatingLw()) { // If the opaque status bar is currently requested to be visible, and not in the // process of animating on or off, then we can tell the app that it is covered by // it. displayFrames.mSystem.top = displayFrames.mStable.top; } } return mStatusBarController.checkHiddenLw(); } private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible, boolean navTranslucent, boolean navAllowedHidden, boolean statusBarForcesShowingNavigation) { if (mNavigationBar == null) { return false; } final Rect navigationFrame = sTmpNavFrame; boolean transientNavBarShowing = mNavigationBarController.isTransientShowing(); // Force the navigation bar to its appropriate place and size. We need to do this directly, // instead of relying on it to bubble up from the nav bar, because this needs to change // atomically with screen rotations. final int rotation = displayFrames.mRotation; final int displayHeight = displayFrames.mDisplayHeight; final int displayWidth = displayFrames.mDisplayWidth; final Rect dockFrame = displayFrames.mDock; mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation); final Rect cutoutSafeUnrestricted = sTmpRect; cutoutSafeUnrestricted.set(displayFrames.mUnrestricted); cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe); if (mNavigationBarPosition == NAV_BAR_BOTTOM) { // It's a system nav bar or a portrait screen; nav bar goes on bottom. final int top = cutoutSafeUnrestricted.bottom - getNavigationBarHeight(rotation, uiMode); final int topNavBar = cutoutSafeUnrestricted.bottom - getNavigationBarFrameHeight(rotation, uiMode); navigationFrame.set(0, topNavBar, displayWidth, displayFrames.mUnrestricted.bottom); displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top; if (transientNavBarShowing) { mNavigationBarController.setBarShowingLw(true); } else if (navVisible) { mNavigationBarController.setBarShowingLw(true); dockFrame.bottom = displayFrames.mRestricted.bottom = displayFrames.mRestrictedOverscan.bottom = top; } else { // We currently want to hide the navigation UI - unless we expanded the status bar. mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation); } if (navVisible && !navTranslucent && !navAllowedHidden && !mNavigationBar.isAnimatingLw() && !mNavigationBarController.wasRecentlyTranslucent()) { // If the opaque nav bar is currently requested to be visible and not in the process // of animating on or off, then we can tell the app that it is covered by it. displayFrames.mSystem.bottom = top; } } else if (mNavigationBarPosition == NAV_BAR_RIGHT) { // Landscape screen; nav bar goes to the right. final int left = cutoutSafeUnrestricted.right - getNavigationBarWidth(rotation, uiMode); navigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight); displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left; if (transientNavBarShowing) { mNavigationBarController.setBarShowingLw(true); } else if (navVisible) { mNavigationBarController.setBarShowingLw(true); dockFrame.right = displayFrames.mRestricted.right = displayFrames.mRestrictedOverscan.right = left; } else { // We currently want to hide the navigation UI - unless we expanded the status bar. mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation); } if (navVisible && !navTranslucent && !navAllowedHidden && !mNavigationBar.isAnimatingLw() && !mNavigationBarController.wasRecentlyTranslucent()) { // If the nav bar is currently requested to be visible, and not in the process of // animating on or off, then we can tell the app that it is covered by it. displayFrames.mSystem.right = left; } } else if (mNavigationBarPosition == NAV_BAR_LEFT) { // Seascape screen; nav bar goes to the left. final int right = cutoutSafeUnrestricted.left + getNavigationBarWidth(rotation, uiMode); navigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight); displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right; if (transientNavBarShowing) { mNavigationBarController.setBarShowingLw(true); } else if (navVisible) { mNavigationBarController.setBarShowingLw(true); dockFrame.left = displayFrames.mRestricted.left = displayFrames.mRestrictedOverscan.left = right; } else { // We currently want to hide the navigation UI - unless we expanded the status bar. mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation); } if (navVisible && !navTranslucent && !navAllowedHidden && !mNavigationBar.isAnimatingLw() && !mNavigationBarController.wasRecentlyTranslucent()) { // If the nav bar is currently requested to be visible, and not in the process of // animating on or off, then we can tell the app that it is covered by it. displayFrames.mSystem.left = right; } } // Make sure the content and current rectangles are updated to account for the restrictions // from the navigation bar. displayFrames.mCurrent.set(dockFrame); displayFrames.mVoiceContent.set(dockFrame); displayFrames.mContent.set(dockFrame); // And compute the final frame. sTmpRect.setEmpty(); mNavigationBar.getWindowFrames().setFrames(navigationFrame /* parentFrame */, navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */, displayFrames.mDisplayCutoutSafe /* contentFrame */, navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */, navigationFrame /* stableFrame */, displayFrames.mDisplayCutoutSafe /* outsetFrame */); mNavigationBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout); mNavigationBar.computeFrameLw(); mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw()); if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame); return mNavigationBarController.checkHiddenLw(); } private void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached, boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf, DisplayFrames displayFrames) { if (!win.isInputMethodTarget() && attached.isInputMethodTarget()) { // Here's a special case: if the child window is not the 'dock window' // or input method target, and the window it is attached to is below // the dock window, then the frames we computed for the window it is // attached to can not be used because the dock is effectively part // of the underlying window and the attached window is floating on top // of the whole thing. So, we ignore the attached window and explicitly // compute the frames that would be appropriate without the dock. vf.set(displayFrames.mDock); cf.set(displayFrames.mDock); of.set(displayFrames.mDock); df.set(displayFrames.mDock); } else { // In case we forced the window to draw behind the navigation bar, restrict df/of to // DF.RestrictedOverscan to simulate old compat behavior. Rect parentDisplayFrame = attached.getDisplayFrameLw(); Rect parentOverscan = attached.getOverscanFrameLw(); final WindowManager.LayoutParams attachedAttrs = attached.mAttrs; if ((attachedAttrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0 && (attachedAttrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 && (attachedAttrs.systemUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0) { parentOverscan = new Rect(parentOverscan); parentOverscan.intersect(displayFrames.mRestrictedOverscan); parentDisplayFrame = new Rect(parentDisplayFrame); parentDisplayFrame.intersect(displayFrames.mRestrictedOverscan); } // The effective display frame of the attached window depends on whether it is taking // care of insetting its content. If not, we need to use the parent's content frame so // that the entire window is positioned within that content. Otherwise we can use the // overscan frame and let the attached window take care of positioning its content // appropriately. if (adjust != SOFT_INPUT_ADJUST_RESIZE) { // Set the content frame of the attached window to the parent's decor frame // (same as content frame when IME isn't present) if specifically requested by // setting {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR} flag. // Otherwise, use the overscan frame. cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0 ? attached.getContentFrameLw() : parentOverscan); } else { // If the window is resizing, then we want to base the content frame on our attached // content frame to resize...however, things can be tricky if the attached window is // NOT in resize mode, in which case its content frame will be larger. // Ungh. So to deal with that, make sure the content frame we end up using is not // covering the IM dock. cf.set(attached.getContentFrameLw()); if (attached.isVoiceInteraction()) { cf.intersectUnchecked(displayFrames.mVoiceContent); } else if (win.isInputMethodTarget() || attached.isInputMethodTarget()) { cf.intersectUnchecked(displayFrames.mContent); } } df.set(insetDecors ? parentDisplayFrame : cf); of.set(insetDecors ? parentOverscan : cf); vf.set(attached.getVisibleFrameLw()); } // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be // positioned relative to its parent or the entire screen. pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df); } private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) { if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0) { return; } // If app is requesting a stable layout, don't let the content insets go below the stable // values. if ((fl & FLAG_FULLSCREEN) != 0) { r.intersectUnchecked(displayFrames.mStableFullscreen); } else { r.intersectUnchecked(displayFrames.mStable); } } private boolean canReceiveInput(WindowState win) { boolean notFocusable = (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0; boolean altFocusableIm = (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0; boolean notFocusableForIm = notFocusable ^ altFocusableIm; return !notFocusableForIm; } /** * Called for each window attached to the window manager as layout is proceeding. The * implementation of this function must take care of setting the window's frame, either here or * in finishLayout(). * * @param win The window being positioned. * @param attached For sub-windows, the window it is attached to; this * window will already have had layoutWindow() called on it * so you can use its Rect. Otherwise null. * @param displayFrames The display frames. */ public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) { // We've already done the navigation bar, status bar, and all screen decor windows. If the // status bar can receive input, we need to layout it again to accommodate for the IME // window. if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar || mScreenDecorWindows.contains(win)) { return; } final WindowManager.LayoutParams attrs = win.getAttrs(); final boolean isDefaultDisplay = win.isDefaultDisplay(); final int type = attrs.type; final int fl = PolicyControl.getWindowFlags(win, attrs); final int pfl = attrs.privateFlags; final int sim = attrs.softInputMode; final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs); final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs); final WindowFrames windowFrames = win.getWindowFrames(); windowFrames.setHasOutsets(false); sTmpLastParentFrame.set(windowFrames.mParentFrame); final Rect pf = windowFrames.mParentFrame; final Rect df = windowFrames.mDisplayFrame; final Rect of = windowFrames.mOverscanFrame; final Rect cf = windowFrames.mContentFrame; final Rect vf = windowFrames.mVisibleFrame; final Rect dcf = windowFrames.mDecorFrame; final Rect sf = windowFrames.mStableFrame; dcf.setEmpty(); windowFrames.setParentFrameWasClippedByDisplayCutout(false); windowFrames.setDisplayCutout(displayFrames.mDisplayCutout); final boolean hasNavBar = hasNavigationBar() && mNavigationBar != null && mNavigationBar.isVisibleLw(); final int adjust = sim & SOFT_INPUT_MASK_ADJUST; final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0 || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN; final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR; sf.set(displayFrames.mStable); if (type == TYPE_INPUT_METHOD) { vf.set(displayFrames.mDock); cf.set(displayFrames.mDock); of.set(displayFrames.mDock); df.set(displayFrames.mDock); windowFrames.mParentFrame.set(displayFrames.mDock); // IM dock windows layout below the nav bar... pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom; // ...with content insets above the nav bar cf.bottom = vf.bottom = displayFrames.mStable.bottom; if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) { // The status bar forces the navigation bar while it's visible. Make sure the IME // avoids the navigation bar in that case. if (mNavigationBarPosition == NAV_BAR_RIGHT) { pf.right = df.right = of.right = cf.right = vf.right = displayFrames.mStable.right; } else if (mNavigationBarPosition == NAV_BAR_LEFT) { pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left; } } // In case the navigation bar is on the bottom, we use the frame height instead of the // regular height for the insets we send to the IME as we need some space to show // additional buttons in SystemUI when the IME is up. if (mNavigationBarPosition == NAV_BAR_BOTTOM) { final int rotation = displayFrames.mRotation; final int uimode = mService.mPolicy.getUiMode(); final int navHeightOffset = getNavigationBarFrameHeight(rotation, uimode) - getNavigationBarHeight(rotation, uimode); if (navHeightOffset > 0) { cf.bottom -= navHeightOffset; sf.bottom -= navHeightOffset; vf.bottom -= navHeightOffset; dcf.bottom -= navHeightOffset; } } // IM dock windows always go to the bottom of the screen. attrs.gravity = Gravity.BOTTOM; } else if (type == TYPE_VOICE_INTERACTION) { of.set(displayFrames.mUnrestricted); df.set(displayFrames.mUnrestricted); pf.set(displayFrames.mUnrestricted); if (adjust != SOFT_INPUT_ADJUST_RESIZE) { cf.set(displayFrames.mDock); } else { cf.set(displayFrames.mContent); } if (adjust != SOFT_INPUT_ADJUST_NOTHING) { vf.set(displayFrames.mCurrent); } else { vf.set(cf); } } else if (type == TYPE_WALLPAPER) { layoutWallpaper(displayFrames, pf, df, of, cf); } else if (win == mStatusBar) { of.set(displayFrames.mUnrestricted); df.set(displayFrames.mUnrestricted); pf.set(displayFrames.mUnrestricted); cf.set(displayFrames.mStable); vf.set(displayFrames.mStable); if (adjust == SOFT_INPUT_ADJUST_RESIZE) { cf.bottom = displayFrames.mContent.bottom; } else { cf.bottom = displayFrames.mDock.bottom; vf.bottom = displayFrames.mContent.bottom; } } else { dcf.set(displayFrames.mSystem); final boolean inheritTranslucentDecor = (attrs.privateFlags & PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0; final boolean isAppWindow = type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW; final boolean topAtRest = win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw(); if (isAppWindow && !inheritTranslucentDecor && !topAtRest) { if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 && (fl & FLAG_FULLSCREEN) == 0 && (fl & FLAG_TRANSLUCENT_STATUS) == 0 && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 && (pfl & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) == 0) { // Ensure policy decor includes status bar dcf.top = displayFrames.mStable.top; } if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0 && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 && (pfl & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) == 0) { // Ensure policy decor includes navigation bar dcf.bottom = displayFrames.mStable.bottom; dcf.right = displayFrames.mStable.right; } } if (layoutInScreen && layoutInsetDecor) { if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): IN_SCREEN, INSET_DECOR"); // This is the case for a normal activity window: we want it to cover all of the // screen space, and it can take care of moving its contents to account for screen // decorations that intrude into that space. if (attached != null) { // If this window is attached to another, our display // frame is the same as the one we are attached to. setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf, displayFrames); } else { if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) { // Status bar panels are the only windows who can go on top of the status // bar. They are protected by the STATUS_BAR_SERVICE permission, so they // have the same privileges as the status bar itself. // // However, they should still dodge the navigation bar if it exists. pf.left = df.left = of.left = hasNavBar ? displayFrames.mDock.left : displayFrames.mUnrestricted.left; pf.top = df.top = of.top = displayFrames.mUnrestricted.top; pf.right = df.right = of.right = hasNavBar ? displayFrames.mRestricted.right : displayFrames.mUnrestricted.right; pf.bottom = df.bottom = of.bottom = hasNavBar ? displayFrames.mRestricted.bottom : displayFrames.mUnrestricted.bottom; if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf); } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0 && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) { // Asking to layout into the overscan region, so give it that pure // unrestricted area. of.set(displayFrames.mOverscan); df.set(displayFrames.mOverscan); pf.set(displayFrames.mOverscan); } else if ((sysUiFl & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 && (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW || type == TYPE_VOLUME_OVERLAY || type == TYPE_KEYGUARD_DIALOG)) { // Asking for layout as if the nav bar is hidden, lets the application // extend into the unrestricted overscan screen area. We only do this for // application windows and certain system windows to ensure no window that // can be above the nav bar can do this. df.set(displayFrames.mOverscan); pf.set(displayFrames.mOverscan); // We need to tell the app about where the frame inside the overscan is, so // it can inset its content by that amount -- it didn't ask to actually // extend itself into the overscan region. of.set(displayFrames.mUnrestricted); } else { df.set(displayFrames.mRestrictedOverscan); pf.set(displayFrames.mRestrictedOverscan); // We need to tell the app about where the frame inside the overscan // is, so it can inset its content by that amount -- it didn't ask // to actually extend itself into the overscan region. of.set(displayFrames.mUnrestricted); } if ((fl & FLAG_FULLSCREEN) == 0) { if (win.isVoiceInteraction()) { cf.set(displayFrames.mVoiceContent); } else { // IME Insets are handled on the client for ADJUST_RESIZE in the new // insets world if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_NONE || adjust != SOFT_INPUT_ADJUST_RESIZE) { cf.set(displayFrames.mDock); } else { cf.set(displayFrames.mContent); } } } else { // Full screen windows are always given a layout that is as if the status // bar and other transient decors are gone. This is to avoid bad states when // moving from a window that is not hiding the status bar to one that is. cf.set(displayFrames.mRestricted); } applyStableConstraints(sysUiFl, fl, cf, displayFrames); if (adjust != SOFT_INPUT_ADJUST_NOTHING) { vf.set(displayFrames.mCurrent); } else { vf.set(cf); } } } else if (layoutInScreen || (sysUiFl & (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) { if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): IN_SCREEN"); // A window that has requested to fill the entire screen just // gets everything, period. if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) { cf.set(displayFrames.mUnrestricted); of.set(displayFrames.mUnrestricted); df.set(displayFrames.mUnrestricted); pf.set(displayFrames.mUnrestricted); if (hasNavBar) { pf.left = df.left = of.left = cf.left = displayFrames.mDock.left; pf.right = df.right = of.right = cf.right = displayFrames.mRestricted.right; pf.bottom = df.bottom = of.bottom = cf.bottom = displayFrames.mRestricted.bottom; } if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf); } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) { // The navigation bar has Real Ultimate Power. of.set(displayFrames.mUnrestricted); df.set(displayFrames.mUnrestricted); pf.set(displayFrames.mUnrestricted); if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf); } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT) && ((fl & FLAG_FULLSCREEN) != 0)) { // Fullscreen secure system overlays get what they ask for. Screenshot region // selection overlay should also expand to full screen. cf.set(displayFrames.mOverscan); of.set(displayFrames.mOverscan); df.set(displayFrames.mOverscan); pf.set(displayFrames.mOverscan); } else if (type == TYPE_BOOT_PROGRESS) { // Boot progress screen always covers entire display. cf.set(displayFrames.mOverscan); of.set(displayFrames.mOverscan); df.set(displayFrames.mOverscan); pf.set(displayFrames.mOverscan); } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0 && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) { // Asking to layout into the overscan region, so give it that pure unrestricted // area. cf.set(displayFrames.mOverscan); of.set(displayFrames.mOverscan); df.set(displayFrames.mOverscan); pf.set(displayFrames.mOverscan); } else if ((sysUiFl & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 && (type == TYPE_STATUS_BAR || type == TYPE_TOAST || type == TYPE_DOCK_DIVIDER || type == TYPE_VOICE_INTERACTION_STARTING || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) { // Asking for layout as if the nav bar is hidden, lets the // application extend into the unrestricted screen area. We // only do this for application windows (or toasts) to ensure no window that // can be above the nav bar can do this. // XXX This assumes that an app asking for this will also // ask for layout in only content. We can't currently figure out // what the screen would be if only laying out to hide the nav bar. cf.set(displayFrames.mUnrestricted); of.set(displayFrames.mUnrestricted); df.set(displayFrames.mUnrestricted); pf.set(displayFrames.mUnrestricted); } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) { of.set(displayFrames.mRestricted); df.set(displayFrames.mRestricted); pf.set(displayFrames.mRestricted); // IME Insets are handled on the client for ADJUST_RESIZE in the new insets // world if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_NONE || adjust != SOFT_INPUT_ADJUST_RESIZE) { cf.set(displayFrames.mDock); } else { cf.set(displayFrames.mContent); } } else { cf.set(displayFrames.mRestricted); of.set(displayFrames.mRestricted); df.set(displayFrames.mRestricted); pf.set(displayFrames.mRestricted); } applyStableConstraints(sysUiFl, fl, cf, displayFrames); if (adjust != SOFT_INPUT_ADJUST_NOTHING) { vf.set(displayFrames.mCurrent); } else { vf.set(cf); } } else if (attached != null) { if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): attached to " + attached); // A child window should be placed inside of the same visible // frame that its parent had. setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf, displayFrames); } else { if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): normal window"); // Otherwise, a normal window must be placed inside the content // of all screen decorations. if (type == TYPE_STATUS_BAR_PANEL) { // Status bar panels can go on // top of the status bar. They are protected by the STATUS_BAR_SERVICE // permission, so they have the same privileges as the status bar itself. cf.set(displayFrames.mRestricted); of.set(displayFrames.mRestricted); df.set(displayFrames.mRestricted); pf.set(displayFrames.mRestricted); } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) { // These dialogs are stable to interim decor changes. cf.set(displayFrames.mStable); of.set(displayFrames.mStable); df.set(displayFrames.mStable); pf.set(displayFrames.mStable); } else { pf.set(displayFrames.mContent); if (win.isVoiceInteraction()) { cf.set(displayFrames.mVoiceContent); of.set(displayFrames.mVoiceContent); df.set(displayFrames.mVoiceContent); } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) { cf.set(displayFrames.mDock); of.set(displayFrames.mDock); df.set(displayFrames.mDock); } else { cf.set(displayFrames.mContent); of.set(displayFrames.mContent); df.set(displayFrames.mContent); } if (adjust != SOFT_INPUT_ADJUST_NOTHING) { vf.set(displayFrames.mCurrent); } else { vf.set(cf); } } } } final int cutoutMode = attrs.layoutInDisplayCutoutMode; final boolean attachedInParent = attached != null && !layoutInScreen; final boolean requestedHideNavigation = (requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0; // TYPE_BASE_APPLICATION windows are never considered floating here because they don't get // cropped / shifted to the displayFrame in WindowState. final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen && type != TYPE_BASE_APPLICATION; // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in // the cutout safe zone. if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) { final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect; displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe); if (layoutInScreen && layoutInsetDecor && !requestedFullscreen && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) { // At the top we have the status bar, so apps that are // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN // already expect that there's an inset there and we don't need to exclude // the window from that area. displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE; } if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) { // Same for the navigation bar. switch (mNavigationBarPosition) { case NAV_BAR_BOTTOM: displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; break; case NAV_BAR_RIGHT: displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE; break; case NAV_BAR_LEFT: displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE; break; } } if (type == TYPE_INPUT_METHOD && mNavigationBarPosition == NAV_BAR_BOTTOM) { // The IME can always extend under the bottom cutout if the navbar is there. displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; } // Windows that are attached to a parent and laid out in said parent already avoid // the cutout according to that parent and don't need to be further constrained. // Floating IN_SCREEN windows get what they ask for and lay out in the full screen. // They will later be cropped or shifted using the displayFrame in WindowState, // which prevents overlap with the DisplayCutout. if (!attachedInParent && !floatingInScreenWindow) { sTmpRect.set(pf); pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars); windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf)); } // Make sure that NO_LIMITS windows clipped to the display don't extend under the // cutout. df.intersectUnchecked(displayCutoutSafeExceptMaybeBars); } // Content should never appear in the cutout. cf.intersectUnchecked(displayFrames.mDisplayCutoutSafe); // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it. // Also, we don't allow windows in multi-window mode to extend out of the screen. if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR && !win.inMultiWindowMode()) { df.left = df.top = -10000; df.right = df.bottom = 10000; if (type != TYPE_WALLPAPER) { of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000; of.right = of.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000; } } // If the device has a chin (e.g. some watches), a dead area at the bottom of the screen we // need to provide information to the clients that want to pretend that you can draw there. // We only want to apply outsets to certain types of windows. For example, we never want to // apply the outsets to floating dialogs, because they wouldn't make sense there. final boolean useOutsets = shouldUseOutsets(attrs, fl); if (isDefaultDisplay && useOutsets) { final Rect osf = windowFrames.mOutsetFrame; osf.set(cf.left, cf.top, cf.right, cf.bottom); windowFrames.setHasOutsets(true); int outset = mWindowOutsetBottom; if (outset > 0) { int rotation = displayFrames.mRotation; if (rotation == Surface.ROTATION_0) { osf.bottom += outset; } else if (rotation == Surface.ROTATION_90) { osf.right += outset; } else if (rotation == Surface.ROTATION_180) { osf.top -= outset; } else if (rotation == Surface.ROTATION_270) { osf.left -= outset; } if (DEBUG_LAYOUT) Slog.v(TAG, "applying bottom outset of " + outset + " with rotation " + rotation + ", result: " + osf); } } if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle() + ": sim=#" + Integer.toHexString(sim) + " attach=" + attached + " type=" + type + String.format(" flags=0x%08x", fl) + " pf=" + pf.toShortString() + " df=" + df.toShortString() + " of=" + of.toShortString() + " cf=" + cf.toShortString() + " vf=" + vf.toShortString() + " dcf=" + dcf.toShortString() + " sf=" + sf.toShortString() + " osf=" + windowFrames.mOutsetFrame.toShortString() + " " + win); if (!sTmpLastParentFrame.equals(pf)) { windowFrames.setContentChanged(true); } win.computeFrameLw(); // Dock windows carve out the bottom of the screen, so normal windows // can't appear underneath them. if (type == TYPE_INPUT_METHOD && win.isVisibleLw() && !win.getGivenInsetsPendingLw()) { offsetInputMethodWindowLw(win, displayFrames); } if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw() && !win.getGivenInsetsPendingLw()) { offsetVoiceInputWindowLw(win, displayFrames); } } private void layoutWallpaper(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect cf) { // The wallpaper has Real Ultimate Power, but we want to tell it about the overscan area. df.set(displayFrames.mOverscan); pf.set(displayFrames.mOverscan); cf.set(displayFrames.mUnrestricted); of.set(displayFrames.mUnrestricted); } private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) { int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top); top += win.getGivenContentInsetsLw().top; displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top); displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top); top = win.getVisibleFrameLw().top; top += win.getGivenVisibleInsetsLw().top; displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top); if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom=" + displayFrames.mDock.bottom + " mContentBottom=" + displayFrames.mContent.bottom + " mCurBottom=" + displayFrames.mCurrent.bottom); } private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) { int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top); top += win.getGivenContentInsetsLw().top; displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top); } /** * Called following layout of all windows before each window has policy applied. */ public void beginPostLayoutPolicyLw() { mTopFullscreenOpaqueWindowState = null; mTopFullscreenOpaqueOrDimmingWindowState = null; mTopDockedOpaqueWindowState = null; mTopDockedOpaqueOrDimmingWindowState = null; mForceStatusBar = false; mForceStatusBarFromKeyguard = false; mForceStatusBarTransparent = false; mForcingShowNavBar = false; mForcingShowNavBarLayer = -1; mAllowLockscreenWhenOn = false; mShowingDream = false; mWindowSleepTokenNeeded = false; } /** * Called following layout of all window to apply policy to each window. * * @param win The window being positioned. * @param attrs The LayoutParams of the window. * @param attached For sub-windows, the window it is attached to. Otherwise null. */ public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs, WindowState attached, WindowState imeTarget) { final boolean affectsSystemUi = win.canAffectSystemUiFlags(); if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi); mService.mPolicy.applyKeyguardPolicyLw(win, imeTarget); final int fl = PolicyControl.getWindowFlags(win, attrs); if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi && attrs.type == TYPE_INPUT_METHOD) { mForcingShowNavBar = true; mForcingShowNavBarLayer = win.getSurfaceLayer(); } if (attrs.type == TYPE_STATUS_BAR) { if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { mForceStatusBarFromKeyguard = true; } if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) { mForceStatusBarTransparent = true; } } boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type < FIRST_SYSTEM_WINDOW; final int windowingMode = win.getWindowingMode(); final boolean inFullScreenOrSplitScreenSecondaryWindowingMode = windowingMode == WINDOWING_MODE_FULLSCREEN || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi) { if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) { mForceStatusBar = true; } if (attrs.type == TYPE_DREAM) { // If the lockscreen was showing when the dream started then wait // for the dream to draw before hiding the lockscreen. if (!mDreamingLockscreen || (win.isVisibleLw() && win.hasDrawnLw())) { mShowingDream = true; appWindow = true; } } // For app windows that are not attached, we decide if all windows in the app they // represent should be hidden or if we should hide the lockscreen. For attached app // windows we defer the decision to the window it is attached to. if (appWindow && attached == null) { if (attrs.isFullscreen() && inFullScreenOrSplitScreenSecondaryWindowingMode) { if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win); mTopFullscreenOpaqueWindowState = win; if (mTopFullscreenOpaqueOrDimmingWindowState == null) { mTopFullscreenOpaqueOrDimmingWindowState = win; } if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) { mAllowLockscreenWhenOn = true; } } } } // Voice interaction overrides both top fullscreen and top docked. if (affectsSystemUi && attrs.type == TYPE_VOICE_INTERACTION) { if (mTopFullscreenOpaqueWindowState == null) { mTopFullscreenOpaqueWindowState = win; if (mTopFullscreenOpaqueOrDimmingWindowState == null) { mTopFullscreenOpaqueOrDimmingWindowState = win; } } if (mTopDockedOpaqueWindowState == null) { mTopDockedOpaqueWindowState = win; if (mTopDockedOpaqueOrDimmingWindowState == null) { mTopDockedOpaqueOrDimmingWindowState = win; } } } // Keep track of the window if it's dimming but not necessarily fullscreen. if (mTopFullscreenOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming() && inFullScreenOrSplitScreenSecondaryWindowingMode) { mTopFullscreenOpaqueOrDimmingWindowState = win; } // We need to keep track of the top "fullscreen" opaque window for the docked stack // separately, because both the "real fullscreen" opaque window and the one for the docked // stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR. if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null && attrs.isFullscreen() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { mTopDockedOpaqueWindowState = win; if (mTopDockedOpaqueOrDimmingWindowState == null) { mTopDockedOpaqueOrDimmingWindowState = win; } } // Also keep track of any windows that are dimming but not necessarily fullscreen in the // docked stack. if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { mTopDockedOpaqueOrDimmingWindowState = win; } } /** * Called following layout of all windows and after policy has been applied * to each window. If in this function you do * something that may have modified the animation state of another window, * be sure to return non-zero in order to perform another pass through layout. * * @return Return any bit set of * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_LAYOUT}, * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_CONFIG}, * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER}, or * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}. */ public int finishPostLayoutPolicyLw() { int changes = 0; boolean topIsFullscreen = false; // If we are not currently showing a dream then remember the current // lockscreen state. We will use this to determine whether the dream // started while the lockscreen was showing and remember this state // while the dream is showing. if (!mShowingDream) { mDreamingLockscreen = mService.mPolicy.isKeyguardShowingAndNotOccluded(); if (mDreamingSleepTokenNeeded) { mDreamingSleepTokenNeeded = false; mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 0, 1).sendToTarget(); } } else { if (!mDreamingSleepTokenNeeded) { mDreamingSleepTokenNeeded = true; mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 1, 1).sendToTarget(); } } if (mStatusBar != null) { if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar + " forcefkg=" + mForceStatusBarFromKeyguard + " top=" + mTopFullscreenOpaqueWindowState); boolean shouldBeTransparent = mForceStatusBarTransparent && !mForceStatusBar && !mForceStatusBarFromKeyguard; if (!shouldBeTransparent) { mStatusBarController.setShowTransparent(false /* transparent */); } else if (!mStatusBar.isVisibleLw()) { mStatusBarController.setShowTransparent(true /* transparent */); } boolean statusBarForcesShowingNavigation = (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; boolean topAppHidesStatusBar = topAppHidesStatusBar(); if (mForceStatusBar || mForceStatusBarFromKeyguard || mForceStatusBarTransparent || statusBarForcesShowingNavigation) { if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced"); if (mStatusBarController.setBarShowingLw(true)) { changes |= FINISH_LAYOUT_REDO_LAYOUT; } // Maintain fullscreen layout until incoming animation is complete. topIsFullscreen = mTopIsFullscreen && mStatusBar.isAnimatingLw(); // Transient status bar is not allowed if status bar is on lockscreen or status bar // is expecting the navigation keys from the user. if ((mForceStatusBarFromKeyguard || statusBarForcesShowingNavigation) && mStatusBarController.isTransientShowing()) { mStatusBarController.updateVisibilityLw(false /*transientAllowed*/, mLastSystemUiFlags, mLastSystemUiFlags); } } else if (mTopFullscreenOpaqueWindowState != null) { topIsFullscreen = topAppHidesStatusBar; // The subtle difference between the window for mTopFullscreenOpaqueWindowState // and mTopIsFullscreen is that mTopIsFullscreen is set only if the window // has the FLAG_FULLSCREEN set. Not sure if there is another way that to be the // case though. if (mStatusBarController.isTransientShowing()) { if (mStatusBarController.setBarShowingLw(true)) { changes |= FINISH_LAYOUT_REDO_LAYOUT; } } else if (topIsFullscreen && !mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM) && !mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar"); if (mStatusBarController.setBarShowingLw(false)) { changes |= FINISH_LAYOUT_REDO_LAYOUT; } else { if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar already hiding"); } } else { if (DEBUG_LAYOUT) Slog.v(TAG, "** SHOWING status bar: top is not fullscreen"); if (mStatusBarController.setBarShowingLw(true)) { changes |= FINISH_LAYOUT_REDO_LAYOUT; } topAppHidesStatusBar = false; } } mStatusBarController.setTopAppHidesStatusBar(topAppHidesStatusBar); } if (mTopIsFullscreen != topIsFullscreen) { if (!topIsFullscreen) { // Force another layout when status bar becomes fully shown. changes |= FINISH_LAYOUT_REDO_LAYOUT; } mTopIsFullscreen = topIsFullscreen; } if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) { // If the navigation bar has been hidden or shown, we need to do another // layout pass to update that window. changes |= FINISH_LAYOUT_REDO_LAYOUT; } if (mShowingDream != mLastShowingDream) { mLastShowingDream = mShowingDream; mService.notifyShowingDreamChanged(); } updateWindowSleepToken(); mService.mPolicy.setAllowLockscreenWhenOn(getDisplayId(), mAllowLockscreenWhenOn); return changes; } private void updateWindowSleepToken() { if (mWindowSleepTokenNeeded && !mLastWindowSleepTokenNeeded) { mHandler.removeCallbacks(mReleaseSleepTokenRunnable); mHandler.post(mAcquireSleepTokenRunnable); } else if (!mWindowSleepTokenNeeded && mLastWindowSleepTokenNeeded) { mHandler.removeCallbacks(mAcquireSleepTokenRunnable); mHandler.post(mReleaseSleepTokenRunnable); } mLastWindowSleepTokenNeeded = mWindowSleepTokenNeeded; } /** * @return Whether the top app should hide the statusbar based on the top fullscreen opaque * window. */ private boolean topAppHidesStatusBar() { if (mTopFullscreenOpaqueWindowState == null) { return false; } final int fl = PolicyControl.getWindowFlags(null, mTopFullscreenOpaqueWindowState.getAttrs()); if (localLOGV) { Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()); Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs() + " lp.flags=0x" + Integer.toHexString(fl)); } return (fl & LayoutParams.FLAG_FULLSCREEN) != 0 || (mLastSystemUiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; } /** * Called when the user is switched. */ public void switchUser() { updateCurrentUserResources(); } /** * Called when the resource overlays change. */ public void onOverlayChangedLw() { updateCurrentUserResources(); onConfigurationChanged(); mSystemGestures.onConfigurationChanged(); } /** * Called when the configuration has changed, and it's safe to load new values from resources. */ public void onConfigurationChanged() { final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); final Resources res = getCurrentUserResources(); final int portraitRotation = displayRotation.getPortraitRotation(); final int upsideDownRotation = displayRotation.getUpsideDownRotation(); final int landscapeRotation = displayRotation.getLandscapeRotation(); final int seascapeRotation = displayRotation.getSeascapeRotation(); final int uiMode = mService.mPolicy.getUiMode(); if (hasStatusBar()) { mStatusBarHeightForRotation[portraitRotation] = mStatusBarHeightForRotation[upsideDownRotation] = res.getDimensionPixelSize(R.dimen.status_bar_height_portrait); mStatusBarHeightForRotation[landscapeRotation] = mStatusBarHeightForRotation[seascapeRotation] = res.getDimensionPixelSize(R.dimen.status_bar_height_landscape); } else { mStatusBarHeightForRotation[portraitRotation] = mStatusBarHeightForRotation[upsideDownRotation] = mStatusBarHeightForRotation[landscapeRotation] = mStatusBarHeightForRotation[seascapeRotation] = 0; } // Height of the navigation bar when presented horizontally at bottom mNavigationBarHeightForRotationDefault[portraitRotation] = mNavigationBarHeightForRotationDefault[upsideDownRotation] = res.getDimensionPixelSize(R.dimen.navigation_bar_height); mNavigationBarHeightForRotationDefault[landscapeRotation] = mNavigationBarHeightForRotationDefault[seascapeRotation] = res.getDimensionPixelSize(R.dimen.navigation_bar_height_landscape); // Height of the navigation bar frame when presented horizontally at bottom mNavigationBarFrameHeightForRotationDefault[portraitRotation] = mNavigationBarFrameHeightForRotationDefault[upsideDownRotation] = res.getDimensionPixelSize(R.dimen.navigation_bar_frame_height); mNavigationBarFrameHeightForRotationDefault[landscapeRotation] = mNavigationBarFrameHeightForRotationDefault[seascapeRotation] = res.getDimensionPixelSize(R.dimen.navigation_bar_frame_height_landscape); // Width of the navigation bar when presented vertically along one side mNavigationBarWidthForRotationDefault[portraitRotation] = mNavigationBarWidthForRotationDefault[upsideDownRotation] = mNavigationBarWidthForRotationDefault[landscapeRotation] = mNavigationBarWidthForRotationDefault[seascapeRotation] = res.getDimensionPixelSize(R.dimen.navigation_bar_width); if (ALTERNATE_CAR_MODE_NAV_SIZE) { // Height of the navigation bar when presented horizontally at bottom mNavigationBarHeightForRotationInCarMode[portraitRotation] = mNavigationBarHeightForRotationInCarMode[upsideDownRotation] = res.getDimensionPixelSize(R.dimen.navigation_bar_height_car_mode); mNavigationBarHeightForRotationInCarMode[landscapeRotation] = mNavigationBarHeightForRotationInCarMode[seascapeRotation] = res.getDimensionPixelSize(R.dimen.navigation_bar_height_landscape_car_mode); // Width of the navigation bar when presented vertically along one side mNavigationBarWidthForRotationInCarMode[portraitRotation] = mNavigationBarWidthForRotationInCarMode[upsideDownRotation] = mNavigationBarWidthForRotationInCarMode[landscapeRotation] = mNavigationBarWidthForRotationInCarMode[seascapeRotation] = res.getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode); } mNavBarOpacityMode = res.getInteger(R.integer.config_navBarOpacityMode); mSideGestureInset = res.getDimensionPixelSize(R.dimen.config_backGestureInset); mNavigationBarLetsThroughTaps = res.getBoolean(R.bool.config_navBarTapThrough); mNavigationBarAlwaysShowOnSideGesture = res.getBoolean(R.bool.config_navBarAlwaysShowOnSideEdgeGesture); // This should calculate how much above the frame we accept gestures. mBottomGestureAdditionalInset = res.getDimensionPixelSize(R.dimen.navigation_bar_gesture_height) - getNavigationBarFrameHeight(portraitRotation, uiMode); updateConfigurationAndScreenSizeDependentBehaviors(); mWindowOutsetBottom = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources()); } void updateConfigurationAndScreenSizeDependentBehaviors() { final Resources res = getCurrentUserResources(); mNavigationBarCanMove = mDisplayContent.mBaseDisplayWidth != mDisplayContent.mBaseDisplayHeight && res.getBoolean(R.bool.config_navBarCanMove); mAllowSeamlessRotationDespiteNavBarMoving = res.getBoolean(R.bool.config_allowSeamlessRotationDespiteNavBarMoving); } /** * Updates the current user's resources to pick up any changes for the current user (including * overlay paths) */ private void updateCurrentUserResources() { final int userId = mService.mAmInternal.getCurrentUserId(); final Context uiContext = getSystemUiContext(); if (userId == UserHandle.USER_SYSTEM) { // Skip the (expensive) recreation of resources for the system user below and just // use the resources from the system ui context mCurrentUserResources = uiContext.getResources(); return; } // For non-system users, ensure that the resources are loaded from the current // user's package info (see ContextImpl.createDisplayContext) final LoadedApk pi = ActivityThread.currentActivityThread().getPackageInfo( uiContext.getPackageName(), null, 0, userId); mCurrentUserResources = ResourcesManager.getInstance().getResources(null, pi.getResDir(), null /* splitResDirs */, pi.getOverlayDirs(), pi.getApplicationInfo().sharedLibraryFiles, mDisplayContent.getDisplayId(), null /* overrideConfig */, uiContext.getResources().getCompatibilityInfo(), null /* classLoader */); } @VisibleForTesting Resources getCurrentUserResources() { if (mCurrentUserResources == null) { updateCurrentUserResources(); } return mCurrentUserResources; } @VisibleForTesting Context getContext() { return mContext; } private Context getSystemUiContext() { final Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext(); return mDisplayContent.isDefaultDisplay ? uiContext : uiContext.createDisplayContext(mDisplayContent.getDisplay()); } private int getNavigationBarWidth(int rotation, int uiMode) { if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { return mNavigationBarWidthForRotationInCarMode[rotation]; } else { return mNavigationBarWidthForRotationDefault[rotation]; } } void notifyDisplayReady() { mHandler.post(() -> { final int displayId = getDisplayId(); getStatusBarManagerInternal().onDisplayReady(displayId); LocalServices.getService(WallpaperManagerInternal.class).onDisplayReady(displayId); }); } /** * Return the display width available after excluding any screen * decorations that could never be removed in Honeycomb. That is, system bar or * button bar. */ public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode, DisplayCutout displayCutout) { int width = fullWidth; if (hasNavigationBar()) { final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation); if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) { width -= getNavigationBarWidth(rotation, uiMode); } } if (displayCutout != null) { width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight(); } return width; } private int getNavigationBarHeight(int rotation, int uiMode) { if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { return mNavigationBarHeightForRotationInCarMode[rotation]; } else { return mNavigationBarHeightForRotationDefault[rotation]; } } /** * Get the Navigation Bar Frame height. This dimension is the height of the navigation bar that * is used for spacing to show additional buttons on the navigation bar (such as the ime * switcher when ime is visible) while {@link #getNavigationBarHeight} is used for the visible * height that we send to the app as content insets that can be smaller. *

* In car mode it will return the same height as {@link #getNavigationBarHeight} * * @param rotation specifies rotation to return dimension from * @param uiMode to determine if in car mode * @return navigation bar frame height */ private int getNavigationBarFrameHeight(int rotation, int uiMode) { if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { return mNavigationBarHeightForRotationInCarMode[rotation]; } else { return mNavigationBarFrameHeightForRotationDefault[rotation]; } } /** * Return the display height available after excluding any screen * decorations that could never be removed in Honeycomb. That is, system bar or * button bar. */ public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode, DisplayCutout displayCutout) { int height = fullHeight; if (hasNavigationBar()) { final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation); if (navBarPosition == NAV_BAR_BOTTOM) { height -= getNavigationBarHeight(rotation, uiMode); } } if (displayCutout != null) { height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom(); } return height; } /** * Return the available screen width that we should report for the * configuration. This must be no larger than * {@link #getNonDecorDisplayWidth(int, int, int, int, DisplayCutout)}; it may be smaller * than that to account for more transient decoration like a status bar. */ public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode, DisplayCutout displayCutout) { return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation, uiMode, displayCutout); } /** * Return the available screen height that we should report for the * configuration. This must be no larger than * {@link #getNonDecorDisplayHeight(int, int, int, int, DisplayCutout)}; it may be smaller * than that to account for more transient decoration like a status bar. */ public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode, DisplayCutout displayCutout) { // There is a separate status bar at the top of the display. We don't count that as part // of the fixed decor, since it can hide; however, for purposes of configurations, // we do want to exclude it since applications can't generally use that part // of the screen. int statusBarHeight = mStatusBarHeightForRotation[rotation]; if (displayCutout != null) { // If there is a cutout, it may already have accounted for some part of the status // bar height. statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop()); } return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation, uiMode, displayCutout) - statusBarHeight; } /** * Return corner radius in pixels that should be used on windows in order to cover the display. * The radius is only valid for built-in displays since the one who configures window corner * radius cannot know the corner radius of non-built-in display. */ float getWindowCornerRadius() { return mDisplayContent.getDisplay().getType() == TYPE_BUILT_IN ? ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources()) : 0f; } boolean isShowingDreamLw() { return mShowingDream; } /** * Calculates the stable insets if we already have the non-decor insets. * * @param inOutInsets The known non-decor insets. It will be modified to stable insets. * @param rotation The current display rotation. */ void convertNonDecorInsetsToStableInsets(Rect inOutInsets, int rotation) { inOutInsets.top = Math.max(inOutInsets.top, mStatusBarHeightForRotation[rotation]); } /** * Calculates the stable insets without running a layout. * * @param displayRotation the current display rotation * @param displayWidth the current display width * @param displayHeight the current display height * @param displayCutout the current display cutout * @param outInsets the insets to return */ public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight, DisplayCutout displayCutout, Rect outInsets) { outInsets.setEmpty(); // Navigation bar and status bar. getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, displayCutout, outInsets); convertNonDecorInsetsToStableInsets(outInsets, displayRotation); } /** * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system * bar or button bar. See {@link #getNonDecorDisplayWidth}. * * @param displayRotation the current display rotation * @param displayWidth the current display width * @param displayHeight the current display height * @param displayCutout the current display cutout * @param outInsets the insets to return */ public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight, DisplayCutout displayCutout, Rect outInsets) { outInsets.setEmpty(); // Only navigation bar if (hasNavigationBar()) { final int uiMode = mService.mPolicy.getUiMode(); int position = navigationBarPosition(displayWidth, displayHeight, displayRotation); if (position == NAV_BAR_BOTTOM) { outInsets.bottom = getNavigationBarHeight(displayRotation, uiMode); } else if (position == NAV_BAR_RIGHT) { outInsets.right = getNavigationBarWidth(displayRotation, uiMode); } else if (position == NAV_BAR_LEFT) { outInsets.left = getNavigationBarWidth(displayRotation, uiMode); } } if (displayCutout != null) { outInsets.left += displayCutout.getSafeInsetLeft(); outInsets.top += displayCutout.getSafeInsetTop(); outInsets.right += displayCutout.getSafeInsetRight(); outInsets.bottom += displayCutout.getSafeInsetBottom(); } } /** * @see IWindowManager#setForwardedInsets */ public void setForwardedInsets(@NonNull Insets forwardedInsets) { mForwardedInsets = forwardedInsets; } @NonNull public Insets getForwardedInsets() { return mForwardedInsets; } @NavigationBarPosition int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) { if (navigationBarCanMove() && displayWidth > displayHeight) { if (displayRotation == Surface.ROTATION_270) { return NAV_BAR_LEFT; } else if (displayRotation == Surface.ROTATION_90) { return NAV_BAR_RIGHT; } } return NAV_BAR_BOTTOM; } /** * @return The side of the screen where navigation bar is positioned. * @see WindowManagerPolicyConstants#NAV_BAR_LEFT * @see WindowManagerPolicyConstants#NAV_BAR_RIGHT * @see WindowManagerPolicyConstants#NAV_BAR_BOTTOM */ @NavigationBarPosition public int getNavBarPosition() { return mNavigationBarPosition; } /** * A new window has been focused. */ public int focusChangedLw(WindowState lastFocus, WindowState newFocus) { mFocusedWindow = newFocus; mLastFocusedWindow = lastFocus; if (mDisplayContent.isDefaultDisplay) { mService.mPolicy.onDefaultDisplayFocusChangedLw(newFocus); } if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) { // If the navigation bar has been hidden or shown, we need to do another // layout pass to update that window. return FINISH_LAYOUT_REDO_LAYOUT; } return 0; } /** * Return true if it is okay to perform animations for an app transition * that is about to occur. You may return false for this if, for example, * the dream window is currently displayed so the switch should happen * immediately. */ public boolean allowAppAnimationsLw() { return !mShowingDream; } private void updateDreamingSleepToken(boolean acquire) { if (acquire) { final int displayId = getDisplayId(); if (mDreamingSleepToken == null) { mDreamingSleepToken = mService.mAtmInternal.acquireSleepToken( "DreamOnDisplay" + displayId, displayId); } } else { if (mDreamingSleepToken != null) { mDreamingSleepToken.release(); mDreamingSleepToken = null; } } } private void requestTransientBars(WindowState swipeTarget) { synchronized (mLock) { if (!mService.mPolicy.isUserSetupComplete()) { // Swipe-up for navigation bar is disabled during setup return; } boolean sb = mStatusBarController.checkShowTransientBarLw(); boolean nb = mNavigationBarController.checkShowTransientBarLw() && !isNavBarEmpty(mLastSystemUiFlags); if (sb || nb) { // Don't show status bar when swiping on already visible navigation bar if (!nb && swipeTarget == mNavigationBar) { if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target"); return; } if (sb) mStatusBarController.showTransient(); if (nb) mNavigationBarController.showTransient(); mImmersiveModeConfirmation.confirmCurrentPrompt(); updateSystemUiVisibilityLw(); } } } private void disposeInputConsumer(InputConsumer inputConsumer) { if (inputConsumer != null) { inputConsumer.dismiss(); } } private boolean isStatusBarKeyguard() { return mStatusBar != null && (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0; } private boolean isKeyguardOccluded() { // TODO (b/113840485): Handle per display keyguard. return mService.mPolicy.isKeyguardOccluded(); } void resetSystemUiVisibilityLw() { mLastSystemUiFlags = 0; updateSystemUiVisibilityLw(); } private int updateSystemUiVisibilityLw() { // If there is no window focused, there will be nobody to handle the events // anyway, so just hang on in whatever state we're in until things settle down. WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow : mTopFullscreenOpaqueWindowState; if (winCandidate == null) { return 0; } // The immersive mode confirmation should never affect the system bar visibility, otherwise // it will unhide the navigation bar and hide itself. if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) { // The immersive mode confirmation took the focus from mLastFocusedWindow which was // controlling the system ui visibility. So if mLastFocusedWindow can still receive // keys, we let it keep controlling the visibility. final boolean lastFocusCanReceiveKeys = (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys()); winCandidate = isStatusBarKeyguard() ? mStatusBar : lastFocusCanReceiveKeys ? mLastFocusedWindow : mTopFullscreenOpaqueWindowState; if (winCandidate == null) { return 0; } } final WindowState win = winCandidate; if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && isKeyguardOccluded()) { // We are updating at a point where the keyguard has gotten // focus, but we were last in a state where the top window is // hiding it. This is probably because the keyguard as been // shown while the top window was displayed, so we want to ignore // it here because this is just a very transient change and it // will quickly lose focus once it correctly gets hidden. return 0; } mDisplayContent.getInsetsStateController().onBarControllingWindowChanged( mTopFullscreenOpaqueWindowState); int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null) & ~mResettingSystemUiFlags & ~mForceClearedSystemUiFlags; if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) { tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS); } final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */, mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState); final int dockedVisibility = updateLightStatusBarLw(0 /* vis */, mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState); mService.getStackBounds( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds); mService.getStackBounds( WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds); final Pair result = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility); final int visibility = result.first; final int diff = visibility ^ mLastSystemUiFlags; final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags; final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags; final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState); if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu && mFocusedApp == win.getAppToken() && mLastNonDockedStackBounds.equals(mNonDockedStackBounds) && mLastDockedStackBounds.equals(mDockedStackBounds)) { return 0; } mLastSystemUiFlags = visibility; mLastFullscreenStackSysUiFlags = fullscreenVisibility; mLastDockedStackSysUiFlags = dockedVisibility; mLastFocusNeedsMenu = needsMenu; mFocusedApp = win.getAppToken(); mLastNonDockedStackBounds.set(mNonDockedStackBounds); mLastDockedStackBounds.set(mDockedStackBounds); final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds); final Rect dockedStackBounds = new Rect(mDockedStackBounds); final boolean isNavbarColorManagedByIme = result.second; mHandler.post(() -> { StatusBarManagerInternal statusBar = getStatusBarManagerInternal(); if (statusBar != null) { final int displayId = getDisplayId(); statusBar.setSystemUiVisibility(displayId, visibility, fullscreenVisibility, dockedVisibility, 0xffffffff, fullscreenStackBounds, dockedStackBounds, isNavbarColorManagedByIme, win.toString()); statusBar.topAppWindowChanged(displayId, needsMenu); } }); return diff; } private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) { final boolean onKeyguard = isStatusBarKeyguard() && !isKeyguardOccluded(); final WindowState statusColorWin = onKeyguard ? mStatusBar : opaqueOrDimming; if (statusColorWin != null && (statusColorWin == opaque || onKeyguard)) { // If the top fullscreen-or-dimming window is also the top fullscreen, respect // its light flag. vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null) & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; } else if (statusColorWin != null && statusColorWin.isDimming()) { // Otherwise if it's dimming, clear the light flag. vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; } return vis; } @VisibleForTesting @Nullable static WindowState chooseNavigationColorWindowLw(WindowState opaque, WindowState opaqueOrDimming, WindowState imeWindow, @NavigationBarPosition int navBarPosition) { // If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME // window can be navigation color window. final boolean imeWindowCanNavColorWindow = imeWindow != null && imeWindow.isVisibleLw() && navBarPosition == NAV_BAR_BOTTOM && (PolicyControl.getWindowFlags(imeWindow, null) & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; if (opaque != null && opaqueOrDimming == opaque) { // If the top fullscreen-or-dimming window is also the top fullscreen, respect it // unless IME window is also eligible, since currently the IME window is always show // above the opaque fullscreen app window, regardless of the IME target window. // TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed. return imeWindowCanNavColorWindow ? imeWindow : opaque; } if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) { // No dimming window is involved. Determine the result only with the IME window. return imeWindowCanNavColorWindow ? imeWindow : null; } if (!imeWindowCanNavColorWindow) { // No IME window is involved. Determine the result only with opaqueOrDimming. return opaqueOrDimming; } // The IME window and the dimming window are competing. Check if the dimming window can be // IME target or not. if (LayoutParams.mayUseInputMethod(PolicyControl.getWindowFlags(opaqueOrDimming, null))) { // The IME window is above the dimming window. return imeWindow; } else { // The dimming window is above the IME window. return opaqueOrDimming; } } @VisibleForTesting static int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming, WindowState imeWindow, WindowState navColorWin) { if (navColorWin != null) { if (navColorWin == imeWindow || navColorWin == opaque) { // Respect the light flag. vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; vis |= PolicyControl.getSystemUiVisibility(navColorWin, null) & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) { // Clear the light flag for dimming window. vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; } } return vis; } private Pair updateSystemBarsLw(WindowState win, int oldVis, int vis) { final boolean dockedStackVisible = mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); final boolean freeformStackVisible = mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM); final boolean resizing = mDisplayContent.getDockedDividerController().isResizing(); // We need to force system bars when the docked stack is visible, when the freeform stack // is visible but also when we are resizing for the transitions when docked stack // visibility changes. mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing || mForceShowSystemBarsFromExternal; final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard; // apply translucent bar vis flags WindowState fullscreenTransWin = isStatusBarKeyguard() && !isKeyguardOccluded() ? mStatusBar : mTopFullscreenOpaqueWindowState; vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis); vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis); int dockedVis = mStatusBarController.applyTranslucentFlagLw( mTopDockedOpaqueWindowState, 0, 0); dockedVis = mNavigationBarController.applyTranslucentFlagLw( mTopDockedOpaqueWindowState, dockedVis, 0); final boolean fullscreenDrawsStatusBarBackground = drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState); final boolean dockedDrawsStatusBarBackground = drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState); final boolean fullscreenDrawsNavBarBackground = drawsNavigationBarBackground(vis, mTopFullscreenOpaqueWindowState); final boolean dockedDrawsNavigationBarBackground = drawsNavigationBarBackground(dockedVis, mTopDockedOpaqueWindowState); // prevent status bar interaction from clearing certain flags int type = win.getAttrs().type; boolean statusBarHasFocus = type == TYPE_STATUS_BAR; if (statusBarHasFocus && !isStatusBarKeyguard()) { int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; if (isKeyguardOccluded()) { flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT; } vis = (vis & ~flags) | (oldVis & flags); } if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) { vis |= View.STATUS_BAR_TRANSPARENT; vis &= ~View.STATUS_BAR_TRANSLUCENT; } else if (forceOpaqueStatusBar) { vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT); } vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing, fullscreenDrawsNavBarBackground, dockedDrawsNavigationBarBackground); // update status bar boolean immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; final boolean hideStatusBarWM = mTopFullscreenOpaqueWindowState != null && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null) & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0; final boolean hideStatusBarSysui = (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; final boolean hideNavBarSysui = (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0; final boolean transientStatusBarAllowed = mStatusBar != null && (statusBarHasFocus || (!mForceShowSystemBars && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky)))); final boolean transientNavBarAllowed = mNavigationBar != null && !mForceShowSystemBars && hideNavBarSysui && immersiveSticky; final long now = SystemClock.uptimeMillis(); final boolean pendingPanic = mPendingPanicGestureUptime != 0 && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION; final DisplayPolicy defaultDisplayPolicy = mService.getDefaultDisplayContentLocked().getDisplayPolicy(); if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard() // TODO (b/111955725): Show keyguard presentation on all external displays && defaultDisplayPolicy.isKeyguardDrawComplete()) { // The user performed the panic gesture recently, we're about to hide the bars, // we're no longer on the Keyguard and the screen is ready. We can now request the bars. mPendingPanicGestureUptime = 0; mStatusBarController.showTransient(); if (!isNavBarEmpty(vis)) { mNavigationBarController.showTransient(); } } final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested() && !transientStatusBarAllowed && hideStatusBarSysui; final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested() && !transientNavBarAllowed; if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) { // clear the clearable flags instead clearClearableFlagsLw(); vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS; } final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; final boolean navAllowedHidden = immersive || immersiveSticky; if (hideNavBarSysui && !navAllowedHidden && mService.mPolicy.getWindowLayerLw(win) > mService.mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_CONSUMER)) { // We can't hide the navbar from this window otherwise the input consumer would not get // the input events. vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); } vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis); // update navigation bar boolean oldImmersiveMode = isImmersiveMode(oldVis); boolean newImmersiveMode = isImmersiveMode(vis); if (oldImmersiveMode != newImmersiveMode) { final String pkg = win.getOwningPackage(); mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode, mService.mPolicy.isUserSetupComplete(), isNavBarEmpty(win.getSystemUiVisibility())); } vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis); final WindowState navColorWin = chooseNavigationColorWindowLw( mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState, mDisplayContent.mInputMethodWindow, mNavigationBarPosition); vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState, mDisplayContent.mInputMethodWindow, navColorWin); // Navbar color is controlled by the IME. final boolean isManagedByIme = navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow; return Pair.create(vis, isManagedByIme); } private boolean drawsBarBackground(int vis, WindowState win, BarController controller, int translucentFlag) { if (!controller.isTransparentAllowed(win)) { return false; } if (win == null) { return true; } final boolean drawsSystemBars = (win.getAttrs().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; final boolean forceDrawsSystemBars = (win.getAttrs().privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0; return forceDrawsSystemBars || drawsSystemBars && (vis & translucentFlag) == 0; } private boolean drawsStatusBarBackground(int vis, WindowState win) { return drawsBarBackground(vis, win, mStatusBarController, FLAG_TRANSLUCENT_STATUS); } private boolean drawsNavigationBarBackground(int vis, WindowState win) { return drawsBarBackground(vis, win, mNavigationBarController, FLAG_TRANSLUCENT_NAVIGATION); } /** * @return the current visibility flags with the nav-bar opacity related flags toggled based * on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}. */ private int configureNavBarOpacity(int visibility, boolean dockedStackVisible, boolean freeformStackVisible, boolean isDockedDividerResizing, boolean fullscreenDrawsBackground, boolean dockedDrawsNavigationBarBackground) { if (mNavBarOpacityMode == NAV_BAR_FORCE_TRANSPARENT) { if (fullscreenDrawsBackground && dockedDrawsNavigationBarBackground) { visibility = setNavBarTransparentFlag(visibility); } else if (dockedStackVisible) { visibility = setNavBarOpaqueFlag(visibility); } } else if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) { if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) { visibility = setNavBarOpaqueFlag(visibility); } else if (fullscreenDrawsBackground) { visibility = setNavBarTransparentFlag(visibility); } } else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) { if (isDockedDividerResizing) { visibility = setNavBarOpaqueFlag(visibility); } else if (freeformStackVisible) { visibility = setNavBarTranslucentFlag(visibility); } else { visibility = setNavBarOpaqueFlag(visibility); } } return visibility; } private int setNavBarOpaqueFlag(int visibility) { return visibility & ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT); } private int setNavBarTranslucentFlag(int visibility) { visibility &= ~View.NAVIGATION_BAR_TRANSPARENT; return visibility | View.NAVIGATION_BAR_TRANSLUCENT; } private int setNavBarTransparentFlag(int visibility) { visibility &= ~View.NAVIGATION_BAR_TRANSLUCENT; return visibility | View.NAVIGATION_BAR_TRANSPARENT; } private void clearClearableFlagsLw() { int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS; if (newVal != mResettingSystemUiFlags) { mResettingSystemUiFlags = newVal; mDisplayContent.reevaluateStatusBarVisibility(); } } private boolean isImmersiveMode(int vis) { final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; return mNavigationBar != null && (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0 && (vis & flags) != 0 && canHideNavigationBar(); } /** * @return whether the navigation bar can be hidden, e.g. the device has a navigation bar */ private boolean canHideNavigationBar() { return hasNavigationBar(); } private static boolean isNavBarEmpty(int systemUiFlags) { final int disableNavigationBar = (View.STATUS_BAR_DISABLE_HOME | View.STATUS_BAR_DISABLE_BACK | View.STATUS_BAR_DISABLE_RECENT); return (systemUiFlags & disableNavigationBar) == disableNavigationBar; } boolean shouldRotateSeamlessly(DisplayRotation displayRotation, int oldRotation, int newRotation) { // For the upside down rotation we don't rotate seamlessly as the navigation // bar moves position. // Note most apps (using orientation:sensor or user as opposed to fullSensor) // will not enter the reverse portrait orientation, so actually the // orientation won't change at all. if (oldRotation == displayRotation.getUpsideDownRotation() || newRotation == displayRotation.getUpsideDownRotation()) { return false; } // If the navigation bar can't change sides, then it will // jump when we change orientations and we don't rotate // seamlessly - unless that is allowed, eg. with gesture // navigation where the navbar is low-profile enough that this isn't very noticeable. if (!navigationBarCanMove() && !mAllowSeamlessRotationDespiteNavBarMoving) { return false; } final WindowState w = mTopFullscreenOpaqueWindowState; if (w == null || w != mFocusedWindow) { return false; } // If the bounds of activity window is different from its parent, then reject to be seamless // because the window position may change after rotation that will look like a sudden jump. if (w.mAppToken != null && !w.mAppToken.matchParentBounds()) { return false; } // We only enable seamless rotation if the top window has requested // it and is in the fullscreen opaque state. Seamless rotation // requires freezing various Surface states and won't work well // with animations, so we disable it in the animation case for now. if (!w.isAnimatingLw() && w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS) { return true; } return false; } private final Runnable mHiddenNavPanic = new Runnable() { @Override public void run() { synchronized (mLock) { if (!mService.mPolicy.isUserSetupComplete()) { // Swipe-up for navigation bar is disabled during setup return; } mPendingPanicGestureUptime = SystemClock.uptimeMillis(); if (!isNavBarEmpty(mLastSystemUiFlags)) { mNavigationBarController.showTransient(); } } } }; void onPowerKeyDown(boolean isScreenOn) { // Detect user pressing the power button in panic when an application has // taken over the whole screen. boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(isScreenOn, SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags), isNavBarEmpty(mLastSystemUiFlags)); if (panic) { mHandler.post(mHiddenNavPanic); } } void onVrStateChangedLw(boolean enabled) { mImmersiveModeConfirmation.onVrStateChangedLw(enabled); } /** * Called when the state of lock task mode changes. This should be used to disable immersive * mode confirmation. * * @param lockTaskState the new lock task mode state. One of * {@link ActivityManager#LOCK_TASK_MODE_NONE}, * {@link ActivityManager#LOCK_TASK_MODE_LOCKED}, * {@link ActivityManager#LOCK_TASK_MODE_PINNED}. */ public void onLockTaskStateChangedLw(int lockTaskState) { mImmersiveModeConfirmation.onLockTaskModeChangedLw(lockTaskState); } /** * Request a screenshot be taken. * * @param screenshotType The type of screenshot, for example either * {@link WindowManager#TAKE_SCREENSHOT_FULLSCREEN} or * {@link WindowManager#TAKE_SCREENSHOT_SELECTED_REGION} */ public void takeScreenshot(int screenshotType) { if (mScreenshotHelper != null) { mScreenshotHelper.takeScreenshot(screenshotType, mStatusBar != null && mStatusBar.isVisibleLw(), mNavigationBar != null && mNavigationBar.isVisibleLw(), mHandler); } } RefreshRatePolicy getRefreshRatePolicy() { return mRefreshRatePolicy; } void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("DisplayPolicy"); prefix += " "; pw.print(prefix); pw.print("mCarDockEnablesAccelerometer="); pw.print(mCarDockEnablesAccelerometer); pw.print(" mDeskDockEnablesAccelerometer="); pw.println(mDeskDockEnablesAccelerometer); pw.print(prefix); pw.print("mDockMode="); pw.print(Intent.dockStateToString(mDockMode)); pw.print(" mLidState="); pw.println(WindowManagerFuncs.lidStateToString(mLidState)); pw.print(prefix); pw.print("mAwake="); pw.print(mAwake); pw.print(" mScreenOnEarly="); pw.print(mScreenOnEarly); pw.print(" mScreenOnFully="); pw.println(mScreenOnFully); pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete); pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete); pw.print(prefix); pw.print("mHdmiPlugged="); pw.println(mHdmiPlugged); if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0 || mForceClearedSystemUiFlags != 0) { pw.print(prefix); pw.print("mLastSystemUiFlags=0x"); pw.print(Integer.toHexString(mLastSystemUiFlags)); pw.print(" mResettingSystemUiFlags=0x"); pw.print(Integer.toHexString(mResettingSystemUiFlags)); pw.print(" mForceClearedSystemUiFlags=0x"); pw.println(Integer.toHexString(mForceClearedSystemUiFlags)); } if (mLastFocusNeedsMenu) { pw.print(prefix); pw.print("mLastFocusNeedsMenu="); pw.println(mLastFocusNeedsMenu); } pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream); pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen); pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken); if (mStatusBar != null) { pw.print(prefix); pw.print("mStatusBar="); pw.print(mStatusBar); pw.print(" isStatusBarKeyguard="); pw.println(isStatusBarKeyguard()); } if (mNavigationBar != null) { pw.print(prefix); pw.print("mNavigationBar="); pw.println(mNavigationBar); pw.print(prefix); pw.print("mNavBarOpacityMode="); pw.println(mNavBarOpacityMode); pw.print(prefix); pw.print("mNavigationBarCanMove="); pw.println(mNavigationBarCanMove); pw.print(prefix); pw.print("mNavigationBarPosition="); pw.println(mNavigationBarPosition); } if (mFocusedWindow != null) { pw.print(prefix); pw.print("mFocusedWindow="); pw.println(mFocusedWindow); } if (mFocusedApp != null) { pw.print(prefix); pw.print("mFocusedApp="); pw.println(mFocusedApp); } if (mTopFullscreenOpaqueWindowState != null) { pw.print(prefix); pw.print("mTopFullscreenOpaqueWindowState="); pw.println(mTopFullscreenOpaqueWindowState); } if (mTopFullscreenOpaqueOrDimmingWindowState != null) { pw.print(prefix); pw.print("mTopFullscreenOpaqueOrDimmingWindowState="); pw.println(mTopFullscreenOpaqueOrDimmingWindowState); } if (mForcingShowNavBar) { pw.print(prefix); pw.print("mForcingShowNavBar="); pw.println(mForcingShowNavBar); pw.print(prefix); pw.print("mForcingShowNavBarLayer="); pw.println(mForcingShowNavBarLayer); } pw.print(prefix); pw.print("mTopIsFullscreen="); pw.print(mTopIsFullscreen); pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar); pw.print(" mForceStatusBarFromKeyguard="); pw.println(mForceStatusBarFromKeyguard); pw.print(" mForceShowSystemBarsFromExternal="); pw.println(mForceShowSystemBarsFromExternal); pw.print(prefix); pw.print("mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn); mStatusBarController.dump(pw, prefix); mNavigationBarController.dump(pw, prefix); pw.print(prefix); pw.println("Looper state:"); mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " "); } private boolean supportsPointerLocation() { return mDisplayContent.isDefaultDisplay || !mDisplayContent.isPrivate(); } void setPointerLocationEnabled(boolean pointerLocationEnabled) { if (!supportsPointerLocation()) { return; } mHandler.sendEmptyMessage(pointerLocationEnabled ? MSG_ENABLE_POINTER_LOCATION : MSG_DISABLE_POINTER_LOCATION); } private void enablePointerLocation() { if (mPointerLocationView != null) { return; } mPointerLocationView = new PointerLocationView(mContext); mPointerLocationView.setPrintCoords(false); final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT); lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; lp.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; if (ActivityManager.isHighEndGfx()) { lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED; } lp.format = PixelFormat.TRANSLUCENT; lp.setTitle("PointerLocation - display " + getDisplayId()); lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; final WindowManager wm = mContext.getSystemService(WindowManager.class); wm.addView(mPointerLocationView, lp); mDisplayContent.registerPointerEventListener(mPointerLocationView); } private void disablePointerLocation() { if (mPointerLocationView == null) { return; } mDisplayContent.unregisterPointerEventListener(mPointerLocationView); final WindowManager wm = mContext.getSystemService(WindowManager.class); wm.removeView(mPointerLocationView); mPointerLocationView = null; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy