• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 com.android.bedstead.nene.activities;
18 
19 import static android.Manifest.permission.REAL_GET_TASKS;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
23 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
24 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
25 import static android.os.Build.VERSION_CODES.Q;
26 import static android.os.Build.VERSION_CODES.S;
27 
28 import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_ACTIVITY_STACKS;
29 import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_ACTIVITY_TASKS;
30 
31 import android.annotation.TargetApi;
32 import android.app.ActivityManager;
33 import android.app.ActivityTaskManager;
34 import android.content.ComponentName;
35 import android.view.Display;
36 
37 import androidx.annotation.Nullable;
38 
39 import com.android.bedstead.nene.TestApis;
40 import com.android.bedstead.nene.annotations.Experimental;
41 import com.android.bedstead.nene.exceptions.AdbException;
42 import com.android.bedstead.nene.exceptions.NeneException;
43 import com.android.bedstead.nene.packages.ComponentReference;
44 import com.android.bedstead.nene.permissions.PermissionContext;
45 import com.android.bedstead.nene.utils.ShellCommand;
46 import com.android.bedstead.nene.utils.Versions;
47 
48 import java.lang.reflect.InvocationTargetException;
49 import java.lang.reflect.Method;
50 import java.util.List;
51 import java.util.stream.Collectors;
52 
53 public final class Activities {
54 
55     public static final Activities sInstance = new Activities();
56 
Activities()57     private Activities() {
58     }
59 
60 
61     /**
62      * Wrap the given {@link NeneActivity} subclass to use Nene APIs.
63      */
wrap(Class<E> clazz, E activity)64     public <E extends NeneActivity> Activity<E> wrap(Class<E> clazz, E activity) {
65         return new Activity<>(activity, activity);
66     }
67 
68     /**
69      * Wrap the given {@link android.app.Activity} to use Nene APIs.
70      */
wrap(android.app.Activity activity)71     public LocalActivity wrap(android.app.Activity activity) {
72         return new LocalActivity(activity);
73     }
74 
75     /**
76      * Get the {@link ComponentReference} instances for each activity at the top of a recent task.
77      *
78      * <p>This is ordered from most recent to least recent and only includes tasks on the
79      * default display.
80      */
81     @Experimental
82     @TargetApi(Q)
recentActivities()83     public List<ComponentReference> recentActivities() {
84         Versions.requireMinimumVersion(Q);
85 
86         try (PermissionContext p = TestApis.permissions().withPermission(REAL_GET_TASKS)) {
87             ActivityManager activityManager =
88                     TestApis.context().instrumentedContext().getSystemService(
89                             ActivityManager.class);
90             return activityManager.getRunningTasks(100).stream()
91                     .filter(r -> getDisplayId(r) == Display.DEFAULT_DISPLAY)
92                     .map(r -> new ComponentReference(r.topActivity))
93                     .collect(Collectors.toList());
94         }
95     }
96 
getDisplayId(ActivityManager.RunningTaskInfo task)97     private int getDisplayId(ActivityManager.RunningTaskInfo task) {
98         if (Versions.meetsMinimumSdkVersionRequirement(Versions.U)) {
99             return task.getDisplayId();
100         }
101 
102         return Display.DEFAULT_DISPLAY;
103     }
104 
105     /**
106      * Get the {@link ComponentReference} of the activity currently in the foreground of the default
107      * display.
108      */
109     @Experimental
110     @Nullable
foregroundActivity()111     public ComponentReference foregroundActivity() {
112         if (!Versions.meetsMinimumSdkVersionRequirement(Q)) {
113             return foregroundActivityPreQ();
114         }
115         return recentActivities().stream().findFirst().orElse(null);
116     }
117 
foregroundActivityPreQ()118     private ComponentReference foregroundActivityPreQ() {
119         try {
120             return ShellCommand.builder("dumpsys activity top")
121                     .executeAndParseOutput((dumpsysOutput) -> {
122                         // The final ACTIVITY is the one on top
123                         String[] activitySplits = dumpsysOutput.split("ACTIVITY ");
124                         String component = activitySplits[activitySplits.length - 1]
125                                 .split(" ", 2)[0];
126                         ComponentName componentName = ComponentName.unflattenFromString(component);
127                         return new ComponentReference(componentName);
128                     });
129         } catch (AdbException | RuntimeException e) {
130             throw new NeneException("Error getting foreground activity pre Q", e);
131         }
132     }
133 
134     /**
135      * Return the current state of task locking. The three possible outcomes
136      * are {@link ActivityManager#LOCK_TASK_MODE_NONE},
137      * {@link ActivityManager#LOCK_TASK_MODE_LOCKED}
138      * and {@link ActivityManager#LOCK_TASK_MODE_PINNED}.
139      */
140     @Experimental
getLockTaskModeState()141     public int getLockTaskModeState() {
142         ActivityManager activityManager =
143                 TestApis.context().instrumentedContext().getSystemService(
144                         ActivityManager.class);
145 
146         return activityManager.getLockTaskModeState();
147     }
148 
149     private final int[] ALL_ACTIVITY_TYPE_BUT_HOME = {
150             ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS,
151             ACTIVITY_TYPE_DREAM, ACTIVITY_TYPE_UNDEFINED
152     };
153 
154     /**
155      * Clear activities.
156      */
157     @Experimental
clearAllActivities()158     public void clearAllActivities() {
159         if (Versions.meetsMinimumSdkVersionRequirement(S)) {
160             try (PermissionContext p = TestApis.permissions().withPermission(
161                     MANAGE_ACTIVITY_TASKS)) {
162                 TestApis.context().instrumentedContext().getSystemService(ActivityTaskManager.class)
163                         .removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
164             }
165         } else {
166             try (PermissionContext p = TestApis.permissions().withPermission(
167                     MANAGE_ACTIVITY_STACKS)) {
168                 Method method = ActivityTaskManager.class.getDeclaredMethod(
169                         "removeStacksWithActivityTypes",
170                         new Class<?>[]{int[].class});
171                 method.invoke(TestApis.context().instrumentedContext().getSystemService(
172                         ActivityTaskManager.class), ALL_ACTIVITY_TYPE_BUT_HOME);
173             } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
174                 throw new NeneException("Error clearing all activities activity pre S", e);
175             }
176         }
177     }
178 }
179