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