[go: nahoru, domu]

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