• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
getSystemUserCallbacks()181     public IBinder getSystemUserCallbacks() {
182         return mSystemToUserCallbacks;
183     }
184 
getTaskLoader()185     public static RecentsTaskLoader getTaskLoader() {
186         return sTaskLoader;
187     }
188 
189 
getSystemServices()190     public static SystemServicesProxy getSystemServices() {
191         return sSystemServicesProxy;
192     }
193 
getConfiguration()194     public static RecentsConfiguration getConfiguration() {
195         return sConfiguration;
196     }
197 
getDebugFlags()198     public static RecentsDebugFlags getDebugFlags() {
199         return sDebugFlags;
200     }
201 
202     @Override
start()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
onBootCompleted()242     public void onBootCompleted() {
243         mImpl.onBootCompleted();
244     }
245 
246     /**
247      * Shows the Recents.
248      */
249     @Override
showRecentApps(boolean triggeredFromAltTab, boolean fromHome)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
hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey)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
toggleRecentApps()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
preloadRecentApps()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
cancelPreloadRecentApps()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
dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds, int metricsDockAction)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 
logDockAttempt(Context ctx, ComponentName activity, int resizeMode)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 
getMetricsCounterForResizeMode(int resizeMode)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
onDraggingInRecents(float distanceFromTop)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
onDraggingInRecentsEnded(float velocity)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
showNextAffiliatedTask()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
showPrevAffiliatedTask()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
appTransitionFinished()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      */
onConfigurationChanged(Configuration newConfig)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      */
onBusEvent(final RecentsVisibilityChangedEvent event)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      */
onBusEvent(final ScreenPinningRequestEvent event)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 
onBusEvent(final RecentsDrawnEvent event)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 
onBusEvent(final DockedTopTaskEvent event)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 
onBusEvent(final RecentsActivityStartingEvent event)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 
onBusEvent(LaunchTaskFailedEvent event)701     public final void onBusEvent(LaunchTaskFailedEvent event) {
702         // Reset the transition when tasks fail to launch
703         mImpl.setWaitingForTransitionStart(false);
704     }
705 
onBusEvent(ConfigurationChangedEvent event)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 
onBusEvent(ShowUserToastEvent event)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 
onBusEvent(SetWaitingForTransitionStartEvent event)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      */
registerWithSystemUser()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      */
postToSystemUser(final Runnable onConnectRunnable)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      */
runAndFlushOnConnectRunnables()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      */
isUserSetup()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      */
proxyToOverridePackage(String action)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
dump(FileDescriptor fd, PrintWriter pw, String[] args)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