1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.view.cts; 17 18 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; 19 import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 20 21 import android.annotation.SuppressLint; 22 import android.app.Activity; 23 import android.content.pm.ActivityInfo; 24 import android.content.pm.PackageManager; 25 import android.server.wm.IgnoreOrientationRequestSession; 26 import android.util.Log; 27 import android.view.AttachedSurfaceControl; 28 29 import androidx.lifecycle.Lifecycle; 30 import androidx.test.core.app.ActivityScenario; 31 import androidx.test.filters.LargeTest; 32 import androidx.test.filters.RequiresDevice; 33 import androidx.test.platform.app.InstrumentationRegistry; 34 import androidx.test.runner.AndroidJUnit4; 35 36 import com.android.compatibility.common.util.SystemUtil; 37 38 import org.junit.After; 39 import org.junit.Assert; 40 import org.junit.Assume; 41 import org.junit.Before; 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 45 import java.util.concurrent.CountDownLatch; 46 import java.util.concurrent.TimeUnit; 47 import java.util.function.IntConsumer; 48 49 @RunWith(AndroidJUnit4.class) 50 @LargeTest 51 @SuppressLint("RtlHardcoded") 52 @RequiresDevice 53 public class AttachedSurfaceControlTest { 54 private static final String TAG = "AttachedSurfaceControlTest"; 55 private static final String FIXED_TO_USER_ROTATION_COMMAND = 56 "cmd window fixed-to-user-rotation"; 57 private IgnoreOrientationRequestSession mOrientationSession; 58 59 private static class TransformHintListener implements 60 AttachedSurfaceControl.OnBufferTransformHintChangedListener { 61 Activity activity; 62 int expectedOrientation; 63 CountDownLatch latch = new CountDownLatch(1); 64 IntConsumer hintConsumer; 65 TransformHintListener(Activity activity, int expectedOrientation, IntConsumer hintConsumer)66 TransformHintListener(Activity activity, int expectedOrientation, 67 IntConsumer hintConsumer) { 68 this.activity = activity; 69 this.expectedOrientation = expectedOrientation; 70 this.hintConsumer = hintConsumer; 71 } 72 73 @Override onBufferTransformHintChanged(int hint)74 public void onBufferTransformHintChanged(int hint) { 75 int orientation = activity.getResources().getConfiguration().orientation; 76 Log.d(TAG, "onBufferTransformHintChanged: orientation actual=" + orientation 77 + " expected=" + expectedOrientation + " transformHint=" + hint); 78 Assert.assertEquals("Failed to switch orientation hint=" + hint, orientation, 79 expectedOrientation); 80 hintConsumer.accept(hint); 81 latch.countDown(); 82 activity.getWindow().getRootSurfaceControl() 83 .removeOnBufferTransformHintChangedListener(this); 84 } 85 } 86 87 @Before setup()88 public void setup() { 89 PackageManager pm = 90 InstrumentationRegistry.getInstrumentation().getContext().getPackageManager(); 91 boolean supportsRotation = pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_PORTRAIT) 92 && pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE); 93 final boolean isFixedToUserRotation = 94 "enabled".equals(SystemUtil.runShellCommand(FIXED_TO_USER_ROTATION_COMMAND).trim()); 95 Assume.assumeTrue(supportsRotation && !isFixedToUserRotation); 96 mOrientationSession = new IgnoreOrientationRequestSession(false /* enable */); 97 } 98 99 @After teardown()100 public void teardown() { 101 if (mOrientationSession != null) { 102 mOrientationSession.close(); 103 } 104 } 105 106 @Test testOnBufferTransformHintChangedListener()107 public void testOnBufferTransformHintChangedListener() throws InterruptedException { 108 final int[] transformHintResult = new int[2]; 109 final CountDownLatch[] firstCallback = new CountDownLatch[1]; 110 final CountDownLatch[] secondCallback = new CountDownLatch[1]; 111 try (ActivityScenario<HandleConfigurationActivity> scenario = 112 ActivityScenario.launch(HandleConfigurationActivity.class)) { 113 scenario.moveToState(Lifecycle.State.RESUMED); 114 scenario.onActivity(activity -> { 115 int requestedOrientation = getRequestedOrientation(activity); 116 TransformHintListener listener = new TransformHintListener(activity, 117 requestedOrientation, hint -> transformHintResult[0] = hint); 118 firstCallback[0] = listener.latch; 119 activity.getWindow().getRootSurfaceControl() 120 .addOnBufferTransformHintChangedListener(listener); 121 setRequestedOrientation(activity, requestedOrientation); 122 }); 123 // Check we get a callback since the orientation has changed and we expect transform 124 // hint to change. 125 Assert.assertTrue(firstCallback[0].await(3, TimeUnit.SECONDS)); 126 127 // Check the callback value matches the call to get the transform hint. 128 scenario.onActivity(activity -> Assert.assertEquals(transformHintResult[0], 129 activity.getWindow().getRootSurfaceControl().getBufferTransformHint())); 130 131 scenario.onActivity(activity -> { 132 int requestedOrientation = getRequestedOrientation(activity); 133 TransformHintListener listener = new TransformHintListener(activity, 134 requestedOrientation, hint -> transformHintResult[1] = hint); 135 secondCallback[0] = listener.latch; 136 activity.getWindow().getRootSurfaceControl() 137 .addOnBufferTransformHintChangedListener(listener); 138 setRequestedOrientation(activity, requestedOrientation); 139 }); 140 // Check we get a callback since the orientation has changed and we expect transform 141 // hint to change. 142 Assert.assertTrue(secondCallback[0].await(3, TimeUnit.SECONDS)); 143 144 // Check the callback value matches the call to get the transform hint. 145 scenario.onActivity(activity -> Assert.assertEquals(transformHintResult[1], 146 activity.getWindow().getRootSurfaceControl().getBufferTransformHint())); 147 } 148 149 // If the app orientation was changed, we should get a different transform hint 150 Assert.assertNotEquals(transformHintResult[0], transformHintResult[1]); 151 } 152 getRequestedOrientation(Activity activity)153 private int getRequestedOrientation(Activity activity) { 154 int currentOrientation = activity.getResources().getConfiguration().orientation; 155 return currentOrientation == ORIENTATION_LANDSCAPE ? ORIENTATION_PORTRAIT 156 : ORIENTATION_LANDSCAPE; 157 } 158 setRequestedOrientation(Activity activity, int requestedOrientation)159 private void setRequestedOrientation(Activity activity, 160 /* @Configuration.Orientation */ int requestedOrientation) { 161 /* @ActivityInfo.ScreenOrientation */ 162 Log.d(TAG, "setRequestedOrientation: requestedOrientation=" + requestedOrientation); 163 int screenOrientation = 164 requestedOrientation == ORIENTATION_LANDSCAPE 165 ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE 166 : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 167 activity.setRequestedOrientation(screenOrientation); 168 } 169 170 @Test testOnBufferTransformHintChangesFromLandToSea()171 public void testOnBufferTransformHintChangesFromLandToSea() throws InterruptedException { 172 final int[] transformHintResult = new int[2]; 173 final CountDownLatch[] firstCallback = new CountDownLatch[1]; 174 final CountDownLatch[] secondCallback = new CountDownLatch[1]; 175 try (ActivityScenario<HandleConfigurationActivity> scenario = 176 ActivityScenario.launch(HandleConfigurationActivity.class)) { 177 scenario.moveToState(Lifecycle.State.RESUMED); 178 scenario.onActivity(activity -> { 179 if (activity.getResources().getConfiguration().orientation 180 == ORIENTATION_LANDSCAPE) { 181 return; 182 } 183 TransformHintListener listener = new TransformHintListener(activity, 184 ORIENTATION_LANDSCAPE, hint -> transformHintResult[0] = hint); 185 firstCallback[0] = listener.latch; 186 activity.getWindow().getRootSurfaceControl() 187 .addOnBufferTransformHintChangedListener(listener); 188 setRequestedOrientation(activity, ORIENTATION_LANDSCAPE); 189 }); 190 191 // If the device is already in landscape, do nothing. 192 if (firstCallback[0] != null) { 193 Assert.assertTrue(firstCallback[0].await(3, TimeUnit.SECONDS)); 194 scenario.onActivity(activity -> Assert.assertEquals(transformHintResult[0], 195 activity.getWindow().getRootSurfaceControl().getBufferTransformHint())); 196 } 197 198 scenario.onActivity(activity -> { 199 TransformHintListener listener = new TransformHintListener(activity, 200 ORIENTATION_LANDSCAPE, hint -> transformHintResult[1] = hint); 201 secondCallback[0] = listener.latch; 202 activity.getWindow().getRootSurfaceControl() 203 .addOnBufferTransformHintChangedListener(listener); 204 activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); 205 }); 206 // Check we get a callback since the orientation has changed and we expect transform 207 // hint to change. 208 Assert.assertTrue(secondCallback[0].await(3, TimeUnit.SECONDS)); 209 210 // Check the callback value matches the call to get the transform hint. 211 scenario.onActivity(activity -> Assert.assertEquals(transformHintResult[1], 212 activity.getWindow().getRootSurfaceControl().getBufferTransformHint())); 213 } 214 215 // If the app orientation was changed, we should get a different transform hint 216 Assert.assertNotEquals(transformHintResult[0], transformHintResult[1]); 217 } 218 219 } 220