1/*
2 * Copyright 2018 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
17package android.app.activity;
18
19import static android.content.Intent.ACTION_EDIT;
20import static android.content.Intent.ACTION_VIEW;
21
22import static org.junit.Assert.assertNull;
23import static org.junit.Assert.assertTrue;
24
25import android.app.Activity;
26import android.app.ActivityThread;
27import android.app.IApplicationThread;
28import android.app.servertransaction.ActivityRelaunchItem;
29import android.app.servertransaction.ClientTransaction;
30import android.app.servertransaction.ClientTransactionItem;
31import android.app.servertransaction.ResumeActivityItem;
32import android.app.servertransaction.StopActivityItem;
33import android.content.Intent;
34import android.os.IBinder;
35import android.support.test.InstrumentationRegistry;
36import android.support.test.filters.MediumTest;
37import android.support.test.rule.ActivityTestRule;
38import android.support.test.runner.AndroidJUnit4;
39import android.util.MergedConfiguration;
40
41import org.junit.Test;
42import org.junit.runner.RunWith;
43
44/**
45 * Test for verifying {@link android.app.ActivityThread} class.
46 * Build/Install/Run:
47 *  atest FrameworksCoreTests:android.app.activity.ActivityThreadTest
48 */
49@RunWith(AndroidJUnit4.class)
50@MediumTest
51public class ActivityThreadTest {
52
53    private final ActivityTestRule mActivityTestRule =
54            new ActivityTestRule(TestActivity.class, true /* initialTouchMode */,
55                    false /* launchActivity */);
56
57    @Test
58    public void testDoubleRelaunch() throws Exception {
59        final Activity activity = mActivityTestRule.launchActivity(new Intent());
60        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
61
62        appThread.scheduleTransaction(newRelaunchResumeTransaction(activity));
63        appThread.scheduleTransaction(newRelaunchResumeTransaction(activity));
64        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
65    }
66
67    @Test
68    public void testResumeAfterRelaunch() throws Exception {
69        final Activity activity = mActivityTestRule.launchActivity(new Intent());
70        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
71
72        appThread.scheduleTransaction(newRelaunchResumeTransaction(activity));
73        appThread.scheduleTransaction(newResumeTransaction(activity));
74        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
75    }
76
77    @Test
78    public void testSleepAndStop() throws Exception {
79        final Activity activity = mActivityTestRule.launchActivity(new Intent());
80        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
81
82        appThread.scheduleSleeping(activity.getActivityToken(), true /* sleeping */);
83        appThread.scheduleTransaction(newStopTransaction(activity));
84        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
85    }
86
87    /** Verify that repeated resume requests to activity will be ignored. */
88    @Test
89    public void testRepeatedResume() throws Exception {
90        final Activity activity = mActivityTestRule.launchActivity(new Intent());
91        final ActivityThread activityThread = activity.getActivityThread();
92        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
93            activityThread.executeTransaction(newResumeTransaction(activity));
94            assertNull(activityThread.performResumeActivity(activity.getActivityToken(),
95                    true /* finalStateRequest */, "test"));
96
97            assertNull(activityThread.performResumeActivity(activity.getActivityToken(),
98                    false /* finalStateRequest */, "test"));
99        });
100    }
101
102    /** Verify that custom intent set via Activity#setIntent() is preserved on relaunch. */
103    @Test
104    public void testCustomIntentPreservedOnRelaunch() throws Exception {
105        final Intent initIntent = new Intent();
106        initIntent.setAction(ACTION_VIEW);
107        final Activity activity = mActivityTestRule.launchActivity(initIntent);
108        IBinder token = activity.getActivityToken();
109
110        final ActivityThread activityThread = activity.getActivityThread();
111        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
112            // Recreate and check that intent is still the same.
113            activity.recreate();
114
115            final Activity newActivity = activityThread.getActivity(token);
116            assertTrue("Original intent must be preserved after recreate",
117                    initIntent.filterEquals(newActivity.getIntent()));
118
119            // Set custom intent, recreate and check if it is preserved.
120            final Intent customIntent = new Intent();
121            customIntent.setAction(ACTION_EDIT);
122            newActivity.setIntent(customIntent);
123
124            activity.recreate();
125
126            final Activity lastActivity = activityThread.getActivity(token);
127            assertTrue("Custom intent must be preserved after recreate",
128                    customIntent.filterEquals(lastActivity.getIntent()));
129        });
130    }
131
132    private static ClientTransaction newRelaunchResumeTransaction(Activity activity) {
133        final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(null,
134                null, 0, new MergedConfiguration(), false /* preserveWindow */);
135        final ResumeActivityItem resumeStateRequest =
136                ResumeActivityItem.obtain(true /* isForward */);
137
138        final ClientTransaction transaction = newTransaction(activity);
139        transaction.addCallback(callbackItem);
140        transaction.setLifecycleStateRequest(resumeStateRequest);
141
142        return transaction;
143    }
144
145    private static ClientTransaction newResumeTransaction(Activity activity) {
146        final ResumeActivityItem resumeStateRequest =
147                ResumeActivityItem.obtain(true /* isForward */);
148
149        final ClientTransaction transaction = newTransaction(activity);
150        transaction.setLifecycleStateRequest(resumeStateRequest);
151
152        return transaction;
153    }
154
155    private static ClientTransaction newStopTransaction(Activity activity) {
156        final StopActivityItem stopStateRequest =
157                StopActivityItem.obtain(false /* showWindow */, 0 /* configChanges */);
158
159        final ClientTransaction transaction = newTransaction(activity);
160        transaction.setLifecycleStateRequest(stopStateRequest);
161
162        return transaction;
163    }
164
165    private static ClientTransaction newTransaction(Activity activity) {
166        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
167        return ClientTransaction.obtain(appThread, activity.getActivityToken());
168    }
169
170    // Test activity
171    public static class TestActivity extends Activity {
172    }
173}
174