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