1 /* 2 * Copyright (C) 2023 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 17 package com.android.server.wm; 18 19 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 20 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.fail; 23 24 import android.app.Activity; 25 import android.app.Instrumentation; 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.pm.ActivityInfo; 30 import android.content.res.Configuration; 31 import android.os.Parcel; 32 import android.platform.test.annotations.Presubmit; 33 import android.util.Log; 34 import android.view.SurfaceControl; 35 import android.view.SurfaceHolder; 36 import android.view.SurfaceView; 37 38 import androidx.annotation.NonNull; 39 import androidx.test.filters.SmallTest; 40 import androidx.test.runner.AndroidJUnit4; 41 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 45 import java.util.concurrent.atomic.AtomicInteger; 46 47 /** 48 * Class for testing {@link SurfaceControl}. 49 * 50 * Build/Install/Run: 51 * atest WmTests:SurfaceControlTests 52 */ 53 @Presubmit 54 @RunWith(AndroidJUnit4.class) 55 public class SurfaceControlTests { 56 57 @SmallTest 58 @Test testUseValidSurface()59 public void testUseValidSurface() { 60 SurfaceControl sc = buildTestSurface(); 61 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 62 t.setVisibility(sc, false); 63 sc.release(); 64 } 65 66 @SmallTest 67 @Test testUseInvalidSurface()68 public void testUseInvalidSurface() { 69 SurfaceControl sc = buildTestSurface(); 70 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 71 sc.release(); 72 try { 73 t.setVisibility(sc, false); 74 fail("Expected exception from updating invalid surface"); 75 } catch (Exception e) { 76 // Expected exception 77 } 78 } 79 80 @SmallTest 81 @Test testUseInvalidSurface_debugEnabled()82 public void testUseInvalidSurface_debugEnabled() { 83 SurfaceControl sc = buildTestSurface(); 84 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 85 try { 86 SurfaceControl.setDebugUsageAfterRelease(true); 87 sc.release(); 88 try { 89 t.setVisibility(sc, false); 90 fail("Expected exception from updating invalid surface"); 91 } catch (IllegalStateException ise) { 92 assertNotNull(ise.getCause()); 93 } catch (Exception e) { 94 fail("Expected IllegalStateException with cause"); 95 } 96 } finally { 97 SurfaceControl.setDebugUsageAfterRelease(false); 98 } 99 } 100 101 @SmallTest 102 @Test testWriteInvalidSurface_debugEnabled()103 public void testWriteInvalidSurface_debugEnabled() { 104 SurfaceControl sc = buildTestSurface(); 105 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 106 Parcel p = Parcel.obtain(); 107 try { 108 SurfaceControl.setDebugUsageAfterRelease(true); 109 sc.release(); 110 try { 111 sc.writeToParcel(p, 0 /* flags */); 112 fail("Expected exception from writing invalid surface to parcel"); 113 } catch (IllegalStateException ise) { 114 assertNotNull(ise.getCause()); 115 } catch (Exception e) { 116 fail("Expected IllegalStateException with cause"); 117 } 118 } finally { 119 SurfaceControl.setDebugUsageAfterRelease(false); 120 p.recycle(); 121 } 122 } 123 124 @Test testSurfaceChangedOnRotation()125 public void testSurfaceChangedOnRotation() { 126 final Instrumentation instrumentation = getInstrumentation(); 127 final Context context = instrumentation.getContext(); 128 final Intent intent = new Intent().setComponent( 129 new ComponentName(context, ActivityOptionsTest.MainActivity.class)) 130 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP); 131 final Activity activity = instrumentation.startActivitySync(intent); 132 final SurfaceView sv = new SurfaceView(activity); 133 final AtomicInteger surfaceChangedCount = new AtomicInteger(); 134 instrumentation.runOnMainSync(() -> activity.setContentView(sv)); 135 sv.getHolder().addCallback(new SurfaceHolder.Callback() { 136 @Override 137 public void surfaceCreated(@NonNull SurfaceHolder holder) { 138 } 139 @Override 140 public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, 141 int height) { 142 surfaceChangedCount.getAndIncrement(); 143 Log.i("surfaceChanged", "width=" + width + " height=" + height 144 + " getTransformHint=" 145 + sv.getViewRootImpl().getSurfaceControl().getTransformHint()); 146 } 147 @Override 148 public void surfaceDestroyed(@NonNull SurfaceHolder holder) { 149 } 150 }); 151 final int rotation = activity.getResources().getConfiguration() 152 .windowConfiguration.getRotation(); 153 activity.setRequestedOrientation(activity.getResources().getConfiguration().orientation 154 == Configuration.ORIENTATION_PORTRAIT 155 ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE 156 : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 157 instrumentation.getUiAutomation().syncInputTransactions(); 158 instrumentation.waitForIdleSync(); 159 final int newRotation = activity.getResources().getConfiguration() 160 .windowConfiguration.getRotation(); 161 if (rotation == newRotation) { 162 // The device might not support requested orientation. 163 activity.finishAndRemoveTask(); 164 return; 165 } 166 final int count = surfaceChangedCount.get(); 167 activity.moveTaskToBack(true /* nonRoot */); 168 instrumentation.getUiAutomation().syncInputTransactions(); 169 context.startActivity(intent); 170 instrumentation.getUiAutomation().syncInputTransactions(); 171 final int countAfterToFront = count - surfaceChangedCount.get(); 172 activity.finishAndRemoveTask(); 173 174 // The first count is triggered from creation, so the target number is 2. 175 if (count > 2) { 176 fail("More than once surfaceChanged for rotation change: " + count); 177 } 178 if (countAfterToFront > 1) { 179 fail("More than once surfaceChanged for app transition with rotation change: " 180 + countAfterToFront); 181 } 182 } 183 buildTestSurface()184 private SurfaceControl buildTestSurface() { 185 return new SurfaceControl.Builder() 186 .setContainerLayer() 187 .setName("SurfaceControlTests") 188 .setCallsite("SurfaceControlTests") 189 .build(); 190 } 191 } 192