1 /* 2 * Copyright (C) 2017 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.am; 18 19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 21 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; 22 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; 23 import static android.view.Display.DEFAULT_DISPLAY; 24 25 import static com.android.server.am.ActivityStack.ActivityState.DESTROYED; 26 import static com.android.server.am.ActivityStack.ActivityState.DESTROYING; 27 import static com.android.server.am.ActivityStack.ActivityState.FINISHING; 28 import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING; 29 import static com.android.server.am.ActivityStack.ActivityState.PAUSED; 30 import static com.android.server.am.ActivityStack.ActivityState.PAUSING; 31 import static com.android.server.am.ActivityStack.ActivityState.STOPPED; 32 import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING; 33 import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; 34 import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT; 35 import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; 36 37 import static junit.framework.TestCase.assertNotNull; 38 39 import static org.junit.Assert.assertEquals; 40 import static org.junit.Assert.assertFalse; 41 import static org.junit.Assert.assertNull; 42 import static org.junit.Assert.assertTrue; 43 import static org.mockito.Mockito.any; 44 import static org.mockito.Mockito.anyInt; 45 import static org.mockito.Mockito.doAnswer; 46 import static org.mockito.Mockito.eq; 47 import static org.mockito.Mockito.times; 48 import static org.mockito.Mockito.verify; 49 import static org.mockito.Mockito.when; 50 51 import android.app.ActivityOptions; 52 import android.app.servertransaction.ClientTransaction; 53 import android.app.servertransaction.PauseActivityItem; 54 import android.graphics.Rect; 55 import android.platform.test.annotations.Presubmit; 56 import android.support.test.filters.MediumTest; 57 import android.support.test.runner.AndroidJUnit4; 58 import android.util.MutableBoolean; 59 60 import org.junit.runner.RunWith; 61 import org.junit.Before; 62 import org.junit.Ignore; 63 import org.junit.Test; 64 import org.mockito.invocation.InvocationOnMock; 65 66 /** 67 * Tests for the {@link ActivityRecord} class. 68 * 69 * Build/Install/Run: 70 * atest FrameworksServicesTests:com.android.server.am.ActivityRecordTests 71 */ 72 @MediumTest 73 @Presubmit 74 @RunWith(AndroidJUnit4.class) 75 public class ActivityRecordTests extends ActivityTestsBase { 76 private ActivityManagerService mService; 77 private TestActivityStack mStack; 78 private TaskRecord mTask; 79 private ActivityRecord mActivity; 80 81 @Before 82 @Override 83 public void setUp() throws Exception { 84 super.setUp(); 85 86 mService = createActivityManagerService(); 87 mStack = mService.mStackSupervisor.getDefaultDisplay().createStack( 88 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); 89 mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build(); 90 mActivity = new ActivityBuilder(mService).setTask(mTask).build(); 91 } 92 93 @Test 94 public void testStackCleanupOnClearingTask() throws Exception { 95 mActivity.setTask(null); 96 assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1); 97 } 98 99 @Test 100 public void testStackCleanupOnActivityRemoval() throws Exception { 101 mTask.removeActivity(mActivity); 102 assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1); 103 } 104 105 @Test 106 public void testStackCleanupOnTaskRemoval() throws Exception { 107 mStack.removeTask(mTask, null /*reason*/, REMOVE_TASK_MODE_MOVING); 108 // Stack should be gone on task removal. 109 assertNull(mService.mStackSupervisor.getStack(mStack.mStackId)); 110 } 111 112 @Test 113 public void testNoCleanupMovingActivityInSameStack() throws Exception { 114 final TaskRecord newTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack) 115 .build(); 116 mActivity.reparent(newTask, 0, null /*reason*/); 117 assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0); 118 } 119 120 @Test 121 public void testPausingWhenVisibleFromStopped() throws Exception { 122 final MutableBoolean pauseFound = new MutableBoolean(false); 123 doAnswer((InvocationOnMock invocationOnMock) -> { 124 final ClientTransaction transaction = invocationOnMock.getArgument(0); 125 if (transaction.getLifecycleStateRequest() instanceof PauseActivityItem) { 126 pauseFound.value = true; 127 } 128 return null; 129 }).when(mActivity.app.thread).scheduleTransaction(any()); 130 131 mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped"); 132 133 // The activity is in the focused stack so it should not move to paused. 134 mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); 135 assertTrue(mActivity.isState(STOPPED)); 136 assertFalse(pauseFound.value); 137 138 // Clear focused stack 139 mActivity.mStackSupervisor.mFocusedStack = null; 140 141 // In the unfocused stack, the activity should move to paused. 142 mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); 143 assertTrue(mActivity.isState(PAUSING)); 144 assertTrue(pauseFound.value); 145 146 // Make sure that the state does not change for current non-stopping states. 147 mActivity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped"); 148 149 mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); 150 151 assertTrue(mActivity.isState(INITIALIZING)); 152 153 // Make sure the state does not change if we are not the current top activity. 154 mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind"); 155 156 // Make sure that the state does not change when we have an activity becoming translucent 157 final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build(); 158 mStack.mTranslucentActivityWaiting = topActivity; 159 mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); 160 161 assertTrue(mActivity.isState(STOPPED)); 162 } 163 164 @Test 165 public void testPositionLimitedAspectRatioNavBarBottom() throws Exception { 166 verifyPositionWithLimitedAspectRatio(NAV_BAR_BOTTOM, new Rect(0, 0, 1000, 2000), 1.5f, 167 new Rect(0, 0, 1000, 1500)); 168 } 169 170 @Test 171 public void testPositionLimitedAspectRatioNavBarLeft() throws Exception { 172 verifyPositionWithLimitedAspectRatio(NAV_BAR_LEFT, new Rect(0, 0, 2000, 1000), 1.5f, 173 new Rect(500, 0, 2000, 1000)); 174 } 175 176 @Test 177 public void testPositionLimitedAspectRatioNavBarRight() throws Exception { 178 verifyPositionWithLimitedAspectRatio(NAV_BAR_RIGHT, new Rect(0, 0, 2000, 1000), 1.5f, 179 new Rect(0, 0, 1500, 1000)); 180 } 181 182 private void verifyPositionWithLimitedAspectRatio(int navBarPosition, Rect taskBounds, 183 float aspectRatio, Rect expectedActivityBounds) { 184 // Verify with nav bar on the right. 185 when(mService.mWindowManager.getNavBarPosition()).thenReturn(navBarPosition); 186 mTask.getConfiguration().windowConfiguration.setAppBounds(taskBounds); 187 mActivity.info.maxAspectRatio = aspectRatio; 188 mActivity.ensureActivityConfiguration( 189 0 /* globalChanges */, false /* preserveWindow */); 190 assertEquals(expectedActivityBounds, mActivity.getBounds()); 191 } 192 193 @Test 194 public void testCanBeLaunchedOnDisplay() throws Exception { 195 testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/, 196 true /*activityResizeable*/, true /*expected*/); 197 198 testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/, 199 false /*activityResizeable*/, false /*expected*/); 200 201 testSupportsLaunchingResizeable(true /*taskPresent*/, false /*taskResizeable*/, 202 true /*activityResizeable*/, false /*expected*/); 203 204 testSupportsLaunchingResizeable(true /*taskPresent*/, true /*taskResizeable*/, 205 false /*activityResizeable*/, true /*expected*/); 206 } 207 208 @Test 209 public void testsApplyOptionsLocked() { 210 ActivityOptions activityOptions = ActivityOptions.makeBasic(); 211 212 // Set and apply options for ActivityRecord. Pending options should be cleared 213 mActivity.updateOptionsLocked(activityOptions); 214 mActivity.applyOptionsLocked(); 215 assertNull(mActivity.pendingOptions); 216 217 // Set options for two ActivityRecords in same Task. Apply one ActivityRecord options. 218 // Pending options should be cleared for both ActivityRecords 219 ActivityRecord activity2 = new ActivityBuilder(mService).setTask(mTask).build(); 220 activity2.updateOptionsLocked(activityOptions); 221 mActivity.updateOptionsLocked(activityOptions); 222 mActivity.applyOptionsLocked(); 223 assertNull(mActivity.pendingOptions); 224 assertNull(activity2.pendingOptions); 225 226 // Set options for two ActivityRecords in separate Tasks. Apply one ActivityRecord options. 227 // Pending options should be cleared for only ActivityRecord that was applied 228 TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build(); 229 activity2 = new ActivityBuilder(mService).setTask(task2).build(); 230 activity2.updateOptionsLocked(activityOptions); 231 mActivity.updateOptionsLocked(activityOptions); 232 mActivity.applyOptionsLocked(); 233 assertNull(mActivity.pendingOptions); 234 assertNotNull(activity2.pendingOptions); 235 } 236 237 private void testSupportsLaunchingResizeable(boolean taskPresent, boolean taskResizeable, 238 boolean activityResizeable, boolean expected) { 239 mService.mSupportsMultiWindow = true; 240 241 final TaskRecord task = taskPresent 242 ? new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build() : null; 243 244 if (task != null) { 245 task.setResizeMode(taskResizeable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE); 246 } 247 248 final ActivityRecord record = new ActivityBuilder(mService).setTask(task).build(); 249 record.info.resizeMode = activityResizeable 250 ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE; 251 252 record.canBeLaunchedOnDisplay(DEFAULT_DISPLAY); 253 254 255 verify(mService.mStackSupervisor, times(1)).canPlaceEntityOnDisplay(anyInt(), eq(expected), 256 anyInt(), anyInt(), eq(record.info)); 257 } 258 } 259