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