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