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