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