1 /* 2 * Copyright (C) 2019 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 android.app.activity; 18 19 import static android.app.ActivityThread.shouldReportChange; 20 import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE; 21 import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY; 22 import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; 23 import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME; 24 import static android.app.servertransaction.ActivityLifecycleItem.ON_START; 25 import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; 26 import static android.content.pm.ActivityInfo.CONFIG_FONT_SCALE; 27 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; 28 29 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 30 31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 32 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 33 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; 34 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 35 36 import static org.junit.Assert.assertEquals; 37 import static org.junit.Assert.assertFalse; 38 import static org.junit.Assert.assertTrue; 39 import static org.mockito.ArgumentMatchers.any; 40 import static org.mockito.ArgumentMatchers.anyInt; 41 import static org.mockito.Mockito.clearInvocations; 42 import static org.mockito.Mockito.doNothing; 43 import static org.mockito.Mockito.never; 44 import static org.mockito.Mockito.timeout; 45 import static org.mockito.Mockito.verify; 46 47 import android.app.Activity; 48 import android.app.ActivityClient; 49 import android.app.ActivityThread; 50 import android.app.ActivityThread.ActivityClientRecord; 51 import android.app.LoadedApk; 52 import android.app.servertransaction.PendingTransactionActions; 53 import android.content.ComponentName; 54 import android.content.Context; 55 import android.content.Intent; 56 import android.content.pm.ActivityInfo; 57 import android.content.pm.ApplicationInfo; 58 import android.content.res.Configuration; 59 import android.os.IBinder; 60 import android.os.UserHandle; 61 import android.platform.test.annotations.Presubmit; 62 import android.testing.PollingCheck; 63 import android.view.WindowManagerGlobal; 64 import android.window.ActivityWindowInfo; 65 import android.window.SizeConfigurationBuckets; 66 67 import androidx.test.annotation.UiThreadTest; 68 import androidx.test.ext.junit.runners.AndroidJUnit4; 69 import androidx.test.filters.MediumTest; 70 import androidx.test.platform.app.InstrumentationRegistry; 71 72 import org.junit.Test; 73 import org.junit.runner.RunWith; 74 import org.mockito.Mockito; 75 import org.mockito.MockitoSession; 76 import org.mockito.quality.Strictness; 77 78 import java.util.concurrent.TimeUnit; 79 80 /** 81 * Test for verifying {@link android.app.ActivityThread} class. 82 * 83 * <p>Build/Install/Run: 84 * atest FrameworksMockingCoreTests:android.app.activity.ActivityThreadClientTest 85 * 86 * <p>This test class is a part of Window Manager Service tests and specified in 87 * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. 88 */ 89 @RunWith(AndroidJUnit4.class) 90 @MediumTest 91 @Presubmit 92 public class ActivityThreadClientTest { 93 private static final long WAIT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10); 94 95 @Test 96 @UiThreadTest testLifecycleAfterFinished_OnCreate()97 public void testLifecycleAfterFinished_OnCreate() throws Exception { 98 try (ClientMockSession clientSession = new ClientMockSession()) { 99 ActivityClientRecord r = clientSession.stubActivityRecord(); 100 101 Activity activity = clientSession.launchActivity(r); 102 activity.finish(); 103 assertEquals(ON_CREATE, r.getLifecycleState()); 104 105 clientSession.startActivity(r); 106 assertEquals(ON_CREATE, r.getLifecycleState()); 107 108 clientSession.resumeActivity(r); 109 assertEquals(ON_CREATE, r.getLifecycleState()); 110 111 clientSession.pauseActivity(r); 112 assertEquals(ON_CREATE, r.getLifecycleState()); 113 114 clientSession.stopActivity(r); 115 assertEquals(ON_CREATE, r.getLifecycleState()); 116 117 clientSession.destroyActivity(r); 118 assertEquals(ON_DESTROY, r.getLifecycleState()); 119 } 120 } 121 122 @Test 123 @UiThreadTest testLifecycleAfterFinished_OnStart()124 public void testLifecycleAfterFinished_OnStart() throws Exception { 125 try (ClientMockSession clientSession = new ClientMockSession()) { 126 ActivityClientRecord r = clientSession.stubActivityRecord(); 127 128 Activity activity = clientSession.launchActivity(r); 129 clientSession.startActivity(r); 130 activity.finish(); 131 assertEquals(ON_START, r.getLifecycleState()); 132 133 clientSession.resumeActivity(r); 134 assertEquals(ON_START, r.getLifecycleState()); 135 136 clientSession.pauseActivity(r); 137 assertEquals(ON_START, r.getLifecycleState()); 138 139 clientSession.stopActivity(r); 140 assertEquals(ON_STOP, r.getLifecycleState()); 141 142 clientSession.destroyActivity(r); 143 assertEquals(ON_DESTROY, r.getLifecycleState()); 144 } 145 } 146 147 @Test 148 @UiThreadTest testLifecycleAfterFinished_OnResume()149 public void testLifecycleAfterFinished_OnResume() throws Exception { 150 try (ClientMockSession clientSession = new ClientMockSession()) { 151 ActivityClientRecord r = clientSession.stubActivityRecord(); 152 153 Activity activity = clientSession.launchActivity(r); 154 clientSession.startActivity(r); 155 clientSession.resumeActivity(r); 156 activity.finish(); 157 assertEquals(ON_RESUME, r.getLifecycleState()); 158 159 clientSession.pauseActivity(r); 160 assertEquals(ON_PAUSE, r.getLifecycleState()); 161 162 clientSession.stopActivity(r); 163 assertEquals(ON_STOP, r.getLifecycleState()); 164 165 clientSession.destroyActivity(r); 166 assertEquals(ON_DESTROY, r.getLifecycleState()); 167 } 168 } 169 170 @Test testLifecycleOfRelaunch()171 public void testLifecycleOfRelaunch() throws Exception { 172 try (ClientMockSession clientSession = new ClientMockSession()) { 173 ActivityThread activityThread = clientSession.mockThread(); 174 ActivityClientRecord r = clientSession.stubActivityRecord(); 175 final TestActivity[] activity = new TestActivity[1]; 176 177 // Verify for ON_CREATE state. Activity should not be relaunched. 178 getInstrumentation().runOnMainSync(() -> { 179 activity[0] = (TestActivity) clientSession.launchActivity(r); 180 }); 181 recreateAndVerifyNoRelaunch(activityThread, activity[0]); 182 183 // Verify for ON_START state. Activity should be relaunched. 184 getInstrumentation().runOnMainSync(() -> clientSession.startActivity(r)); 185 recreateAndVerifyRelaunched(activityThread, activity[0], r, ON_PAUSE); 186 187 // Verify for ON_RESUME state. Activity should be relaunched. 188 getInstrumentation().runOnMainSync(() -> clientSession.resumeActivity(r)); 189 recreateAndVerifyRelaunched(activityThread, activity[0], r, ON_RESUME); 190 191 // Verify for ON_PAUSE state. Activity should be relaunched. 192 getInstrumentation().runOnMainSync(() -> clientSession.pauseActivity(r)); 193 recreateAndVerifyRelaunched(activityThread, activity[0], r, ON_PAUSE); 194 195 // Verify for ON_STOP state. Activity should be relaunched. 196 getInstrumentation().runOnMainSync(() -> clientSession.stopActivity(r)); 197 recreateAndVerifyRelaunched(activityThread, activity[0], r, ON_STOP); 198 199 // Verify for ON_DESTROY state. Activity should not be relaunched. 200 getInstrumentation().runOnMainSync(() -> clientSession.destroyActivity(r)); 201 recreateAndVerifyNoRelaunch(activityThread, activity[0]); 202 } 203 } 204 205 @Test testShouldReportChange()206 public void testShouldReportChange() { 207 final Configuration newConfig = new Configuration(); 208 final Configuration currentConfig = new Configuration(); 209 210 assertFalse("Must not report change if no public diff", 211 shouldReportChange(currentConfig, newConfig, null /* sizeBuckets */, 212 0 /* handledConfigChanges */, false /* alwaysReportChange */)); 213 214 final int[] verticalThresholds = {100, 400}; 215 final SizeConfigurationBuckets buckets = new SizeConfigurationBuckets( 216 null /* horizontal */, 217 verticalThresholds, 218 null /* smallest */, 219 null /* screenLayoutSize */, 220 false /* screenLayoutLongSet */); 221 currentConfig.screenHeightDp = 200; 222 newConfig.screenHeightDp = 300; 223 224 assertFalse("Must not report changes if the diff is small and not handled", 225 shouldReportChange(currentConfig, newConfig, buckets, 226 CONFIG_FONT_SCALE /* handledConfigChanges */, 227 false /* alwaysReportChange */)); 228 229 assertTrue("Must report changes if the small diff is handled", 230 shouldReportChange(currentConfig, newConfig, buckets, 231 CONFIG_SCREEN_SIZE /* handledConfigChanges */, 232 false /* alwaysReportChange */)); 233 234 assertTrue("Must report changes if it should, even it is small and not handled", 235 shouldReportChange(currentConfig, newConfig, buckets, 236 CONFIG_FONT_SCALE /* handledConfigChanges */, 237 true /* alwaysReportChange */)); 238 239 currentConfig.fontScale = 0.8f; 240 newConfig.fontScale = 1.2f; 241 242 assertTrue("Must report handled changes regardless of small unhandled change", 243 shouldReportChange(currentConfig, newConfig, buckets, 244 CONFIG_FONT_SCALE /* handledConfigChanges */, 245 false /* alwaysReportChange */)); 246 247 newConfig.screenHeightDp = 500; 248 249 assertFalse("Must not report changes if there's unhandled big changes", 250 shouldReportChange(currentConfig, newConfig, buckets, 251 CONFIG_FONT_SCALE /* handledConfigChanges */, 252 false /* alwaysReportChange */)); 253 } 254 recreateAndVerifyNoRelaunch(ActivityThread activityThread, TestActivity activity)255 private void recreateAndVerifyNoRelaunch(ActivityThread activityThread, TestActivity activity) { 256 clearInvocations(activityThread); 257 getInstrumentation().runOnMainSync(() -> activity.recreate()); 258 getInstrumentation().waitForIdleSync(); 259 260 verify(activityThread, never()).handleRelaunchActivity(any(), any()); 261 } 262 recreateAndVerifyRelaunched(ActivityThread activityThread, TestActivity activity, ActivityClientRecord r, int expectedState)263 private void recreateAndVerifyRelaunched(ActivityThread activityThread, TestActivity activity, 264 ActivityClientRecord r, int expectedState) throws Exception { 265 clearInvocations(activityThread); 266 getInstrumentation().runOnMainSync(() -> activity.recreate()); 267 268 verify(activityThread, timeout(WAIT_TIMEOUT_MS)).handleRelaunchActivity(any(), any()); 269 270 // Wait for the relaunch to complete. 271 PollingCheck.check("Waiting for the expected state " + expectedState + " timeout", 272 WAIT_TIMEOUT_MS, 273 () -> expectedState == r.getLifecycleState()); 274 assertEquals(expectedState, r.getLifecycleState()); 275 } 276 277 private class ClientMockSession implements AutoCloseable { 278 private MockitoSession mMockSession; 279 private ActivityThread mThread; 280 ClientMockSession()281 private ClientMockSession() { 282 mThread = ActivityThread.currentActivityThread(); 283 mMockSession = mockitoSession() 284 .strictness(Strictness.LENIENT) 285 .spyStatic(ActivityClient.class) 286 .spyStatic(WindowManagerGlobal.class) 287 .startMocking(); 288 doReturn(Mockito.mock(WindowManagerGlobal.class)) 289 .when(WindowManagerGlobal::getInstance); 290 final ActivityClient mockAc = Mockito.mock(ActivityClient.class); 291 doReturn(mockAc).when(ActivityClient::getInstance); 292 doReturn(true).when(mockAc).finishActivity(any() /* token */, 293 anyInt() /* resultCode */, any() /* resultData */, anyInt() /* finishTask */); 294 } 295 launchActivity(ActivityClientRecord r)296 private Activity launchActivity(ActivityClientRecord r) { 297 return mThread.handleLaunchActivity(r, null /* pendingActions */, 298 Context.DEVICE_ID_DEFAULT, null /* customIntent */); 299 } 300 startActivity(ActivityClientRecord r)301 private void startActivity(ActivityClientRecord r) { 302 mThread.handleStartActivity(r, null /* pendingActions */, null /* activityOptions */); 303 } 304 resumeActivity(ActivityClientRecord r)305 private void resumeActivity(ActivityClientRecord r) { 306 mThread.handleResumeActivity(r, true /* finalStateRequest */, 307 true /* isForward */, false /* shouldSendCompatFakeFocus */, "test"); 308 } 309 pauseActivity(ActivityClientRecord r)310 private void pauseActivity(ActivityClientRecord r) { 311 mThread.handlePauseActivity(r, false /* finished */, 312 false /* userLeaving */, false /* autoEnteringPip */, 313 null /* pendingActions */, "test"); 314 } 315 stopActivity(ActivityClientRecord r)316 private void stopActivity(ActivityClientRecord r) { 317 mThread.handleStopActivity(r, 318 new PendingTransactionActions(), false /* finalStateRequest */, "test"); 319 } 320 destroyActivity(ActivityClientRecord r)321 private void destroyActivity(ActivityClientRecord r) { 322 mThread.handleDestroyActivity(r, true /* finishing */, 323 false /* getNonConfigInstance */, "test"); 324 } 325 mockThread()326 private ActivityThread mockThread() { 327 spyOn(mThread); 328 return mThread; 329 } 330 stubActivityRecord()331 private ActivityClientRecord stubActivityRecord() { 332 ComponentName component = new ComponentName( 333 InstrumentationRegistry.getInstrumentation().getContext(), TestActivity.class); 334 ActivityInfo info = new ActivityInfo(); 335 info.packageName = component.getPackageName(); 336 info.name = component.getClassName(); 337 info.exported = true; 338 info.applicationInfo = new ApplicationInfo(); 339 info.applicationInfo.packageName = info.packageName; 340 info.applicationInfo.uid = UserHandle.myUserId(); 341 info.applicationInfo.sourceDir = "/test/sourceDir"; 342 343 // mock the function for preventing NPE in emulator environment. The purpose of the 344 // test is for activity client state changes, we don't focus on the updating for the 345 // ApplicationInfo. 346 LoadedApk packageInfo = mThread.peekPackageInfo(info.packageName, 347 true /* includeCode */); 348 spyOn(packageInfo); 349 doNothing().when(packageInfo).updateApplicationInfo(any(), any()); 350 351 return new ActivityClientRecord(mock(IBinder.class), Intent.makeMainActivity(component), 352 0 /* ident */, info, new Configuration(), null /* referrer */, 353 null /* voiceInteractor */, null /* state */, null /* persistentState */, 354 null /* pendingResults */, null /* pendingNewIntents */, 355 null /* activityOptions */, true /* isForward */, null /* profilerInfo */, 356 mThread /* client */, null /* asssitToken */, null /* shareableActivityToken */, 357 false /* launchedFromBubble */, null /* taskfragmentToken */, 358 null /* initialCallerInfoAccessToken */, new ActivityWindowInfo()); 359 } 360 361 @Override close()362 public void close() { 363 mMockSession.finishMocking(); 364 } 365 } 366 367 // Test activity 368 public static class TestActivity extends Activity { 369 } 370 } 371