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.WindowConfiguration.ACTIVITY_TYPE_RECENTS; 24 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; 25 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 26 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; 27 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; 28 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 29 30 import android.annotation.NonNull; 31 import android.app.ActivityManager; 32 import android.app.ActivityManager.RecentTaskInfo; 33 import android.app.ActivityManager.RunningTaskInfo; 34 import android.app.ActivityOptions; 35 import android.app.ActivityTaskManager; 36 import android.app.AppGlobals; 37 import android.app.IAssistDataReceiver; 38 import android.app.WindowConfiguration; 39 import android.app.WindowConfiguration.ActivityType; 40 import android.content.ContentResolver; 41 import android.content.Context; 42 import android.content.Intent; 43 import android.content.pm.ActivityInfo; 44 import android.content.pm.ApplicationInfo; 45 import android.content.pm.PackageManager; 46 import android.content.pm.UserInfo; 47 import android.graphics.Bitmap; 48 import android.graphics.Rect; 49 import android.os.Bundle; 50 import android.os.Handler; 51 import android.os.IBinder; 52 import android.os.Looper; 53 import android.os.RemoteException; 54 import android.os.ServiceManager; 55 import android.os.UserHandle; 56 import android.provider.Settings; 57 import android.util.Log; 58 import android.view.IRecentsAnimationController; 59 import android.view.IRecentsAnimationRunner; 60 import android.view.RemoteAnimationTarget; 61 62 import com.android.internal.app.IVoiceInteractionManagerService; 63 import com.android.systemui.shared.recents.model.Task; 64 import com.android.systemui.shared.recents.model.Task.TaskKey; 65 import com.android.systemui.shared.recents.model.ThumbnailData; 66 67 import java.util.ArrayList; 68 import java.util.List; 69 import java.util.concurrent.Future; 70 import java.util.function.Consumer; 71 72 public class ActivityManagerWrapper { 73 74 private static final String TAG = "ActivityManagerWrapper"; 75 76 private static final ActivityManagerWrapper sInstance = new ActivityManagerWrapper(); 77 78 // Should match the values in PhoneWindowManager 79 public static final String CLOSE_SYSTEM_WINDOWS_REASON_RECENTS = "recentapps"; 80 81 private final PackageManager mPackageManager; 82 private final BackgroundExecutor mBackgroundExecutor; 83 private final TaskStackChangeListeners mTaskStackChangeListeners; 84 ActivityManagerWrapper()85 private ActivityManagerWrapper() { 86 final Context context = AppGlobals.getInitialApplication(); 87 mPackageManager = context.getPackageManager(); 88 mBackgroundExecutor = BackgroundExecutor.get(); 89 mTaskStackChangeListeners = new TaskStackChangeListeners(Looper.getMainLooper()); 90 } 91 getInstance()92 public static ActivityManagerWrapper getInstance() { 93 return sInstance; 94 } 95 96 /** 97 * @return the current user's id. 98 */ getCurrentUserId()99 public int getCurrentUserId() { 100 UserInfo ui; 101 try { 102 ui = ActivityManager.getService().getCurrentUser(); 103 return ui != null ? ui.id : 0; 104 } catch (RemoteException e) { 105 throw e.rethrowFromSystemServer(); 106 } 107 } 108 109 /** 110 * @return the top running task (can be {@code null}). 111 */ getRunningTask()112 public ActivityManager.RunningTaskInfo getRunningTask() { 113 return getRunningTask(ACTIVITY_TYPE_RECENTS /* ignoreActivityType */); 114 } 115 getRunningTask(@ctivityType int ignoreActivityType)116 public ActivityManager.RunningTaskInfo getRunningTask(@ActivityType int ignoreActivityType) { 117 // Note: The set of running tasks from the system is ordered by recency 118 try { 119 List<ActivityManager.RunningTaskInfo> tasks = 120 ActivityTaskManager.getService().getFilteredTasks(1, ignoreActivityType, 121 WINDOWING_MODE_PINNED /* ignoreWindowingMode */); 122 if (tasks.isEmpty()) { 123 return null; 124 } 125 return tasks.get(0); 126 } catch (RemoteException e) { 127 return null; 128 } 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 try { 136 return ActivityTaskManager.getService().getRecentTasks(numTasks, 137 RECENT_IGNORE_UNAVAILABLE, userId).getList(); 138 } catch (RemoteException e) { 139 Log.e(TAG, "Failed to get recent tasks", e); 140 return new ArrayList<>(); 141 } 142 } 143 144 /** 145 * @return the task snapshot for the given {@param taskId}. 146 */ getTaskThumbnail(int taskId, boolean reducedResolution)147 public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean reducedResolution) { 148 ActivityManager.TaskSnapshot snapshot = null; 149 try { 150 snapshot = ActivityTaskManager.getService().getTaskSnapshot(taskId, reducedResolution); 151 } catch (RemoteException e) { 152 Log.w(TAG, "Failed to retrieve task snapshot", e); 153 } 154 if (snapshot != null) { 155 return new ThumbnailData(snapshot); 156 } else { 157 return new ThumbnailData(); 158 } 159 } 160 161 /** 162 * @return the activity label, badging if necessary. 163 */ getBadgedActivityLabel(ActivityInfo info, int userId)164 public String getBadgedActivityLabel(ActivityInfo info, int userId) { 165 return getBadgedLabel(info.loadLabel(mPackageManager).toString(), userId); 166 } 167 168 /** 169 * @return the application label, badging if necessary. 170 */ getBadgedApplicationLabel(ApplicationInfo appInfo, int userId)171 public String getBadgedApplicationLabel(ApplicationInfo appInfo, int userId) { 172 return getBadgedLabel(appInfo.loadLabel(mPackageManager).toString(), userId); 173 } 174 175 /** 176 * @return the content description for a given task, badging it if necessary. The content 177 * description joins the app and activity labels. 178 */ getBadgedContentDescription(ActivityInfo info, int userId, ActivityManager.TaskDescription td)179 public String getBadgedContentDescription(ActivityInfo info, int userId, 180 ActivityManager.TaskDescription td) { 181 String activityLabel; 182 if (td != null && td.getLabel() != null) { 183 activityLabel = td.getLabel(); 184 } else { 185 activityLabel = info.loadLabel(mPackageManager).toString(); 186 } 187 String applicationLabel = info.applicationInfo.loadLabel(mPackageManager).toString(); 188 String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId); 189 return applicationLabel.equals(activityLabel) 190 ? badgedApplicationLabel 191 : badgedApplicationLabel + " " + activityLabel; 192 } 193 194 /** 195 * @return the given label for a user, badging if necessary. 196 */ getBadgedLabel(String label, int userId)197 private String getBadgedLabel(String label, int userId) { 198 if (userId != UserHandle.myUserId()) { 199 label = mPackageManager.getUserBadgedLabel(label, new UserHandle(userId)).toString(); 200 } 201 return label; 202 } 203 204 /** 205 * Starts the recents activity. The caller should manage the thread on which this is called. 206 */ startRecentsActivity(Intent intent, final AssistDataReceiver assistDataReceiver, final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback, Handler resultCallbackHandler)207 public void startRecentsActivity(Intent intent, final AssistDataReceiver assistDataReceiver, 208 final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback, 209 Handler resultCallbackHandler) { 210 try { 211 IAssistDataReceiver receiver = null; 212 if (assistDataReceiver != null) { 213 receiver = new IAssistDataReceiver.Stub() { 214 public void onHandleAssistData(Bundle resultData) { 215 assistDataReceiver.onHandleAssistData(resultData); 216 } 217 public void onHandleAssistScreenshot(Bitmap screenshot) { 218 assistDataReceiver.onHandleAssistScreenshot(screenshot); 219 } 220 }; 221 } 222 IRecentsAnimationRunner runner = null; 223 if (animationHandler != null) { 224 runner = new IRecentsAnimationRunner.Stub() { 225 @Override 226 public void onAnimationStart(IRecentsAnimationController controller, 227 RemoteAnimationTarget[] apps, Rect homeContentInsets, 228 Rect minimizedHomeBounds) { 229 final RecentsAnimationControllerCompat controllerCompat = 230 new RecentsAnimationControllerCompat(controller); 231 final RemoteAnimationTargetCompat[] appsCompat = 232 RemoteAnimationTargetCompat.wrap(apps); 233 animationHandler.onAnimationStart(controllerCompat, appsCompat, 234 homeContentInsets, minimizedHomeBounds); 235 } 236 237 @Override 238 public void onAnimationCanceled(boolean deferredWithScreenshot) { 239 animationHandler.onAnimationCanceled( 240 deferredWithScreenshot ? new ThumbnailData() : null); 241 } 242 }; 243 } 244 ActivityTaskManager.getService().startRecentsActivity(intent, receiver, runner); 245 if (resultCallback != null) { 246 resultCallbackHandler.post(new Runnable() { 247 @Override 248 public void run() { 249 resultCallback.accept(true); 250 } 251 }); 252 } 253 } catch (Exception e) { 254 if (resultCallback != null) { 255 resultCallbackHandler.post(new Runnable() { 256 @Override 257 public void run() { 258 resultCallback.accept(false); 259 } 260 }); 261 } 262 } 263 } 264 265 /** 266 * Cancels the remote recents animation started from {@link #startRecentsActivity}. 267 */ cancelRecentsAnimation(boolean restoreHomeStackPosition)268 public void cancelRecentsAnimation(boolean restoreHomeStackPosition) { 269 try { 270 ActivityTaskManager.getService().cancelRecentsAnimation(restoreHomeStackPosition); 271 } catch (RemoteException e) { 272 Log.e(TAG, "Failed to cancel recents animation", e); 273 } 274 } 275 276 /** 277 * Starts a task from Recents. 278 * 279 * @see {@link #startActivityFromRecentsAsync(TaskKey, ActivityOptions, int, int, Consumer, Handler)} 280 */ startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options, Consumer<Boolean> resultCallback, Handler resultCallbackHandler)281 public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options, 282 Consumer<Boolean> resultCallback, Handler resultCallbackHandler) { 283 startActivityFromRecentsAsync(taskKey, options, WINDOWING_MODE_UNDEFINED, 284 ACTIVITY_TYPE_UNDEFINED, resultCallback, resultCallbackHandler); 285 } 286 287 /** 288 * Starts a task from Recents. 289 * 290 * @param resultCallback The result success callback 291 * @param resultCallbackHandler The handler to receive the result callback 292 */ startActivityFromRecentsAsync(final Task.TaskKey taskKey, ActivityOptions options, int windowingMode, int activityType, final Consumer<Boolean> resultCallback, final Handler resultCallbackHandler)293 public void startActivityFromRecentsAsync(final Task.TaskKey taskKey, ActivityOptions options, 294 int windowingMode, int activityType, final Consumer<Boolean> resultCallback, 295 final Handler resultCallbackHandler) { 296 if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { 297 // We show non-visible docked tasks in Recents, but we always want to launch 298 // them in the fullscreen stack. 299 if (options == null) { 300 options = ActivityOptions.makeBasic(); 301 } 302 options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); 303 } else if (windowingMode != WINDOWING_MODE_UNDEFINED 304 || activityType != ACTIVITY_TYPE_UNDEFINED) { 305 if (options == null) { 306 options = ActivityOptions.makeBasic(); 307 } 308 options.setLaunchWindowingMode(windowingMode); 309 options.setLaunchActivityType(activityType); 310 } 311 final ActivityOptions finalOptions = options; 312 313 314 boolean result = false; 315 try { 316 result = startActivityFromRecents(taskKey.id, finalOptions); 317 } catch (Exception e) { 318 // Fall through 319 } 320 final boolean finalResult = result; 321 if (resultCallback != null) { 322 resultCallbackHandler.post(new Runnable() { 323 @Override 324 public void run() { 325 resultCallback.accept(finalResult); 326 } 327 }); 328 } 329 } 330 331 /** 332 * Starts a task from Recents synchronously. 333 */ startActivityFromRecents(int taskId, ActivityOptions options)334 public boolean startActivityFromRecents(int taskId, ActivityOptions options) { 335 try { 336 Bundle optsBundle = options == null ? null : options.toBundle(); 337 ActivityTaskManager.getService().startActivityFromRecents(taskId, optsBundle); 338 return true; 339 } catch (Exception e) { 340 return false; 341 } 342 } 343 344 /** 345 * Moves an already resumed task to the side of the screen to initiate split screen. 346 */ setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, Rect initialBounds)347 public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, 348 Rect initialBounds) { 349 try { 350 return ActivityTaskManager.getService().setTaskWindowingModeSplitScreenPrimary(taskId, 351 createMode, true /* onTop */, false /* animate */, initialBounds, 352 true /* showRecents */); 353 } catch (RemoteException e) { 354 return false; 355 } 356 } 357 358 /** 359 * Registers a task stack listener with the system. 360 * This should be called on the main thread. 361 */ registerTaskStackListener(TaskStackChangeListener listener)362 public void registerTaskStackListener(TaskStackChangeListener listener) { 363 synchronized (mTaskStackChangeListeners) { 364 mTaskStackChangeListeners.addListener(ActivityManager.getService(), listener); 365 } 366 } 367 368 /** 369 * Unregisters a task stack listener with the system. 370 * This should be called on the main thread. 371 */ unregisterTaskStackListener(TaskStackChangeListener listener)372 public void unregisterTaskStackListener(TaskStackChangeListener listener) { 373 synchronized (mTaskStackChangeListeners) { 374 mTaskStackChangeListeners.removeListener(listener); 375 } 376 } 377 378 /** 379 * Requests that the system close any open system windows (including other SystemUI). 380 */ closeSystemWindows(final String reason)381 public Future<?> closeSystemWindows(final String reason) { 382 return mBackgroundExecutor.submit(new Runnable() { 383 @Override 384 public void run() { 385 try { 386 ActivityManager.getService().closeSystemDialogs(reason); 387 } catch (RemoteException e) { 388 Log.w(TAG, "Failed to close system windows", e); 389 } 390 } 391 }); 392 } 393 394 /** 395 * Removes a task by id. 396 */ 397 public void removeTask(final int taskId) { 398 mBackgroundExecutor.submit(new Runnable() { 399 @Override 400 public void run() { 401 try { 402 ActivityTaskManager.getService().removeTask(taskId); 403 } catch (RemoteException e) { 404 Log.w(TAG, "Failed to remove task=" + taskId, e); 405 } 406 } 407 }); 408 } 409 410 /** 411 * Removes all the recent tasks. 412 */ 413 public void removeAllRecentTasks() { 414 mBackgroundExecutor.submit(new Runnable() { 415 @Override 416 public void run() { 417 try { 418 ActivityTaskManager.getService().removeAllVisibleRecentTasks(); 419 } catch (RemoteException e) { 420 Log.w(TAG, "Failed to remove all tasks", e); 421 } 422 } 423 }); 424 } 425 426 /** 427 * Cancels the current window transtion to/from Recents for the given task id. 428 */ 429 public void cancelWindowTransition(int taskId) { 430 try { 431 ActivityTaskManager.getService().cancelTaskWindowTransition(taskId); 432 } catch (RemoteException e) { 433 Log.w(TAG, "Failed to cancel window transition for task=" + taskId, e); 434 } 435 } 436 437 /** 438 * @return whether screen pinning is active. 439 */ 440 public boolean isScreenPinningActive() { 441 try { 442 return ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED; 443 } catch (RemoteException e) { 444 return false; 445 } 446 } 447 448 /** 449 * @return whether screen pinning is enabled. 450 */ 451 public boolean isScreenPinningEnabled() { 452 final ContentResolver cr = AppGlobals.getInitialApplication().getContentResolver(); 453 return Settings.System.getInt(cr, Settings.System.LOCK_TO_APP_ENABLED, 0) != 0; 454 } 455 456 /** 457 * @return whether there is currently a locked task (ie. in screen pinning). 458 */ 459 public boolean isLockToAppActive() { 460 try { 461 return ActivityTaskManager.getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE; 462 } catch (RemoteException e) { 463 return false; 464 } 465 } 466 467 /** 468 * @return whether lock task mode is active in kiosk-mode (not screen pinning). 469 */ 470 public boolean isLockTaskKioskModeActive() { 471 try { 472 return ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_LOCKED; 473 } catch (RemoteException e) { 474 return false; 475 } 476 } 477 478 /** 479 * Shows a voice session identified by {@code token} 480 * @return true if the session was shown, false otherwise 481 */ 482 public boolean showVoiceSession(IBinder token, Bundle args, int flags) { 483 IVoiceInteractionManagerService service = IVoiceInteractionManagerService.Stub.asInterface( 484 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)); 485 if (service == null) { 486 return false; 487 } 488 try { 489 return service.showSessionFromSession(token, args, flags); 490 } catch (RemoteException e) { 491 return false; 492 } 493 } 494 495 /** 496 * Returns true if the system supports freeform multi-window. 497 */ 498 public boolean supportsFreeformMultiWindow(Context context) { 499 final boolean freeformDevOption = Settings.Global.getInt(context.getContentResolver(), 500 Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; 501 return ActivityTaskManager.supportsMultiWindow(context) 502 && (context.getPackageManager().hasSystemFeature( 503 PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) 504 || freeformDevOption); 505 } 506 507 /** 508 * Returns true if the running task represents the home task 509 */ 510 public static boolean isHomeTask(RunningTaskInfo info) { 511 return info.configuration.windowConfiguration.getActivityType() 512 == WindowConfiguration.ACTIVITY_TYPE_HOME; 513 } 514 } 515