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.window.TaskSnapshot; 32 import android.app.ActivityOptions; 33 import android.app.ActivityTaskManager; 34 import android.app.AppGlobals; 35 import android.app.WindowConfiguration; 36 import android.content.ContentResolver; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.content.pm.PackageManager; 40 import android.content.pm.UserInfo; 41 import android.graphics.Rect; 42 import android.os.Bundle; 43 import android.os.Handler; 44 import android.os.IBinder; 45 import android.os.RemoteException; 46 import android.os.ServiceManager; 47 import android.os.SystemClock; 48 import android.provider.Settings; 49 import android.util.Log; 50 import android.view.IRecentsAnimationController; 51 import android.view.IRecentsAnimationRunner; 52 import android.view.RemoteAnimationTarget; 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 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 * @return a list of the recents tasks. 117 */ getRecentTasks(int numTasks, int userId)118 public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) { 119 return mAtm.getRecentTasks(numTasks, RECENT_IGNORE_UNAVAILABLE, userId); 120 } 121 122 /** 123 * @return the task snapshot for the given {@param taskId}. 124 */ getTaskThumbnail(int taskId, boolean isLowResolution)125 public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean isLowResolution) { 126 TaskSnapshot snapshot = null; 127 try { 128 snapshot = getService().getTaskSnapshot(taskId, isLowResolution); 129 } catch (RemoteException e) { 130 Log.w(TAG, "Failed to retrieve task snapshot", e); 131 } 132 if (snapshot != null) { 133 return new ThumbnailData(snapshot); 134 } else { 135 return new ThumbnailData(); 136 } 137 } 138 139 /** 140 * Removes the outdated snapshot of home task. 141 */ invalidateHomeTaskSnapshot(final Activity homeActivity)142 public void invalidateHomeTaskSnapshot(final Activity homeActivity) { 143 try { 144 ActivityClient.getInstance().invalidateHomeTaskSnapshot( 145 homeActivity.getActivityToken()); 146 } catch (Throwable e) { 147 Log.w(TAG, "Failed to invalidate home snapshot", e); 148 } 149 } 150 151 /** 152 * Starts the recents activity. The caller should manage the thread on which this is called. 153 */ startRecentsActivity(Intent intent, long eventTime, final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback, Handler resultCallbackHandler)154 public void startRecentsActivity(Intent intent, long eventTime, 155 final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback, 156 Handler resultCallbackHandler) { 157 boolean result = startRecentsActivity(intent, eventTime, animationHandler); 158 if (resultCallback != null) { 159 resultCallbackHandler.post(new Runnable() { 160 @Override 161 public void run() { 162 resultCallback.accept(result); 163 } 164 }); 165 } 166 } 167 168 /** 169 * Starts the recents activity. The caller should manage the thread on which this is called. 170 */ startRecentsActivity( Intent intent, long eventTime, RecentsAnimationListener animationHandler)171 public boolean startRecentsActivity( 172 Intent intent, long eventTime, RecentsAnimationListener animationHandler) { 173 try { 174 IRecentsAnimationRunner runner = null; 175 if (animationHandler != null) { 176 runner = new IRecentsAnimationRunner.Stub() { 177 @Override 178 public void onAnimationStart(IRecentsAnimationController controller, 179 RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, 180 Rect homeContentInsets, Rect minimizedHomeBounds) { 181 final RecentsAnimationControllerCompat controllerCompat = 182 new RecentsAnimationControllerCompat(controller); 183 final RemoteAnimationTargetCompat[] appsCompat = 184 RemoteAnimationTargetCompat.wrap(apps); 185 final RemoteAnimationTargetCompat[] wallpapersCompat = 186 RemoteAnimationTargetCompat.wrap(wallpapers); 187 animationHandler.onAnimationStart(controllerCompat, appsCompat, 188 wallpapersCompat, homeContentInsets, minimizedHomeBounds); 189 } 190 191 @Override 192 public void onAnimationCanceled(TaskSnapshot taskSnapshot) { 193 animationHandler.onAnimationCanceled( 194 taskSnapshot != null ? new ThumbnailData(taskSnapshot) : null); 195 } 196 197 @Override 198 public void onTaskAppeared(RemoteAnimationTarget app) { 199 animationHandler.onTaskAppeared(new RemoteAnimationTargetCompat(app)); 200 } 201 }; 202 } 203 getService().startRecentsActivity(intent, eventTime, runner); 204 return true; 205 } catch (Exception e) { 206 return false; 207 } 208 } 209 210 /** 211 * Cancels the remote recents animation started from {@link #startRecentsActivity}. 212 */ cancelRecentsAnimation(boolean restoreHomeRootTaskPosition)213 public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) { 214 try { 215 getService().cancelRecentsAnimation(restoreHomeRootTaskPosition); 216 } catch (RemoteException e) { 217 Log.e(TAG, "Failed to cancel recents animation", e); 218 } 219 } 220 221 /** 222 * Starts a task from Recents. 223 * 224 * @param resultCallback The result success callback 225 * @param resultCallbackHandler The handler to receive the result callback 226 */ startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options, Consumer<Boolean> resultCallback, Handler resultCallbackHandler)227 public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options, 228 Consumer<Boolean> resultCallback, Handler resultCallbackHandler) { 229 final boolean result = startActivityFromRecents(taskKey, options); 230 if (resultCallback != null) { 231 resultCallbackHandler.post(new Runnable() { 232 @Override 233 public void run() { 234 resultCallback.accept(result); 235 } 236 }); 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 ActivityOptionsCompat.addTaskInfo(options, taskKey); 245 return startActivityFromRecents(taskKey.id, options); 246 } 247 248 /** 249 * Starts a task from Recents synchronously. 250 */ startActivityFromRecents(int taskId, ActivityOptions options)251 public boolean startActivityFromRecents(int taskId, ActivityOptions options) { 252 try { 253 Bundle optsBundle = options == null ? null : options.toBundle(); 254 getService().startActivityFromRecents(taskId, optsBundle); 255 return true; 256 } catch (Exception e) { 257 return false; 258 } 259 } 260 261 /** 262 * @deprecated use {@link TaskStackChangeListeners#registerTaskStackListener} 263 */ registerTaskStackListener(TaskStackChangeListener listener)264 public void registerTaskStackListener(TaskStackChangeListener listener) { 265 TaskStackChangeListeners.getInstance().registerTaskStackListener(listener); 266 } 267 268 /** 269 * @deprecated use {@link TaskStackChangeListeners#unregisterTaskStackListener} 270 */ unregisterTaskStackListener(TaskStackChangeListener listener)271 public void unregisterTaskStackListener(TaskStackChangeListener listener) { 272 TaskStackChangeListeners.getInstance().unregisterTaskStackListener(listener); 273 } 274 275 /** 276 * Requests that the system close any open system windows (including other SystemUI). 277 */ closeSystemWindows(final String reason)278 public void closeSystemWindows(final String reason) { 279 try { 280 ActivityManager.getService().closeSystemDialogs(reason); 281 } catch (RemoteException e) { 282 Log.w(TAG, "Failed to close system windows", e); 283 } 284 } 285 286 /** 287 * Removes a task by id. 288 */ removeTask(final int taskId)289 public void removeTask(final int taskId) { 290 try { 291 getService().removeTask(taskId); 292 } catch (RemoteException e) { 293 Log.w(TAG, "Failed to remove task=" + taskId, e); 294 } 295 } 296 297 /** 298 * Removes all the recent tasks. 299 */ removeAllRecentTasks()300 public void removeAllRecentTasks() { 301 try { 302 getService().removeAllVisibleRecentTasks(); 303 } catch (RemoteException e) { 304 Log.w(TAG, "Failed to remove all tasks", e); 305 } 306 } 307 308 /** 309 * @return whether screen pinning is active. 310 */ isScreenPinningActive()311 public boolean isScreenPinningActive() { 312 try { 313 return getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED; 314 } catch (RemoteException e) { 315 return false; 316 } 317 } 318 319 /** 320 * @return whether screen pinning is enabled. 321 */ isScreenPinningEnabled()322 public boolean isScreenPinningEnabled() { 323 final ContentResolver cr = AppGlobals.getInitialApplication().getContentResolver(); 324 return Settings.System.getInt(cr, Settings.System.LOCK_TO_APP_ENABLED, 0) != 0; 325 } 326 327 /** 328 * @return whether there is currently a locked task (ie. in screen pinning). 329 */ isLockToAppActive()330 public boolean isLockToAppActive() { 331 try { 332 return getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE; 333 } catch (RemoteException e) { 334 return false; 335 } 336 } 337 338 /** 339 * @return whether lock task mode is active in kiosk-mode (not screen pinning). 340 */ isLockTaskKioskModeActive()341 public boolean isLockTaskKioskModeActive() { 342 try { 343 return getService().getLockTaskModeState() == LOCK_TASK_MODE_LOCKED; 344 } catch (RemoteException e) { 345 return false; 346 } 347 } 348 349 /** 350 * Shows a voice session identified by {@code token} 351 * @return true if the session was shown, false otherwise 352 */ showVoiceSession(IBinder token, Bundle args, int flags)353 public boolean showVoiceSession(IBinder token, Bundle args, int flags) { 354 IVoiceInteractionManagerService service = IVoiceInteractionManagerService.Stub.asInterface( 355 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)); 356 if (service == null) { 357 return false; 358 } 359 args.putLong(INVOCATION_TIME_MS_KEY, SystemClock.elapsedRealtime()); 360 361 try { 362 return service.showSessionFromSession(token, args, flags); 363 } catch (RemoteException e) { 364 return false; 365 } 366 } 367 368 /** 369 * Returns true if the system supports freeform multi-window. 370 */ supportsFreeformMultiWindow(Context context)371 public boolean supportsFreeformMultiWindow(Context context) { 372 final boolean freeformDevOption = Settings.Global.getInt(context.getContentResolver(), 373 Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; 374 return ActivityTaskManager.supportsMultiWindow(context) 375 && (context.getPackageManager().hasSystemFeature( 376 PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) 377 || freeformDevOption); 378 } 379 380 /** 381 * Returns true if the running task represents the home task 382 */ isHomeTask(RunningTaskInfo info)383 public static boolean isHomeTask(RunningTaskInfo info) { 384 return info.configuration.windowConfiguration.getActivityType() 385 == WindowConfiguration.ACTIVITY_TYPE_HOME; 386 } 387 } 388