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