• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.server.wm;
18 
19 import static android.app.AppOpsManager.MODE_ERRORED;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
22 import static android.server.wm.ComponentNameUtils.getActivityName;
23 import static android.server.wm.backgroundactivity.common.CommonComponents.COMMON_FOREGROUND_ACTIVITY_EXTRAS;
24 
25 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
26 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
27 
28 import static com.google.common.truth.Truth.assertWithMessage;
29 
30 import static org.junit.Assert.assertEquals;
31 import static org.junit.Assert.assertNull;
32 
33 import android.content.ComponentName;
34 import android.content.Intent;
35 import android.content.ServiceConnection;
36 import android.os.UserManager;
37 import android.server.wm.WindowManagerState.Task;
38 import android.util.Log;
39 
40 import androidx.annotation.CallSuper;
41 
42 import com.android.compatibility.common.util.AppOpsUtils;
43 
44 import org.junit.After;
45 import org.junit.Before;
46 
47 import java.util.Arrays;
48 import java.util.List;
49 import java.util.stream.Collectors;
50 import java.util.stream.Stream;
51 
52 public abstract class BackgroundActivityTestBase extends ActivityManagerTestBase {
53 
54     private static final String TAG = BackgroundActivityTestBase.class.getSimpleName();
55 
56     static final String APP_A_PACKAGE = "android.server.wm.backgroundactivity.appa";
57     static final android.server.wm.backgroundactivity.appa.Components APP_A =
58             android.server.wm.backgroundactivity.appa.Components.get(APP_A_PACKAGE);
59     static final android.server.wm.backgroundactivity.appa.Components APP_A_33 =
60             android.server.wm.backgroundactivity.appa.Components.get(APP_A_PACKAGE + "33");
61 
62     static final String APP_B_PACKAGE = "android.server.wm.backgroundactivity.appb";
63     static final android.server.wm.backgroundactivity.appb.Components APP_B =
64             android.server.wm.backgroundactivity.appb.Components.get(APP_B_PACKAGE);
65     static final android.server.wm.backgroundactivity.appb.Components APP_B_33 =
66             android.server.wm.backgroundactivity.appb.Components.get(APP_B_PACKAGE + "33");
67 
68     static final List<android.server.wm.backgroundactivity.appa.Components> ALL_A =
69             List.of(APP_A, APP_A_33);
70     static final List<android.server.wm.backgroundactivity.appb.Components> ALL_B =
71             List.of(APP_B, APP_B_33);
72 
73     static final String SHELL_PACKAGE = "com.android.shell";
74     static final int ACTIVITY_FOCUS_TIMEOUT_MS = 3000;
75 
76     // TODO(b/258792202): Cleanup with feature flag
77     static final String NAMESPACE_WINDOW_MANAGER = "window_manager";
78 
79     ServiceConnection mBalServiceConnection;
80 
81     @Override
82     @Before
83     @CallSuper
setUp()84     public void setUp() throws Exception {
85         // disable SAW appopp for AppA (it's granted automatically when installed in CTS)
86         for (android.server.wm.backgroundactivity.appa.Components components : ALL_A) {
87             AppOpsUtils.setOpMode(components.APP_PACKAGE_NAME, "android:system_alert_window",
88                     MODE_ERRORED);
89             assertEquals(AppOpsUtils.getOpMode(components.APP_PACKAGE_NAME,
90                             "android:system_alert_window"),
91                     MODE_ERRORED);
92         }
93 
94         super.setUp();
95 
96         for (android.server.wm.backgroundactivity.appa.Components appA : ALL_A) {
97             assertNull(mWmState.getTaskByActivity(appA.BACKGROUND_ACTIVITY));
98             assertNull(mWmState.getTaskByActivity(appA.FOREGROUND_ACTIVITY));
99             runShellCommand("cmd deviceidle tempwhitelist -d 100000 "
100                     + appA.APP_PACKAGE_NAME);
101         }
102         for (android.server.wm.backgroundactivity.appb.Components appB : ALL_B) {
103             assertNull(mWmState.getTaskByActivity(appB.FOREGROUND_ACTIVITY));
104             runShellCommand("cmd deviceidle tempwhitelist -d 100000 "
105                     + appB.APP_PACKAGE_NAME);
106         }
107     }
108 
109     @After
tearDown()110     public void tearDown() throws Exception {
111         // We do this before anything else, because having an active device owner can prevent us
112         // from being able to force stop apps. (b/142061276)
113         for (android.server.wm.backgroundactivity.appa.Components appA : ALL_A) {
114             runWithShellPermissionIdentity(() -> {
115                 runShellCommand("dpm remove-active-admin --user 0 "
116                         + appA.SIMPLE_ADMIN_RECEIVER.flattenToString());
117                 if (UserManager.isHeadlessSystemUserMode()) {
118                     // Must also remove the PO from current user
119                     runShellCommand("dpm remove-active-admin --user cur "
120                             + appA.SIMPLE_ADMIN_RECEIVER.flattenToString());
121                 }
122             });
123             stopTestPackage(appA.APP_PACKAGE_NAME);
124             AppOpsUtils.reset(appA.APP_PACKAGE_NAME);
125 
126         }
127         for (android.server.wm.backgroundactivity.appb.Components appB : ALL_B) {
128             stopTestPackage(appB.APP_PACKAGE_NAME);
129         }
130         AppOpsUtils.reset(SHELL_PACKAGE);
131         if (mBalServiceConnection != null) {
132             mContext.unbindService(mBalServiceConnection);
133         }
134     }
135 
waitForActivityFocused(ComponentName componentName)136     boolean waitForActivityFocused(ComponentName componentName) {
137         return waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS, componentName);
138     }
139 
assertPinnedStackDoesNotExist()140     void assertPinnedStackDoesNotExist() {
141         mWmState.assertDoesNotContainStack("Must not contain pinned stack.",
142                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
143     }
assertTaskStackIsEmpty(ComponentName sourceComponent)144     void assertTaskStackIsEmpty(ComponentName sourceComponent) {
145         Task task = mWmState.getTaskByActivity(sourceComponent);
146         assertWithMessage("task for %s", sourceComponent.flattenToShortString()).that(task)
147                 .isNull();
148     }
149 
assertTaskStackHasComponents(ComponentName sourceComponent, ComponentName... expectedComponents)150     void assertTaskStackHasComponents(ComponentName sourceComponent,
151             ComponentName... expectedComponents) {
152         Task task = mWmState.getTaskByActivity(sourceComponent);
153         assertWithMessage("task for %s", sourceComponent.flattenToShortString()).that(task)
154                 .isNotNull();
155         Log.d(TAG, "Task for " + sourceComponent.flattenToShortString() + ": " + task
156                 + " Activities: " + task.mActivities);
157         List<String> actualNames = getActivityNames(task.mActivities);
158         List<String> expectedNames = Arrays.stream(expectedComponents)
159                 .map((c) -> c.flattenToShortString()).collect(Collectors.toList());
160 
161         assertWithMessage("task activities").that(actualNames)
162                 .containsExactlyElementsIn(expectedNames).inOrder();
163     }
164 
assertTaskDoesNotHaveVisibleComponents(ComponentName sourceComponent, ComponentName... expectedComponents)165     void assertTaskDoesNotHaveVisibleComponents(ComponentName sourceComponent,
166             ComponentName... expectedComponents) {
167         Task task = mWmState.getTaskByActivity(sourceComponent);
168         Log.d(TAG, "Task for " + sourceComponent.flattenToShortString() + ": " + task);
169         List<WindowManagerState.Activity> actual = getVisibleActivities(task.mActivities);
170         Log.v(TAG, "Task activities: all=" + task.mActivities + ", visible=" + actual);
171         if (actual == null) {
172             return;
173         }
174         List<String> actualNames = getActivityNames(actual);
175         List<String> expectedNames = Arrays.stream(expectedComponents)
176                 .map((c) -> c.flattenToShortString()).collect(Collectors.toList());
177 
178         assertWithMessage("task activities").that(actualNames).containsNoneIn(expectedNames);
179     }
180 
getVisibleActivities( List<WindowManagerState.Activity> activities)181     List<WindowManagerState.Activity> getVisibleActivities(
182             List<WindowManagerState.Activity> activities) {
183         return activities.stream().filter(WindowManagerState.Activity::isVisible)
184                 .collect(Collectors.toList());
185     }
186 
getActivityNames(List<WindowManagerState.Activity> activities)187     List<String> getActivityNames(List<WindowManagerState.Activity> activities) {
188         return activities.stream().map(a -> a.getName()).collect(Collectors.toList());
189     }
190 
getLaunchActivitiesBroadcast(android.server.wm.backgroundactivity.appa.Components appA, ComponentName... componentNames)191     Intent getLaunchActivitiesBroadcast(android.server.wm.backgroundactivity.appa.Components appA,
192             ComponentName... componentNames) {
193         Intent broadcastIntent = new Intent(
194                 appA.FOREGROUND_ACTIVITY_ACTIONS.LAUNCH_BACKGROUND_ACTIVITIES);
195         Intent[] intents = Stream.of(componentNames)
196                 .map(c -> {
197                     Intent intent = new Intent();
198                     intent.setComponent(c);
199                     return intent;
200                 })
201                 .toArray(Intent[]::new);
202         broadcastIntent.putExtra(appA.FOREGROUND_ACTIVITY_EXTRA.LAUNCH_INTENTS, intents);
203         return broadcastIntent;
204     }
205 
206     class ActivityStartVerifier {
207         private Intent mBroadcastIntent = new Intent();
208         private Intent mLaunchIntent = new Intent();
209 
setupTaskWithForegroundActivity( android.server.wm.backgroundactivity.appa.Components appA)210         ActivityStartVerifier setupTaskWithForegroundActivity(
211                 android.server.wm.backgroundactivity.appa.Components appA) {
212             setupTaskWithForegroundActivity(appA, -1);
213             return this;
214         }
215 
setupTaskWithForegroundActivity( android.server.wm.backgroundactivity.appa.Components appA, int id)216         ActivityStartVerifier setupTaskWithForegroundActivity(
217                 android.server.wm.backgroundactivity.appa.Components appA, int id) {
218             Intent intent = new Intent();
219             intent.setComponent(appA.FOREGROUND_ACTIVITY);
220             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
221             intent.putExtra(COMMON_FOREGROUND_ACTIVITY_EXTRAS.ACTIVITY_ID, id);
222             mContext.startActivity(intent);
223             mWmState.waitForValidState(appA.FOREGROUND_ACTIVITY);
224             return this;
225         }
226 
setupTaskWithEmbeddingActivity( android.server.wm.backgroundactivity.appa.Components appA)227         ActivityStartVerifier setupTaskWithEmbeddingActivity(
228                 android.server.wm.backgroundactivity.appa.Components appA) {
229             Intent intent = new Intent();
230             intent.setComponent(appA.FOREGROUND_EMBEDDING_ACTIVITY);
231             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
232             mContext.startActivity(intent);
233             mWmState.waitForValidState(appA.FOREGROUND_EMBEDDING_ACTIVITY);
234             return this;
235         }
236 
startFromForegroundActivity( android.server.wm.backgroundactivity.appa.Components appA)237         ActivityStartVerifier startFromForegroundActivity(
238                 android.server.wm.backgroundactivity.appa.Components appA) {
239             mBroadcastIntent.setAction(
240                     appA.FOREGROUND_ACTIVITY_ACTIONS.LAUNCH_BACKGROUND_ACTIVITIES);
241             return this;
242         }
243 
startFromForegroundActivity( android.server.wm.backgroundactivity.appb.Components appB)244         ActivityStartVerifier startFromForegroundActivity(
245                 android.server.wm.backgroundactivity.appb.Components appB) {
246             mBroadcastIntent.setAction(
247                     appB.FOREGROUND_ACTIVITY_ACTIONS.LAUNCH_BACKGROUND_ACTIVITIES);
248             return this;
249         }
250 
startFromForegroundActivity( android.server.wm.backgroundactivity.appa.Components appA, int id)251         ActivityStartVerifier startFromForegroundActivity(
252                 android.server.wm.backgroundactivity.appa.Components appA, int id) {
253             startFromForegroundActivity(appA);
254             mBroadcastIntent.putExtra(COMMON_FOREGROUND_ACTIVITY_EXTRAS.ACTIVITY_ID, id);
255             return this;
256         }
257 
startFromForegroundActivity( android.server.wm.backgroundactivity.appb.Components appB, int id)258         ActivityStartVerifier startFromForegroundActivity(
259                 android.server.wm.backgroundactivity.appb.Components appB, int id) {
260             startFromForegroundActivity(appB);
261             mBroadcastIntent.putExtra(COMMON_FOREGROUND_ACTIVITY_EXTRAS.ACTIVITY_ID, id);
262             return this;
263         }
264 
startFromEmbeddingActivity( android.server.wm.backgroundactivity.appa.Components appA)265         ActivityStartVerifier startFromEmbeddingActivity(
266                 android.server.wm.backgroundactivity.appa.Components appA) {
267             mBroadcastIntent.setAction(
268                     appA.FOREGROUND_EMBEDDING_ACTIVITY_ACTIONS.LAUNCH_EMBEDDED_ACTIVITY);
269             return this;
270         }
271 
withBroadcastExtra(String key, boolean value)272         ActivityStartVerifier withBroadcastExtra(String key, boolean value) {
273             mBroadcastIntent.putExtra(key, value);
274             return this;
275         }
276 
activity(ComponentName to)277         ActivityStartVerifier activity(ComponentName to) {
278             mLaunchIntent.setComponent(to);
279             mBroadcastIntent.putExtra(COMMON_FOREGROUND_ACTIVITY_EXTRAS.LAUNCH_INTENTS,
280                     new Intent[]{mLaunchIntent});
281             return this;
282         }
283 
activity(ComponentName to, int id)284         ActivityStartVerifier activity(ComponentName to, int id) {
285             activity(to);
286             mLaunchIntent.putExtra(COMMON_FOREGROUND_ACTIVITY_EXTRAS.ACTIVITY_ID, id);
287             return this;
288         }
289 
290         /**
291          * Broadcasts the specified intents, asserts that the launch succeeded or failed, then
292          * resets all ActivityStartVerifier state (i.e - intent component and flags) so the
293          * ActivityStartVerifier can be reused.
294          */
executeAndAssertLaunch(boolean succeeds)295         ActivityStartVerifier executeAndAssertLaunch(boolean succeeds) {
296             mContext.sendBroadcast(mBroadcastIntent);
297 
298             ComponentName launchedComponent = mLaunchIntent.getComponent();
299             mWmState.waitForValidState(launchedComponent);
300             boolean result = waitForActivityFocused(launchedComponent);
301             assertEquals("Activity: " + launchedComponent.flattenToShortString() + " launch ",
302                     succeeds, result);
303 
304             // Reset intents to remove any added flags
305             reset();
306             return this;
307         }
308 
reset()309         void reset() {
310             mBroadcastIntent = new Intent();
311             mLaunchIntent = new Intent();
312         }
313 
thenAssert(Runnable run)314         ActivityStartVerifier thenAssert(Runnable run) {
315             run.run();
316             return this;
317         }
318 
thenAssertTaskStack(ComponentName... expectedComponents)319         ActivityStartVerifier thenAssertTaskStack(ComponentName... expectedComponents) {
320             assertTaskStackHasComponents(expectedComponents[expectedComponents.length - 1],
321                     expectedComponents);
322             return this;
323         }
324 
325         /**
326          * <pre>
327          * | expectedRootActivity | expectedEmbeddedActivities |
328          * |  fragment 1 - left   |     fragment 0 - right     |
329          * |----------------------|----------------------------|
330          * |                      |             A4             |  top
331          * |                      |             A3             |
332          * |          A1          |             A2             |  bottom
333          * </pre>
334          * @param expectedEmbeddedActivities The expected activities on the right side of the split
335          *                                   (fragment 0), top to bottom
336          * @param expectedRootActivity The expected activity on the left side of the split
337          *                             (fragment 1)
338          */
thenAssertEmbeddingTaskStack( ComponentName[] expectedEmbeddedActivities, ComponentName expectedRootActivity)339         ActivityStartVerifier thenAssertEmbeddingTaskStack(
340                 ComponentName[] expectedEmbeddedActivities, ComponentName expectedRootActivity) {
341             List<WindowManagerState.TaskFragment> fragments = mWmState.getTaskByActivity(
342                     expectedRootActivity).getTaskFragments();
343             assertEquals(2, fragments.size());
344 
345             List<WindowManagerState.Activity> embeddedActivities = fragments.get(0).getActivities();
346             List<WindowManagerState.Activity> rootActivity = fragments.get(1).getActivities();
347 
348             assertEquals(1, rootActivity.size());
349             assertEquals(expectedRootActivity.flattenToShortString(),
350                     rootActivity.get(0).getName());
351 
352             assertEquals(expectedEmbeddedActivities.length, embeddedActivities.size());
353             for (int i = 0; i < expectedEmbeddedActivities.length; i++) {
354                 assertEquals(expectedEmbeddedActivities[i].flattenToShortString(),
355                         embeddedActivities.get(i).getName());
356             }
357             return this;
358         }
359     }
360 
361 
assertActivityFocused(ComponentName componentName)362     protected void assertActivityFocused(ComponentName componentName) {
363         assertActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS, componentName);
364     }
365 
assertActivityNotFocused(ComponentName componentName)366     protected void assertActivityNotFocused(ComponentName componentName) {
367         assertActivityNotFocused(ACTIVITY_FOCUS_TIMEOUT_MS, componentName);
368     }
369 
assertActivityFocused(ComponentName componentName, String message)370     protected void assertActivityFocused(ComponentName componentName, String message) {
371         assertActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS, componentName, message);
372     }
373 
assertActivityNotFocused(ComponentName componentName, String message)374     protected void assertActivityNotFocused(ComponentName componentName, String message) {
375         assertActivityNotFocused(ACTIVITY_FOCUS_TIMEOUT_MS, componentName, message);
376     }
377 
378     /** Asserts the activity is focused before timeout. */
assertActivityFocused(int timeoutMs, ComponentName componentName)379     protected void assertActivityFocused(int timeoutMs, ComponentName componentName) {
380         assertActivityFocused(timeoutMs, componentName,
381                 "activity should be focused within " + timeoutMs + "ms");
382     }
383 
384     /** Asserts the activity is not focused until timeout. */
assertActivityNotFocused(int timeoutMs, ComponentName componentName)385     protected void assertActivityNotFocused(int timeoutMs, ComponentName componentName) {
386         assertActivityNotFocused(timeoutMs, componentName,
387                 "activity should not be focused within " + timeoutMs + "ms");
388     }
389 
390     /** Asserts the activity is focused before timeout. */
assertActivityFocused(int timeoutMs, ComponentName componentName, String message)391     protected void assertActivityFocused(int timeoutMs, ComponentName componentName,
392             String message) {
393         waitForActivityResumed(timeoutMs, componentName);
394         assertWithMessage(message).that(mWmState.getFocusedActivity()).isEqualTo(
395                 getActivityName(componentName));
396     }
397 
398     /** Asserts the activity is not focused until timeout. */
assertActivityNotFocused(int timeoutMs, ComponentName componentName, String message)399     protected void assertActivityNotFocused(int timeoutMs, ComponentName componentName,
400             String message) {
401         waitForActivityResumed(timeoutMs, componentName);
402         assertWithMessage(message).that(mWmState.getFocusedActivity())
403                 .isNotEqualTo(getActivityName(componentName));
404     }
405 
406 }
407