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 import static android.server.wm.backgroundactivity.common.CommonComponents.TEST_SERVICE;
25 
26 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
27 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
28 
29 import static com.google.common.truth.Truth.assertWithMessage;
30 
31 import static org.junit.Assert.assertEquals;
32 import static org.junit.Assert.assertNull;
33 import static org.junit.Assert.assertTrue;
34 
35 import android.app.PendingIntent;
36 import android.content.ComponentName;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.os.Build;
40 import android.os.UserManager;
41 import android.server.wm.WindowManagerState.Task;
42 import android.server.wm.backgroundactivity.appa.Components;
43 import android.server.wm.backgroundactivity.common.ITestService;
44 import android.util.Log;
45 
46 import androidx.annotation.CallSuper;
47 
48 import com.android.compatibility.common.util.AppOpsUtils;
49 import com.android.compatibility.common.util.DeviceConfigStateHelper;
50 
51 import org.junit.After;
52 import org.junit.Before;
53 
54 import java.time.Duration;
55 import java.util.Arrays;
56 import java.util.HashMap;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.stream.Collectors;
60 import java.util.stream.Stream;
61 
62 public abstract class BackgroundActivityTestBase extends ActivityManagerTestBase {
63 
64     private static final String TAG = BackgroundActivityTestBase.class.getSimpleName();
65 
66     static final String APP_A_PACKAGE = "android.server.wm.backgroundactivity.appa";
67     static final Components APP_A = Components.get(APP_A_PACKAGE);
68     static final Components APP_A_33 = Components.get(APP_A_PACKAGE + "33");
69 
70     static final String APP_B_PACKAGE = "android.server.wm.backgroundactivity.appb";
71     static final Components APP_B = Components.get(APP_B_PACKAGE);
72     static final Components APP_B_33 = Components.get(APP_B_PACKAGE + "33");
73 
74     static final String APP_C_PACKAGE = "android.server.wm.backgroundactivity.appc";
75     static final Components APP_C = Components.get(APP_C_PACKAGE);
76     static final Components APP_C_33 = Components.get(APP_C_PACKAGE + "33");
77     static final Components APP_ASM_OPT_IN =
78             Components.get("android.server.wm.backgroundactivity.appasmoptin");
79 
80     static final String APP_ASM_OPT_OUT_PACKAGE =
81             "android.server.wm.backgroundactivity.appasmoptout";
82     static final Components APP_ASM_OPT_OUT = Components.get(APP_ASM_OPT_OUT_PACKAGE);
83 
84     static final List<Components> ALL_APPS =
85             List.of(APP_A, APP_A_33, APP_B, APP_B_33, APP_C, APP_C_33);
86 
87     static final String SHELL_PACKAGE = "com.android.shell";
88     // This can be long as the activity should start
89     static final Duration ACTIVITY_FOCUS_TIMEOUT = Duration.ofSeconds(10);
90     // Here we don't expect the activity to start, so we always have to wait. Keep this short.
91     static final Duration ACTIVITY_NOT_FOCUS_TIMEOUT = Duration.ofSeconds(3);
92 
93     // TODO(b/258792202): Cleanup with feature flag
94     static final String NAMESPACE_WINDOW_MANAGER = "window_manager";
95     static final String ASM_RESTRICTIONS_ENABLED =
96             "ActivitySecurity__asm_restrictions_enabled";
97     private static final int TEST_SERVICE_SETUP_TIMEOUT_MS = 2000;
98     final DeviceConfigStateHelper mDeviceConfig =
99             new DeviceConfigStateHelper(NAMESPACE_WINDOW_MANAGER);
100 
101     private final Map<ComponentName, FutureConnection<ITestService>> mServiceConnections =
102             new HashMap<>();
103 
104     @Before
enableFeatureFlags()105     public void enableFeatureFlags() {
106         if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
107             mDeviceConfig.set(ASM_RESTRICTIONS_ENABLED, "1");
108         }
109     }
110 
111     @After
disableFeatureFlags()112     public void disableFeatureFlags() throws Exception {
113         if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
114             mDeviceConfig.close();
115         } else {
116             try {
117                 mDeviceConfig.close();
118             } catch (Exception e) {
119                 Log.w(TAG, "Failed to tear down feature flags.", e);
120             }
121         }
122     }
123 
124     @Override
125     @Before
126     @CallSuper
setUp()127     public void setUp() throws Exception {
128         // disable SAW appopp (it's granted automatically when installed in CTS)
129         for (Components components : ALL_APPS) {
130             AppOpsUtils.setOpMode(components.APP_PACKAGE_NAME, "android:system_alert_window",
131                     MODE_ERRORED);
132             assertEquals(AppOpsUtils.getOpMode(components.APP_PACKAGE_NAME,
133                             "android:system_alert_window"),
134                     MODE_ERRORED);
135         }
136 
137         super.setUp();
138 
139         for (Components app : ALL_APPS) {
140             assertNull(mWmState.getTaskByActivity(app.BACKGROUND_ACTIVITY));
141             assertNull(mWmState.getTaskByActivity(app.FOREGROUND_ACTIVITY));
142             runShellCommand("cmd deviceidle tempwhitelist -d 100000 "
143                     + app.APP_PACKAGE_NAME);
144         }
145     }
146 
147     @After
tearDown()148     public void tearDown() throws Exception {
149         // We do this before anything else, because having an active device owner can prevent us
150         // from being able to force stop apps. (b/142061276)
151         for (Components app : ALL_APPS) {
152             runWithShellPermissionIdentity(() -> {
153                 runShellCommand("dpm remove-active-admin --user 0 "
154                         + app.SIMPLE_ADMIN_RECEIVER.flattenToString());
155                 if (UserManager.isHeadlessSystemUserMode()) {
156                     // Must also remove the PO from current user
157                     runShellCommand("dpm remove-active-admin --user cur "
158                             + app.SIMPLE_ADMIN_RECEIVER.flattenToString());
159                 }
160             });
161             stopTestPackage(app.APP_PACKAGE_NAME);
162             AppOpsUtils.reset(app.APP_PACKAGE_NAME);
163 
164         }
165         AppOpsUtils.reset(SHELL_PACKAGE);
166         for (FutureConnection<ITestService> fc : mServiceConnections.values()) {
167             mContext.unbindService(fc);
168         }
169     }
170 
assertPinnedStackDoesNotExist()171     void assertPinnedStackDoesNotExist() {
172         mWmState.assertDoesNotContainStack("Must not contain pinned stack.",
173                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
174     }
assertTaskStackIsEmpty(ComponentName sourceComponent)175     void assertTaskStackIsEmpty(ComponentName sourceComponent) {
176         Task task = mWmState.getTaskByActivity(sourceComponent);
177         assertWithMessage("task for %s", sourceComponent.flattenToShortString()).that(task)
178                 .isNull();
179     }
180 
assertTaskStackHasComponents(ComponentName sourceComponent, ComponentName... expectedComponents)181     void assertTaskStackHasComponents(ComponentName sourceComponent,
182             ComponentName... expectedComponents) {
183         Task task = mWmState.getTaskByActivity(sourceComponent);
184         assertWithMessage("task for %s", sourceComponent.flattenToShortString()).that(task)
185                 .isNotNull();
186         Log.d(TAG, "Task for " + sourceComponent.flattenToShortString() + ": " + task
187                 + " Activities: " + task.mActivities);
188         List<String> actualNames = getActivityNames(task.mActivities);
189         List<String> expectedNames = Arrays.stream(expectedComponents)
190                 .map((c) -> c.flattenToShortString()).collect(Collectors.toList());
191 
192         assertWithMessage("task activities").that(actualNames)
193                 .containsExactlyElementsIn(expectedNames).inOrder();
194     }
195 
assertTaskDoesNotHaveVisibleComponents(ComponentName sourceComponent, ComponentName... expectedComponents)196     void assertTaskDoesNotHaveVisibleComponents(ComponentName sourceComponent,
197             ComponentName... expectedComponents) {
198         Task task = mWmState.getTaskByActivity(sourceComponent);
199         Log.d(TAG, "Task for " + sourceComponent.flattenToShortString() + ": " + task);
200         List<WindowManagerState.Activity> actual = getVisibleActivities(task.mActivities);
201         Log.v(TAG, "Task activities: all=" + task.mActivities + ", visible=" + actual);
202         if (actual == null) {
203             return;
204         }
205         List<String> actualNames = getActivityNames(actual);
206         List<String> expectedNames = Arrays.stream(expectedComponents)
207                 .map((c) -> c.flattenToShortString()).collect(Collectors.toList());
208 
209         assertWithMessage("task activities").that(actualNames).containsNoneIn(expectedNames);
210     }
211 
getVisibleActivities( List<WindowManagerState.Activity> activities)212     List<WindowManagerState.Activity> getVisibleActivities(
213             List<WindowManagerState.Activity> activities) {
214         return activities.stream().filter(WindowManagerState.Activity::isVisible)
215                 .collect(Collectors.toList());
216     }
217 
getActivityNames(List<WindowManagerState.Activity> activities)218     List<String> getActivityNames(List<WindowManagerState.Activity> activities) {
219         return activities.stream().map(a -> a.getName()).collect(Collectors.toList());
220     }
221 
getLaunchActivitiesBroadcast(Components app, ComponentName... componentNames)222     Intent getLaunchActivitiesBroadcast(Components app,
223             ComponentName... componentNames) {
224         Intent broadcastIntent = new Intent(
225                 app.FOREGROUND_ACTIVITY_ACTIONS.LAUNCH_BACKGROUND_ACTIVITIES);
226         Intent[] intents = Stream.of(componentNames)
227                 .map(c -> {
228                     Intent intent = new Intent();
229                     intent.setComponent(c);
230                     return intent;
231                 })
232                 .toArray(Intent[]::new);
233         broadcastIntent.putExtra(app.FOREGROUND_ACTIVITY_EXTRA.LAUNCH_INTENTS, intents);
234         return broadcastIntent;
235     }
236 
getLaunchActivitiesBroadcast(Components app, PendingIntent... pendingIntents)237     Intent getLaunchActivitiesBroadcast(Components app,
238             PendingIntent... pendingIntents) {
239         Intent broadcastIntent = new Intent(
240                 app.FOREGROUND_ACTIVITY_ACTIONS.LAUNCH_BACKGROUND_ACTIVITIES);
241         broadcastIntent.putExtra(app.FOREGROUND_ACTIVITY_EXTRA.LAUNCH_PENDING_INTENTS,
242                 pendingIntents);
243         return broadcastIntent;
244     }
245 
getLaunchAndFinishActivitiesBroadcast(Components app, PendingIntent... pendingIntents)246     Intent getLaunchAndFinishActivitiesBroadcast(Components app, PendingIntent... pendingIntents) {
247         Intent broadcastIntent = new Intent(
248                 app.FOREGROUND_ACTIVITY_ACTIONS.LAUNCH_BACKGROUND_ACTIVITIES);
249         broadcastIntent.putExtra(app.FOREGROUND_ACTIVITY_EXTRA.LAUNCH_PENDING_INTENTS,
250                 pendingIntents);
251         broadcastIntent.putExtra(app.FOREGROUND_ACTIVITY_EXTRA.LAUNCH_FOR_RESULT_AND_FINISH, true);
252         return broadcastIntent;
253     }
254 
255     class ActivityStartVerifier {
256         private Intent mBroadcastIntent = new Intent();
257         private Intent mLaunchIntent = new Intent();
258 
setupTaskWithForegroundActivity(Components app)259         ActivityStartVerifier setupTaskWithForegroundActivity(Components app) {
260             setupTaskWithForegroundActivity(app, -1);
261             return this;
262         }
263 
setupTaskWithForegroundActivity(Components app, int id)264         ActivityStartVerifier setupTaskWithForegroundActivity(Components app, int id) {
265             Intent intent = new Intent();
266             intent.setComponent(app.FOREGROUND_ACTIVITY);
267             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
268             intent.putExtra(COMMON_FOREGROUND_ACTIVITY_EXTRAS.ACTIVITY_ID, id);
269             mContext.startActivity(intent);
270             mWmState.waitForValidState(app.FOREGROUND_ACTIVITY);
271             return this;
272         }
273 
setupTaskWithEmbeddingActivity(Components app)274         ActivityStartVerifier setupTaskWithEmbeddingActivity(Components app) {
275             Intent intent = new Intent();
276             intent.setComponent(app.FOREGROUND_EMBEDDING_ACTIVITY);
277             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
278             mContext.startActivity(intent);
279             mWmState.waitForValidState(app.FOREGROUND_EMBEDDING_ACTIVITY);
280             return this;
281         }
282 
startFromForegroundActivity(Components app)283         ActivityStartVerifier startFromForegroundActivity(Components app) {
284             mBroadcastIntent.setAction(
285                     app.FOREGROUND_ACTIVITY_ACTIONS.LAUNCH_BACKGROUND_ACTIVITIES);
286             return this;
287         }
288 
startFromForegroundActivity(Components app, int id)289         ActivityStartVerifier startFromForegroundActivity(Components app, int id) {
290             startFromForegroundActivity(app);
291             mBroadcastIntent.putExtra(COMMON_FOREGROUND_ACTIVITY_EXTRAS.ACTIVITY_ID, id);
292             return this;
293         }
294 
startFromEmbeddingActivity(Components app)295         ActivityStartVerifier startFromEmbeddingActivity(Components app) {
296             mBroadcastIntent.setAction(
297                     app.FOREGROUND_EMBEDDING_ACTIVITY_ACTIONS.LAUNCH_EMBEDDED_ACTIVITY);
298             return this;
299         }
300 
withBroadcastExtra(String key, boolean value)301         ActivityStartVerifier withBroadcastExtra(String key, boolean value) {
302             mBroadcastIntent.putExtra(key, value);
303             return this;
304         }
305 
activity(ComponentName to)306         ActivityStartVerifier activity(ComponentName to) {
307             mLaunchIntent.setComponent(to);
308             mBroadcastIntent.putExtra(COMMON_FOREGROUND_ACTIVITY_EXTRAS.LAUNCH_INTENTS,
309                     new Intent[]{mLaunchIntent});
310             return this;
311         }
312 
activity(ComponentName to, int id)313         ActivityStartVerifier activity(ComponentName to, int id) {
314             activity(to);
315             mLaunchIntent.putExtra(COMMON_FOREGROUND_ACTIVITY_EXTRAS.ACTIVITY_ID, id);
316             return this;
317         }
318 
activityIntoNewTask(ComponentName to)319         ActivityStartVerifier activityIntoNewTask(ComponentName to) {
320             activity(to);
321             mLaunchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
322             return this;
323         }
324 
allowCrossUidLaunch()325         ActivityStartVerifier allowCrossUidLaunch() {
326             mLaunchIntent.putExtra(COMMON_FOREGROUND_ACTIVITY_EXTRAS.ALLOW_CROSS_UID, true);
327             return this;
328         }
329 
330         /**
331          * Broadcasts the specified intents, asserts that the launch succeeded or failed, then
332          * resets all ActivityStartVerifier state (i.e - intent component and flags) so the
333          * ActivityStartVerifier can be reused.
334          */
executeAndAssertLaunch(boolean succeeds)335         ActivityStartVerifier executeAndAssertLaunch(boolean succeeds) {
336             mContext.sendBroadcast(mBroadcastIntent);
337 
338             ComponentName launchedComponent = mLaunchIntent.getComponent();
339             mWmState.waitForValidState(launchedComponent);
340             if (succeeds) {
341                 assertActivityFocused(launchedComponent);
342             } else {
343                 assertActivityNotFocused(launchedComponent);
344             }
345 
346             // Reset intents to remove any added flags
347             reset();
348             return this;
349         }
350 
reset()351         void reset() {
352             mBroadcastIntent = new Intent();
353             mLaunchIntent = new Intent();
354         }
355 
thenAssert(Runnable run)356         ActivityStartVerifier thenAssert(Runnable run) {
357             run.run();
358             return this;
359         }
360 
thenAssertTaskStack(ComponentName... expectedComponents)361         ActivityStartVerifier thenAssertTaskStack(ComponentName... expectedComponents) {
362             assertTaskStackHasComponents(expectedComponents[expectedComponents.length - 1],
363                     expectedComponents);
364             return this;
365         }
366 
367         /**
368          * <pre>
369          * | expectedRootActivity | expectedEmbeddedActivities |
370          * |  fragment 1 - left   |     fragment 0 - right     |
371          * |----------------------|----------------------------|
372          * |                      |             A4             |  top
373          * |                      |             A3             |
374          * |          A1          |             A2             |  bottom
375          * </pre>
376          * @param expectedEmbeddedActivities The expected activities on the right side of the split
377          *                                   (fragment 0), top to bottom
378          * @param expectedRootActivity The expected activity on the left side of the split
379          *                             (fragment 1)
380          */
thenAssertEmbeddingTaskStack( ComponentName[] expectedEmbeddedActivities, ComponentName expectedRootActivity)381         ActivityStartVerifier thenAssertEmbeddingTaskStack(
382                 ComponentName[] expectedEmbeddedActivities, ComponentName expectedRootActivity) {
383             List<WindowManagerState.TaskFragment> fragments = mWmState.getTaskByActivity(
384                     expectedRootActivity).getTaskFragments();
385             assertEquals(2, fragments.size());
386 
387             List<WindowManagerState.Activity> embeddedActivities = fragments.get(0).getActivities();
388             List<WindowManagerState.Activity> rootActivity = fragments.get(1).getActivities();
389 
390             assertEquals(1, rootActivity.size());
391             assertEquals(expectedRootActivity.flattenToShortString(),
392                     rootActivity.get(0).getName());
393 
394             assertEquals(expectedEmbeddedActivities.length, embeddedActivities.size());
395             for (int i = 0; i < expectedEmbeddedActivities.length; i++) {
396                 assertEquals(expectedEmbeddedActivities[i].flattenToShortString(),
397                         embeddedActivities.get(i).getName());
398             }
399             return this;
400         }
401     }
402 
403 
assertActivityFocused(ComponentName componentName)404     protected void assertActivityFocused(ComponentName componentName) {
405         assertActivityFocused(ACTIVITY_FOCUS_TIMEOUT, componentName);
406     }
407 
assertActivityNotFocused(ComponentName componentName)408     protected void assertActivityNotFocused(ComponentName componentName) {
409         assertActivityNotFocused(ACTIVITY_NOT_FOCUS_TIMEOUT, componentName);
410     }
411 
assertActivityFocused(ComponentName componentName, String message)412     protected void assertActivityFocused(ComponentName componentName, String message) {
413         assertActivityFocused(ACTIVITY_FOCUS_TIMEOUT, componentName, message);
414     }
415 
assertActivityNotFocused(ComponentName componentName, String message)416     protected void assertActivityNotFocused(ComponentName componentName, String message) {
417         assertActivityNotFocused(ACTIVITY_NOT_FOCUS_TIMEOUT, componentName, message);
418     }
419 
420     /** Asserts the activity is focused before timeout. */
assertActivityFocused(Duration timeout, ComponentName componentName)421     protected void assertActivityFocused(Duration timeout, ComponentName componentName) {
422         assertActivityFocused(timeout, componentName,
423                 "activity should be focused within " + timeout);
424     }
425 
426     /** Asserts the activity is not focused until timeout. */
assertActivityNotFocused(Duration timeout, ComponentName componentName)427     protected void assertActivityNotFocused(Duration timeout, ComponentName componentName) {
428         assertActivityNotFocused(timeout, componentName,
429                 "activity should not be focused within " + timeout);
430     }
431 
waitForActivityResumed(Duration timeout, ComponentName componentName)432     private void waitForActivityResumed(Duration timeout, ComponentName componentName) {
433         waitForActivityResumed((int) timeout.toMillis(), componentName);
434     }
435 
436     /** Asserts the activity is focused before timeout. */
assertActivityFocused(Duration timeout, ComponentName componentName, String message)437     protected void assertActivityFocused(Duration timeout, ComponentName componentName,
438             String message) {
439         waitForActivityResumed(timeout, componentName);
440         assertWithMessage(message).that(mWmState.getFocusedActivity()).isEqualTo(
441                 getActivityName(componentName));
442     }
443 
444     /** Asserts the activity is not focused until timeout. */
assertActivityNotFocused(Duration timeout, ComponentName componentName, String message)445     protected void assertActivityNotFocused(Duration timeout, ComponentName componentName,
446             String message) {
447         waitForActivityResumed(timeout, componentName);
448         assertWithMessage(message).that(mWmState.getFocusedActivity())
449                 .isNotEqualTo(getActivityName(componentName));
450     }
451 
getTestService(Components c)452     protected TestServiceClient getTestService(Components c) throws Exception {
453         return getTestService(new ComponentName(c.APP_PACKAGE_NAME, TEST_SERVICE));
454     }
455 
getTestService(ComponentName componentName)456     private TestServiceClient getTestService(ComponentName componentName) throws Exception {
457         FutureConnection<ITestService> futureConnection = mServiceConnections.get(componentName);
458         if (futureConnection == null) {
459             // need to setup new test service connection for the component
460             Intent bindIntent = new Intent();
461             bindIntent.setComponent(componentName);
462             futureConnection = new FutureConnection<>(ITestService.Stub::asInterface);
463             mServiceConnections.put(componentName, futureConnection);
464             boolean success = mContext.bindService(bindIntent, futureConnection,
465                     Context.BIND_AUTO_CREATE);
466             assertTrue("Failed to setup " + componentName.toString(), success);
467         }
468         return new TestServiceClient(futureConnection.get(TEST_SERVICE_SETUP_TIMEOUT_MS));
469     }
470 
471 }
472