Merge "Update Landscape <-> Autorotate QS animations" into oc-dr1-dev
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 6834ba8..be31d1b 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -42,6 +42,9 @@
 import java.util.ArrayList;
 import java.util.Locale;
 
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_UNDEFINED;
+
 /**
  * This class describes all device configuration information that can
  * impact the resources the application retrieves.  This includes both
@@ -597,6 +600,13 @@
      */
     public int orientation;
 
+    /**
+     * The mRotation used at the time orientation was determined.
+     * TODO(b/36812336): Move mRotation out of {@link Configuration}.
+     * {@hide}
+     */
+    private int mRotation;
+
     /** Constant for {@link #uiMode}: bits that encode the mode type. */
     public static final int UI_MODE_TYPE_MASK = 0x0f;
     /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
@@ -884,6 +894,7 @@
         navigation = o.navigation;
         navigationHidden = o.navigationHidden;
         orientation = o.orientation;
+        mRotation = o.mRotation;
         screenLayout = o.screenLayout;
         colorMode = o.colorMode;
         uiMode = o.uiMode;
@@ -1074,6 +1085,7 @@
         navigation = NAVIGATION_UNDEFINED;
         navigationHidden = NAVIGATIONHIDDEN_UNDEFINED;
         orientation = ORIENTATION_UNDEFINED;
+        mRotation = ROTATION_UNDEFINED;
         screenLayout = SCREENLAYOUT_UNDEFINED;
         colorMode = COLOR_MODE_UNDEFINED;
         uiMode = UI_MODE_TYPE_UNDEFINED;
@@ -1182,6 +1194,11 @@
             changed |= ActivityInfo.CONFIG_ORIENTATION;
             orientation = delta.orientation;
         }
+        if (delta.mRotation != ROTATION_UNDEFINED
+                && mRotation != delta.mRotation) {
+            changed |= ActivityInfo.CONFIG_ORIENTATION;
+            mRotation = delta.mRotation;
+        }
 
         if (((delta.screenLayout & SCREENLAYOUT_SIZE_MASK) != SCREENLAYOUT_SIZE_UNDEFINED)
                 && (delta.screenLayout & SCREENLAYOUT_SIZE_MASK)
@@ -1376,6 +1393,10 @@
                 && orientation != delta.orientation) {
             changed |= ActivityInfo.CONFIG_ORIENTATION;
         }
+        if ((compareUndefined || delta.mRotation != ROTATION_UNDEFINED)
+                && mRotation != delta.mRotation) {
+            changed |= ActivityInfo.CONFIG_ORIENTATION;
+        }
         if ((compareUndefined || getScreenLayoutNoDirection(delta.screenLayout) !=
                 (SCREENLAYOUT_SIZE_UNDEFINED | SCREENLAYOUT_LONG_UNDEFINED))
                 && getScreenLayoutNoDirection(screenLayout) !=
@@ -1512,6 +1533,7 @@
         dest.writeInt(navigation);
         dest.writeInt(navigationHidden);
         dest.writeInt(orientation);
+        dest.writeInt(mRotation);
         dest.writeInt(screenLayout);
         dest.writeInt(colorMode);
         dest.writeInt(uiMode);
@@ -1548,6 +1570,7 @@
         navigation = source.readInt();
         navigationHidden = source.readInt();
         orientation = source.readInt();
+        mRotation = source.readInt();
         screenLayout = source.readInt();
         colorMode = source.readInt();
         uiMode = source.readInt();
@@ -1632,6 +1655,8 @@
         if (n != 0) return n;
         n = this.orientation - that.orientation;
         if (n != 0) return n;
+        n = this.mRotation - that.mRotation;
+        if (n != 0) return n;
         n = this.colorMode - that.colorMode;
         if (n != 0) return n;
         n = this.screenLayout - that.screenLayout;
@@ -1763,6 +1788,24 @@
     /**
      * @hide
      *
+     * Setter for orientation converts from {@link Surface} values to internal representation.
+     */
+    public void setRotation(int rotation) {
+        this.mRotation = rotation;
+    }
+
+    /**
+     * @hide
+     *
+     * Getter for orientation. Converts from internal representation to  {@link Surface} values.
+     */
+    public int getRotation() {
+        return mRotation != ROTATION_UNDEFINED ? mRotation : ROTATION_0;
+    }
+
+    /**
+     * @hide
+     *
      * Clears the locale without changing layout direction.
      */
     public void clearLocales() {
@@ -2193,6 +2236,10 @@
             delta.orientation = change.orientation;
         }
 
+        if (base.mRotation != change.mRotation) {
+            base.mRotation = change.mRotation;
+        }
+
         if ((base.screenLayout & SCREENLAYOUT_SIZE_MASK) !=
                 (change.screenLayout & SCREENLAYOUT_SIZE_MASK)) {
             delta.screenLayout |= change.screenLayout & SCREENLAYOUT_SIZE_MASK;
@@ -2264,6 +2311,7 @@
     private static final String XML_ATTR_NAVIGATION = "nav";
     private static final String XML_ATTR_NAVIGATION_HIDDEN = "navHid";
     private static final String XML_ATTR_ORIENTATION = "ori";
+    private static final String XML_ATTR_ROTATION = "rot";
     private static final String XML_ATTR_SCREEN_LAYOUT = "scrLay";
     private static final String XML_ATTR_COLOR_MODE = "clrMod";
     private static final String XML_ATTR_UI_MODE = "ui";
@@ -2323,6 +2371,8 @@
                 DENSITY_DPI_UNDEFINED);
         configOut.appBounds =
             Rect.unflattenFromString(XmlUtils.readStringAttribute(parser, XML_ATTR_APP_BOUNDS));
+        configOut.mRotation = XmlUtils.readIntAttribute(parser, XML_ATTR_ROTATION,
+                ROTATION_UNDEFINED);
 
         // For persistence, we don't care about assetsSeq, so do not read it out.
     }
@@ -2399,6 +2449,10 @@
                 config.appBounds.flattenToString());
         }
 
+        if (config.mRotation != ROTATION_UNDEFINED) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_ROTATION, config.mRotation);
+        }
+
         // For persistence, we do not care about assetsSeq, so do not write it out.
     }
 }
diff --git a/core/java/android/util/MergedConfiguration.java b/core/java/android/util/MergedConfiguration.java
index 68d0309..ae66050 100644
--- a/core/java/android/util/MergedConfiguration.java
+++ b/core/java/android/util/MergedConfiguration.java
@@ -161,6 +161,21 @@
         return "{mGlobalConfig=" + mGlobalConfig + " mOverrideConfig=" + mOverrideConfig + "}";
     }
 
+    @Override
+    public int hashCode() {
+        return mMergedConfig.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object that) {
+        if (!(that instanceof MergedConfiguration)) {
+            return false;
+        }
+
+        if (that == this) return true;
+        return mMergedConfig.equals(((MergedConfiguration) that).mMergedConfig);
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         pw.println(prefix + "mGlobalConfig=" + mGlobalConfig);
         pw.println(prefix + "mOverrideConfig=" + mOverrideConfig);
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index cdb9b82..7346a21 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -726,6 +726,17 @@
     }
 
     /**
+     * Returns the rotation associated with this display as used during layout. This is currently
+     * derived from the {@link Configuration}.
+     *
+     * @hide
+     */
+    @Surface.Rotation
+    public int getLayoutRotation() {
+        return mResources.getConfiguration().getRotation();
+    }
+
+    /**
      * @deprecated use {@link #getRotation}
      * @return orientation of this display.
      */
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 4f9dbd5..1b70232 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -131,11 +131,17 @@
     public static final int SCALING_MODE_NO_SCALE_CROP = 3;
 
     /** @hide */
-    @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
+    @IntDef({ROTATION_UNDEFINED, ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Rotation {}
 
     /**
+     * Rotation constant: undefined
+     * @hide
+     */
+    public static final int ROTATION_UNDEFINED = -1;
+
+    /**
      * Rotation constant: 0 degree rotation (natural orientation)
      */
     public static final int ROTATION_0 = 0;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e27eab9..e38a55f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1904,14 +1904,16 @@
                         + " outsets=" + mPendingOutsets.toShortString()
                         + " surface=" + mSurface);
 
-                final Configuration pendingMergedConfig =
-                        mPendingMergedConfiguration.getMergedConfiguration();
-                if (pendingMergedConfig.seq != 0) {
+                // If the pending {@link MergedConfiguration} handed back from
+                // {@link #relayoutWindow} does not match the one last reported,
+                // WindowManagerService has reported back a frame from a configuration not yet
+                // handled by the client. In this case, we need to accept the configuration so we
+                // do not lay out and draw with the wrong configuration.
+                if (!mPendingMergedConfiguration.equals(mLastReportedMergedConfiguration)) {
                     if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: "
-                            + pendingMergedConfig);
+                            + mPendingMergedConfiguration.getMergedConfiguration());
                     performConfigurationChange(mPendingMergedConfiguration, !mFirst,
                             INVALID_DISPLAY /* same display */);
-                    pendingMergedConfig.seq = 0;
                     updatedConfiguration = true;
                 }
 
@@ -3596,6 +3598,13 @@
                 mView.setLayoutDirection(currentLayoutDirection);
             }
             mView.dispatchConfigurationChanged(config);
+
+            // We could have gotten this {@link Configuration} update after we called
+            // {@link #performTraversals} with an older {@link Configuration}. As a result, our
+            // window frame may be stale. We must ensure the next pass of {@link #performTraversals}
+            // catches this.
+            mForceNextWindowRelayout = true;
+            requestLayout();
         }
     }
 
@@ -3757,10 +3766,10 @@
                     SomeArgs args = (SomeArgs) msg.obj;
 
                     final int displayId = args.argi3;
-                    final MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg4;
+                    MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg4;
                     final boolean displayChanged = mDisplay.getDisplayId() != displayId;
 
-                    if (mergedConfiguration != null) {
+                    if (!mLastReportedMergedConfiguration.equals(mergedConfiguration)) {
                         // If configuration changed - notify about that and, maybe, about move to
                         // display.
                         performConfigurationChange(mergedConfiguration, false /* force */,
@@ -6094,7 +6103,7 @@
         if (params != null) {
             if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);
         }
-        mPendingMergedConfiguration.getMergedConfiguration().seq = 0;
+
         //Log.d(mTag, ">>>>>> CALLING relayout");
         if (params != null && mOrigWindowType != params.type) {
             // For compatibility with old apps, don't crash here.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 14d0b7e..33d92fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -309,7 +309,7 @@
     public static final boolean DEBUG_GESTURES = false;
     public static final boolean DEBUG_MEDIA = false;
     public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
-    public static final boolean DEBUG_CAMERA_LIFT = true; // false once b/62623620 is fixed
+    public static final boolean DEBUG_CAMERA_LIFT = false;
 
     public static final boolean DEBUG_WINDOW_STATE = false;
 
@@ -425,6 +425,7 @@
     private boolean mWakeUpComingFromTouch;
     private PointF mWakeUpTouchLocation;
     private boolean mScreenTurningOn;
+    private boolean mScreenFullyOff;
 
     int mPixelFormat;
     Object mQueueLock = new Object();
@@ -2811,7 +2812,9 @@
     }
 
     public void onScreenTurnedOff() {
+        mScreenFullyOff = true;
         mFalsingManager.onScreenOff();
+        updateIsKeyguard();
     }
 
     public NotificationStackScrollLayout getNotificationScrollLayout() {
@@ -4188,8 +4191,11 @@
 
     private boolean updateIsKeyguard() {
         // For dozing, keyguard needs to be shown whenever the device is non-interactive. Otherwise
-        // there's no surface we can show to the user.
-        boolean keyguardForDozing = mDozingRequested && !mDeviceInteractive;
+        // there's no surface we can show to the user. Note that the device goes fully interactive
+        // late in the transition, so we also allow the device to start dozing once the screen has
+        // turned off fully.
+        boolean keyguardForDozing = mDozingRequested &&
+                (!mDeviceInteractive || mStartedGoingToSleep && (mScreenFullyOff || mIsKeyguard));
         boolean shouldBeKeyguard = mKeyguardRequested || keyguardForDozing;
         if (keyguardForDozing) {
             updatePanelExpansionForKeyguard();
@@ -5130,6 +5136,7 @@
     }
 
     public void onScreenTurningOn() {
+        mScreenFullyOff = false;
         mScreenTurningOn = true;
         mFalsingManager.onScreenTurningOn();
         mNotificationPanel.onScreenTurningOn();
@@ -5297,7 +5304,7 @@
                 mDozingRequested = true;
                 DozeLog.traceDozing(mContext, mDozing);
                 updateDozing();
-
+                updateIsKeyguard();
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 9b0307d..2833ff1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -152,7 +152,7 @@
      * {@link KeyguardBouncer#needsFullscreenBouncer()}.
      */
     protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
-        if (mBouncer.needsFullscreenBouncer()) {
+        if (mBouncer.needsFullscreenBouncer() && !mDozing) {
 
             // The keyguard might be showing (already). So we need to hide it.
             mStatusBar.hideKeyguard();
@@ -258,8 +258,11 @@
     }
 
     public void setDozing(boolean dozing) {
-        mDozing = dozing;
-        updateStates();
+        if (mDozing != dozing) {
+            mDozing = dozing;
+            reset(dozing /* hideBouncerWhenShowing */);
+            updateStates();
+        }
     }
 
     public void onScreenTurnedOff() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index 93dc6de..6104bef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -138,7 +138,7 @@
     }
 
     private void adjustScreenOrientation(State state) {
-        if (state.isKeyguardShowingAndNotOccluded()) {
+        if (state.isKeyguardShowingAndNotOccluded() || state.dozing) {
             if (mKeyguardScreenRotation) {
                 mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
             } else {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/RoundedCornersTest.java b/packages/SystemUI/tests/src/com/android/systemui/RoundedCornersTest.java
index 13ed2a2..2a44771 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/RoundedCornersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/RoundedCornersTest.java
@@ -46,7 +46,6 @@
 import com.android.systemui.tuner.TunerService;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -105,7 +104,6 @@
     }
 
     @Test
-    @Ignore
     public void testRounding() {
         mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 20);
         mContext.getOrCreateTestableResources()
@@ -115,8 +113,8 @@
         // Add 2 windows for rounded corners (top and bottom).
         verify(mWindowManager, times(2)).addView(any(), any());
 
-        // Add 3 tag listeners for each of the fragments that are needed.
-        verify(mFragmentHostManager, times(3)).addTagListener(any(), any());
+        // Add 2 tag listeners for each of the fragments that are needed.
+        verify(mFragmentHostManager, times(2)).addTagListener(any(), any());
         // One tunable.
         verify(mTunerService, times(1)).addTunable(any(), any());
         // One TunablePadding.
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 4c9495a..a903f3d 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -57,7 +57,7 @@
  */
 public class GestureLauncherService extends SystemService {
     private static final boolean DBG = false;
-    private static final boolean DBG_CAMERA_LIFT = true; // false once b/62623620 is fixed
+    private static final boolean DBG_CAMERA_LIFT = false;
     private static final String TAG = "GestureLauncherService";
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index ab3aff9..8269042 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -132,7 +132,7 @@
 
     private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;
 
-    private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER = 1;
+    private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
     private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2;
     private static final int MSG_DELIVER_DISPLAY_EVENT = 3;
     private static final int MSG_REQUEST_TRAVERSAL = 4;
@@ -266,7 +266,6 @@
 
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
-
     }
 
     public void setupSchedulerPolicies() {
@@ -284,9 +283,9 @@
         // We need to pre-load the persistent data store so it's ready before the default display
         // adapter is up so that we have it's configuration. We could load it lazily, but since
         // we're going to have to read it in eventually we may as well do it here rather than after
-        // we've waited for the diplay to register itself with us.
+        // we've waited for the display to register itself with us.
         mPersistentDataStore.loadIfNeeded();
-        mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);
+        mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS);
 
         publishBinderService(Context.DISPLAY_SERVICE, new BinderService(),
                 true /*allowIsolated*/);
@@ -298,12 +297,16 @@
     public void onBootPhase(int phase) {
         if (phase == PHASE_WAIT_FOR_DEFAULT_DISPLAY) {
             synchronized (mSyncRoot) {
-                long timeout = SystemClock.uptimeMillis() + WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT;
-                while (mLogicalDisplays.get(Display.DEFAULT_DISPLAY) == null) {
+                long timeout = SystemClock.uptimeMillis()
+                        + mInjector.getDefaultDisplayDelayTimeout();
+                while (mLogicalDisplays.get(Display.DEFAULT_DISPLAY) == null ||
+                        mVirtualDisplayAdapter == null) {
                     long delay = timeout - SystemClock.uptimeMillis();
                     if (delay <= 0) {
                         throw new RuntimeException("Timeout waiting for default display "
-                                + "to be initialized.");
+                                + "to be initialized. DefaultDisplay="
+                                + mLogicalDisplays.get(Display.DEFAULT_DISPLAY)
+                                + ", mVirtualDisplayAdapter=" + mVirtualDisplayAdapter);
                     }
                     if (DEBUG) {
                         Slog.d(TAG, "waitForDefaultDisplay: waiting, timeout=" + delay);
@@ -685,11 +688,23 @@
         }
     }
 
-    private void registerDefaultDisplayAdapter() {
-        // Register default display adapter.
+    private void registerDefaultDisplayAdapters() {
+        // Register default display adapters.
         synchronized (mSyncRoot) {
+            // main display adapter
             registerDisplayAdapterLocked(new LocalDisplayAdapter(
                     mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
+
+            // Standalone VR devices rely on a virtual display as their primary display for
+            // 2D UI. We register virtual display adapter along side the main display adapter
+            // here so that it is ready by the time the system sends the home Intent for
+            // early apps like SetupWizard/Launcher. In particular, SUW is displayed using
+            // the virtual display inside VR before any VR-specific apps even run.
+            mVirtualDisplayAdapter = mInjector.getVirtualDisplayAdapter(mSyncRoot, mContext,
+                    mHandler, mDisplayAdapterListener);
+            if (mVirtualDisplayAdapter != null) {
+                registerDisplayAdapterLocked(mVirtualDisplayAdapter);
+            }
         }
     }
 
@@ -698,7 +713,6 @@
             if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {
                 registerOverlayDisplayAdapterLocked();
                 registerWifiDisplayAdapterLocked();
-                registerVirtualDisplayAdapterLocked();
             }
         }
     }
@@ -719,12 +733,6 @@
         }
     }
 
-    private void registerVirtualDisplayAdapterLocked() {
-        mVirtualDisplayAdapter = mInjector.getVirtualDisplayAdapter(mSyncRoot, mContext, mHandler,
-                mDisplayAdapterListener);
-        registerDisplayAdapterLocked(mVirtualDisplayAdapter);
-    }
-
     private boolean shouldRegisterNonEssentialDisplayAdaptersLocked() {
         // In safe mode, we disable non-essential display adapters to give the user
         // an opportunity to fix broken settings or other problems that might affect
@@ -1219,6 +1227,10 @@
                 Handler handler, DisplayAdapter.Listener displayAdapterListener) {
             return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener);
         }
+
+        long getDefaultDisplayDelayTimeout() {
+            return WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT;
+        }
     }
 
     @VisibleForTesting
@@ -1241,8 +1253,8 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER:
-                    registerDefaultDisplayAdapter();
+                case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS:
+                    registerDefaultDisplayAdapters();
                     break;
 
                 case MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS:
diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java
index fa98f17..b1c1df1 100644
--- a/services/core/java/com/android/server/vr/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java
@@ -35,7 +35,6 @@
     private final static String TAG = "Vr2dDisplay";
     private final static boolean DEBUG = false;
 
-    // TODO: Go over these values and figure out what is best
     private int mVirtualDisplayHeight;
     private int mVirtualDisplayWidth;
     private int mVirtualDisplayDpi;
@@ -55,17 +54,17 @@
     /**
      * The default width of the VR virtual display
      */
-    public static final int DEFAULT_VR_DISPLAY_WIDTH = 1400;
+    public static final int DEFAULT_VIRTUAL_DISPLAY_WIDTH = 1400;
 
     /**
      * The default height of the VR virtual display
      */
-    public static final int DEFAULT_VR_DISPLAY_HEIGHT = 1800;
+    public static final int DEFAULT_VIRTUAL_DISPLAY_HEIGHT = 1400;
 
     /**
      * The default height of the VR virtual dpi.
      */
-    public static final int DEFAULT_VR_DISPLAY_DPI = 320;
+    public static final int DEFAULT_VIRTUAL_DISPLAY_DPI = 320;
 
     /**
      * The minimum height, width and dpi of VR virtual display.
@@ -87,8 +86,8 @@
             new IPersistentVrStateCallbacks.Stub() {
         @Override
         public void onPersistentVrStateChanged(boolean enabled) {
-            if (enabled != mIsVrModeEnabled) {
-                mIsVrModeEnabled = enabled;
+            if (enabled != mIsPersistentVrModeEnabled) {
+                mIsPersistentVrModeEnabled = enabled;
                 updateVirtualDisplay();
             }
         }
@@ -98,26 +97,33 @@
     private Surface mSurface;
     private ImageReader mImageReader;
     private Runnable mStopVDRunnable;
-    private boolean mIsVrModeOverrideEnabled;
-    private boolean mIsVrModeEnabled;
-    private boolean mIsVirtualDisplayAllowed = true;
+    private boolean mIsVrModeOverrideEnabled;  // debug override to set vr mode.
+    private boolean mIsVirtualDisplayAllowed = true;  // Virtual-display feature toggle
+    private boolean mIsPersistentVrModeEnabled;  // indicates we are in vr persistent mode.
+    private boolean mBootsToVr = false;  // The device boots into VR (standalone VR device)
 
     public Vr2dDisplay(DisplayManager displayManager,
            ActivityManagerInternal activityManagerInternal, IVrManager vrManager) {
         mDisplayManager = displayManager;
         mActivityManagerInternal = activityManagerInternal;
         mVrManager = vrManager;
-        mVirtualDisplayWidth = DEFAULT_VR_DISPLAY_WIDTH;
-        mVirtualDisplayHeight = DEFAULT_VR_DISPLAY_HEIGHT;
-        mVirtualDisplayDpi = DEFAULT_VR_DISPLAY_DPI;
+        mVirtualDisplayWidth = DEFAULT_VIRTUAL_DISPLAY_WIDTH;
+        mVirtualDisplayHeight = DEFAULT_VIRTUAL_DISPLAY_HEIGHT;
+        mVirtualDisplayDpi = DEFAULT_VIRTUAL_DISPLAY_DPI;
     }
 
     /**
      * Initializes the compabilitiy display by listening to VR mode changes.
      */
-    public void init(Context context) {
+    public void init(Context context, boolean bootsToVr) {
         startVrModeListener();
         startDebugOnlyBroadcastReceiver(context);
+        mBootsToVr = bootsToVr;
+        if (mBootsToVr) {
+          // If we are booting into VR, we need to start the virtual display immediately. This
+          // ensures that the virtual display is up by the time Setup Wizard is started.
+          updateVirtualDisplay();
+        }
     }
 
     /**
@@ -125,11 +131,13 @@
      */
     private void updateVirtualDisplay() {
         if (DEBUG) {
-            Log.i(TAG, "isVrMode: " + mIsVrModeEnabled + ", override: " + mIsVrModeOverrideEnabled
-                    + ", isAllowed: " + mIsVirtualDisplayAllowed);
+            Log.i(TAG, "isVrMode: " + mIsPersistentVrModeEnabled + ", override: "
+                    + mIsVrModeOverrideEnabled + ", isAllowed: " + mIsVirtualDisplayAllowed
+                    + ", bootsToVr: " + mBootsToVr);
         }
 
         if (shouldRunVirtualDisplay()) {
+            Log.i(TAG, "Attempting to start virtual display");
             // TODO: Consider not creating the display until ActivityManager needs one on
             // which to display a 2D application.
             startVirtualDisplay();
@@ -383,6 +391,12 @@
     }
 
     private boolean shouldRunVirtualDisplay() {
-        return mIsVirtualDisplayAllowed && (mIsVrModeEnabled || mIsVrModeOverrideEnabled);
+        // Virtual Display should run whenever:
+        // * Virtual Display is allowed/enabled AND
+        // (1) BootsToVr is set indicating the device never leaves VR
+        // (2) VR (persistent) mode is enabled
+        // (3) VR mode is overridden to be enabled.
+        return mIsVirtualDisplayAllowed &&
+                (mBootsToVr || mIsPersistentVrModeEnabled || mIsVrModeOverrideEnabled);
     }
 }
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 55d4719..f13cc76 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -619,7 +619,7 @@
                     (DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE);
             ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
             mVr2dDisplay = new Vr2dDisplay(dm, ami, mVrManager);
-            mVr2dDisplay.init(getContext());
+            mVr2dDisplay.init(getContext(), mBootsToVr);
 
             IntentFilter intentFilter = new IntentFilter();
             intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c98d60d..9fe7381 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1183,6 +1183,8 @@
         final int dh = displayInfo.logicalHeight;
         config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT :
                 Configuration.ORIENTATION_LANDSCAPE;
+        config.setRotation(displayInfo.rotation);
+
         config.screenWidthDp =
                 (int)(mService.mPolicy.getConfigDisplayWidth(dw, dh, displayInfo.rotation,
                         config.uiMode, mDisplayId) / mDisplayMetrics.density);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ceddc4e..613c830 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -30,7 +30,6 @@
 import static android.os.Process.ROOT_UID;
 import static android.os.Process.SHELL_UID;
 import static android.os.Process.SYSTEM_UID;
-import static android.os.Process.THREAD_PRIORITY_DISPLAY;
 import static android.os.Process.myPid;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.os.UserHandle.USER_NULL;
@@ -50,7 +49,6 @@
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -233,7 +231,6 @@
 import com.android.server.EventLogTags;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
-import com.android.server.ThreadPriorityBooster;
 import com.android.server.UiThread;
 import com.android.server.Watchdog;
 import com.android.server.input.InputManagerService;
@@ -2020,8 +2017,7 @@
                     win.setDisplayLayoutNeeded();
                     mWindowPlacerLocked.performSurfacePlacement(true);
                 }
-                result = win.relayoutVisibleWindow(mergedConfiguration, result, attrChanges,
-                        oldVisibility);
+                result = win.relayoutVisibleWindow(result, attrChanges, oldVisibility);
 
                 try {
                     result = createSurfaceControl(outSurface, result, win, winAnimator);
@@ -2159,6 +2155,15 @@
             if (!win.isGoneForLayoutLw()) {
                 win.mResizedWhileGone = false;
             }
+
+            // We must always send the latest {@link MergedConfiguration}, regardless of whether we
+            // have already reported it. The client might not have processed the previous value yet
+            // and needs process it before handling the corresponding window frame. the variable
+            // {@code mergedConfiguration} is an out parameter that will be passed back to the
+            // client over IPC and checked there.
+            win.getMergedConfiguration(mergedConfiguration);
+            win.setReportedConfiguration(mergedConfiguration);
+
             outFrame.set(win.mCompatFrame);
             outOverscanInsets.set(win.mOverscanInsets);
             outContentInsets.set(win.mContentInsets);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f74948f..d1fbf90 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2207,8 +2207,7 @@
         }
     }
 
-    void prepareWindowToDisplayDuringRelayout(MergedConfiguration mergedConfiguration,
-            boolean wasVisible) {
+    void prepareWindowToDisplayDuringRelayout(boolean wasVisible) {
         // We need to turn on screen regardless of visibility.
         if ((mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0) {
             if (DEBUG_VISIBILITY) Slog.v(TAG, "Relayout window turning screen on: " + this);
@@ -2230,16 +2229,16 @@
         if (isDrawnLw() && mService.okToDisplay()) {
             mWinAnimator.applyEnterAnimationLocked();
         }
+    }
 
-        if (isConfigChanged()) {
-            final Configuration globalConfig = mService.mRoot.getConfiguration();
-            final Configuration overrideConfig = getMergedOverrideConfiguration();
-            mergedConfiguration.setConfiguration(globalConfig, overrideConfig);
-            if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + this
-                    + " visible with new global config: " + globalConfig
-                    + " merged override config: " + overrideConfig);
-            mLastReportedConfiguration.setTo(getConfiguration());
-        }
+    void getMergedConfiguration(MergedConfiguration outConfiguration) {
+        final Configuration globalConfig = mService.mRoot.getConfiguration();
+        final Configuration overrideConfig = getMergedOverrideConfiguration();
+        outConfiguration.setConfiguration(globalConfig, overrideConfig);
+    }
+
+    void setReportedConfiguration(MergedConfiguration config) {
+        mLastReportedConfiguration.setTo(config.getMergedConfiguration());
     }
 
     void adjustStartingWindowFlags() {
@@ -3005,14 +3004,12 @@
         try {
             if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
                     + ": " + mCompatFrame);
-            final MergedConfiguration mergedConfiguration;
-            if (isConfigChanged()) {
-                mergedConfiguration = new MergedConfiguration(mService.mRoot.getConfiguration(),
-                        getMergedOverrideConfiguration());
-                mLastReportedConfiguration.setTo(mergedConfiguration.getMergedConfiguration());
-            } else {
-                mergedConfiguration = null;
-            }
+            final MergedConfiguration mergedConfiguration =
+                    new MergedConfiguration(mService.mRoot.getConfiguration(),
+                    getMergedOverrideConfiguration());
+
+            setReportedConfiguration(mergedConfiguration);
+
             if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == DRAW_PENDING)
                 Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
 
@@ -4342,8 +4339,7 @@
         return !mLastSurfaceInsets.equals(mAttrs.surfaceInsets);
     }
 
-    int relayoutVisibleWindow(MergedConfiguration mergedConfiguration, int result, int attrChanges,
-            int oldVisibility) {
+    int relayoutVisibleWindow(int result, int attrChanges, int oldVisibility) {
         final boolean wasVisible = isVisibleLw();
 
         result |= (!wasVisible || !isDrawnLw()) ? RELAYOUT_RES_FIRST_TIME : 0;
@@ -4366,7 +4362,7 @@
 
         mWinAnimator.mEnteringAnimation = true;
 
-        prepareWindowToDisplayDuringRelayout(mergedConfiguration, wasVisible);
+        prepareWindowToDisplayDuringRelayout(wasVisible);
 
         if ((attrChanges & FORMAT_CHANGED) != 0) {
             // If the format can't be changed in place, preserve the old surface until the app draws
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index e3ce17b..61df22e 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -29,9 +29,11 @@
 import android.view.WindowManagerInternal;
 
 import com.android.server.LocalServices;
+import com.android.server.SystemService;
 import com.android.server.display.DisplayDeviceInfo;
 import com.android.server.display.DisplayManagerService.SyncRoot;
 import com.android.server.display.VirtualDisplayAdapter.SurfaceControlDisplayFactory;
+import com.android.server.lights.LightsManager;
 
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -45,35 +47,51 @@
 
 @SmallTest
 public class DisplayManagerServiceTest extends AndroidTestCase {
-    private Handler mHandler;
-    private DisplayManagerService mDisplayManager;
+    private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
+    private static final long SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS = 10;
+
+    private final DisplayManagerService.Injector mShortMockedInjector =
+            new DisplayManagerService.Injector() {
+                @Override
+                VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot,
+                        Context context, Handler handler, DisplayAdapter.Listener listener) {
+                    return mMockVirtualDisplayAdapter;
+                }
+
+                @Override
+                long getDefaultDisplayDelayTimeout() {
+                    return SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS;
+                }
+            };
+    private final DisplayManagerService.Injector mBasicInjector =
+            new DisplayManagerService.Injector() {
+                @Override
+                VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot,
+                        Context context, Handler handler,
+                        DisplayAdapter.Listener displayAdapterListener) {
+                    return new VirtualDisplayAdapter(syncRoot, context, handler,
+                            displayAdapterListener,
+                            (String name, boolean secure) -> mMockDisplayToken);
+                }
+            };
+
     @Mock InputManagerInternal mMockInputManagerInternal;
     @Mock IVirtualDisplayCallback.Stub mMockAppToken;
     @Mock WindowManagerInternal mMockWindowManagerInternal;
+    @Mock LightsManager mMockLightsManager;
     @Mock VirtualDisplayAdapter mMockVirtualDisplayAdapter;
     @Mock IBinder mMockDisplayToken;
 
     @Override
     protected void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mDisplayManager = new DisplayManagerService(mContext,
-        new DisplayManagerService.Injector() {
-            @Override
-            VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
-                    Handler handler, DisplayAdapter.Listener displayAdapterListener) {
-                return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
-                        (String name, boolean secure) -> mMockDisplayToken);
-            }
-        });
-        mHandler = mDisplayManager.getDisplayHandler();
 
         LocalServices.removeServiceForTest(InputManagerInternal.class);
         LocalServices.addService(InputManagerInternal.class, mMockInputManagerInternal);
         LocalServices.removeServiceForTest(WindowManagerInternal.class);
         LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerInternal);
-
-        mDisplayManager.systemReady(false /* safeMode */, false /* onlyCore */);
-        mDisplayManager.windowManagerAndInputReady();
+        LocalServices.removeServiceForTest(LightsManager.class);
+        LocalServices.addService(LightsManager.class, mMockLightsManager);
         super.setUp();
     }
 
@@ -83,8 +101,14 @@
     }
 
     public void testCreateVirtualDisplay_sentToInputManager() throws Exception {
+        DisplayManagerService displayManager =
+                new DisplayManagerService(mContext, mBasicInjector);
+        registerDefaultDisplays(displayManager);
+        displayManager.systemReady(false /* safeMode */, true /* onlyCore */);
+        displayManager.windowManagerAndInputReady();
+
         // This is effectively the DisplayManager service published to ServiceManager.
-        DisplayManagerService.BinderService bs = mDisplayManager.new BinderService();
+        DisplayManagerService.BinderService bs = displayManager.new BinderService();
 
         String uniqueId = "uniqueId --- Test";
         String uniqueIdPrefix = "virtual:" + mContext.getPackageName() + ":";
@@ -99,10 +123,10 @@
                 "Test Virtual Display", width, height, dpi, null /* surface */, flags /* flags */,
                 uniqueId);
 
-        mDisplayManager.performTraversalInTransactionFromWindowManagerInternal();
+        displayManager.performTraversalInTransactionFromWindowManagerInternal();
 
         // flush the handler
-        mHandler.runWithScissors(() -> {}, 0 /* now */);
+        displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
 
         ArgumentCaptor<List<DisplayViewport>> virtualViewportCaptor =
                 ArgumentCaptor.forClass(List.class);
@@ -118,8 +142,12 @@
     }
 
     public void testCreateVirtualDisplayRotatesWithContent() throws Exception {
+        DisplayManagerService displayManager =
+                new DisplayManagerService(mContext, mBasicInjector);
+        registerDefaultDisplays(displayManager);
+
         // This is effectively the DisplayManager service published to ServiceManager.
-        DisplayManagerService.BinderService bs = mDisplayManager.new BinderService();
+        DisplayManagerService.BinderService bs = displayManager.new BinderService();
 
         String uniqueId = "uniqueId --- Rotates With Content Test";
         int width = 600;
@@ -133,13 +161,76 @@
                 "Test Virtual Display", width, height, dpi, null /* surface */, flags /* flags */,
                 uniqueId);
 
-        mDisplayManager.performTraversalInTransactionFromWindowManagerInternal();
+        displayManager.performTraversalInTransactionFromWindowManagerInternal();
 
         // flush the handler
-        mHandler.runWithScissors(() -> {}, 0 /* now */);
+        displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
 
-        DisplayDeviceInfo ddi = mDisplayManager.getDisplayDeviceInfoInternal(displayId);
+        DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);
         assertNotNull(ddi);
         assertTrue((ddi.flags & DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT) != 0);
     }
+
+    /**
+     * Tests that the virtual display is created along-side the default display.
+     */
+    public void testStartVirtualDisplayWithDefaultDisplay_Succeeds() throws Exception {
+        DisplayManagerService displayManager =
+                new DisplayManagerService(mContext, mShortMockedInjector);
+        registerDefaultDisplays(displayManager);
+        displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+    }
+
+    /**
+     * Tests that we get a Runtime exception when we cannot initialize the default display.
+     */
+    public void testStartVirtualDisplayWithDefDisplay_NoDefaultDisplay() throws Exception {
+        DisplayManagerService displayManager =
+                new DisplayManagerService(mContext, mShortMockedInjector);
+        Handler handler = displayManager.getDisplayHandler();
+        handler.runWithScissors(() -> {}, 0 /* now */);
+
+        try {
+            displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+        } catch (RuntimeException e) {
+            return;
+        }
+        fail("Expected DisplayManager to throw RuntimeException when it cannot initialize the"
+                + " default display");
+    }
+
+    /**
+     * Tests that we get a Runtime exception when we cannot initialize the virtual display.
+     */
+    public void testStartVirtualDisplayWithDefDisplay_NoVirtualDisplayAdapter() throws Exception {
+        DisplayManagerService displayManager = new DisplayManagerService(mContext,
+                new DisplayManagerService.Injector() {
+                    @Override
+                    VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot,
+                            Context context, Handler handler, DisplayAdapter.Listener listener) {
+                        return null;  // return null for the adapter.  This should cause a failure.
+                    }
+
+                    @Override
+                    long getDefaultDisplayDelayTimeout() {
+                        return SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS;
+                    }
+                });
+        try {
+            displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+        } catch (RuntimeException e) {
+            return;
+        }
+        fail("Expected DisplayManager to throw RuntimeException when it cannot initialize the"
+                + " virtual display adapter");
+    }
+
+    private void registerDefaultDisplays(DisplayManagerService displayManager) {
+        Handler handler = displayManager.getDisplayHandler();
+        // Would prefer to call displayManager.onStart() directly here but it performs binderService
+        // registration which triggers security exceptions when running from a test.
+        handler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS);
+        // flush the handler
+        handler.runWithScissors(() -> {}, 0 /* now */);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index c809c32..67db5f4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -218,8 +218,7 @@
         root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
         root.mTurnOnScreen = false;
 
-        root.prepareWindowToDisplayDuringRelayout(new MergedConfiguration(),
-                wasVisible /*wasVisible*/);
+        root.prepareWindowToDisplayDuringRelayout(wasVisible /*wasVisible*/);
         assertTrue(root.mTurnOnScreen);
     }
 }