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