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 static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
20
21import android.app.ActivityManager;
22import android.content.ComponentName;
23import android.content.ContentResolver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.ServiceConnection;
27import android.content.pm.ActivityInfo;
28import android.content.res.Configuration;
29import android.graphics.Point;
30import android.graphics.Rect;
31import android.hardware.display.DisplayManager;
32import android.os.Build;
33import android.os.Handler;
34import android.os.IBinder;
35import android.os.RemoteException;
36import android.os.SystemProperties;
37import android.os.UserHandle;
38import android.provider.Settings;
39import android.util.EventLog;
40import android.util.Log;
41import android.view.Display;
42import android.widget.Toast;
43
44import com.android.internal.logging.MetricsLogger;
45import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
46import com.android.systemui.EventLogConstants;
47import com.android.systemui.EventLogTags;
48import com.android.systemui.R;
49import com.android.systemui.RecentsComponent;
50import com.android.systemui.SystemUI;
51import com.android.systemui.recents.events.EventBus;
52import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
53import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
54import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
55import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
56import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
57import com.android.systemui.recents.events.component.ShowUserToastEvent;
58import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
59import com.android.systemui.recents.misc.SystemServicesProxy;
60import com.android.systemui.recents.model.RecentsTaskLoader;
61import com.android.systemui.stackdivider.Divider;
62import com.android.systemui.statusbar.CommandQueue;
63
64import java.io.FileDescriptor;
65import java.io.PrintWriter;
66import java.util.ArrayList;
67import java.util.HashSet;
68import java.util.Set;
69
70
71/**
72 * An implementation of the SystemUI recents component, which supports both system and secondary
73 * users.
74 */
75public 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     */
179    public IBinder getSystemUserCallbacks() {
180        return mSystemToUserCallbacks;
181    }
182
183    public static RecentsTaskLoader getTaskLoader() {
184        return sTaskLoader;
185    }
186
187
188    public static SystemServicesProxy getSystemServices() {
189        return sSystemServicesProxy;
190    }
191
192    public static RecentsConfiguration getConfiguration() {
193        return sConfiguration;
194    }
195
196    public static RecentsDebugFlags getDebugFlags() {
197        return sDebugFlags;
198    }
199
200    @Override
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
240    public void onBootCompleted() {
241        mImpl.onBootCompleted();
242    }
243
244    /**
245     * Shows the Recents.
246     */
247    @Override
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
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
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
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
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
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
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
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
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
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
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
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     */
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     */
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     */
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
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
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
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
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
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     */
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     */
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     */
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     */
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     */
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
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