OSDN Git Service

Pin electron beam surface to natural orientation.
authorJeff Brown <jeffbrown@google.com>
Tue, 4 Dec 2012 22:04:28 +0000 (14:04 -0800)
committerJeff Brown <jeffbrown@google.com>
Tue, 4 Dec 2012 22:40:23 +0000 (14:40 -0800)
If a rotation occurred while the electron beam surface was showing,
the surface may have appeared in the wrong orientation.  We fix this
problem by adjusting the transformation matrix of the electron beam
surface according to the display orientation whenever a display
transaction occurs.

The rotation itself is allowed to proceed but it is not visible
to the user.  We must let this happen so that the lock screen
is correctly oriented when the screen is turned back on.

Note that the electron beam surface serves two purposes.

First, it is used to play the screen off animation.
When the animation is finished, the surface remains visible but is
solid black.  Then we turn the screen off.

Second, when we turn the screen back on we leave the electron beam
surface showing until the window manager is ready to show the
new content.  This prevents the user from seeing a flash of the
old content while the screen is being turned on.  When everything is
ready, we dismiss the electron beam.

It's important for the electron beam to remain visible for
the entire duration from just before the screen is turned off until
after the screen is turned on and is ready to be seen.  This is
why we cannot fix the bug by deferring rotation or otherwise
getting in the way of the window manager doing what it needs
to do to get the screen ready when the screen is turned on again.

Bug: 7479740
Change-Id: I2fcf35114ad9b2e00fdfc67793be6df62c8dc4c3

core/java/android/view/DisplayInfo.java
services/java/com/android/server/display/DisplayManagerService.java
services/java/com/android/server/display/DisplayTransactionListener.java [new file with mode: 0644]
services/java/com/android/server/power/DisplayPowerController.java
services/java/com/android/server/power/ElectronBeam.java
services/java/com/android/server/power/PowerManagerService.java

index f3841d5..305fd5c 100644 (file)
@@ -285,6 +285,16 @@ public final class DisplayInfo implements Parcelable {
         getMetricsWithSize(outMetrics, cih, logicalWidth, logicalHeight);
     }
 
+    public int getNaturalWidth() {
+        return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ?
+                logicalWidth : logicalHeight;
+    }
+
+    public int getNaturalHeight() {
+        return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ?
+                logicalHeight : logicalWidth;
+    }
+
     private void getMetricsWithSize(DisplayMetrics outMetrics, CompatibilityInfoHolder cih,
             int width, int height) {
         outMetrics.densityDpi = outMetrics.noncompatDensityDpi = logicalDensityDpi;
index e58a0a5..e09970e 100644 (file)
@@ -41,6 +41,7 @@ import android.view.DisplayInfo;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * Manages attached displays.
@@ -152,6 +153,10 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
             new SparseArray<LogicalDisplay>();
     private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1;
 
+    // List of all display transaction listeners.
+    private final CopyOnWriteArrayList<DisplayTransactionListener> mDisplayTransactionListeners =
+            new CopyOnWriteArrayList<DisplayTransactionListener>();
+
     // Set to true if all displays have been blanked by the power manager.
     private int mAllDisplayBlankStateFromPowerManager;
 
@@ -261,6 +266,36 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
     }
 
     /**
+     * Registers a display transaction listener to provide the client a chance to
+     * update its surfaces within the same transaction as any display layout updates.
+     *
+     * @param listener The listener to register.
+     */
+    public void registerDisplayTransactionListener(DisplayTransactionListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener must not be null");
+        }
+
+        // List is self-synchronized copy-on-write.
+        mDisplayTransactionListeners.add(listener);
+    }
+
+    /**
+     * Unregisters a display transaction listener to provide the client a chance to
+     * update its surfaces within the same transaction as any display layout updates.
+     *
+     * @param listener The listener to unregister.
+     */
+    public void unregisterDisplayTransactionListener(DisplayTransactionListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener must not be null");
+        }
+
+        // List is self-synchronized copy-on-write.
+        mDisplayTransactionListeners.remove(listener);
+    }
+
+    /**
      * Overrides the display information of a particular logical display.
      * This is used by the window manager to control the size and characteristics
      * of the default display.  It is expected to apply the requested change
@@ -298,6 +333,11 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
 
             performTraversalInTransactionLocked();
         }
+
+        // List is self-synchronized copy-on-write.
+        for (DisplayTransactionListener listener : mDisplayTransactionListeners) {
+            listener.onDisplayTransaction();
+        }
     }
 
     /**
diff --git a/services/java/com/android/server/display/DisplayTransactionListener.java b/services/java/com/android/server/display/DisplayTransactionListener.java
new file mode 100644 (file)
index 0000000..34eb8f9
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2012 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.display;
+
+/**
+ * Called within a Surface transaction whenever the size or orientation of a
+ * display may have changed.  Provides an opportunity for the client to
+ * update the position of its surfaces as part of the same transaction.
+ */
+public interface DisplayTransactionListener {
+    void onDisplayTransaction();
+}
index 317fec0..724e126 100644 (file)
@@ -19,6 +19,7 @@ package com.android.server.power;
 import com.android.server.LightsService;
 import com.android.server.TwilightService;
 import com.android.server.TwilightService.TwilightState;
+import com.android.server.display.DisplayManagerService;
 
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
@@ -29,7 +30,6 @@ import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.hardware.SystemSensorManager;
-import android.hardware.display.DisplayManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -40,7 +40,6 @@ import android.util.FloatMath;
 import android.util.Slog;
 import android.util.Spline;
 import android.util.TimeUtils;
-import android.view.Display;
 
 import java.io.PrintWriter;
 
@@ -183,7 +182,7 @@ final class DisplayPowerController {
     private final TwilightService mTwilight;
 
     // The display manager.
-    private final DisplayManager mDisplayManager;
+    private final DisplayManagerService mDisplayManager;
 
     // The sensor manager.
     private final SensorManager mSensorManager;
@@ -346,6 +345,7 @@ final class DisplayPowerController {
      */
     public DisplayPowerController(Looper looper, Context context, Notifier notifier,
             LightsService lights, TwilightService twilight,
+            DisplayManagerService displayManager,
             DisplayBlanker displayBlanker,
             Callbacks callbacks, Handler callbackHandler) {
         mHandler = new DisplayControllerHandler(looper);
@@ -357,7 +357,7 @@ final class DisplayPowerController {
         mLights = lights;
         mTwilight = twilight;
         mSensorManager = new SystemSensorManager(mHandler.getLooper());
-        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+        mDisplayManager = displayManager;
 
         final Resources resources = context.getResources();
 
@@ -518,9 +518,8 @@ final class DisplayPowerController {
     }
 
     private void initialize() {
-        Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
         mPowerState = new DisplayPowerState(
-                new ElectronBeam(display), mDisplayBlanker,
+                new ElectronBeam(mDisplayManager), mDisplayBlanker,
                 mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT));
 
         mElectronBeamOnAnimator = ObjectAnimator.ofFloat(
index 9a53648..8e19e11 100644 (file)
@@ -16,6 +16,9 @@
 
 package com.android.server.power;
 
+import com.android.server.display.DisplayManagerService;
+import com.android.server.display.DisplayTransactionListener;
+
 import android.graphics.Bitmap;
 import android.graphics.PixelFormat;
 import android.opengl.EGL14;
@@ -72,14 +75,13 @@ final class ElectronBeam {
     private boolean mPrepared;
     private int mMode;
 
-    private final Display mDisplay;
-    private final DisplayInfo mDisplayInfo = new DisplayInfo();
+    private final DisplayManagerService mDisplayManager;
     private int mDisplayLayerStack; // layer stack associated with primary display
-    private int mDisplayRotation;
     private int mDisplayWidth;      // real width, not rotated
     private int mDisplayHeight;     // real height, not rotated
     private SurfaceSession mSurfaceSession;
     private Surface mSurface;
+    private NaturalSurfaceLayout mSurfaceLayout;
     private EGLDisplay mEglDisplay;
     private EGLConfig mEglConfig;
     private EGLContext mEglContext;
@@ -111,8 +113,8 @@ final class ElectronBeam {
      */
     public static final int MODE_FADE = 2;
 
-    public ElectronBeam(Display display) {
-        mDisplay = display;
+    public ElectronBeam(DisplayManagerService displayManager) {
+        mDisplayManager = displayManager;
     }
 
     /**
@@ -129,18 +131,12 @@ final class ElectronBeam {
 
         mMode = mode;
 
-        // Get the display size and adjust it for rotation.
-        mDisplay.getDisplayInfo(mDisplayInfo);
-        mDisplayLayerStack = mDisplay.getLayerStack();
-        mDisplayRotation = mDisplayInfo.rotation;
-        if (mDisplayRotation == Surface.ROTATION_90
-                || mDisplayRotation == Surface.ROTATION_270) {
-            mDisplayWidth = mDisplayInfo.logicalHeight;
-            mDisplayHeight = mDisplayInfo.logicalWidth;
-        } else {
-            mDisplayWidth = mDisplayInfo.logicalWidth;
-            mDisplayHeight = mDisplayInfo.logicalHeight;
-        }
+        // Get the display size and layer stack.
+        // This is not expected to change while the electron beam surface is showing.
+        DisplayInfo displayInfo = mDisplayManager.getDisplayInfo(Display.DEFAULT_DISPLAY);
+        mDisplayLayerStack = displayInfo.layerStack;
+        mDisplayWidth = displayInfo.getNaturalWidth();
+        mDisplayHeight = displayInfo.getNaturalHeight();
 
         // Prepare the surface for drawing.
         if (!tryPrepare()) {
@@ -551,24 +547,8 @@ final class ElectronBeam {
             mSurface.setLayerStack(mDisplayLayerStack);
             mSurface.setSize(mDisplayWidth, mDisplayHeight);
 
-            switch (mDisplayRotation) {
-                case Surface.ROTATION_0:
-                    mSurface.setPosition(0, 0);
-                    mSurface.setMatrix(1, 0, 0, 1);
-                    break;
-                case Surface.ROTATION_90:
-                    mSurface.setPosition(0, mDisplayWidth);
-                    mSurface.setMatrix(0, -1, 1, 0);
-                    break;
-                case Surface.ROTATION_180:
-                    mSurface.setPosition(mDisplayWidth, mDisplayHeight);
-                    mSurface.setMatrix(-1, 0, 0, -1);
-                    break;
-                case Surface.ROTATION_270:
-                    mSurface.setPosition(mDisplayHeight, 0);
-                    mSurface.setMatrix(0, 1, -1, 0);
-                    break;
-            }
+            mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManager, mSurface);
+            mSurfaceLayout.onDisplayTransaction();
         } finally {
             Surface.closeTransaction();
         }
@@ -601,6 +581,8 @@ final class ElectronBeam {
 
     private void destroySurface() {
         if (mSurface != null) {
+            mSurfaceLayout.dispose();
+            mSurfaceLayout = null;
             Surface.openTransaction();
             try {
                 mSurface.destroy();
@@ -711,10 +693,63 @@ final class ElectronBeam {
         pw.println("  mPrepared=" + mPrepared);
         pw.println("  mMode=" + mMode);
         pw.println("  mDisplayLayerStack=" + mDisplayLayerStack);
-        pw.println("  mDisplayRotation=" + mDisplayRotation);
         pw.println("  mDisplayWidth=" + mDisplayWidth);
         pw.println("  mDisplayHeight=" + mDisplayHeight);
         pw.println("  mSurfaceVisible=" + mSurfaceVisible);
         pw.println("  mSurfaceAlpha=" + mSurfaceAlpha);
     }
+
+    /**
+     * Keeps a surface aligned with the natural orientation of the device.
+     * Updates the position and transformation of the matrix whenever the display
+     * is rotated.  This is a little tricky because the display transaction
+     * callback can be invoked on any thread, not necessarily the thread that
+     * owns the electron beam.
+     */
+    private static final class NaturalSurfaceLayout implements DisplayTransactionListener {
+        private final DisplayManagerService mDisplayManager;
+        private Surface mSurface;
+
+        public NaturalSurfaceLayout(DisplayManagerService displayManager, Surface surface) {
+            mDisplayManager = displayManager;
+            mSurface = surface;
+            mDisplayManager.registerDisplayTransactionListener(this);
+        }
+
+        public void dispose() {
+            synchronized (this) {
+                mSurface = null;
+            }
+            mDisplayManager.unregisterDisplayTransactionListener(this);
+        }
+
+        @Override
+        public void onDisplayTransaction() {
+            synchronized (this) {
+                if (mSurface == null) {
+                    return;
+                }
+
+                DisplayInfo displayInfo = mDisplayManager.getDisplayInfo(Display.DEFAULT_DISPLAY);
+                switch (displayInfo.rotation) {
+                    case Surface.ROTATION_0:
+                        mSurface.setPosition(0, 0);
+                        mSurface.setMatrix(1, 0, 0, 1);
+                        break;
+                    case Surface.ROTATION_90:
+                        mSurface.setPosition(0, displayInfo.logicalHeight);
+                        mSurface.setMatrix(0, -1, 1, 0);
+                        break;
+                    case Surface.ROTATION_180:
+                        mSurface.setPosition(displayInfo.logicalWidth, displayInfo.logicalHeight);
+                        mSurface.setMatrix(-1, 0, 0, -1);
+                        break;
+                    case Surface.ROTATION_270:
+                        mSurface.setPosition(displayInfo.logicalWidth, 0);
+                        mSurface.setMatrix(0, 1, -1, 0);
+                        break;
+                }
+            }
+        }
+    }
 }
index 2690442..7f83c17 100644 (file)
@@ -432,7 +432,7 @@ public final class PowerManagerService extends IPowerManager.Stub
             // The display power controller runs on the power manager service's
             // own handler thread.
             mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(),
-                    mContext, mNotifier, mLightsService, twilight,
+                    mContext, mNotifier, mLightsService, twilight, mDisplayManagerService,
                     mDisplayBlanker, mDisplayPowerControllerCallbacks, mHandler);
 
             mSettingsObserver = new SettingsObserver(mHandler);