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 android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
22 import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
23 
24 import android.app.ActivityManager;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.ServiceConnection;
29 import android.content.pm.ActivityInfo;
30 import android.content.res.Configuration;
31 import android.content.res.Resources;
32 import android.graphics.Point;
33 import android.graphics.Rect;
34 import android.hardware.display.DisplayManager;
35 import android.os.Handler;
36 import android.os.IBinder;
37 import android.os.RemoteException;
38 import android.os.UserHandle;
39 import android.util.EventLog;
40 import android.util.Log;
41 import android.view.Display;
42 import android.widget.Toast;
43 import com.android.internal.logging.MetricsLogger;
44 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
45 import com.android.systemui.EventLogConstants;
46 import com.android.systemui.EventLogTags;
47 import com.android.systemui.R;
48 import com.android.systemui.SysUiServiceProvider;
49 import com.android.systemui.pip.PipUI;
50 import com.android.systemui.recents.events.EventBus;
51 import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
52 import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
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.ExpandPipEvent;
57 import com.android.systemui.recents.events.component.HidePipMenuEvent;
58 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
59 import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
60 import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
61 import com.android.systemui.recents.events.component.ShowUserToastEvent;
62 import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
63 import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
64 import com.android.systemui.recents.misc.SystemServicesProxy;
65 import com.android.systemui.recents.model.RecentsTaskLoader;
66 import com.android.systemui.shared.system.ActivityManagerWrapper;
67 import com.android.systemui.stackdivider.Divider;
68 import java.io.PrintWriter;
69 import java.util.ArrayList;
70 import java.util.HashSet;
71 import java.util.Set;
72 
73 /**
74  * An implementation of the SystemUI recents component, which supports both system and secondary
75  * users.
76  */
77 public class LegacyRecentsImpl implements RecentsImplementation {
78 
79     private final static String TAG = "Recents";
80 
81     public final static int EVENT_BUS_PRIORITY = 1;
82     public final static int BIND_TO_SYSTEM_USER_RETRY_DELAY = 5000;
83 
84     public final static Set<String> RECENTS_ACTIVITIES = new HashSet<>();
85     static {
86         RECENTS_ACTIVITIES.add(RecentsImpl.RECENTS_ACTIVITY);
87     }
88 
89     private static final String COUNTER_WINDOW_SUPPORTED = "window_enter_supported";
90     private static final String COUNTER_WINDOW_UNSUPPORTED = "window_enter_unsupported";
91     private static final String COUNTER_WINDOW_INCOMPATIBLE = "window_enter_incompatible";
92 
93     private static SystemServicesProxy sSystemServicesProxy;
94     private static RecentsDebugFlags sDebugFlags;
95     private static RecentsTaskLoader sTaskLoader;
96     private static RecentsConfiguration sConfiguration;
97 
98     private Context mContext;
99     private SysUiServiceProvider mSysUiServiceProvider;
100     private Handler mHandler;
101     private RecentsImpl mImpl;
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      */
getSystemUserCallbacks()168     public IBinder getSystemUserCallbacks() {
169         return mSystemToUserCallbacks;
170     }
171 
getTaskLoader()172     public static RecentsTaskLoader getTaskLoader() {
173         return sTaskLoader;
174     }
175 
176 
getSystemServices()177     public static SystemServicesProxy getSystemServices() {
178         return sSystemServicesProxy;
179     }
180 
getConfiguration()181     public static RecentsConfiguration getConfiguration() {
182         return sConfiguration;
183     }
184 
getDebugFlags()185     public static RecentsDebugFlags getDebugFlags() {
186         return sDebugFlags;
187     }
188 
189     @Override
onStart(Context context, SysUiServiceProvider sysUiServiceProvider)190     public void onStart(Context context, SysUiServiceProvider sysUiServiceProvider) {
191         mContext = context;
192         mSysUiServiceProvider = sysUiServiceProvider;
193         final Resources res = mContext.getResources();
194         final int defaultTaskBarBackgroundColor =
195                 mContext.getColor(R.color.recents_task_bar_default_background_color);
196         final int defaultTaskViewBackgroundColor =
197                 mContext.getColor(R.color.recents_task_view_default_background_color);
198         sDebugFlags = new RecentsDebugFlags();
199         sSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
200         sConfiguration = new RecentsConfiguration(mContext);
201         sTaskLoader = new RecentsTaskLoader(mContext,
202                 // TODO: Once we start building the AAR, move these into the loader
203                 res.getInteger(R.integer.config_recents_max_thumbnail_count),
204                 res.getInteger(R.integer.config_recents_max_icon_count),
205                 res.getInteger(R.integer.recents_svelte_level));
206         sTaskLoader.setDefaultColors(defaultTaskBarBackgroundColor, defaultTaskViewBackgroundColor);
207         mHandler = new Handler();
208         mImpl = new RecentsImpl(mContext);
209 
210         // Register with the event bus
211         EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
212         EventBus.getDefault().register(sSystemServicesProxy, EVENT_BUS_PRIORITY);
213         EventBus.getDefault().register(sTaskLoader, EVENT_BUS_PRIORITY);
214 
215         // Due to the fact that RecentsActivity is per-user, we need to establish and interface for
216         // the system user's Recents component to pass events (like show/hide/toggleRecents) to the
217         // secondary user, and vice versa (like visibility change, screen pinning).
218         final int processUser = sSystemServicesProxy.getProcessUser();
219         if (sSystemServicesProxy.isSystemUser(processUser)) {
220             // For the system user, initialize an instance of the interface that we can pass to the
221             // secondary user
222             mSystemToUserCallbacks = new RecentsSystemUser(mContext, mImpl);
223         } else {
224             // For the secondary user, bind to the primary user's service to get a persistent
225             // interface to register its implementation and to later update its state
226             registerWithSystemUser();
227         }
228     }
229 
230     @Override
onBootCompleted()231     public void onBootCompleted() {
232         mImpl.onBootCompleted();
233     }
234 
235 
236     @Override
growRecents()237     public void growRecents() {
238         EventBus.getDefault().send(new RecentsGrowingEvent());
239     }
240 
241     /**
242      * Shows the Recents.
243      */
244     @Override
showRecentApps(boolean triggeredFromAltTab)245     public void showRecentApps(boolean triggeredFromAltTab) {
246         ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
247         int recentsGrowTarget = getComponent(Divider.class).getView().growsRecents();
248         int currentUser = sSystemServicesProxy.getCurrentUser();
249         if (sSystemServicesProxy.isSystemUser(currentUser)) {
250             mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
251                     true /* animate */, recentsGrowTarget);
252         } else {
253             if (mSystemToUserCallbacks != null) {
254                 IRecentsNonSystemUserCallbacks callbacks =
255                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
256                 if (callbacks != null) {
257                     try {
258                         callbacks.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
259                                 true /* animate */, recentsGrowTarget);
260                     } catch (RemoteException e) {
261                         Log.e(TAG, "Callback failed", e);
262                     }
263                 } else {
264                     Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
265                 }
266             }
267         }
268     }
269 
270     /**
271      * Hides the Recents.
272      */
273     @Override
hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey)274     public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
275         int currentUser = sSystemServicesProxy.getCurrentUser();
276         if (sSystemServicesProxy.isSystemUser(currentUser)) {
277             mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
278         } else {
279             if (mSystemToUserCallbacks != null) {
280                 IRecentsNonSystemUserCallbacks callbacks =
281                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
282                 if (callbacks != null) {
283                     try {
284                         callbacks.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
285                     } catch (RemoteException e) {
286                         Log.e(TAG, "Callback failed", e);
287                     }
288                 } else {
289                     Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
290                 }
291             }
292         }
293     }
294 
295     /**
296      * Toggles the Recents activity.
297      */
298     @Override
toggleRecentApps()299     public void toggleRecentApps() {
300         int growTarget = getComponent(Divider.class).getView().growsRecents();
301         int currentUser = sSystemServicesProxy.getCurrentUser();
302         if (sSystemServicesProxy.isSystemUser(currentUser)) {
303             mImpl.toggleRecents(growTarget);
304         } else {
305             if (mSystemToUserCallbacks != null) {
306                 IRecentsNonSystemUserCallbacks callbacks =
307                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
308                 if (callbacks != null) {
309                     try {
310                         callbacks.toggleRecents(growTarget);
311                     } catch (RemoteException e) {
312                         Log.e(TAG, "Callback failed", e);
313                     }
314                 } else {
315                     Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
316                 }
317             }
318         }
319     }
320 
321     /**
322      * Preloads info for the Recents activity.
323      */
324     @Override
preloadRecentApps()325     public void preloadRecentApps() {
326         int currentUser = sSystemServicesProxy.getCurrentUser();
327         if (sSystemServicesProxy.isSystemUser(currentUser)) {
328             mImpl.preloadRecents();
329         } else {
330             if (mSystemToUserCallbacks != null) {
331                 IRecentsNonSystemUserCallbacks callbacks =
332                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
333                 if (callbacks != null) {
334                     try {
335                         callbacks.preloadRecents();
336                     } catch (RemoteException e) {
337                         Log.e(TAG, "Callback failed", e);
338                     }
339                 } else {
340                     Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
341                 }
342             }
343         }
344     }
345 
346     @Override
cancelPreloadRecentApps()347     public void cancelPreloadRecentApps() {
348         int currentUser = sSystemServicesProxy.getCurrentUser();
349         if (sSystemServicesProxy.isSystemUser(currentUser)) {
350             mImpl.cancelPreloadingRecents();
351         } else {
352             if (mSystemToUserCallbacks != null) {
353                 IRecentsNonSystemUserCallbacks callbacks =
354                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
355                 if (callbacks != null) {
356                     try {
357                         callbacks.cancelPreloadingRecents();
358                     } catch (RemoteException e) {
359                         Log.e(TAG, "Callback failed", e);
360                     }
361                 } else {
362                     Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
363                 }
364             }
365         }
366     }
367 
368     @Override
splitPrimaryTask(int stackCreateMode, Rect initialBounds, int metricsDockAction)369     public boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds, int metricsDockAction) {
370         Point realSize = new Point();
371         if (initialBounds == null) {
372             mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
373                     .getRealSize(realSize);
374             initialBounds = new Rect(0, 0, realSize.x, realSize.y);
375         }
376 
377         int currentUser = sSystemServicesProxy.getCurrentUser();
378         ActivityManager.RunningTaskInfo runningTask =
379                 ActivityManagerWrapper.getInstance().getRunningTask();
380         final int activityType = runningTask != null
381                 ? runningTask.configuration.windowConfiguration.getActivityType()
382                 : ACTIVITY_TYPE_UNDEFINED;
383         boolean screenPinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
384         boolean isRunningTaskInHomeOrRecentsStack =
385                 activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
386         if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
387             logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode);
388             if (runningTask.supportsSplitScreenMultiWindow) {
389                 if (metricsDockAction != -1) {
390                     MetricsLogger.action(mContext, metricsDockAction,
391                             runningTask.topActivity.flattenToShortString());
392                 }
393                 if (sSystemServicesProxy.isSystemUser(currentUser)) {
394                     mImpl.splitPrimaryTask(runningTask.id, stackCreateMode, initialBounds);
395                 } else {
396                     if (mSystemToUserCallbacks != null) {
397                         IRecentsNonSystemUserCallbacks callbacks =
398                                 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
399                         if (callbacks != null) {
400                             try {
401                                 callbacks.splitPrimaryTask(runningTask.id, stackCreateMode,
402                                         initialBounds);
403                             } catch (RemoteException e) {
404                                 Log.e(TAG, "Callback failed", e);
405                             }
406                         } else {
407                             Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
408                         }
409                     }
410                 }
411 
412                 return true;
413             } else {
414                 EventBus.getDefault().send(new ShowUserToastEvent(
415                         R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT));
416                 return false;
417             }
418         } else {
419             return false;
420         }
421     }
422 
logDockAttempt(Context ctx, ComponentName activity, int resizeMode)423     public static void logDockAttempt(Context ctx, ComponentName activity, int resizeMode) {
424         if (resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE) {
425             MetricsLogger.action(ctx, MetricsEvent.ACTION_WINDOW_DOCK_UNRESIZABLE,
426                     activity.flattenToShortString());
427         }
428         MetricsLogger.count(ctx, getMetricsCounterForResizeMode(resizeMode), 1);
429     }
430 
getMetricsCounterForResizeMode(int resizeMode)431     private static String getMetricsCounterForResizeMode(int resizeMode) {
432         switch (resizeMode) {
433             case ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE:
434                 return COUNTER_WINDOW_UNSUPPORTED;
435             case ActivityInfo.RESIZE_MODE_RESIZEABLE:
436             case ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION:
437                 return COUNTER_WINDOW_SUPPORTED;
438             default:
439                 return COUNTER_WINDOW_INCOMPATIBLE;
440         }
441     }
442 
443     @Override
onAppTransitionFinished()444     public void onAppTransitionFinished() {
445         if (!LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
446             // Fallback, reset the flag once an app transition ends
447             EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(
448                     false /* waitingForTransitionStart */));
449         }
450     }
451 
452     /**
453      * Updates on configuration change.
454      */
onConfigurationChanged(Configuration newConfig)455     public void onConfigurationChanged(Configuration newConfig) {
456         int currentUser = sSystemServicesProxy.getCurrentUser();
457         if (sSystemServicesProxy.isSystemUser(currentUser)) {
458             mImpl.onConfigurationChanged();
459         } else {
460             if (mSystemToUserCallbacks != null) {
461                 IRecentsNonSystemUserCallbacks callbacks =
462                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
463                 if (callbacks != null) {
464                     try {
465                         callbacks.onConfigurationChanged();
466                     } catch (RemoteException e) {
467                         Log.e(TAG, "Callback failed", e);
468                     }
469                 } else {
470                     Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
471                 }
472             }
473         }
474     }
475 
476     /**
477      * Handle Recents activity visibility changed.
478      */
onBusEvent(final RecentsVisibilityChangedEvent event)479     public final void onBusEvent(final RecentsVisibilityChangedEvent event) {
480         SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
481         int processUser = ssp.getProcessUser();
482         if (ssp.isSystemUser(processUser)) {
483             mImpl.onVisibilityChanged(event.applicationContext, event.visible);
484         } else {
485             postToSystemUser(new Runnable() {
486                 @Override
487                 public void run() {
488                     try {
489                         mUserToSystemCallbacks.updateRecentsVisibility(event.visible);
490                     } catch (RemoteException e) {
491                         Log.e(TAG, "Callback failed", e);
492                     }
493                 }
494             });
495         }
496 
497         // This will catch the cases when a user launches from recents to another app
498         // (and vice versa) that is not in the recents stack (such as home or bugreport) and it
499         // would not reset the wait for transition flag. This will catch it and make sure that the
500         // flag is reset.
501         if (!event.visible) {
502             mImpl.setWaitingForTransitionStart(false);
503         }
504     }
505 
onBusEvent(DockedFirstAnimationFrameEvent event)506     public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
507         SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
508         int processUser = ssp.getProcessUser();
509         if (ssp.isSystemUser(processUser)) {
510             final Divider divider = getComponent(Divider.class);
511             if (divider != null) {
512                 divider.onDockedFirstAnimationFrame();
513             }
514         } else {
515             postToSystemUser(new Runnable() {
516                 @Override
517                 public void run() {
518                     try {
519                         mUserToSystemCallbacks.sendDockedFirstAnimationFrameEvent();
520                     } catch (RemoteException e) {
521                         Log.e(TAG, "Callback failed", e);
522                     }
523                 }
524             });
525         }
526     }
527 
528     /**
529      * Handle screen pinning request.
530      */
onBusEvent(final ScreenPinningRequestEvent event)531     public final void onBusEvent(final ScreenPinningRequestEvent event) {
532         int processUser = sSystemServicesProxy.getProcessUser();
533         if (sSystemServicesProxy.isSystemUser(processUser)) {
534             mImpl.onStartScreenPinning(event.applicationContext, event.taskId);
535         } else {
536             postToSystemUser(new Runnable() {
537                 @Override
538                 public void run() {
539                     try {
540                         mUserToSystemCallbacks.startScreenPinning(event.taskId);
541                     } catch (RemoteException e) {
542                         Log.e(TAG, "Callback failed", e);
543                     }
544                 }
545             });
546         }
547     }
548 
onBusEvent(final RecentsDrawnEvent event)549     public final void onBusEvent(final RecentsDrawnEvent event) {
550         int processUser = sSystemServicesProxy.getProcessUser();
551         if (sSystemServicesProxy.isSystemUser(processUser)) {
552             final Divider divider = getComponent(Divider.class);
553             if (divider != null) {
554                 divider.onRecentsDrawn();
555             }
556         } else {
557             postToSystemUser(new Runnable() {
558                 @Override
559                 public void run() {
560                     try {
561                         mUserToSystemCallbacks.sendRecentsDrawnEvent();
562                     } catch (RemoteException e) {
563                         Log.e(TAG, "Callback failed", e);
564                     }
565                 }
566             });
567         }
568     }
569 
onBusEvent(final DockedTopTaskEvent event)570     public final void onBusEvent(final DockedTopTaskEvent event) {
571         int processUser = sSystemServicesProxy.getProcessUser();
572         if (sSystemServicesProxy.isSystemUser(processUser)) {
573             final Divider divider = getComponent(Divider.class);
574             if (divider != null) {
575                 divider.onDockedTopTask();
576             }
577         } else {
578             postToSystemUser(new Runnable() {
579                 @Override
580                 public void run() {
581                     try {
582                         mUserToSystemCallbacks.sendDockingTopTaskEvent(event.initialRect);
583                     } catch (RemoteException e) {
584                         Log.e(TAG, "Callback failed", e);
585                     }
586                 }
587             });
588         }
589     }
590 
onBusEvent(final RecentsActivityStartingEvent event)591     public final void onBusEvent(final RecentsActivityStartingEvent event) {
592         int processUser = sSystemServicesProxy.getProcessUser();
593         if (sSystemServicesProxy.isSystemUser(processUser)) {
594             final Divider divider = getComponent(Divider.class);
595             if (divider != null) {
596                 divider.onRecentsActivityStarting();
597             }
598         } else {
599             postToSystemUser(new Runnable() {
600                 @Override
601                 public void run() {
602                     try {
603                         mUserToSystemCallbacks.sendLaunchRecentsEvent();
604                     } catch (RemoteException e) {
605                         Log.e(TAG, "Callback failed", e);
606                     }
607                 }
608             });
609         }
610     }
611 
onBusEvent(LaunchTaskFailedEvent event)612     public final void onBusEvent(LaunchTaskFailedEvent event) {
613         // Reset the transition when tasks fail to launch
614         mImpl.setWaitingForTransitionStart(false);
615     }
616 
onBusEvent(ConfigurationChangedEvent event)617     public final void onBusEvent(ConfigurationChangedEvent event) {
618         // Update the configuration for the Recents component when the activity configuration
619         // changes as well
620         mImpl.onConfigurationChanged();
621     }
622 
onBusEvent(ShowUserToastEvent event)623     public final void onBusEvent(ShowUserToastEvent event) {
624         int currentUser = sSystemServicesProxy.getCurrentUser();
625         if (sSystemServicesProxy.isSystemUser(currentUser)) {
626             mImpl.onShowCurrentUserToast(event.msgResId, event.msgLength);
627         } else {
628             if (mSystemToUserCallbacks != null) {
629                 IRecentsNonSystemUserCallbacks callbacks =
630                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
631                 if (callbacks != null) {
632                     try {
633                         callbacks.showCurrentUserToast(event.msgResId, event.msgLength);
634                     } catch (RemoteException e) {
635                         Log.e(TAG, "Callback failed", e);
636                     }
637                 } else {
638                     Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
639                 }
640             }
641         }
642     }
643 
onBusEvent(SetWaitingForTransitionStartEvent event)644     public final void onBusEvent(SetWaitingForTransitionStartEvent event) {
645         int processUser = sSystemServicesProxy.getProcessUser();
646         if (sSystemServicesProxy.isSystemUser(processUser)) {
647             mImpl.setWaitingForTransitionStart(event.waitingForTransitionStart);
648         } else {
649             postToSystemUser(new Runnable() {
650                 @Override
651                 public void run() {
652                     try {
653                         mUserToSystemCallbacks.setWaitingForTransitionStartEvent(
654                                 event.waitingForTransitionStart);
655                     } catch (RemoteException e) {
656                         Log.e(TAG, "Callback failed", e);
657                     }
658                 }
659             });
660         }
661     }
662 
onBusEvent(ExpandPipEvent event)663     public final void onBusEvent(ExpandPipEvent event) {
664         PipUI pipUi = getComponent(PipUI.class);
665         if (pipUi == null) {
666             return;
667         }
668         pipUi.expandPip();
669     }
670 
onBusEvent(HidePipMenuEvent event)671     public final void onBusEvent(HidePipMenuEvent event) {
672         PipUI pipUi = getComponent(PipUI.class);
673         if (pipUi == null) {
674             return;
675         }
676         event.getAnimationTrigger().increment();
677         pipUi.hidePipMenu(() -> {
678                 event.getAnimationTrigger().increment();
679             }, () -> {
680                 event.getAnimationTrigger().decrement();
681             });
682         event.getAnimationTrigger().decrement();
683     }
684 
685     /**
686      * Attempts to register with the system user.
687      */
registerWithSystemUser()688     private void registerWithSystemUser() {
689         final int processUser = sSystemServicesProxy.getProcessUser();
690         postToSystemUser(new Runnable() {
691             @Override
692             public void run() {
693                 try {
694                     mUserToSystemCallbacks.registerNonSystemUserCallbacks(
695                             new RecentsImplProxy(mImpl), processUser);
696                 } catch (RemoteException e) {
697                     Log.e(TAG, "Failed to register", e);
698                 }
699             }
700         });
701     }
702 
703     /**
704      * Runs the runnable in the system user's Recents context, connecting to the service if
705      * necessary.
706      */
postToSystemUser(final Runnable onConnectRunnable)707     private void postToSystemUser(final Runnable onConnectRunnable) {
708         mOnConnectRunnables.add(onConnectRunnable);
709         if (mUserToSystemCallbacks == null) {
710             Intent systemUserServiceIntent = new Intent();
711             systemUserServiceIntent.setClass(mContext, RecentsSystemUserService.class);
712             boolean bound = mContext.bindServiceAsUser(systemUserServiceIntent,
713                     mUserToSystemServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
714             EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
715                     EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_BIND_SERVICE,
716                     sSystemServicesProxy.getProcessUser());
717             if (!bound) {
718                 // Retry after a fixed duration
719                 mHandler.postDelayed(new Runnable() {
720                     @Override
721                     public void run() {
722                         registerWithSystemUser();
723                     }
724                 }, BIND_TO_SYSTEM_USER_RETRY_DELAY);
725             }
726         } else {
727             runAndFlushOnConnectRunnables();
728         }
729     }
730 
731     /**
732      * Runs all the queued runnables after a service connection is made.
733      */
runAndFlushOnConnectRunnables()734     private void runAndFlushOnConnectRunnables() {
735         for (Runnable r : mOnConnectRunnables) {
736             r.run();
737         }
738         mOnConnectRunnables.clear();
739     }
740 
getComponent(Class<T> clazz)741     private <T> T getComponent(Class<T> clazz) {
742         return mSysUiServiceProvider.getComponent(clazz);
743     }
744 
745     @Override
dump(PrintWriter pw)746     public void dump(PrintWriter pw) {
747         pw.println("Recents");
748         pw.println("  currentUserId=" + SystemServicesProxy.getInstance(mContext).getCurrentUser());
749     }
750 }
751