1 /* 2 * Copyright (C) 2014 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.recents; 18 19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; 22 import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS; 23 24 import android.app.ActivityManager; 25 import android.app.trust.TrustManager; 26 import android.content.ComponentName; 27 import android.content.ContentResolver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.ServiceConnection; 31 import android.content.pm.ActivityInfo; 32 import android.content.res.Configuration; 33 import android.content.res.Resources; 34 import android.graphics.Point; 35 import android.graphics.Rect; 36 import android.hardware.display.DisplayManager; 37 import android.os.Handler; 38 import android.os.IBinder; 39 import android.os.RemoteException; 40 import android.os.UserHandle; 41 import android.provider.Settings; 42 import android.util.EventLog; 43 import android.util.Log; 44 import android.view.Display; 45 import android.widget.Toast; 46 47 import com.android.internal.logging.MetricsLogger; 48 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 49 import com.android.systemui.Dependency; 50 import com.android.systemui.EventLogConstants; 51 import com.android.systemui.EventLogTags; 52 import com.android.systemui.OverviewProxyService; 53 import com.android.systemui.R; 54 import com.android.systemui.RecentsComponent; 55 import com.android.systemui.SystemUIApplication; 56 import com.android.systemui.shared.recents.IOverviewProxy; 57 import com.android.systemui.SystemUI; 58 import com.android.systemui.recents.events.EventBus; 59 import com.android.systemui.recents.events.activity.ConfigurationChangedEvent; 60 import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent; 61 import com.android.systemui.recents.events.activity.DockedTopTaskEvent; 62 import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent; 63 import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; 64 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; 65 import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; 66 import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent; 67 import com.android.systemui.recents.events.component.ShowUserToastEvent; 68 import com.android.systemui.recents.events.ui.RecentsDrawnEvent; 69 import com.android.systemui.recents.misc.SystemServicesProxy; 70 import com.android.systemui.shared.recents.model.RecentsTaskLoader; 71 import com.android.systemui.shared.system.ActivityManagerWrapper; 72 import com.android.systemui.stackdivider.Divider; 73 import com.android.systemui.statusbar.CommandQueue; 74 75 import com.android.systemui.statusbar.phone.StatusBar; 76 import java.io.FileDescriptor; 77 import java.io.PrintWriter; 78 import java.util.ArrayList; 79 import java.util.HashSet; 80 import java.util.Set; 81 82 83 /** 84 * An implementation of the SystemUI recents component, which supports both system and secondary 85 * users. 86 */ 87 public class Recents extends SystemUI 88 implements RecentsComponent, CommandQueue.Callbacks { 89 90 private final static String TAG = "Recents"; 91 92 public final static int EVENT_BUS_PRIORITY = 1; 93 public final static int BIND_TO_SYSTEM_USER_RETRY_DELAY = 5000; 94 95 public final static Set<String> RECENTS_ACTIVITIES = new HashSet<>(); 96 static { 97 RECENTS_ACTIVITIES.add(RecentsImpl.RECENTS_ACTIVITY); 98 } 99 100 private static final String COUNTER_WINDOW_SUPPORTED = "window_enter_supported"; 101 private static final String COUNTER_WINDOW_UNSUPPORTED = "window_enter_unsupported"; 102 private static final String COUNTER_WINDOW_INCOMPATIBLE = "window_enter_incompatible"; 103 104 private static SystemServicesProxy sSystemServicesProxy; 105 private static RecentsDebugFlags sDebugFlags; 106 private static RecentsTaskLoader sTaskLoader; 107 private static RecentsConfiguration sConfiguration; 108 109 private OverviewProxyService mOverviewProxyService; 110 111 private Handler mHandler; 112 private RecentsImpl mImpl; 113 private TrustManager mTrustManager; 114 private int mDraggingInRecentsCurrentUser; 115 116 // Only For system user, this is the callbacks instance we return to each secondary user 117 private RecentsSystemUser mSystemToUserCallbacks; 118 119 // Only for secondary users, this is the callbacks instance provided by the system user to make 120 // calls back 121 private IRecentsSystemUserCallbacks mUserToSystemCallbacks; 122 123 // The set of runnables to run after binding to the system user's service. 124 private final ArrayList<Runnable> mOnConnectRunnables = new ArrayList<>(); 125 126 // Only for secondary users, this is the death handler for the binder from the system user 127 private final IBinder.DeathRecipient mUserToSystemCallbacksDeathRcpt = new IBinder.DeathRecipient() { 128 @Override 129 public void binderDied() { 130 mUserToSystemCallbacks = null; 131 EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION, 132 EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_UNBOUND, 133 sSystemServicesProxy.getProcessUser()); 134 135 // Retry after a fixed duration 136 mHandler.postDelayed(new Runnable() { 137 @Override 138 public void run() { 139 registerWithSystemUser(); 140 } 141 }, BIND_TO_SYSTEM_USER_RETRY_DELAY); 142 } 143 }; 144 145 // Only for secondary users, this is the service connection we use to connect to the system user 146 private final ServiceConnection mUserToSystemServiceConnection = new ServiceConnection() { 147 @Override 148 public void onServiceConnected(ComponentName name, IBinder service) { 149 if (service != null) { 150 mUserToSystemCallbacks = IRecentsSystemUserCallbacks.Stub.asInterface( 151 service); 152 EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION, 153 EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_BOUND, 154 sSystemServicesProxy.getProcessUser()); 155 156 // Listen for system user's death, so that we can reconnect later 157 try { 158 service.linkToDeath(mUserToSystemCallbacksDeathRcpt, 0); 159 } catch (RemoteException e) { 160 Log.e(TAG, "Lost connection to (System) SystemUI", e); 161 } 162 163 // Run each of the queued runnables 164 runAndFlushOnConnectRunnables(); 165 } 166 167 // Unbind ourselves now that we've registered our callbacks. The 168 // binder to the system user are still valid at this point. 169 mContext.unbindService(this); 170 } 171 172 @Override 173 public void onServiceDisconnected(ComponentName name) { 174 // Do nothing 175 } 176 }; 177 178 /** 179 * Returns the callbacks interface that non-system users can call. 180 */ getSystemUserCallbacks()181 public IBinder getSystemUserCallbacks() { 182 return mSystemToUserCallbacks; 183 } 184 getTaskLoader()185 public static RecentsTaskLoader getTaskLoader() { 186 return sTaskLoader; 187 } 188 189 getSystemServices()190 public static SystemServicesProxy getSystemServices() { 191 return sSystemServicesProxy; 192 } 193 getConfiguration()194 public static RecentsConfiguration getConfiguration() { 195 return sConfiguration; 196 } 197 getDebugFlags()198 public static RecentsDebugFlags getDebugFlags() { 199 return sDebugFlags; 200 } 201 202 @Override start()203 public void start() { 204 final Resources res = mContext.getResources(); 205 final int defaultTaskBarBackgroundColor = 206 mContext.getColor(R.color.recents_task_bar_default_background_color); 207 final int defaultTaskViewBackgroundColor = 208 mContext.getColor(R.color.recents_task_view_default_background_color); 209 sDebugFlags = new RecentsDebugFlags(); 210 sSystemServicesProxy = SystemServicesProxy.getInstance(mContext); 211 sConfiguration = new RecentsConfiguration(mContext); 212 sTaskLoader = new RecentsTaskLoader(mContext, 213 // TODO: Once we start building the AAR, move these into the loader 214 res.getInteger(R.integer.config_recents_max_thumbnail_count), 215 res.getInteger(R.integer.config_recents_max_icon_count), 216 res.getInteger(R.integer.recents_svelte_level)); 217 sTaskLoader.setDefaultColors(defaultTaskBarBackgroundColor, defaultTaskViewBackgroundColor); 218 mHandler = new Handler(); 219 mImpl = new RecentsImpl(mContext); 220 mOverviewProxyService = Dependency.get(OverviewProxyService.class); 221 222 // Register with the event bus 223 EventBus.getDefault().register(this, EVENT_BUS_PRIORITY); 224 EventBus.getDefault().register(sSystemServicesProxy, EVENT_BUS_PRIORITY); 225 EventBus.getDefault().register(sTaskLoader, EVENT_BUS_PRIORITY); 226 227 // Due to the fact that RecentsActivity is per-user, we need to establish and interface for 228 // the system user's Recents component to pass events (like show/hide/toggleRecents) to the 229 // secondary user, and vice versa (like visibility change, screen pinning). 230 final int processUser = sSystemServicesProxy.getProcessUser(); 231 if (sSystemServicesProxy.isSystemUser(processUser)) { 232 // For the system user, initialize an instance of the interface that we can pass to the 233 // secondary user 234 getComponent(CommandQueue.class).addCallbacks(this); 235 mSystemToUserCallbacks = new RecentsSystemUser(mContext, mImpl); 236 } else { 237 // For the secondary user, bind to the primary user's service to get a persistent 238 // interface to register its implementation and to later update its state 239 registerWithSystemUser(); 240 } 241 putComponent(Recents.class, this); 242 243 mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE); 244 } 245 246 @Override onBootCompleted()247 public void onBootCompleted() { 248 mImpl.onBootCompleted(); 249 } 250 251 /** 252 * Shows the Recents. 253 */ 254 @Override showRecentApps(boolean triggeredFromAltTab)255 public void showRecentApps(boolean triggeredFromAltTab) { 256 // Ensure the device has been provisioned before allowing the user to interact with 257 // recents 258 if (!isUserSetup()) { 259 return; 260 } 261 262 IOverviewProxy overviewProxy = mOverviewProxyService.getProxy(); 263 if (overviewProxy != null) { 264 try { 265 overviewProxy.onOverviewShown(triggeredFromAltTab); 266 return; 267 } catch (RemoteException e) { 268 Log.e(TAG, "Failed to send overview show event to launcher.", e); 269 } 270 } 271 272 ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS); 273 int recentsGrowTarget = getComponent(Divider.class).getView().growsRecents(); 274 int currentUser = sSystemServicesProxy.getCurrentUser(); 275 if (sSystemServicesProxy.isSystemUser(currentUser)) { 276 mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */, 277 true /* animate */, recentsGrowTarget); 278 } else { 279 if (mSystemToUserCallbacks != null) { 280 IRecentsNonSystemUserCallbacks callbacks = 281 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); 282 if (callbacks != null) { 283 try { 284 callbacks.showRecents(triggeredFromAltTab, false /* draggingInRecents */, 285 true /* animate */, recentsGrowTarget); 286 } catch (RemoteException e) { 287 Log.e(TAG, "Callback failed", e); 288 } 289 } else { 290 Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); 291 } 292 } 293 } 294 } 295 296 /** 297 * Hides the Recents. 298 */ 299 @Override hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey)300 public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { 301 // Ensure the device has been provisioned before allowing the user to interact with 302 // recents 303 if (!isUserSetup()) { 304 return; 305 } 306 307 IOverviewProxy overviewProxy = mOverviewProxyService.getProxy(); 308 if (overviewProxy != null) { 309 try { 310 overviewProxy.onOverviewHidden(triggeredFromAltTab, triggeredFromHomeKey); 311 return; 312 } catch (RemoteException e) { 313 Log.e(TAG, "Failed to send overview hide event to launcher.", e); 314 } 315 } 316 317 int currentUser = sSystemServicesProxy.getCurrentUser(); 318 if (sSystemServicesProxy.isSystemUser(currentUser)) { 319 mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey); 320 } else { 321 if (mSystemToUserCallbacks != null) { 322 IRecentsNonSystemUserCallbacks callbacks = 323 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); 324 if (callbacks != null) { 325 try { 326 callbacks.hideRecents(triggeredFromAltTab, triggeredFromHomeKey); 327 } catch (RemoteException e) { 328 Log.e(TAG, "Callback failed", e); 329 } 330 } else { 331 Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); 332 } 333 } 334 } 335 } 336 337 /** 338 * Toggles the Recents activity. 339 */ 340 @Override toggleRecentApps()341 public void toggleRecentApps() { 342 // Ensure the device has been provisioned before allowing the user to interact with 343 // recents 344 if (!isUserSetup()) { 345 return; 346 } 347 348 // If connected to launcher service, let it handle the toggle logic 349 IOverviewProxy overviewProxy = mOverviewProxyService.getProxy(); 350 if (overviewProxy != null) { 351 final Runnable toggleRecents = () -> { 352 try { 353 if (mOverviewProxyService.getProxy() != null) { 354 mOverviewProxyService.getProxy().onOverviewToggle(); 355 } 356 } catch (RemoteException e) { 357 Log.e(TAG, "Cannot send toggle recents through proxy service.", e); 358 } 359 }; 360 // Preload only if device for current user is unlocked 361 final StatusBar statusBar = getComponent(StatusBar.class); 362 if (statusBar != null && statusBar.isKeyguardShowing()) { 363 statusBar.executeRunnableDismissingKeyguard(() -> { 364 // Flush trustmanager before checking device locked per user 365 mTrustManager.reportKeyguardShowingChanged(); 366 mHandler.post(toggleRecents); 367 }, null, true /* dismissShade */, false /* afterKeyguardGone */, 368 true /* deferred */); 369 } else { 370 toggleRecents.run(); 371 } 372 return; 373 } 374 375 int growTarget = getComponent(Divider.class).getView().growsRecents(); 376 int currentUser = sSystemServicesProxy.getCurrentUser(); 377 if (sSystemServicesProxy.isSystemUser(currentUser)) { 378 mImpl.toggleRecents(growTarget); 379 } else { 380 if (mSystemToUserCallbacks != null) { 381 IRecentsNonSystemUserCallbacks callbacks = 382 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); 383 if (callbacks != null) { 384 try { 385 callbacks.toggleRecents(growTarget); 386 } catch (RemoteException e) { 387 Log.e(TAG, "Callback failed", e); 388 } 389 } else { 390 Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); 391 } 392 } 393 } 394 } 395 396 /** 397 * Preloads info for the Recents activity. 398 */ 399 @Override preloadRecentApps()400 public void preloadRecentApps() { 401 // Ensure the device has been provisioned before allowing the user to interact with 402 // recents 403 if (!isUserSetup()) { 404 return; 405 } 406 407 if (mOverviewProxyService.getProxy() != null) { 408 // TODO: Proxy to Launcher 409 return; 410 } 411 412 int currentUser = sSystemServicesProxy.getCurrentUser(); 413 if (sSystemServicesProxy.isSystemUser(currentUser)) { 414 mImpl.preloadRecents(); 415 } else { 416 if (mSystemToUserCallbacks != null) { 417 IRecentsNonSystemUserCallbacks callbacks = 418 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); 419 if (callbacks != null) { 420 try { 421 callbacks.preloadRecents(); 422 } catch (RemoteException e) { 423 Log.e(TAG, "Callback failed", e); 424 } 425 } else { 426 Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); 427 } 428 } 429 } 430 } 431 432 @Override cancelPreloadRecentApps()433 public void cancelPreloadRecentApps() { 434 // Ensure the device has been provisioned before allowing the user to interact with 435 // recents 436 if (!isUserSetup()) { 437 return; 438 } 439 440 if (mOverviewProxyService.getProxy() != null) { 441 // TODO: Proxy to Launcher 442 return; 443 } 444 445 int currentUser = sSystemServicesProxy.getCurrentUser(); 446 if (sSystemServicesProxy.isSystemUser(currentUser)) { 447 mImpl.cancelPreloadingRecents(); 448 } else { 449 if (mSystemToUserCallbacks != null) { 450 IRecentsNonSystemUserCallbacks callbacks = 451 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); 452 if (callbacks != null) { 453 try { 454 callbacks.cancelPreloadingRecents(); 455 } catch (RemoteException e) { 456 Log.e(TAG, "Callback failed", e); 457 } 458 } else { 459 Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); 460 } 461 } 462 } 463 } 464 465 @Override splitPrimaryTask(int dragMode, int stackCreateMode, Rect initialBounds, int metricsDockAction)466 public boolean splitPrimaryTask(int dragMode, int stackCreateMode, Rect initialBounds, 467 int metricsDockAction) { 468 // Ensure the device has been provisioned before allowing the user to interact with 469 // recents 470 if (!isUserSetup()) { 471 return false; 472 } 473 474 Point realSize = new Point(); 475 if (initialBounds == null) { 476 mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY) 477 .getRealSize(realSize); 478 initialBounds = new Rect(0, 0, realSize.x, realSize.y); 479 } 480 481 int currentUser = sSystemServicesProxy.getCurrentUser(); 482 ActivityManager.RunningTaskInfo runningTask = 483 ActivityManagerWrapper.getInstance().getRunningTask(); 484 final int activityType = runningTask != null 485 ? runningTask.configuration.windowConfiguration.getActivityType() 486 : ACTIVITY_TYPE_UNDEFINED; 487 boolean screenPinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive(); 488 boolean isRunningTaskInHomeOrRecentsStack = 489 activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS; 490 if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) { 491 logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode); 492 if (runningTask.supportsSplitScreenMultiWindow) { 493 if (metricsDockAction != -1) { 494 MetricsLogger.action(mContext, metricsDockAction, 495 runningTask.topActivity.flattenToShortString()); 496 } 497 if (sSystemServicesProxy.isSystemUser(currentUser)) { 498 mImpl.splitPrimaryTask(runningTask.id, dragMode, stackCreateMode, 499 initialBounds); 500 } else { 501 if (mSystemToUserCallbacks != null) { 502 IRecentsNonSystemUserCallbacks callbacks = 503 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); 504 if (callbacks != null) { 505 try { 506 callbacks.splitPrimaryTask(runningTask.id, dragMode, 507 stackCreateMode, initialBounds); 508 } catch (RemoteException e) { 509 Log.e(TAG, "Callback failed", e); 510 } 511 } else { 512 Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); 513 } 514 } 515 } 516 mDraggingInRecentsCurrentUser = currentUser; 517 518 if (mOverviewProxyService.getProxy() != null) { 519 // The overview service is handling split screen, so just skip the wait for the 520 // first draw and notify the divider to start animating now 521 EventBus.getDefault().post(new RecentsDrawnEvent()); 522 } 523 524 return true; 525 } else { 526 EventBus.getDefault().send(new ShowUserToastEvent( 527 R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT)); 528 return false; 529 } 530 } else { 531 return false; 532 } 533 } 534 logDockAttempt(Context ctx, ComponentName activity, int resizeMode)535 public static void logDockAttempt(Context ctx, ComponentName activity, int resizeMode) { 536 if (resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE) { 537 MetricsLogger.action(ctx, MetricsEvent.ACTION_WINDOW_DOCK_UNRESIZABLE, 538 activity.flattenToShortString()); 539 } 540 MetricsLogger.count(ctx, getMetricsCounterForResizeMode(resizeMode), 1); 541 } 542 getMetricsCounterForResizeMode(int resizeMode)543 private static String getMetricsCounterForResizeMode(int resizeMode) { 544 switch (resizeMode) { 545 case ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE: 546 return COUNTER_WINDOW_UNSUPPORTED; 547 case ActivityInfo.RESIZE_MODE_RESIZEABLE: 548 case ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION: 549 return COUNTER_WINDOW_SUPPORTED; 550 default: 551 return COUNTER_WINDOW_INCOMPATIBLE; 552 } 553 } 554 555 @Override onDraggingInRecents(float distanceFromTop)556 public void onDraggingInRecents(float distanceFromTop) { 557 if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) { 558 mImpl.onDraggingInRecents(distanceFromTop); 559 } else { 560 if (mSystemToUserCallbacks != null) { 561 IRecentsNonSystemUserCallbacks callbacks = 562 mSystemToUserCallbacks.getNonSystemUserRecentsForUser( 563 mDraggingInRecentsCurrentUser); 564 if (callbacks != null) { 565 try { 566 callbacks.onDraggingInRecents(distanceFromTop); 567 } catch (RemoteException e) { 568 Log.e(TAG, "Callback failed", e); 569 } 570 } else { 571 Log.e(TAG, "No SystemUI callbacks found for user: " 572 + mDraggingInRecentsCurrentUser); 573 } 574 } 575 } 576 } 577 578 @Override onDraggingInRecentsEnded(float velocity)579 public void onDraggingInRecentsEnded(float velocity) { 580 if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) { 581 mImpl.onDraggingInRecentsEnded(velocity); 582 } else { 583 if (mSystemToUserCallbacks != null) { 584 IRecentsNonSystemUserCallbacks callbacks = 585 mSystemToUserCallbacks.getNonSystemUserRecentsForUser( 586 mDraggingInRecentsCurrentUser); 587 if (callbacks != null) { 588 try { 589 callbacks.onDraggingInRecentsEnded(velocity); 590 } catch (RemoteException e) { 591 Log.e(TAG, "Callback failed", e); 592 } 593 } else { 594 Log.e(TAG, "No SystemUI callbacks found for user: " 595 + mDraggingInRecentsCurrentUser); 596 } 597 } 598 } 599 } 600 601 @Override showNextAffiliatedTask()602 public void showNextAffiliatedTask() { 603 // Ensure the device has been provisioned before allowing the user to interact with 604 // recents 605 if (!isUserSetup()) { 606 return; 607 } 608 609 mImpl.showNextAffiliatedTask(); 610 } 611 612 @Override showPrevAffiliatedTask()613 public void showPrevAffiliatedTask() { 614 // Ensure the device has been provisioned before allowing the user to interact with 615 // recents 616 if (!isUserSetup()) { 617 return; 618 } 619 620 mImpl.showPrevAffiliatedTask(); 621 } 622 623 @Override appTransitionFinished()624 public void appTransitionFinished() { 625 if (!Recents.getConfiguration().isLowRamDevice) { 626 // Fallback, reset the flag once an app transition ends 627 EventBus.getDefault().send(new SetWaitingForTransitionStartEvent( 628 false /* waitingForTransitionStart */)); 629 } 630 } 631 632 /** 633 * Updates on configuration change. 634 */ onConfigurationChanged(Configuration newConfig)635 public void onConfigurationChanged(Configuration newConfig) { 636 int currentUser = sSystemServicesProxy.getCurrentUser(); 637 if (sSystemServicesProxy.isSystemUser(currentUser)) { 638 mImpl.onConfigurationChanged(); 639 } else { 640 if (mSystemToUserCallbacks != null) { 641 IRecentsNonSystemUserCallbacks callbacks = 642 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); 643 if (callbacks != null) { 644 try { 645 callbacks.onConfigurationChanged(); 646 } catch (RemoteException e) { 647 Log.e(TAG, "Callback failed", e); 648 } 649 } else { 650 Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); 651 } 652 } 653 } 654 } 655 656 /** 657 * Handle Recents activity visibility changed. 658 */ onBusEvent(final RecentsVisibilityChangedEvent event)659 public final void onBusEvent(final RecentsVisibilityChangedEvent event) { 660 SystemServicesProxy ssp = Recents.getSystemServices(); 661 int processUser = ssp.getProcessUser(); 662 if (ssp.isSystemUser(processUser)) { 663 mImpl.onVisibilityChanged(event.applicationContext, event.visible); 664 } else { 665 postToSystemUser(new Runnable() { 666 @Override 667 public void run() { 668 try { 669 mUserToSystemCallbacks.updateRecentsVisibility(event.visible); 670 } catch (RemoteException e) { 671 Log.e(TAG, "Callback failed", e); 672 } 673 } 674 }); 675 } 676 677 // This will catch the cases when a user launches from recents to another app 678 // (and vice versa) that is not in the recents stack (such as home or bugreport) and it 679 // would not reset the wait for transition flag. This will catch it and make sure that the 680 // flag is reset. 681 if (!event.visible) { 682 mImpl.setWaitingForTransitionStart(false); 683 } 684 } 685 onBusEvent(DockedFirstAnimationFrameEvent event)686 public final void onBusEvent(DockedFirstAnimationFrameEvent event) { 687 SystemServicesProxy ssp = Recents.getSystemServices(); 688 int processUser = ssp.getProcessUser(); 689 if (!ssp.isSystemUser(processUser)) { 690 postToSystemUser(new Runnable() { 691 @Override 692 public void run() { 693 try { 694 mUserToSystemCallbacks.sendDockedFirstAnimationFrameEvent(); 695 } catch (RemoteException e) { 696 Log.e(TAG, "Callback failed", e); 697 } 698 } 699 }); 700 } 701 } 702 703 /** 704 * Handle screen pinning request. 705 */ onBusEvent(final ScreenPinningRequestEvent event)706 public final void onBusEvent(final ScreenPinningRequestEvent event) { 707 int processUser = sSystemServicesProxy.getProcessUser(); 708 if (sSystemServicesProxy.isSystemUser(processUser)) { 709 mImpl.onStartScreenPinning(event.applicationContext, event.taskId); 710 } else { 711 postToSystemUser(new Runnable() { 712 @Override 713 public void run() { 714 try { 715 mUserToSystemCallbacks.startScreenPinning(event.taskId); 716 } catch (RemoteException e) { 717 Log.e(TAG, "Callback failed", e); 718 } 719 } 720 }); 721 } 722 } 723 onBusEvent(final RecentsDrawnEvent event)724 public final void onBusEvent(final RecentsDrawnEvent event) { 725 int processUser = sSystemServicesProxy.getProcessUser(); 726 if (!sSystemServicesProxy.isSystemUser(processUser)) { 727 postToSystemUser(new Runnable() { 728 @Override 729 public void run() { 730 try { 731 mUserToSystemCallbacks.sendRecentsDrawnEvent(); 732 } catch (RemoteException e) { 733 Log.e(TAG, "Callback failed", e); 734 } 735 } 736 }); 737 } 738 } 739 onBusEvent(final DockedTopTaskEvent event)740 public final void onBusEvent(final DockedTopTaskEvent event) { 741 int processUser = sSystemServicesProxy.getProcessUser(); 742 if (!sSystemServicesProxy.isSystemUser(processUser)) { 743 postToSystemUser(new Runnable() { 744 @Override 745 public void run() { 746 try { 747 mUserToSystemCallbacks.sendDockingTopTaskEvent(event.dragMode, 748 event.initialRect); 749 } catch (RemoteException e) { 750 Log.e(TAG, "Callback failed", e); 751 } 752 } 753 }); 754 } 755 } 756 onBusEvent(final RecentsActivityStartingEvent event)757 public final void onBusEvent(final RecentsActivityStartingEvent event) { 758 int processUser = sSystemServicesProxy.getProcessUser(); 759 if (!sSystemServicesProxy.isSystemUser(processUser)) { 760 postToSystemUser(new Runnable() { 761 @Override 762 public void run() { 763 try { 764 mUserToSystemCallbacks.sendLaunchRecentsEvent(); 765 } catch (RemoteException e) { 766 Log.e(TAG, "Callback failed", e); 767 } 768 } 769 }); 770 } 771 } 772 onBusEvent(LaunchTaskFailedEvent event)773 public final void onBusEvent(LaunchTaskFailedEvent event) { 774 // Reset the transition when tasks fail to launch 775 mImpl.setWaitingForTransitionStart(false); 776 } 777 onBusEvent(ConfigurationChangedEvent event)778 public final void onBusEvent(ConfigurationChangedEvent event) { 779 // Update the configuration for the Recents component when the activity configuration 780 // changes as well 781 mImpl.onConfigurationChanged(); 782 } 783 onBusEvent(ShowUserToastEvent event)784 public final void onBusEvent(ShowUserToastEvent event) { 785 int currentUser = sSystemServicesProxy.getCurrentUser(); 786 if (sSystemServicesProxy.isSystemUser(currentUser)) { 787 mImpl.onShowCurrentUserToast(event.msgResId, event.msgLength); 788 } else { 789 if (mSystemToUserCallbacks != null) { 790 IRecentsNonSystemUserCallbacks callbacks = 791 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); 792 if (callbacks != null) { 793 try { 794 callbacks.showCurrentUserToast(event.msgResId, event.msgLength); 795 } catch (RemoteException e) { 796 Log.e(TAG, "Callback failed", e); 797 } 798 } else { 799 Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); 800 } 801 } 802 } 803 } 804 onBusEvent(SetWaitingForTransitionStartEvent event)805 public final void onBusEvent(SetWaitingForTransitionStartEvent event) { 806 int processUser = sSystemServicesProxy.getProcessUser(); 807 if (sSystemServicesProxy.isSystemUser(processUser)) { 808 mImpl.setWaitingForTransitionStart(event.waitingForTransitionStart); 809 } else { 810 postToSystemUser(new Runnable() { 811 @Override 812 public void run() { 813 try { 814 mUserToSystemCallbacks.setWaitingForTransitionStartEvent( 815 event.waitingForTransitionStart); 816 } catch (RemoteException e) { 817 Log.e(TAG, "Callback failed", e); 818 } 819 } 820 }); 821 } 822 } 823 824 /** 825 * Attempts to register with the system user. 826 */ registerWithSystemUser()827 private void registerWithSystemUser() { 828 final int processUser = sSystemServicesProxy.getProcessUser(); 829 postToSystemUser(new Runnable() { 830 @Override 831 public void run() { 832 try { 833 mUserToSystemCallbacks.registerNonSystemUserCallbacks( 834 new RecentsImplProxy(mImpl), processUser); 835 } catch (RemoteException e) { 836 Log.e(TAG, "Failed to register", e); 837 } 838 } 839 }); 840 } 841 842 /** 843 * Runs the runnable in the system user's Recents context, connecting to the service if 844 * necessary. 845 */ postToSystemUser(final Runnable onConnectRunnable)846 private void postToSystemUser(final Runnable onConnectRunnable) { 847 mOnConnectRunnables.add(onConnectRunnable); 848 if (mUserToSystemCallbacks == null) { 849 Intent systemUserServiceIntent = new Intent(); 850 systemUserServiceIntent.setClass(mContext, RecentsSystemUserService.class); 851 boolean bound = mContext.bindServiceAsUser(systemUserServiceIntent, 852 mUserToSystemServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM); 853 EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION, 854 EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_BIND_SERVICE, 855 sSystemServicesProxy.getProcessUser()); 856 if (!bound) { 857 // Retry after a fixed duration 858 mHandler.postDelayed(new Runnable() { 859 @Override 860 public void run() { 861 registerWithSystemUser(); 862 } 863 }, BIND_TO_SYSTEM_USER_RETRY_DELAY); 864 } 865 } else { 866 runAndFlushOnConnectRunnables(); 867 } 868 } 869 870 /** 871 * Runs all the queued runnables after a service connection is made. 872 */ runAndFlushOnConnectRunnables()873 private void runAndFlushOnConnectRunnables() { 874 for (Runnable r : mOnConnectRunnables) { 875 r.run(); 876 } 877 mOnConnectRunnables.clear(); 878 } 879 880 /** 881 * @return whether this device is provisioned and the current user is set up. 882 */ isUserSetup()883 private boolean isUserSetup() { 884 ContentResolver cr = mContext.getContentResolver(); 885 return (Settings.Global.getInt(cr, Settings.Global.DEVICE_PROVISIONED, 0) != 0) && 886 (Settings.Secure.getInt(cr, Settings.Secure.USER_SETUP_COMPLETE, 0) != 0); 887 } 888 889 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)890 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 891 pw.println("Recents"); 892 pw.println(" currentUserId=" + SystemServicesProxy.getInstance(mContext).getCurrentUser()); 893 } 894 } 895