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