summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Adrian Roos <roosa@google.com> 2018-08-17 14:29:02 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2018-08-17 14:29:02 +0000
commit804574644849ef39d63d6515497469ee3b33b1d8 (patch)
tree57cbce980ddb7d9ee2b594efe02f8354469542ce
parent6e354f0a2c927e4d58d8233276c0d57a33f4a58c (diff)
parentfaa102f723c1c0416ec111911610d9461b18f187 (diff)
Merge "ScreenDecorations: Fix stuck / delayed rotations (second try)"
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java196
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java33
-rw-r--r--services/core/java/com/android/server/wm/ForcedSeamlessRotator.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java4
4 files changed, 198 insertions, 46 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 520e40aa1e56..9bbcfbcd07dd 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -43,9 +43,12 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.SystemProperties;
import android.provider.Settings.Secure;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
@@ -60,6 +63,7 @@ import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import com.android.internal.util.Preconditions;
import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
@@ -79,6 +83,9 @@ import androidx.annotation.VisibleForTesting;
* for antialiasing and emulation purposes.
*/
public class ScreenDecorations extends SystemUI implements Tunable {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "ScreenDecorations";
+
public static final String SIZE = "sysui_rounded_size";
public static final String PADDING = "sysui_rounded_content_padding";
private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS =
@@ -99,9 +106,24 @@ public class ScreenDecorations extends SystemUI implements Tunable {
private DisplayCutoutView mCutoutBottom;
private SecureSetting mColorInversionSetting;
private boolean mPendingRotationChange;
+ private Handler mHandler;
@Override
public void start() {
+ mHandler = startHandlerThread();
+ mHandler.post(this::startOnScreenDecorationsThread);
+ setupStatusBarPaddingIfNeeded();
+ }
+
+ @VisibleForTesting
+ Handler startHandlerThread() {
+ HandlerThread thread = new HandlerThread("ScreenDecorations");
+ thread.start();
+ return thread.getThreadHandler();
+ }
+
+ private void startOnScreenDecorationsThread() {
+ mRotation = RotationUtils.getExactRotation(mContext);
mWindowManager = mContext.getSystemService(WindowManager.class);
mRoundedDefault = mContext.getResources().getDimensionPixelSize(
R.dimen.rounded_corner_radius);
@@ -113,12 +135,6 @@ public class ScreenDecorations extends SystemUI implements Tunable {
setupDecorations();
}
- int padding = mContext.getResources().getDimensionPixelSize(
- R.dimen.rounded_corner_content_padding);
- if (padding != 0) {
- setupPadding(padding);
- }
-
mDisplayListener = new DisplayManager.DisplayListener() {
@Override
public void onDisplayAdded(int displayId) {
@@ -132,8 +148,8 @@ public class ScreenDecorations extends SystemUI implements Tunable {
@Override
public void onDisplayChanged(int displayId) {
- if ((hasRoundedCorners() || shouldDrawCutout()) &&
- mRotation != RotationUtils.getExactRotation(mContext)) {
+ final int newRotation = RotationUtils.getExactRotation(mContext);
+ if (mOverlay != null && mBottomOverlay != null && mRotation != newRotation) {
// We cannot immediately update the orientation. Otherwise
// WindowManager is still deferring layout until it has finished dispatching
// the config changes, which may cause divergence between what we draw
@@ -142,19 +158,24 @@ public class ScreenDecorations extends SystemUI implements Tunable {
// - we are trying to redraw. This because WM resized our window and told us to.
// - the config change has been dispatched, so WM is no longer deferring layout.
mPendingRotationChange = true;
+ if (DEBUG) {
+ Log.i(TAG, "Rotation changed, deferring " + newRotation + ", staying at "
+ + mRotation);
+ }
+
mOverlay.getViewTreeObserver().addOnPreDrawListener(
- new RestartingPreDrawListener(mOverlay));
+ new RestartingPreDrawListener(mOverlay, newRotation));
mBottomOverlay.getViewTreeObserver().addOnPreDrawListener(
- new RestartingPreDrawListener(mBottomOverlay));
+ new RestartingPreDrawListener(mBottomOverlay, newRotation));
}
updateOrientation();
}
};
- mRotation = -1;
mDisplayManager = (DisplayManager) mContext.getSystemService(
Context.DISPLAY_SERVICE);
- mDisplayManager.registerDisplayListener(mDisplayListener, null);
+ mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+ updateOrientation();
}
private void setupDecorations() {
@@ -184,10 +205,11 @@ public class ScreenDecorations extends SystemUI implements Tunable {
mWindowManager.getDefaultDisplay().getMetrics(metrics);
mDensity = metrics.density;
- Dependency.get(TunerService.class).addTunable(this, SIZE);
+ Dependency.get(Dependency.MAIN_HANDLER).post(
+ () -> Dependency.get(TunerService.class).addTunable(this, SIZE));
// Watch color inversion and invert the overlay as needed.
- mColorInversionSetting = new SecureSetting(mContext, Dependency.get(Dependency.MAIN_HANDLER),
+ mColorInversionSetting = new SecureSetting(mContext, mHandler,
Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
@@ -199,7 +221,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiver(mIntentReceiver, filter);
+ mContext.registerReceiver(mIntentReceiver, filter, null /* permission */, mHandler);
mOverlay.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
@@ -217,6 +239,11 @@ public class ScreenDecorations extends SystemUI implements Tunable {
.start();
}
});
+
+ mOverlay.getViewTreeObserver().addOnPreDrawListener(
+ new ValidatingPreDrawListener(mOverlay));
+ mBottomOverlay.getViewTreeObserver().addOnPreDrawListener(
+ new ValidatingPreDrawListener(mBottomOverlay));
}
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@@ -246,14 +273,28 @@ public class ScreenDecorations extends SystemUI implements Tunable {
@Override
protected void onConfigurationChanged(Configuration newConfig) {
- mPendingRotationChange = false;
- updateOrientation();
- if (shouldDrawCutout() && mOverlay == null) {
- setupDecorations();
- }
+ mHandler.post(() -> {
+ int oldRotation = mRotation;
+ mPendingRotationChange = false;
+ updateOrientation();
+ if (DEBUG) Log.i(TAG, "onConfigChanged from rot " + oldRotation + " to " + mRotation);
+ if (shouldDrawCutout() && mOverlay == null) {
+ setupDecorations();
+ }
+ if (mOverlay != null) {
+ // Updating the layout params ensures that ViewRootImpl will call relayoutWindow(),
+ // which ensures that the forced seamless rotation will end, even if we updated
+ // the rotation before window manager was ready (and was still waiting for sending
+ // the updated rotation).
+ updateLayoutParams();
+ }
+ });
}
- protected void updateOrientation() {
+ private void updateOrientation() {
+ Preconditions.checkState(mHandler.getLooper().getThread() == Thread.currentThread(),
+ "must call on " + mHandler.getLooper().getThread()
+ + ", but was " + Thread.currentThread());
if (mPendingRotationChange) {
return;
}
@@ -333,7 +374,19 @@ public class ScreenDecorations extends SystemUI implements Tunable {
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout);
}
- private void setupPadding(int padding) {
+
+ private void setupStatusBarPaddingIfNeeded() {
+ // TODO: This should be moved to a more appropriate place, as it is not related to the
+ // screen decorations overlay.
+ int padding = mContext.getResources().getDimensionPixelSize(
+ R.dimen.rounded_corner_content_padding);
+ if (padding != 0) {
+ setupStatusBarPadding(padding);
+ }
+
+ }
+
+ private void setupStatusBarPadding(int padding) {
// Add some padding to all the content near the edge of the screen.
StatusBar sb = getComponent(StatusBar.class);
View statusBar = (sb != null ? sb.getStatusBarWindow() : null);
@@ -402,30 +455,32 @@ public class ScreenDecorations extends SystemUI implements Tunable {
@Override
public void onTuningChanged(String key, String newValue) {
- if (mOverlay == null) return;
- if (SIZE.equals(key)) {
- int size = mRoundedDefault;
- int sizeTop = mRoundedDefaultTop;
- int sizeBottom = mRoundedDefaultBottom;
- if (newValue != null) {
- try {
- size = (int) (Integer.parseInt(newValue) * mDensity);
- } catch (Exception e) {
+ mHandler.post(() -> {
+ if (mOverlay == null) return;
+ if (SIZE.equals(key)) {
+ int size = mRoundedDefault;
+ int sizeTop = mRoundedDefaultTop;
+ int sizeBottom = mRoundedDefaultBottom;
+ if (newValue != null) {
+ try {
+ size = (int) (Integer.parseInt(newValue) * mDensity);
+ } catch (Exception e) {
+ }
}
- }
- if (sizeTop == 0) {
- sizeTop = size;
- }
- if (sizeBottom == 0) {
- sizeBottom = size;
- }
+ if (sizeTop == 0) {
+ sizeTop = size;
+ }
+ if (sizeBottom == 0) {
+ sizeBottom = size;
+ }
- setSize(mOverlay.findViewById(R.id.left), sizeTop);
- setSize(mOverlay.findViewById(R.id.right), sizeTop);
- setSize(mBottomOverlay.findViewById(R.id.left), sizeBottom);
- setSize(mBottomOverlay.findViewById(R.id.right), sizeBottom);
- }
+ setSize(mOverlay.findViewById(R.id.left), sizeTop);
+ setSize(mOverlay.findViewById(R.id.right), sizeTop);
+ setSize(mBottomOverlay.findViewById(R.id.left), sizeBottom);
+ setSize(mBottomOverlay.findViewById(R.id.right), sizeBottom);
+ }
+ });
}
private void setSize(View view, int pixelSize) {
@@ -484,6 +539,11 @@ public class ScreenDecorations extends SystemUI implements Tunable {
mVisibilityChangedListener = visibilityChangedListener;
mDecorations = decorations;
setId(R.id.display_cutout);
+ if (DEBUG) {
+ getViewTreeObserver().addOnDrawListener(() -> Log.i(TAG,
+ (mInitialStart ? "OverlayTop" : "OverlayBottom")
+ + " drawn in rot " + mRotation));
+ }
}
public void setColor(int color) {
@@ -719,20 +779,66 @@ public class ScreenDecorations extends SystemUI implements Tunable {
private class RestartingPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
private final View mView;
+ private final int mTargetRotation;
- private RestartingPreDrawListener(View view) {
+ private RestartingPreDrawListener(View view, int targetRotation) {
mView = view;
+ mTargetRotation = targetRotation;
}
@Override
public boolean onPreDraw() {
- mPendingRotationChange = false;
mView.getViewTreeObserver().removeOnPreDrawListener(this);
+
+ if (mTargetRotation == mRotation) {
+ if (DEBUG) {
+ Log.i(TAG, (mView == mOverlay ? "OverlayTop" : "OverlayBottom")
+ + " already in target rot "
+ + mTargetRotation + ", allow draw without restarting it");
+ }
+ return true;
+ }
+
+ mPendingRotationChange = false;
// This changes the window attributes - we need to restart the traversal for them to
// take effect.
updateOrientation();
+ if (DEBUG) {
+ Log.i(TAG, (mView == mOverlay ? "OverlayTop" : "OverlayBottom")
+ + " restarting listener fired, restarting draw for rot " + mRotation);
+ }
mView.invalidate();
return false;
}
}
+
+ /**
+ * A pre-draw listener, that validates that the rotation we draw in matches the displays
+ * rotation before continuing the draw.
+ *
+ * This is to prevent a race condition, where we have not received the display changed event
+ * yet, and would thus draw in an old orientation.
+ */
+ private class ValidatingPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
+
+ private final View mView;
+
+ public ValidatingPreDrawListener(View view) {
+ mView = view;
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ final int displayRotation = RotationUtils.getExactRotation(mContext);
+ if (displayRotation != mRotation && !mPendingRotationChange) {
+ if (DEBUG) {
+ Log.i(TAG, "Drawing rot " + mRotation + ", but display is at rot "
+ + displayRotation + ". Restarting draw");
+ }
+ mView.invalidate();
+ return false;
+ }
+ return true;
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index f1bf31d7a58a..644c0b347bde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -34,8 +34,10 @@ import static org.mockito.Mockito.when;
import android.app.Fragment;
import android.content.res.Configuration;
+import android.os.Handler;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.Display;
import android.view.View;
@@ -60,6 +62,7 @@ import org.junit.runner.RunWith;
@SmallTest
public class ScreenDecorationsTest extends SysuiTestCase {
+ private TestableLooper mTestableLooper;
private ScreenDecorations mScreenDecorations;
private StatusBar mStatusBar;
private WindowManager mWindowManager;
@@ -71,6 +74,10 @@ public class ScreenDecorationsTest extends SysuiTestCase {
@Before
public void setup() {
+ mTestableLooper = TestableLooper.get(this);
+ mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
+ new Handler(mTestableLooper.getLooper()));
+
mStatusBar = mock(StatusBar.class);
mWindowManager = mock(WindowManager.class);
mView = spy(new StatusBarWindowView(mContext, null));
@@ -88,7 +95,31 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mTunerService = mDependency.injectMockDependency(TunerService.class);
- mScreenDecorations = new ScreenDecorations();
+
+ mScreenDecorations = new ScreenDecorations() {
+ @Override
+ public void start() {
+ super.start();
+ mTestableLooper.processAllMessages();
+ }
+
+ @Override
+ Handler startHandlerThread() {
+ return new Handler(mTestableLooper.getLooper());
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mTestableLooper.processAllMessages();
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ super.onTuningChanged(key, newValue);
+ mTestableLooper.processAllMessages();
+ }
+ };
mScreenDecorations.mContext = mContext;
mScreenDecorations.mComponents = mContext.getComponents();
diff --git a/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java b/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java
index f2cbc86eb9d0..f5e6e7210356 100644
--- a/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java
@@ -21,6 +21,7 @@ import static android.view.Surface.ROTATION_90;
import android.graphics.Matrix;
import android.view.DisplayInfo;
+import android.view.Surface.Rotation;
import com.android.server.wm.utils.CoordinateTransforms;
@@ -66,6 +67,16 @@ public class ForcedSeamlessRotator {
}
/**
+ * Returns the rotation of the display before it started rotating.
+ *
+ * @return the old rotation of the display
+ */
+ @Rotation
+ public int getOldRotation() {
+ return mOldRotation;
+ }
+
+ /**
* Removes the transform to the window token's surface that undoes the effect of the global
* display rotation.
*
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0d093d1e3e35..58fb7a0f0a5e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -634,6 +634,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void forceSeamlesslyRotateIfAllowed(int oldRotation, int rotation) {
if (mForceSeamlesslyRotate) {
+ if (mPendingForcedSeamlessRotate != null) {
+ oldRotation = mPendingForcedSeamlessRotate.getOldRotation();
+ }
+
mPendingForcedSeamlessRotate = new ForcedSeamlessRotator(
oldRotation, rotation, getDisplayInfo());
mPendingForcedSeamlessRotate.unrotate(this.mToken);