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 17package com.android.systemui.recents; 18 19import android.app.ActivityManager; 20import android.app.UiModeManager; 21import android.content.ComponentName; 22import android.content.ContentResolver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.ServiceConnection; 26import android.content.pm.ActivityInfo; 27import android.content.res.Configuration; 28import android.graphics.Point; 29import android.graphics.Rect; 30import android.hardware.display.DisplayManager; 31import android.os.Build; 32import android.os.Handler; 33import android.os.IBinder; 34import android.os.RemoteException; 35import android.os.SystemProperties; 36import android.os.UserHandle; 37import android.provider.Settings; 38import android.util.EventLog; 39import android.util.Log; 40import android.view.Display; 41import android.widget.Toast; 42 43import com.android.internal.logging.MetricsLogger; 44import com.android.internal.logging.MetricsProto.MetricsEvent; 45import com.android.systemui.EventLogConstants; 46import com.android.systemui.EventLogTags; 47import com.android.systemui.R; 48import com.android.systemui.RecentsComponent; 49import com.android.systemui.SystemUI; 50import com.android.systemui.recents.events.EventBus; 51import com.android.systemui.recents.events.activity.ConfigurationChangedEvent; 52import com.android.systemui.recents.events.activity.DockedTopTaskEvent; 53import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; 54import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; 55import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; 56import com.android.systemui.recents.events.ui.RecentsDrawnEvent; 57import com.android.systemui.recents.misc.SystemServicesProxy; 58import com.android.systemui.recents.model.RecentsTaskLoader; 59import com.android.systemui.recents.tv.RecentsTvImpl; 60import com.android.systemui.stackdivider.Divider; 61 62import java.util.ArrayList; 63 64 65/** 66 * An implementation of the SystemUI recents component, which supports both system and secondary 67 * users. 68 */ 69public class Recents extends SystemUI 70 implements RecentsComponent { 71 72 private final static String TAG = "Recents"; 73 private final static boolean DEBUG = false; 74 75 public final static int EVENT_BUS_PRIORITY = 1; 76 public final static int BIND_TO_SYSTEM_USER_RETRY_DELAY = 5000; 77 public final static int RECENTS_GROW_TARGET_INVALID = -1; 78 79 // Purely for experimentation 80 private final static String RECENTS_OVERRIDE_SYSPROP_KEY = "persist.recents_override_pkg"; 81 private final static String ACTION_SHOW_RECENTS = "com.android.systemui.recents.ACTION_SHOW"; 82 private final static String ACTION_HIDE_RECENTS = "com.android.systemui.recents.ACTION_HIDE"; 83 private final static String ACTION_TOGGLE_RECENTS = "com.android.systemui.recents.ACTION_TOGGLE"; 84 85 private static final String COUNTER_WINDOW_SUPPORTED = "window_enter_supported"; 86 private static final String COUNTER_WINDOW_UNSUPPORTED = "window_enter_unsupported"; 87 private static final String COUNTER_WINDOW_INCOMPATIBLE = "window_enter_incompatible"; 88 89 private static SystemServicesProxy sSystemServicesProxy; 90 private static RecentsDebugFlags sDebugFlags; 91 private static RecentsTaskLoader sTaskLoader; 92 private static RecentsConfiguration sConfiguration; 93 94 // For experiments only, allows another package to handle recents if it is defined in the system 95 // properties. This is limited to show/toggle/hide, and does not tie into the ActivityManager, 96 // and does not reside in the home stack. 97 private String mOverrideRecentsPackageName; 98 99 private Handler mHandler; 100 private RecentsImpl mImpl; 101 private int mDraggingInRecentsCurrentUser; 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 */ 168 public IBinder getSystemUserCallbacks() { 169 return mSystemToUserCallbacks; 170 } 171 172 public static RecentsTaskLoader getTaskLoader() { 173 return sTaskLoader; 174 } 175 176 public static SystemServicesProxy getSystemServices() { 177 return sSystemServicesProxy; 178 } 179 180 public static RecentsConfiguration getConfiguration() { 181 return sConfiguration; 182 } 183 184 public static RecentsDebugFlags getDebugFlags() { 185 return sDebugFlags; 186 } 187 188 @Override 189 public void start() { 190 sDebugFlags = new RecentsDebugFlags(mContext); 191 sSystemServicesProxy = SystemServicesProxy.getInstance(mContext); 192 sTaskLoader = new RecentsTaskLoader(mContext); 193 sConfiguration = new RecentsConfiguration(mContext); 194 mHandler = new Handler(); 195 UiModeManager uiModeManager = (UiModeManager) mContext. 196 getSystemService(Context.UI_MODE_SERVICE); 197 if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) { 198 mImpl = new RecentsTvImpl(mContext); 199 } else { 200 mImpl = new RecentsImpl(mContext); 201 } 202 203 // Check if there is a recents override package 204 if ("userdebug".equals(Build.TYPE) || "eng".equals(Build.TYPE)) { 205 String cnStr = SystemProperties.get(RECENTS_OVERRIDE_SYSPROP_KEY); 206 if (!cnStr.isEmpty()) { 207 mOverrideRecentsPackageName = cnStr; 208 } 209 } 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 putComponent(Recents.class, this); 230 } 231 232 @Override 233 public void onBootCompleted() { 234 mImpl.onBootCompleted(); 235 } 236 237 /** 238 * Shows the Recents. 239 */ 240 @Override 241 public void showRecents(boolean triggeredFromAltTab, boolean fromHome) { 242 // Ensure the device has been provisioned before allowing the user to interact with 243 // recents 244 if (!isUserSetup()) { 245 return; 246 } 247 248 if (proxyToOverridePackage(ACTION_SHOW_RECENTS)) { 249 return; 250 } 251 252 int recentsGrowTarget = getComponent(Divider.class).getView().growsRecents(); 253 254 int currentUser = sSystemServicesProxy.getCurrentUser(); 255 if (sSystemServicesProxy.isSystemUser(currentUser)) { 256 mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */, 257 true /* animate */, false /* reloadTasks */, fromHome, recentsGrowTarget); 258 } else { 259 if (mSystemToUserCallbacks != null) { 260 IRecentsNonSystemUserCallbacks callbacks = 261 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); 262 if (callbacks != null) { 263 try { 264 callbacks.showRecents(triggeredFromAltTab, false /* draggingInRecents */, 265 true /* animate */, false /* reloadTasks */, fromHome, 266 recentsGrowTarget); 267 } catch (RemoteException e) { 268 Log.e(TAG, "Callback failed", e); 269 } 270 } else { 271 Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); 272 } 273 } 274 } 275 } 276 277 /** 278 * Hides the Recents. 279 */ 280 @Override 281 public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { 282 // Ensure the device has been provisioned before allowing the user to interact with 283 // recents 284 if (!isUserSetup()) { 285 return; 286 } 287 288 if (proxyToOverridePackage(ACTION_HIDE_RECENTS)) { 289 return; 290 } 291 292 int currentUser = sSystemServicesProxy.getCurrentUser(); 293 if (sSystemServicesProxy.isSystemUser(currentUser)) { 294 mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey); 295 } else { 296 if (mSystemToUserCallbacks != null) { 297 IRecentsNonSystemUserCallbacks callbacks = 298 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); 299 if (callbacks != null) { 300 try { 301 callbacks.hideRecents(triggeredFromAltTab, triggeredFromHomeKey); 302 } catch (RemoteException e) { 303 Log.e(TAG, "Callback failed", e); 304 } 305 } else { 306 Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); 307 } 308 } 309 } 310 } 311 312 /** 313 * Toggles the Recents activity. 314 */ 315 @Override 316 public void toggleRecents(Display display) { 317 // Ensure the device has been provisioned before allowing the user to interact with 318 // recents 319 if (!isUserSetup()) { 320 return; 321 } 322 323 if (proxyToOverridePackage(ACTION_TOGGLE_RECENTS)) { 324 return; 325 } 326 327 int growTarget = getComponent(Divider.class).getView().growsRecents(); 328 329 int currentUser = sSystemServicesProxy.getCurrentUser(); 330 if (sSystemServicesProxy.isSystemUser(currentUser)) { 331 mImpl.toggleRecents(growTarget); 332 } else { 333 if (mSystemToUserCallbacks != null) { 334 IRecentsNonSystemUserCallbacks callbacks = 335 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); 336 if (callbacks != null) { 337 try { 338 callbacks.toggleRecents(growTarget); 339 } catch (RemoteException e) { 340 Log.e(TAG, "Callback failed", e); 341 } 342 } else { 343 Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); 344 } 345 } 346 } 347 } 348 349 /** 350 * Preloads info for the Recents activity. 351 */ 352 @Override 353 public void preloadRecents() { 354 // Ensure the device has been provisioned before allowing the user to interact with 355 // recents 356 if (!isUserSetup()) { 357 return; 358 } 359 360 int currentUser = sSystemServicesProxy.getCurrentUser(); 361 if (sSystemServicesProxy.isSystemUser(currentUser)) { 362 mImpl.preloadRecents(); 363 } else { 364 if (mSystemToUserCallbacks != null) { 365 IRecentsNonSystemUserCallbacks callbacks = 366 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); 367 if (callbacks != null) { 368 try { 369 callbacks.preloadRecents(); 370 } catch (RemoteException e) { 371 Log.e(TAG, "Callback failed", e); 372 } 373 } else { 374 Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); 375 } 376 } 377 } 378 } 379 380 @Override 381 public void cancelPreloadingRecents() { 382 // Ensure the device has been provisioned before allowing the user to interact with 383 // recents 384 if (!isUserSetup()) { 385 return; 386 } 387 388 int currentUser = sSystemServicesProxy.getCurrentUser(); 389 if (sSystemServicesProxy.isSystemUser(currentUser)) { 390 mImpl.cancelPreloadingRecents(); 391 } else { 392 if (mSystemToUserCallbacks != null) { 393 IRecentsNonSystemUserCallbacks callbacks = 394 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); 395 if (callbacks != null) { 396 try { 397 callbacks.cancelPreloadingRecents(); 398 } catch (RemoteException e) { 399 Log.e(TAG, "Callback failed", e); 400 } 401 } else { 402 Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); 403 } 404 } 405 } 406 } 407 408 @Override 409 public boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds, 410 int metricsDockAction) { 411 // Ensure the device has been provisioned before allowing the user to interact with 412 // recents 413 if (!isUserSetup()) { 414 return false; 415 } 416 417 Point realSize = new Point(); 418 if (initialBounds == null) { 419 mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY) 420 .getRealSize(realSize); 421 initialBounds = new Rect(0, 0, realSize.x, realSize.y); 422 } 423 424 int currentUser = sSystemServicesProxy.getCurrentUser(); 425 SystemServicesProxy ssp = Recents.getSystemServices(); 426 ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask(); 427 boolean screenPinningActive = ssp.isScreenPinningActive(); 428 boolean isRunningTaskInHomeStack = runningTask != null && 429 SystemServicesProxy.isHomeStack(runningTask.stackId); 430 if (runningTask != null && !isRunningTaskInHomeStack && !screenPinningActive) { 431 logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode); 432 if (runningTask.isDockable) { 433 if (metricsDockAction != -1) { 434 MetricsLogger.action(mContext, metricsDockAction, 435 runningTask.topActivity.flattenToShortString()); 436 } 437 if (sSystemServicesProxy.isSystemUser(currentUser)) { 438 mImpl.dockTopTask(runningTask.id, dragMode, stackCreateMode, initialBounds); 439 } else { 440 if (mSystemToUserCallbacks != null) { 441 IRecentsNonSystemUserCallbacks callbacks = 442 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); 443 if (callbacks != null) { 444 try { 445 callbacks.dockTopTask(runningTask.id, dragMode, stackCreateMode, 446 initialBounds); 447 } catch (RemoteException e) { 448 Log.e(TAG, "Callback failed", e); 449 } 450 } else { 451 Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); 452 } 453 } 454 } 455 mDraggingInRecentsCurrentUser = currentUser; 456 return true; 457 } else { 458 Toast.makeText(mContext, R.string.recents_incompatible_app_message, 459 Toast.LENGTH_SHORT).show(); 460 return false; 461 } 462 } else { 463 return false; 464 } 465 } 466 467 public static void logDockAttempt(Context ctx, ComponentName activity, int resizeMode) { 468 if (resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE) { 469 MetricsLogger.action(ctx, MetricsEvent.ACTION_WINDOW_DOCK_UNRESIZABLE, 470 activity.flattenToShortString()); 471 } 472 MetricsLogger.count(ctx, getMetricsCounterForResizeMode(resizeMode), 1); 473 } 474 475 private static String getMetricsCounterForResizeMode(int resizeMode) { 476 switch (resizeMode) { 477 case ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE: 478 return COUNTER_WINDOW_UNSUPPORTED; 479 case ActivityInfo.RESIZE_MODE_RESIZEABLE: 480 case ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE: 481 return COUNTER_WINDOW_SUPPORTED; 482 default: 483 return COUNTER_WINDOW_INCOMPATIBLE; 484 } 485 } 486 487 @Override 488 public void onDraggingInRecents(float distanceFromTop) { 489 if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) { 490 mImpl.onDraggingInRecents(distanceFromTop); 491 } else { 492 if (mSystemToUserCallbacks != null) { 493 IRecentsNonSystemUserCallbacks callbacks = 494 mSystemToUserCallbacks.getNonSystemUserRecentsForUser( 495 mDraggingInRecentsCurrentUser); 496 if (callbacks != null) { 497 try { 498 callbacks.onDraggingInRecents(distanceFromTop); 499 } catch (RemoteException e) { 500 Log.e(TAG, "Callback failed", e); 501 } 502 } else { 503 Log.e(TAG, "No SystemUI callbacks found for user: " 504 + mDraggingInRecentsCurrentUser); 505 } 506 } 507 } 508 } 509 510 @Override 511 public void onDraggingInRecentsEnded(float velocity) { 512 if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) { 513 mImpl.onDraggingInRecentsEnded(velocity); 514 } else { 515 if (mSystemToUserCallbacks != null) { 516 IRecentsNonSystemUserCallbacks callbacks = 517 mSystemToUserCallbacks.getNonSystemUserRecentsForUser( 518 mDraggingInRecentsCurrentUser); 519 if (callbacks != null) { 520 try { 521 callbacks.onDraggingInRecentsEnded(velocity); 522 } catch (RemoteException e) { 523 Log.e(TAG, "Callback failed", e); 524 } 525 } else { 526 Log.e(TAG, "No SystemUI callbacks found for user: " 527 + mDraggingInRecentsCurrentUser); 528 } 529 } 530 } 531 } 532 533 @Override 534 public void showNextAffiliatedTask() { 535 // Ensure the device has been provisioned before allowing the user to interact with 536 // recents 537 if (!isUserSetup()) { 538 return; 539 } 540 541 mImpl.showNextAffiliatedTask(); 542 } 543 544 @Override 545 public void showPrevAffiliatedTask() { 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.showPrevAffiliatedTask(); 553 } 554 555 /** 556 * Updates on configuration change. 557 */ 558 public void onConfigurationChanged(Configuration newConfig) { 559 int currentUser = sSystemServicesProxy.getCurrentUser(); 560 if (sSystemServicesProxy.isSystemUser(currentUser)) { 561 mImpl.onConfigurationChanged(); 562 } else { 563 if (mSystemToUserCallbacks != null) { 564 IRecentsNonSystemUserCallbacks callbacks = 565 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); 566 if (callbacks != null) { 567 try { 568 callbacks.onConfigurationChanged(); 569 } catch (RemoteException e) { 570 Log.e(TAG, "Callback failed", e); 571 } 572 } else { 573 Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); 574 } 575 } 576 } 577 } 578 579 /** 580 * Handle Recents activity visibility changed. 581 */ 582 public final void onBusEvent(final RecentsVisibilityChangedEvent event) { 583 SystemServicesProxy ssp = Recents.getSystemServices(); 584 int processUser = ssp.getProcessUser(); 585 if (ssp.isSystemUser(processUser)) { 586 mImpl.onVisibilityChanged(event.applicationContext, event.visible); 587 } else { 588 postToSystemUser(new Runnable() { 589 @Override 590 public void run() { 591 try { 592 mUserToSystemCallbacks.updateRecentsVisibility(event.visible); 593 } catch (RemoteException e) { 594 Log.e(TAG, "Callback failed", e); 595 } 596 } 597 }); 598 } 599 } 600 601 /** 602 * Handle screen pinning request. 603 */ 604 public final void onBusEvent(final ScreenPinningRequestEvent event) { 605 int processUser = sSystemServicesProxy.getProcessUser(); 606 if (sSystemServicesProxy.isSystemUser(processUser)) { 607 mImpl.onStartScreenPinning(event.applicationContext, event.taskId); 608 } else { 609 postToSystemUser(new Runnable() { 610 @Override 611 public void run() { 612 try { 613 mUserToSystemCallbacks.startScreenPinning(event.taskId); 614 } catch (RemoteException e) { 615 Log.e(TAG, "Callback failed", e); 616 } 617 } 618 }); 619 } 620 } 621 622 public final void onBusEvent(final RecentsDrawnEvent event) { 623 int processUser = sSystemServicesProxy.getProcessUser(); 624 if (!sSystemServicesProxy.isSystemUser(processUser)) { 625 postToSystemUser(new Runnable() { 626 @Override 627 public void run() { 628 try { 629 mUserToSystemCallbacks.sendRecentsDrawnEvent(); 630 } catch (RemoteException e) { 631 Log.e(TAG, "Callback failed", e); 632 } 633 } 634 }); 635 } 636 } 637 638 public final void onBusEvent(final DockedTopTaskEvent event) { 639 int processUser = sSystemServicesProxy.getProcessUser(); 640 if (!sSystemServicesProxy.isSystemUser(processUser)) { 641 postToSystemUser(new Runnable() { 642 @Override 643 public void run() { 644 try { 645 mUserToSystemCallbacks.sendDockingTopTaskEvent(event.dragMode, 646 event.initialRect); 647 } catch (RemoteException e) { 648 Log.e(TAG, "Callback failed", e); 649 } 650 } 651 }); 652 } 653 } 654 655 public final void onBusEvent(final RecentsActivityStartingEvent event) { 656 int processUser = sSystemServicesProxy.getProcessUser(); 657 if (!sSystemServicesProxy.isSystemUser(processUser)) { 658 postToSystemUser(new Runnable() { 659 @Override 660 public void run() { 661 try { 662 mUserToSystemCallbacks.sendLaunchRecentsEvent(); 663 } catch (RemoteException e) { 664 Log.e(TAG, "Callback failed", e); 665 } 666 } 667 }); 668 } 669 } 670 671 public final void onBusEvent(ConfigurationChangedEvent event) { 672 // Update the configuration for the Recents component when the activity configuration 673 // changes as well 674 mImpl.onConfigurationChanged(); 675 } 676 677 /** 678 * Attempts to register with the system user. 679 */ 680 private void registerWithSystemUser() { 681 final int processUser = sSystemServicesProxy.getProcessUser(); 682 postToSystemUser(new Runnable() { 683 @Override 684 public void run() { 685 try { 686 mUserToSystemCallbacks.registerNonSystemUserCallbacks( 687 new RecentsImplProxy(mImpl), processUser); 688 } catch (RemoteException e) { 689 Log.e(TAG, "Failed to register", e); 690 } 691 } 692 }); 693 } 694 695 /** 696 * Runs the runnable in the system user's Recents context, connecting to the service if 697 * necessary. 698 */ 699 private void postToSystemUser(final Runnable onConnectRunnable) { 700 mOnConnectRunnables.add(onConnectRunnable); 701 if (mUserToSystemCallbacks == null) { 702 Intent systemUserServiceIntent = new Intent(); 703 systemUserServiceIntent.setClass(mContext, RecentsSystemUserService.class); 704 boolean bound = mContext.bindServiceAsUser(systemUserServiceIntent, 705 mUserToSystemServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM); 706 EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION, 707 EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_BIND_SERVICE, 708 sSystemServicesProxy.getProcessUser()); 709 if (!bound) { 710 // Retry after a fixed duration 711 mHandler.postDelayed(new Runnable() { 712 @Override 713 public void run() { 714 registerWithSystemUser(); 715 } 716 }, BIND_TO_SYSTEM_USER_RETRY_DELAY); 717 } 718 } else { 719 runAndFlushOnConnectRunnables(); 720 } 721 } 722 723 /** 724 * Runs all the queued runnables after a service connection is made. 725 */ 726 private void runAndFlushOnConnectRunnables() { 727 for (Runnable r : mOnConnectRunnables) { 728 r.run(); 729 } 730 mOnConnectRunnables.clear(); 731 } 732 733 /** 734 * @return whether this device is provisioned and the current user is set up. 735 */ 736 private boolean isUserSetup() { 737 ContentResolver cr = mContext.getContentResolver(); 738 return (Settings.Global.getInt(cr, Settings.Global.DEVICE_PROVISIONED, 0) != 0) && 739 (Settings.Secure.getInt(cr, Settings.Secure.USER_SETUP_COMPLETE, 0) != 0); 740 } 741 742 /** 743 * Attempts to proxy the following action to the override recents package. 744 * @return whether the proxying was successful 745 */ 746 private boolean proxyToOverridePackage(String action) { 747 if (mOverrideRecentsPackageName != null) { 748 Intent intent = new Intent(action); 749 intent.setPackage(mOverrideRecentsPackageName); 750 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 751 mContext.sendBroadcast(intent); 752 return true; 753 } 754 return false; 755 } 756} 757