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 a {@link ThumbnailData} with {@link TaskSnapshot} for the given {@param taskId}. 141 * The snapshot will be triggered if no cached {@link TaskSnapshot} exists. 142 */ getTaskThumbnail(int taskId, boolean isLowResolution)143 public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean isLowResolution) { 144 TaskSnapshot snapshot = null; 145 try { 146 snapshot = getService().getTaskSnapshot(taskId, isLowResolution, 147 true /* takeSnapshotIfNeeded */); 148 } catch (RemoteException e) { 149 Log.w(TAG, "Failed to retrieve task snapshot", e); 150 } 151 if (snapshot != null) { 152 return new ThumbnailData(snapshot); 153 } else { 154 return new ThumbnailData(); 155 } 156 } 157 158 /** 159 * Removes the outdated snapshot of home task. 160 * 161 * @param homeActivity The home task activity, or null if you have the 162 * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS} permission and 163 * want us to find the home task for you. 164 */ invalidateHomeTaskSnapshot(@ullable final Activity homeActivity)165 public void invalidateHomeTaskSnapshot(@Nullable final Activity homeActivity) { 166 try { 167 ActivityClient.getInstance().invalidateHomeTaskSnapshot( 168 homeActivity == null ? null : homeActivity.getActivityToken()); 169 } catch (Throwable e) { 170 Log.w(TAG, "Failed to invalidate home snapshot", e); 171 } 172 } 173 174 /** 175 * Starts the recents activity. The caller should manage the thread on which this is called. 176 */ startRecentsActivity(Intent intent, long eventTime, final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback, Handler resultCallbackHandler)177 public void startRecentsActivity(Intent intent, long eventTime, 178 final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback, 179 Handler resultCallbackHandler) { 180 boolean result = startRecentsActivity(intent, eventTime, animationHandler); 181 if (resultCallback != null) { 182 resultCallbackHandler.post(new Runnable() { 183 @Override 184 public void run() { 185 resultCallback.accept(result); 186 } 187 }); 188 } 189 } 190 191 /** 192 * Starts the recents activity. The caller should manage the thread on which this is called. 193 */ startRecentsActivity( Intent intent, long eventTime, RecentsAnimationListener animationHandler)194 public boolean startRecentsActivity( 195 Intent intent, long eventTime, RecentsAnimationListener animationHandler) { 196 try { 197 IRecentsAnimationRunner runner = null; 198 if (animationHandler != null) { 199 runner = new IRecentsAnimationRunner.Stub() { 200 @Override 201 public void onAnimationStart(IRecentsAnimationController controller, 202 RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, 203 Rect homeContentInsets, Rect minimizedHomeBounds) { 204 final RecentsAnimationControllerCompat controllerCompat = 205 new RecentsAnimationControllerCompat(controller); 206 animationHandler.onAnimationStart(controllerCompat, apps, 207 wallpapers, homeContentInsets, minimizedHomeBounds); 208 } 209 210 @Override 211 public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) { 212 animationHandler.onAnimationCanceled( 213 ThumbnailData.wrap(taskIds, taskSnapshots)); 214 } 215 216 @Override 217 public void onTasksAppeared(RemoteAnimationTarget[] apps) { 218 animationHandler.onTasksAppeared(apps); 219 } 220 }; 221 } 222 getService().startRecentsActivity(intent, eventTime, runner); 223 return true; 224 } catch (Exception e) { 225 return false; 226 } 227 } 228 229 /** 230 * Cancels the remote recents animation started from {@link #startRecentsActivity}. 231 */ cancelRecentsAnimation(boolean restoreHomeRootTaskPosition)232 public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) { 233 try { 234 getService().cancelRecentsAnimation(restoreHomeRootTaskPosition); 235 } catch (RemoteException e) { 236 Log.e(TAG, "Failed to cancel recents animation", e); 237 } 238 } 239 240 /** 241 * Starts a task from Recents synchronously. 242 */ startActivityFromRecents(Task.TaskKey taskKey, ActivityOptions options)243 public boolean startActivityFromRecents(Task.TaskKey taskKey, ActivityOptions options) { 244 return startActivityFromRecents(taskKey.id, options); 245 } 246 247 /** 248 * Starts a task from Recents synchronously. 249 */ startActivityFromRecents(int taskId, ActivityOptions options)250 public boolean startActivityFromRecents(int taskId, ActivityOptions options) { 251 try { 252 Bundle optsBundle = options == null ? null : options.toBundle(); 253 return ActivityManager.isStartResultSuccessful( 254 getService().startActivityFromRecents( 255 taskId, optsBundle)); 256 } catch (Exception e) { 257 return false; 258 } 259 } 260 261 /** 262 * Requests that the system close any open system windows (including other SystemUI). 263 */ closeSystemWindows(final String reason)264 public void closeSystemWindows(final String reason) { 265 try { 266 ActivityManager.getService().closeSystemDialogs(reason); 267 } catch (RemoteException e) { 268 Log.w(TAG, "Failed to close system windows", e); 269 } 270 } 271 272 /** 273 * Removes a task by id. 274 */ removeTask(final int taskId)275 public void removeTask(final int taskId) { 276 try { 277 getService().removeTask(taskId); 278 } catch (RemoteException e) { 279 Log.w(TAG, "Failed to remove task=" + taskId, e); 280 } 281 } 282 283 /** 284 * Removes all the recent tasks. 285 */ removeAllRecentTasks()286 public void removeAllRecentTasks() { 287 try { 288 getService().removeAllVisibleRecentTasks(); 289 } catch (RemoteException e) { 290 Log.w(TAG, "Failed to remove all tasks", e); 291 } 292 } 293 294 /** 295 * @return whether screen pinning is enabled. 296 */ isScreenPinningEnabled()297 public boolean isScreenPinningEnabled() { 298 final ContentResolver cr = AppGlobals.getInitialApplication().getContentResolver(); 299 return Settings.System.getInt(cr, Settings.System.LOCK_TO_APP_ENABLED, 0) != 0; 300 } 301 302 /** 303 * @return whether there is currently a locked task (ie. in screen pinning). 304 */ isLockToAppActive()305 public boolean isLockToAppActive() { 306 try { 307 return getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE; 308 } catch (RemoteException e) { 309 return false; 310 } 311 } 312 313 /** 314 * @return whether lock task mode is active in kiosk-mode (not screen pinning). 315 */ isLockTaskKioskModeActive()316 public boolean isLockTaskKioskModeActive() { 317 try { 318 return getService().getLockTaskModeState() == LOCK_TASK_MODE_LOCKED; 319 } catch (RemoteException e) { 320 return false; 321 } 322 } 323 324 /** 325 * Shows a voice session identified by {@code token} 326 * @return true if the session was shown, false otherwise 327 */ showVoiceSession(IBinder token, Bundle args, int flags)328 public boolean showVoiceSession(IBinder token, Bundle args, int flags) { 329 IVoiceInteractionManagerService service = IVoiceInteractionManagerService.Stub.asInterface( 330 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)); 331 if (service == null) { 332 return false; 333 } 334 args.putLong(INVOCATION_TIME_MS_KEY, SystemClock.elapsedRealtime()); 335 336 try { 337 return service.showSessionFromSession(token, args, flags); 338 } catch (RemoteException e) { 339 return false; 340 } 341 } 342 343 /** 344 * Returns true if the system supports freeform multi-window. 345 */ supportsFreeformMultiWindow(Context context)346 public boolean supportsFreeformMultiWindow(Context context) { 347 final boolean freeformDevOption = Settings.Global.getInt(context.getContentResolver(), 348 Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; 349 return ActivityTaskManager.supportsMultiWindow(context) 350 && (context.getPackageManager().hasSystemFeature( 351 PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) 352 || freeformDevOption); 353 } 354 355 /** 356 * Returns true if the running task represents the home task 357 */ isHomeTask(RunningTaskInfo info)358 public static boolean isHomeTask(RunningTaskInfo info) { 359 return info.configuration.windowConfiguration.getActivityType() 360 == WindowConfiguration.ACTIVITY_TYPE_HOME; 361 } 362 } 363