1 /* 2 * Copyright (C) 2015 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.systemui.shared.system; 18 19 import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; 20 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; 21 import static android.app.ActivityTaskManager.getService; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.app.Activity; 26 import android.app.ActivityClient; 27 import android.app.ActivityManager; 28 import android.app.ActivityManager.RunningTaskInfo; 29 import android.app.ActivityOptions; 30 import android.app.ActivityTaskManager; 31 import android.app.AppGlobals; 32 import android.app.WindowConfiguration; 33 import android.content.ContentResolver; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.pm.PackageManager; 37 import android.content.pm.UserInfo; 38 import android.graphics.Rect; 39 import android.os.Bundle; 40 import android.os.Handler; 41 import android.os.IBinder; 42 import android.os.RemoteException; 43 import android.os.ServiceManager; 44 import android.os.SystemClock; 45 import android.provider.Settings; 46 import android.util.Log; 47 import android.view.Display; 48 import android.view.IRecentsAnimationController; 49 import android.view.IRecentsAnimationRunner; 50 import android.view.RemoteAnimationTarget; 51 import android.window.TaskSnapshot; 52 53 import com.android.internal.app.IVoiceInteractionManagerService; 54 import com.android.systemui.shared.recents.model.Task; 55 import com.android.systemui.shared.recents.model.ThumbnailData; 56 57 import java.util.List; 58 import java.util.function.Consumer; 59 60 public class ActivityManagerWrapper { 61 62 private static final String TAG = "ActivityManagerWrapper"; 63 private static final int NUM_RECENT_ACTIVITIES_REQUEST = 3; 64 private static final ActivityManagerWrapper sInstance = new ActivityManagerWrapper(); 65 66 // Should match the values in PhoneWindowManager 67 public static final String CLOSE_SYSTEM_WINDOWS_REASON_RECENTS = "recentapps"; 68 public static final String CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY = "homekey"; 69 70 // Should match the value in AssistManager 71 private static final String INVOCATION_TIME_MS_KEY = "invocation_time_ms"; 72 73 private final ActivityTaskManager mAtm = ActivityTaskManager.getInstance(); ActivityManagerWrapper()74 private ActivityManagerWrapper() { } 75 getInstance()76 public static ActivityManagerWrapper getInstance() { 77 return sInstance; 78 } 79 80 /** 81 * @return the current user's id. 82 */ getCurrentUserId()83 public int getCurrentUserId() { 84 UserInfo ui; 85 try { 86 ui = ActivityManager.getService().getCurrentUser(); 87 return ui != null ? ui.id : 0; 88 } catch (RemoteException e) { 89 throw e.rethrowFromSystemServer(); 90 } 91 } 92 93 /** 94 * @return the top running task (can be {@code null}). 95 */ getRunningTask()96 public ActivityManager.RunningTaskInfo getRunningTask() { 97 return getRunningTask(false /* filterVisibleRecents */); 98 } 99 100 /** 101 * @return the top running task filtering only for tasks that can be visible in the recent tasks 102 * list (can be {@code null}). 103 */ getRunningTask(boolean filterOnlyVisibleRecents)104 public ActivityManager.RunningTaskInfo getRunningTask(boolean filterOnlyVisibleRecents) { 105 // Note: The set of running tasks from the system is ordered by recency 106 List<ActivityManager.RunningTaskInfo> tasks = 107 mAtm.getTasks(1, filterOnlyVisibleRecents); 108 if (tasks.isEmpty()) { 109 return null; 110 } 111 return tasks.get(0); 112 } 113 114 /** 115 * @see #getRunningTasks(boolean , int) 116 */ getRunningTasks(boolean filterOnlyVisibleRecents)117 public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents) { 118 return getRunningTasks(filterOnlyVisibleRecents, Display.INVALID_DISPLAY); 119 } 120 121 /** 122 * We ask for {@link #NUM_RECENT_ACTIVITIES_REQUEST} activities because when in split screen, 123 * we'll get back 2 activities for each split app and one for launcher. Launcher might be more 124 * "recently" used than one of the split apps so if we only request 2 tasks, then we might miss 125 * out on one of the split apps 126 * 127 * @return an array of up to {@link #NUM_RECENT_ACTIVITIES_REQUEST} running tasks 128 * filtering only for tasks that can be visible in the recent tasks list. 129 */ getRunningTasks(boolean filterOnlyVisibleRecents, int displayId)130 public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents, 131 int displayId) { 132 // Note: The set of running tasks from the system is ordered by recency 133 List<ActivityManager.RunningTaskInfo> tasks = 134 mAtm.getTasks(NUM_RECENT_ACTIVITIES_REQUEST, 135 filterOnlyVisibleRecents, /* keepInExtras= */ false, displayId); 136 return tasks.toArray(new RunningTaskInfo[tasks.size()]); 137 } 138 139 /** 140 * @return the task snapshot for the given {@param taskId}. 141 */ getTaskThumbnail(int taskId, boolean isLowResolution)142 public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean isLowResolution) { 143 TaskSnapshot snapshot = null; 144 try { 145 snapshot = getService().getTaskSnapshot(taskId, isLowResolution); 146 } catch (RemoteException e) { 147 Log.w(TAG, "Failed to retrieve task snapshot", e); 148 } 149 if (snapshot != null) { 150 return ThumbnailData.fromSnapshot(snapshot); 151 } else { 152 return new ThumbnailData(); 153 } 154 } 155 156 157 /** 158 * Requests for a new snapshot to be taken for the given task, stores it in the cache, and 159 * returns a {@link ThumbnailData} with the result. 160 */ 161 @NonNull takeTaskThumbnail(int taskId)162 public ThumbnailData takeTaskThumbnail(int taskId) { 163 TaskSnapshot snapshot = null; 164 try { 165 snapshot = getService().takeTaskSnapshot(taskId, /* updateCache= */ true); 166 } catch (RemoteException e) { 167 Log.w(TAG, "Failed to take task snapshot", e); 168 } 169 if (snapshot != null) { 170 return ThumbnailData.fromSnapshot(snapshot); 171 } else { 172 return new ThumbnailData(); 173 } 174 } 175 176 /** 177 * Removes the outdated snapshot of home task. 178 * 179 * @param homeActivity The home task activity, or null if you have the 180 * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS} permission and 181 * want us to find the home task for you. 182 */ invalidateHomeTaskSnapshot(@ullable final Activity homeActivity)183 public void invalidateHomeTaskSnapshot(@Nullable final Activity homeActivity) { 184 try { 185 ActivityClient.getInstance().invalidateHomeTaskSnapshot( 186 homeActivity == null ? null : homeActivity.getActivityToken()); 187 } catch (Throwable e) { 188 Log.w(TAG, "Failed to invalidate home snapshot", e); 189 } 190 } 191 192 /** 193 * Starts the recents activity. The caller should manage the thread on which this is called. 194 */ startRecentsActivity(Intent intent, long eventTime, final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback, Handler resultCallbackHandler)195 public void startRecentsActivity(Intent intent, long eventTime, 196 final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback, 197 Handler resultCallbackHandler) { 198 boolean result = startRecentsActivity(intent, eventTime, animationHandler); 199 if (resultCallback != null && resultCallbackHandler != null) { 200 resultCallbackHandler.post(new Runnable() { 201 @Override 202 public void run() { 203 resultCallback.accept(result); 204 } 205 }); 206 } 207 } 208 209 /** 210 * Starts the recents activity. The caller should manage the thread on which this is called. 211 */ startRecentsActivity( Intent intent, long eventTime, RecentsAnimationListener animationHandler)212 public boolean startRecentsActivity( 213 Intent intent, long eventTime, RecentsAnimationListener animationHandler) { 214 try { 215 IRecentsAnimationRunner runner = null; 216 if (animationHandler != null) { 217 runner = new IRecentsAnimationRunner.Stub() { 218 @Override 219 public void onAnimationStart(IRecentsAnimationController controller, 220 RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, 221 Rect homeContentInsets, Rect minimizedHomeBounds, 222 Bundle extras) { 223 final RecentsAnimationControllerCompat controllerCompat = 224 new RecentsAnimationControllerCompat(controller); 225 animationHandler.onAnimationStart(controllerCompat, apps, 226 wallpapers, homeContentInsets, minimizedHomeBounds, extras); 227 } 228 229 @Override 230 public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) { 231 animationHandler.onAnimationCanceled( 232 ThumbnailData.wrap(taskIds, taskSnapshots)); 233 } 234 235 @Override 236 public void onTasksAppeared(RemoteAnimationTarget[] apps) { 237 animationHandler.onTasksAppeared(apps); 238 } 239 }; 240 } 241 getService().startRecentsActivity(intent, eventTime, runner); 242 return true; 243 } catch (Exception e) { 244 return false; 245 } 246 } 247 248 /** 249 * Cancels the remote recents animation started from {@link #startRecentsActivity}. 250 */ cancelRecentsAnimation(boolean restoreHomeRootTaskPosition)251 public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) { 252 try { 253 getService().cancelRecentsAnimation(restoreHomeRootTaskPosition); 254 } catch (RemoteException e) { 255 Log.e(TAG, "Failed to cancel recents animation", e); 256 } 257 } 258 259 /** 260 * Starts a task from Recents synchronously. 261 */ startActivityFromRecents(Task.TaskKey taskKey, ActivityOptions options)262 public boolean startActivityFromRecents(Task.TaskKey taskKey, ActivityOptions options) { 263 return startActivityFromRecents(taskKey.id, options); 264 } 265 266 /** 267 * Starts a task from Recents synchronously. 268 */ startActivityFromRecents(int taskId, ActivityOptions options)269 public boolean startActivityFromRecents(int taskId, ActivityOptions options) { 270 try { 271 Bundle optsBundle = options == null ? null : options.toBundle(); 272 return ActivityManager.isStartResultSuccessful( 273 getService().startActivityFromRecents( 274 taskId, optsBundle)); 275 } catch (Exception e) { 276 return false; 277 } 278 } 279 280 /** 281 * Requests that the system close any open system windows (including other SystemUI). 282 */ closeSystemWindows(final String reason)283 public void closeSystemWindows(final String reason) { 284 try { 285 ActivityManager.getService().closeSystemDialogs(reason); 286 } catch (RemoteException e) { 287 Log.w(TAG, "Failed to close system windows", e); 288 } 289 } 290 291 /** 292 * Removes a task by id. 293 */ removeTask(final int taskId)294 public void removeTask(final int taskId) { 295 try { 296 getService().removeTask(taskId); 297 } catch (RemoteException e) { 298 Log.w(TAG, "Failed to remove task=" + taskId, e); 299 } 300 } 301 302 /** 303 * Removes all the recent tasks. 304 */ removeAllRecentTasks()305 public void removeAllRecentTasks() { 306 try { 307 getService().removeAllVisibleRecentTasks(); 308 } catch (RemoteException e) { 309 Log.w(TAG, "Failed to remove all tasks", e); 310 } 311 } 312 313 /** 314 * @return whether screen pinning is enabled. 315 */ isScreenPinningEnabled()316 public boolean isScreenPinningEnabled() { 317 final ContentResolver cr = AppGlobals.getInitialApplication().getContentResolver(); 318 return Settings.System.getInt(cr, Settings.System.LOCK_TO_APP_ENABLED, 0) != 0; 319 } 320 321 /** 322 * @return whether there is currently a locked task (ie. in screen pinning). 323 */ isLockToAppActive()324 public boolean isLockToAppActive() { 325 try { 326 return getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE; 327 } catch (RemoteException e) { 328 return false; 329 } 330 } 331 332 /** 333 * @return whether lock task mode is active in kiosk-mode (not screen pinning). 334 */ isLockTaskKioskModeActive()335 public boolean isLockTaskKioskModeActive() { 336 try { 337 return getService().getLockTaskModeState() == LOCK_TASK_MODE_LOCKED; 338 } catch (RemoteException e) { 339 return false; 340 } 341 } 342 343 /** 344 * Shows a voice session identified by {@code token} 345 * @return true if the session was shown, false otherwise 346 */ showVoiceSession(@onNull IBinder token, @NonNull Bundle args, int flags, @Nullable String attributionTag)347 public boolean showVoiceSession(@NonNull IBinder token, @NonNull Bundle args, int flags, 348 @Nullable String attributionTag) { 349 IVoiceInteractionManagerService service = IVoiceInteractionManagerService.Stub.asInterface( 350 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)); 351 if (service == null) { 352 return false; 353 } 354 args.putLong(INVOCATION_TIME_MS_KEY, SystemClock.elapsedRealtime()); 355 356 try { 357 return service.showSessionFromSession(token, args, flags, attributionTag); 358 } catch (RemoteException e) { 359 return false; 360 } 361 } 362 363 /** 364 * Returns true if the system supports freeform multi-window. 365 */ supportsFreeformMultiWindow(Context context)366 public boolean supportsFreeformMultiWindow(Context context) { 367 final boolean freeformDevOption = Settings.Global.getInt(context.getContentResolver(), 368 Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; 369 return ActivityTaskManager.supportsMultiWindow(context) 370 && (context.getPackageManager().hasSystemFeature( 371 PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) 372 || freeformDevOption); 373 } 374 375 /** 376 * Returns true if the running task represents the home task 377 */ isHomeTask(RunningTaskInfo info)378 public static boolean isHomeTask(RunningTaskInfo info) { 379 return info.configuration.windowConfiguration.getActivityType() 380 == WindowConfiguration.ACTIVITY_TYPE_HOME; 381 } 382 } 383