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