diff options
| author | 2021-02-09 00:39:40 +0000 | |
|---|---|---|
| committer | 2021-02-09 00:39:40 +0000 | |
| commit | abffec262db8816ab3955276816ec895490c64b5 (patch) | |
| tree | 12c08ccc1f4dad5fad86f865d83319183b7fbff2 | |
| parent | 076669f905517c3637066dfa3caf6821e400358c (diff) | |
| parent | 10d2031e1b47dc0753cd718902866df5f2482013 (diff) | |
Merge "Gets suggested screen rotations from RotationResolverService." into sc-dev
5 files changed, 283 insertions, 14 deletions
diff --git a/core/java/android/rotationresolver/RotationResolverInternal.java b/core/java/android/rotationresolver/RotationResolverInternal.java index db879a7e3f8f..1f1a66c17d97 100644 --- a/core/java/android/rotationresolver/RotationResolverInternal.java +++ b/core/java/android/rotationresolver/RotationResolverInternal.java @@ -46,7 +46,6 @@ public abstract class RotationResolverInternal { * error is captured. {@link RotationResolverCallbackInternal} * @param proposedRotation the screen rotation that is proposed by the system. * @param currentRotation the current screen rotation. - * @param packageName the package name of the current activity that is running in foreground. * @param timeoutMillis the timeout in millisecond for the query. If the query doesn't get * fulfilled within this amount of time. It will be discarded and the * callback will receive a failure result code {@link @@ -55,8 +54,7 @@ public abstract class RotationResolverInternal { */ public abstract void resolveRotation(@NonNull RotationResolverCallbackInternal callback, @Surface.Rotation int proposedRotation, @Surface.Rotation int currentRotation, - String packageName, @DurationMillisLong long timeoutMillis, - @NonNull CancellationSignal cancellationSignal); + @DurationMillisLong long timeoutMillis, @NonNull CancellationSignal cancellationSignal); /** * Internal interfaces for the rotation resolver callback. diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java index 03d76649e7ee..57cf986842da 100644 --- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java +++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java @@ -150,7 +150,7 @@ public class RotationResolverManagerService extends @Override public void resolveRotation( @NonNull RotationResolverCallbackInternal callbackInternal, int proposedRotation, - int currentRotation, String packageName, long timeout, + int currentRotation, long timeout, @NonNull CancellationSignal cancellationSignalInternal) { Objects.requireNonNull(callbackInternal); Objects.requireNonNull(cancellationSignalInternal); @@ -159,7 +159,8 @@ public class RotationResolverManagerService extends final RotationResolverManagerPerUserService service = getServiceForUserLocked( UserHandle.getCallingUserId()); service.resolveRotationLocked(callbackInternal, proposedRotation, - currentRotation, packageName, timeout, cancellationSignalInternal); + currentRotation, /* packageName */ "", timeout, + cancellationSignalInternal); } else { Slog.w(TAG, "Rotation Resolver service is disabled."); callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED); diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 35611c055ea5..b106657dee99 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -1499,6 +1499,21 @@ public class DisplayRotation { } @Override + public boolean canUseRotationResolver() { + if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) return false; + + switch (mCurrentAppOrientation) { + case ActivityInfo.SCREEN_ORIENTATION_FULL_USER: + case ActivityInfo.SCREEN_ORIENTATION_USER: + case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED: + case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE: + case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: + return true; + } + return false; + } + + @Override public void onProposedRotationChanged(int rotation) { ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation); Runnable r = mRunnableCache.get(rotation, null); diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java index da31bb253831..5ef9420a10d8 100644 --- a/services/core/java/com/android/server/wm/WindowOrientationListener.java +++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java @@ -16,25 +16,36 @@ package com.android.server.wm; +import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER; + import static com.android.server.wm.WindowOrientationListenerProto.ENABLED; import static com.android.server.wm.WindowOrientationListenerProto.ROTATION; +import android.app.ActivityThread; import android.content.Context; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.os.CancellationSignal; import android.os.Handler; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.UserHandle; +import android.provider.DeviceConfig; +import android.provider.Settings; +import android.rotationresolver.RotationResolverInternal; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.Surface; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; +import com.android.server.LocalServices; import java.io.PrintWriter; import java.util.List; +import java.util.Set; /** * A special helper class used by the WindowManager @@ -55,6 +66,9 @@ public abstract class WindowOrientationListener { private static final boolean USE_GRAVITY_SENSOR = false; private static final int DEFAULT_BATCH_LATENCY = 100000; + private static final int DEFAULT_ROTATION_RESOLVER_ENABLED = 0; // disabled + private static final String KEY_ROTATION_RESOLVER_TIMEOUT = "rotation_resolver_timeout_millis"; + private static final long DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS = 700L; private Handler mHandler; private SensorManager mSensorManager; @@ -62,7 +76,13 @@ public abstract class WindowOrientationListener { private int mRate; private String mSensorType; private Sensor mSensor; - private OrientationJudge mOrientationJudge; + + @VisibleForTesting + OrientationJudge mOrientationJudge; + + @VisibleForTesting + RotationResolverInternal mRotationResolverService; + private int mCurrentRotation = -1; private final Context mContext; private final WindowManagerConstants mConstants; @@ -256,6 +276,32 @@ public abstract class WindowOrientationListener { } /** + * Returns true if the current status of the phone is suitable for using rotation resolver + * service. + * + * To reduce the power consumption of rotation resolver service, rotation query should run less + * frequently than other low power orientation sensors. This method is used to check whether + * the current status of the phone is necessary to request a suggested screen rotation from the + * rotation resolver service. Note that it always returns {@code false} in the base class. It + * should be overridden in the derived classes. + */ + public boolean canUseRotationResolver() { + return false; + } + + /** + * Returns true if the rotation resolver feature is enabled by setting. It means {@link + * WindowOrientationListener} will then ask {@link RotationResolverInternal} for the appropriate + * screen rotation. + */ + @VisibleForTesting + boolean isRotationResolverEnabled() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.CAMERA_AUTOROTATE, DEFAULT_ROTATION_RESOLVER_ENABLED, + UserHandle.USER_CURRENT) == 1; + } + + /** * Called when the rotation view of the device has changed. * * This method is called whenever the orientation becomes certain of an orientation. @@ -1045,6 +1091,30 @@ public abstract class WindowOrientationListener { private int mProposedRotation = -1; private int mDesiredRotation = -1; private boolean mRotationEvaluationScheduled; + private long mRotationResolverTimeoutMillis; + + OrientationSensorJudge() { + super(); + setupRotationResolverParameters(); + } + + private void setupRotationResolverParameters() { + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_WINDOW_MANAGER, + ActivityThread.currentApplication().getMainExecutor(), (properties) -> { + final Set<String> keys = properties.getKeyset(); + if (keys.contains(KEY_ROTATION_RESOLVER_TIMEOUT)) { + readRotationResolverParameters(); + } + }); + readRotationResolverParameters(); + } + + private void readRotationResolverParameters() { + mRotationResolverTimeoutMillis = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_WINDOW_MANAGER, + KEY_ROTATION_RESOLVER_TIMEOUT, + DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS); + } @Override public int getProposedRotationLocked() { @@ -1069,19 +1139,13 @@ public abstract class WindowOrientationListener { @Override public void onSensorChanged(SensorEvent event) { - int newRotation; - int reportedRotation = (int) event.values[0]; if (reportedRotation < 0 || reportedRotation > 3) { return; } - synchronized (mLock) { - mDesiredRotation = reportedRotation; - newRotation = evaluateRotationChangeLocked(); - } - if (newRotation >= 0) { - onProposedRotationChanged(newRotation); + // Log raw sensor rotation. + if (evaluateRotationChangeLocked() >= 0) { if (mConstants.mRawSensorLoggingEnabled) { FrameworkStatsLog.write( FrameworkStatsLog.DEVICE_ROTATED, @@ -1089,6 +1153,35 @@ public abstract class WindowOrientationListener { rotationToLogEnum(reportedRotation)); } } + + if (isRotationResolverEnabled() && canUseRotationResolver()) { + if (mRotationResolverService == null) { + mRotationResolverService = LocalServices.getService( + RotationResolverInternal.class); + } + + final CancellationSignal cancellationSignal = new CancellationSignal(); + mRotationResolverService.resolveRotation( + new RotationResolverInternal.RotationResolverCallbackInternal() { + @Override + public void onSuccess(int result) { + finalizeRotation(result); + } + + @Override + public void onFailure(int error) { + finalizeRotation(reportedRotation); + } + }, + reportedRotation, + mCurrentRotation, + mRotationResolverTimeoutMillis, + cancellationSignal); + getHandler().postDelayed(cancellationSignal::cancel, + mRotationResolverTimeoutMillis); + } else { + finalizeRotation(reportedRotation); + } } @Override @@ -1131,6 +1224,17 @@ public abstract class WindowOrientationListener { return -1; } + private void finalizeRotation(int reportedRotation) { + int newRotation; + synchronized (mLock) { + mDesiredRotation = reportedRotation; + newRotation = evaluateRotationChangeLocked(); + } + if (newRotation >= 0) { + onProposedRotationChanged(newRotation); + } + } + private boolean isDesiredRotationAcceptableLocked(long now) { if (mTouching) { return false; diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java new file mode 100644 index 000000000000..f5d0ca7c5f9f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2021 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 com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; + +import android.annotation.DurationMillisLong; +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorManager; +import android.hardware.input.InputSensorInfo; +import android.os.CancellationSignal; +import android.os.Handler; +import android.rotationresolver.RotationResolverInternal; +import android.view.Surface; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link com.android.server.wm.WindowOrientationListener} + */ +public class WindowOrientationListenerTest { + + @Mock + private Context mMockContext; + @Mock + private Handler mMockHandler; + @Mock + private InputSensorInfo mMockInputSensorInfo; + @Mock + private SensorManager mMockSensorManager; + @Mock + private WindowManagerService mMockWindowManagerService; + + private TestableRotationResolver mFakeRotationResolverInternal; + private com.android.server.wm.WindowOrientationListener mWindowOrientationListener; + private int mFinalizedRotation; + private boolean mRotationResolverEnabled; + private boolean mCanUseRotationResolver; + private SensorEvent mFakeSensorEvent; + private Sensor mFakeSensor; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mRotationResolverEnabled = true; + mCanUseRotationResolver = true; + + mFakeRotationResolverInternal = new TestableRotationResolver(); + doReturn(mMockSensorManager).when(mMockContext).getSystemService(Context.SENSOR_SERVICE); + mWindowOrientationListener = new TestableWindowOrientationListener(mMockContext, + mMockHandler, mMockWindowManagerService); + mWindowOrientationListener.mRotationResolverService = mFakeRotationResolverInternal; + + mFakeSensor = new Sensor(mMockInputSensorInfo); + mFakeSensorEvent = new SensorEvent(mFakeSensor, /* accuracy */ 1, /* timestamp */ 1L, + new float[]{(float) Surface.ROTATION_90}); + } + + @Test + public void testOnSensorChanged_rotationResolverDisabled_useSensorResult() { + mRotationResolverEnabled = false; + + mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent); + + assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_90); + } + + @Test + public void testOnSensorChanged_cannotUseRotationResolver_useSensorResult() { + mCanUseRotationResolver = false; + + mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent); + + assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_90); + + } + + @Test + public void testOnSensorChanged_normalCase() { + mFakeRotationResolverInternal.mResult = Surface.ROTATION_180; + + mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent); + + assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_180); + } + + final class TestableRotationResolver extends RotationResolverInternal { + @Surface.Rotation + int mResult; + + @Override + public boolean isRotationResolverSupported() { + return true; + } + + @Override + public void resolveRotation(@NonNull RotationResolverCallbackInternal callback, + @Surface.Rotation int proposedRotation, @Surface.Rotation int currentRotation, + @DurationMillisLong long timeoutMillis, + @NonNull CancellationSignal cancellationSignal) { + callback.onSuccess(mResult); + } + } + + final class TestableWindowOrientationListener extends WindowOrientationListener { + + TestableWindowOrientationListener(Context context, Handler handler, + WindowManagerService service) { + super(context, handler, service); + this.mOrientationJudge = new OrientationSensorJudge(); + } + + @Override + public void onProposedRotationChanged(int rotation) { + mFinalizedRotation = rotation; + } + + @Override + public boolean canUseRotationResolver() { + return mCanUseRotationResolver; + } + + @Override + public boolean isRotationResolverEnabled() { + return mRotationResolverEnabled; + } + } +} |