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.RecentsActivityStartingEvent;
55 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
56 import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
57 import com.android.systemui.recents.events.component.ShowUserToastEvent;
58 import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
59 import com.android.systemui.recents.misc.SystemServicesProxy;
60 import com.android.systemui.recents.model.RecentsTaskLoader;
61 import com.android.systemui.stackdivider.Divider;
62 import com.android.systemui.statusbar.CommandQueue;
63 
64 import java.io.FileDescriptor;
65 import java.io.PrintWriter;
66 import java.util.ArrayList;
67 import java.util.HashSet;
68 import java.util.Set;
69 
70 
71 /**
72  * An implementation of the SystemUI recents component, which supports both system and secondary
73  * users.
74  */
75 public class Recents extends SystemUI
76         implements RecentsComponent, CommandQueue.Callbacks {
77 
78     private final static String TAG = "Recents";
79     private final static boolean DEBUG = false;
80 
81     public final static int EVENT_BUS_PRIORITY = 1;
82     public final static int BIND_TO_SYSTEM_USER_RETRY_DELAY = 5000;
83     public final static int RECENTS_GROW_TARGET_INVALID = -1;
84 
85     public final static Set<String> RECENTS_ACTIVITIES = new HashSet<>();
86     static {
87         RECENTS_ACTIVITIES.add(RecentsImpl.RECENTS_ACTIVITY);
88     }
89 
90     // Purely for experimentation
91     private final static String RECENTS_OVERRIDE_SYSPROP_KEY = "persist.recents_override_pkg";
92     private final static String ACTION_SHOW_RECENTS = "com.android.systemui.recents.ACTION_SHOW";
93     private final static String ACTION_HIDE_RECENTS = "com.android.systemui.recents.ACTION_HIDE";
94     private final static String ACTION_TOGGLE_RECENTS = "com.android.systemui.recents.ACTION_TOGGLE";
95 
96     private static final String COUNTER_WINDOW_SUPPORTED = "window_enter_supported";
97     private static final String COUNTER_WINDOW_UNSUPPORTED = "window_enter_unsupported";
98     private static final String COUNTER_WINDOW_INCOMPATIBLE = "window_enter_incompatible";
99 
100     private static SystemServicesProxy sSystemServicesProxy;
101     private static RecentsDebugFlags sDebugFlags;
102     private static RecentsTaskLoader sTaskLoader;
103     private static RecentsConfiguration sConfiguration;
104 
105     // For experiments only, allows another package to handle recents if it is defined in the system
106     // properties.  This is limited to show/toggle/hide, and does not tie into the ActivityManager,
107     // and does not reside in the home stack.
108     private String mOverrideRecentsPackageName;
109 
110     private Handler mHandler;
111     private RecentsImpl mImpl;
112     private int mDraggingInRecentsCurrentUser;
113 
114     // Only For system user, this is the callbacks instance we return to each secondary user
115     private RecentsSystemUser mSystemToUserCallbacks;
116 
117     // Only for secondary users, this is the callbacks instance provided by the system user to make
118     // calls back
119     private IRecentsSystemUserCallbacks mUserToSystemCallbacks;
120 
121     // The set of runnables to run after binding to the system user's service.
122     private final ArrayList<Runnable> mOnConnectRunnables = new ArrayList<>();
123 
124     // Only for secondary users, this is the death handler for the binder from the system user
125     private final IBinder.DeathRecipient mUserToSystemCallbacksDeathRcpt = new IBinder.DeathRecipient() {
126         @Override
127         public void binderDied() {
128             mUserToSystemCallbacks = null;
129             EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
130                     EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_UNBOUND,
131                     sSystemServicesProxy.getProcessUser());
132 
133             // Retry after a fixed duration
134             mHandler.postDelayed(new Runnable() {
135                 @Override
136                 public void run() {
137                     registerWithSystemUser();
138                 }
139             }, BIND_TO_SYSTEM_USER_RETRY_DELAY);
140         }
141     };
142 
143     // Only for secondary users, this is the service connection we use to connect to the system user
144     private final ServiceConnection mUserToSystemServiceConnection = new ServiceConnection() {
145         @Override
146         public void onServiceConnected(ComponentName name, IBinder service) {
147             if (service != null) {
148                 mUserToSystemCallbacks = IRecentsSystemUserCallbacks.Stub.asInterface(
149                         service);
150                 EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
151                         EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_BOUND,
152                         sSystemServicesProxy.getProcessUser());
153 
154                 // Listen for system user's death, so that we can reconnect later
155                 try {
156                     service.linkToDeath(mUserToSystemCallbacksDeathRcpt, 0);
157                 } catch (RemoteException e) {
158                     Log.e(TAG, "Lost connection to (System) SystemUI", e);
159                 }
160 
161                 // Run each of the queued runnables
162                 runAndFlushOnConnectRunnables();
163             }
164 
165             // Unbind ourselves now that we've registered our callbacks.  The
166             // binder to the system user are still valid at this point.
167             mContext.unbindService(this);
168         }
169 
170         @Override
171         public void onServiceDisconnected(ComponentName name) {
172             // Do nothing
173         }
174     };
175 
176     /**
177      * Returns the callbacks interface that non-system users can call.
178      */
getSystemUserCallbacks()179     public IBinder getSystemUserCallbacks() {
180         return mSystemToUserCallbacks;
181     }
182 
getTaskLoader()183     public static RecentsTaskLoader getTaskLoader() {
184         return sTaskLoader;
185     }
186 
187 
getSystemServices()188     public static SystemServicesProxy getSystemServices() {
189         return sSystemServicesProxy;
190     }
191 
getConfiguration()192     public static RecentsConfiguration getConfiguration() {
193         return sConfiguration;
194     }
195 
getDebugFlags()196     public static RecentsDebugFlags getDebugFlags() {
197         return sDebugFlags;
198     }
199 
200     @Override
start()201     public void start() {
202         sDebugFlags = new RecentsDebugFlags(mContext);
203         sSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
204         sTaskLoader = new RecentsTaskLoader(mContext);
205         sConfiguration = new RecentsConfiguration(mContext);
206         mHandler = new Handler();
207         mImpl = new RecentsImpl(mContext);
208 
209         // Check if there is a recents override package
210         if ("userdebug".equals(Build.TYPE) || "eng".equals(Build.TYPE)) {
211             String cnStr = SystemProperties.get(RECENTS_OVERRIDE_SYSPROP_KEY);
212             if (!cnStr.isEmpty()) {
213                 mOverrideRecentsPackageName = cnStr;
214             }
215         }
216 
217         // Register with the event bus
218         EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
219         EventBus.getDefault().register(sSystemServicesProxy, EVENT_BUS_PRIORITY);
220         EventBus.getDefault().register(sTaskLoader, EVENT_BUS_PRIORITY);
221 
222         // Due to the fact that RecentsActivity is per-user, we need to establish and interface for
223         // the system user's Recents component to pass events (like show/hide/toggleRecents) to the
224         // secondary user, and vice versa (like visibility change, screen pinning).
225         final int processUser = sSystemServicesProxy.getProcessUser();
226         if (sSystemServicesProxy.isSystemUser(processUser)) {
227             // For the system user, initialize an instance of the interface that we can pass to the
228             // secondary user
229             getComponent(CommandQueue.class).addCallbacks(this);
230             mSystemToUserCallbacks = new RecentsSystemUser(mContext, mImpl);
231         } else {
232             // For the secondary user, bind to the primary user's service to get a persistent
233             // interface to register its implementation and to later update its state
234             registerWithSystemUser();
235         }
236         putComponent(Recents.class, this);
237     }
238 
239     @Override
onBootCompleted()240     public void onBootCompleted() {
241         mImpl.onBootCompleted();
242     }
243 
244     /**
245      * Shows the Recents.
246      */
247     @Override
showRecentApps(boolean triggeredFromAltTab, boolean fromHome)248     public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) {
249         // Ensure the device has been provisioned before allowing the user to interact with
250         // recents
251         if (!isUserSetup()) {
252             return;
253         }
254 
255         if (proxyToOverridePackage(ACTION_SHOW_RECENTS)) {
256             return;
257         }
258         try {
259             ActivityManager.getService().closeSystemDialogs(SYSTEM_DIALOG_REASON_RECENT_APPS);
260         } catch (RemoteException e) {
261         }
262 
263         int recentsGrowTarget = getComponent(Divider.class).getView().growsRecents();
264 
265         int currentUser = sSystemServicesProxy.getCurrentUser();
266         if (sSystemServicesProxy.isSystemUser(currentUser)) {
267             mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
268                     true /* animate */, false /* reloadTasks */, fromHome, recentsGrowTarget);
269         } else {
270             if (mSystemToUserCallbacks != null) {
271                 IRecentsNonSystemUserCallbacks callbacks =
272                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
273                 if (callbacks != null) {
274                     try {
275                         callbacks.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
276                                 true /* animate */, false /* reloadTasks */, fromHome,
277                                 recentsGrowTarget);
278                     } catch (RemoteException e) {
279                         Log.e(TAG, "Callback failed", e);
280                     }
281                 } else {
282                     Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
283                 }
284             }
285         }
286     }
287 
288     /**
289      * Hides the Recents.
290      */
291     @Override
hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey)292     public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
293         // Ensure the device has been provisioned before allowing the user to interact with
294         // recents
295         if (!isUserSetup()) {
296             return;
297         }
298 
299         if (proxyToOverridePackage(ACTION_HIDE_RECENTS)) {
300             return;
301         }
302 
303         int currentUser = sSystemServicesProxy.getCurrentUser();
304         if (sSystemServicesProxy.isSystemUser(currentUser)) {
305             mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
306         } else {
307             if (mSystemToUserCallbacks != null) {
308                 IRecentsNonSystemUserCallbacks callbacks =
309                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
310                 if (callbacks != null) {
311                     try {
312                         callbacks.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
313                     } catch (RemoteException e) {
314                         Log.e(TAG, "Callback failed", e);
315                     }
316                 } else {
317                     Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
318                 }
319             }
320         }
321     }
322 
323     /**
324      * Toggles the Recents activity.
325      */
326     @Override
toggleRecentApps()327     public void toggleRecentApps() {
328         // Ensure the device has been provisioned before allowing the user to interact with
329         // recents
330         if (!isUserSetup()) {
331             return;
332         }
333 
334         if (proxyToOverridePackage(ACTION_TOGGLE_RECENTS)) {
335             return;
336         }
337 
338         int growTarget = getComponent(Divider.class).getView().growsRecents();
339 
340         int currentUser = sSystemServicesProxy.getCurrentUser();
341         if (sSystemServicesProxy.isSystemUser(currentUser)) {
342             mImpl.toggleRecents(growTarget);
343         } else {
344             if (mSystemToUserCallbacks != null) {
345                 IRecentsNonSystemUserCallbacks callbacks =
346                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
347                 if (callbacks != null) {
348                     try {
349                         callbacks.toggleRecents(growTarget);
350                     } catch (RemoteException e) {
351                         Log.e(TAG, "Callback failed", e);
352                     }
353                 } else {
354                     Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
355                 }
356             }
357         }
358     }
359 
360     /**
361      * Preloads info for the Recents activity.
362      */
363     @Override
preloadRecentApps()364     public void preloadRecentApps() {
365         // Ensure the device has been provisioned before allowing the user to interact with
366         // recents
367         if (!isUserSetup()) {
368             return;
369         }
370 
371         int currentUser = sSystemServicesProxy.getCurrentUser();
372         if (sSystemServicesProxy.isSystemUser(currentUser)) {
373             mImpl.preloadRecents();
374         } else {
375             if (mSystemToUserCallbacks != null) {
376                 IRecentsNonSystemUserCallbacks callbacks =
377                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
378                 if (callbacks != null) {
379                     try {
380                         callbacks.preloadRecents();
381                     } catch (RemoteException e) {
382                         Log.e(TAG, "Callback failed", e);
383                     }
384                 } else {
385                     Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
386                 }
387             }
388         }
389     }
390 
391     @Override
cancelPreloadRecentApps()392     public void cancelPreloadRecentApps() {
393         // Ensure the device has been provisioned before allowing the user to interact with
394         // recents
395         if (!isUserSetup()) {
396             return;
397         }
398 
399         int currentUser = sSystemServicesProxy.getCurrentUser();
400         if (sSystemServicesProxy.isSystemUser(currentUser)) {
401             mImpl.cancelPreloadingRecents();
402         } else {
403             if (mSystemToUserCallbacks != null) {
404                 IRecentsNonSystemUserCallbacks callbacks =
405                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
406                 if (callbacks != null) {
407                     try {
408                         callbacks.cancelPreloadingRecents();
409                     } catch (RemoteException e) {
410                         Log.e(TAG, "Callback failed", e);
411                     }
412                 } else {
413                     Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
414                 }
415             }
416         }
417     }
418 
419     @Override
dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds, int metricsDockAction)420     public boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds,
421             int metricsDockAction) {
422         // Ensure the device has been provisioned before allowing the user to interact with
423         // recents
424         if (!isUserSetup()) {
425             return false;
426         }
427 
428         Point realSize = new Point();
429         if (initialBounds == null) {
430             mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
431                     .getRealSize(realSize);
432             initialBounds = new Rect(0, 0, realSize.x, realSize.y);
433         }
434 
435         int currentUser = sSystemServicesProxy.getCurrentUser();
436         SystemServicesProxy ssp = Recents.getSystemServices();
437         ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
438         boolean screenPinningActive = ssp.isScreenPinningActive();
439         boolean isRunningTaskInHomeOrRecentsStack = runningTask != null &&
440                 ActivityManager.StackId.isHomeOrRecentsStack(runningTask.stackId);
441         if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
442             logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode);
443             if (runningTask.supportsSplitScreenMultiWindow) {
444                 if (metricsDockAction != -1) {
445                     MetricsLogger.action(mContext, metricsDockAction,
446                             runningTask.topActivity.flattenToShortString());
447                 }
448                 if (sSystemServicesProxy.isSystemUser(currentUser)) {
449                     mImpl.dockTopTask(runningTask.id, dragMode, stackCreateMode, initialBounds);
450                 } else {
451                     if (mSystemToUserCallbacks != null) {
452                         IRecentsNonSystemUserCallbacks callbacks =
453                                 mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
454                         if (callbacks != null) {
455                             try {
456                                 callbacks.dockTopTask(runningTask.id, dragMode, stackCreateMode,
457                                         initialBounds);
458                             } catch (RemoteException e) {
459                                 Log.e(TAG, "Callback failed", e);
460                             }
461                         } else {
462                             Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
463                         }
464                     }
465                 }
466                 mDraggingInRecentsCurrentUser = currentUser;
467                 return true;
468             } else {
469                 EventBus.getDefault().send(new ShowUserToastEvent(
470                         R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT));
471                 return false;
472             }
473         } else {
474             return false;
475         }
476     }
477 
logDockAttempt(Context ctx, ComponentName activity, int resizeMode)478     public static void logDockAttempt(Context ctx, ComponentName activity, int resizeMode) {
479         if (resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE) {
480             MetricsLogger.action(ctx, MetricsEvent.ACTION_WINDOW_DOCK_UNRESIZABLE,
481                     activity.flattenToShortString());
482         }
483         MetricsLogger.count(ctx, getMetricsCounterForResizeMode(resizeMode), 1);
484     }
485 
getMetricsCounterForResizeMode(int resizeMode)486     private static String getMetricsCounterForResizeMode(int resizeMode) {
487         switch (resizeMode) {
488             case ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE:
489                 return COUNTER_WINDOW_UNSUPPORTED;
490             case ActivityInfo.RESIZE_MODE_RESIZEABLE:
491             case ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION:
492                 return COUNTER_WINDOW_SUPPORTED;
493             default:
494                 return COUNTER_WINDOW_INCOMPATIBLE;
495         }
496     }
497 
498     @Override
onDraggingInRecents(float distanceFromTop)499     public void onDraggingInRecents(float distanceFromTop) {
500         if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) {
501             mImpl.onDraggingInRecents(distanceFromTop);
502         } else {
503             if (mSystemToUserCallbacks != null) {
504                 IRecentsNonSystemUserCallbacks callbacks =
505                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(
506                                 mDraggingInRecentsCurrentUser);
507                 if (callbacks != null) {
508                     try {
509                         callbacks.onDraggingInRecents(distanceFromTop);
510                     } catch (RemoteException e) {
511                         Log.e(TAG, "Callback failed", e);
512                     }
513                 } else {
514                     Log.e(TAG, "No SystemUI callbacks found for user: "
515                             + mDraggingInRecentsCurrentUser);
516                 }
517             }
518         }
519     }
520 
521     @Override
onDraggingInRecentsEnded(float velocity)522     public void onDraggingInRecentsEnded(float velocity) {
523         if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) {
524             mImpl.onDraggingInRecentsEnded(velocity);
525         } else {
526             if (mSystemToUserCallbacks != null) {
527                 IRecentsNonSystemUserCallbacks callbacks =
528                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(
529                                 mDraggingInRecentsCurrentUser);
530                 if (callbacks != null) {
531                     try {
532                         callbacks.onDraggingInRecentsEnded(velocity);
533                     } catch (RemoteException e) {
534                         Log.e(TAG, "Callback failed", e);
535                     }
536                 } else {
537                     Log.e(TAG, "No SystemUI callbacks found for user: "
538                             + mDraggingInRecentsCurrentUser);
539                 }
540             }
541         }
542     }
543 
544     @Override
showNextAffiliatedTask()545     public void showNextAffiliatedTask() {
546         // Ensure the device has been provisioned before allowing the user to interact with
547         // recents
548         if (!isUserSetup()) {
549             return;
550         }
551 
552         mImpl.showNextAffiliatedTask();
553     }
554 
555     @Override
showPrevAffiliatedTask()556     public void showPrevAffiliatedTask() {
557         // Ensure the device has been provisioned before allowing the user to interact with
558         // recents
559         if (!isUserSetup()) {
560             return;
561         }
562 
563         mImpl.showPrevAffiliatedTask();
564     }
565 
566     /**
567      * Updates on configuration change.
568      */
onConfigurationChanged(Configuration newConfig)569     public void onConfigurationChanged(Configuration newConfig) {
570         int currentUser = sSystemServicesProxy.getCurrentUser();
571         if (sSystemServicesProxy.isSystemUser(currentUser)) {
572             mImpl.onConfigurationChanged();
573         } else {
574             if (mSystemToUserCallbacks != null) {
575                 IRecentsNonSystemUserCallbacks callbacks =
576                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
577                 if (callbacks != null) {
578                     try {
579                         callbacks.onConfigurationChanged();
580                     } catch (RemoteException e) {
581                         Log.e(TAG, "Callback failed", e);
582                     }
583                 } else {
584                     Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
585                 }
586             }
587         }
588     }
589 
590     /**
591      * Handle Recents activity visibility changed.
592      */
onBusEvent(final RecentsVisibilityChangedEvent event)593     public final void onBusEvent(final RecentsVisibilityChangedEvent event) {
594         SystemServicesProxy ssp = Recents.getSystemServices();
595         int processUser = ssp.getProcessUser();
596         if (ssp.isSystemUser(processUser)) {
597             mImpl.onVisibilityChanged(event.applicationContext, event.visible);
598         } else {
599             postToSystemUser(new Runnable() {
600                 @Override
601                 public void run() {
602                     try {
603                         mUserToSystemCallbacks.updateRecentsVisibility(event.visible);
604                     } catch (RemoteException e) {
605                         Log.e(TAG, "Callback failed", e);
606                     }
607                 }
608             });
609         }
610     }
611 
612     /**
613      * Handle screen pinning request.
614      */
onBusEvent(final ScreenPinningRequestEvent event)615     public final void onBusEvent(final ScreenPinningRequestEvent event) {
616         int processUser = sSystemServicesProxy.getProcessUser();
617         if (sSystemServicesProxy.isSystemUser(processUser)) {
618             mImpl.onStartScreenPinning(event.applicationContext, event.taskId);
619         } else {
620             postToSystemUser(new Runnable() {
621                 @Override
622                 public void run() {
623                     try {
624                         mUserToSystemCallbacks.startScreenPinning(event.taskId);
625                     } catch (RemoteException e) {
626                         Log.e(TAG, "Callback failed", e);
627                     }
628                 }
629             });
630         }
631     }
632 
onBusEvent(final RecentsDrawnEvent event)633     public final void onBusEvent(final RecentsDrawnEvent event) {
634         int processUser = sSystemServicesProxy.getProcessUser();
635         if (!sSystemServicesProxy.isSystemUser(processUser)) {
636             postToSystemUser(new Runnable() {
637                 @Override
638                 public void run() {
639                     try {
640                         mUserToSystemCallbacks.sendRecentsDrawnEvent();
641                     } catch (RemoteException e) {
642                         Log.e(TAG, "Callback failed", e);
643                     }
644                 }
645             });
646         }
647     }
648 
onBusEvent(final DockedTopTaskEvent event)649     public final void onBusEvent(final DockedTopTaskEvent event) {
650         int processUser = sSystemServicesProxy.getProcessUser();
651         if (!sSystemServicesProxy.isSystemUser(processUser)) {
652             postToSystemUser(new Runnable() {
653                 @Override
654                 public void run() {
655                     try {
656                         mUserToSystemCallbacks.sendDockingTopTaskEvent(event.dragMode,
657                                 event.initialRect);
658                     } catch (RemoteException e) {
659                         Log.e(TAG, "Callback failed", e);
660                     }
661                 }
662             });
663         }
664     }
665 
onBusEvent(final RecentsActivityStartingEvent event)666     public final void onBusEvent(final RecentsActivityStartingEvent event) {
667         int processUser = sSystemServicesProxy.getProcessUser();
668         if (!sSystemServicesProxy.isSystemUser(processUser)) {
669             postToSystemUser(new Runnable() {
670                 @Override
671                 public void run() {
672                     try {
673                         mUserToSystemCallbacks.sendLaunchRecentsEvent();
674                     } catch (RemoteException e) {
675                         Log.e(TAG, "Callback failed", e);
676                     }
677                 }
678             });
679         }
680     }
681 
onBusEvent(ConfigurationChangedEvent event)682     public final void onBusEvent(ConfigurationChangedEvent event) {
683         // Update the configuration for the Recents component when the activity configuration
684         // changes as well
685         mImpl.onConfigurationChanged();
686     }
687 
onBusEvent(ShowUserToastEvent event)688     public final void onBusEvent(ShowUserToastEvent event) {
689         int currentUser = sSystemServicesProxy.getCurrentUser();
690         if (sSystemServicesProxy.isSystemUser(currentUser)) {
691             mImpl.onShowCurrentUserToast(event.msgResId, event.msgLength);
692         } else {
693             if (mSystemToUserCallbacks != null) {
694                 IRecentsNonSystemUserCallbacks callbacks =
695                         mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
696                 if (callbacks != null) {
697                     try {
698                         callbacks.showCurrentUserToast(event.msgResId, event.msgLength);
699                     } catch (RemoteException e) {
700                         Log.e(TAG, "Callback failed", e);
701                     }
702                 } else {
703                     Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
704                 }
705             }
706         }
707     }
708 
709     /**
710      * Attempts to register with the system user.
711      */
registerWithSystemUser()712     private void registerWithSystemUser() {
713         final int processUser = sSystemServicesProxy.getProcessUser();
714         postToSystemUser(new Runnable() {
715             @Override
716             public void run() {
717                 try {
718                     mUserToSystemCallbacks.registerNonSystemUserCallbacks(
719                             new RecentsImplProxy(mImpl), processUser);
720                 } catch (RemoteException e) {
721                     Log.e(TAG, "Failed to register", e);
722                 }
723             }
724         });
725     }
726 
727     /**
728      * Runs the runnable in the system user's Recents context, connecting to the service if
729      * necessary.
730      */
postToSystemUser(final Runnable onConnectRunnable)731     private void postToSystemUser(final Runnable onConnectRunnable) {
732         mOnConnectRunnables.add(onConnectRunnable);
733         if (mUserToSystemCallbacks == null) {
734             Intent systemUserServiceIntent = new Intent();
735             systemUserServiceIntent.setClass(mContext, RecentsSystemUserService.class);
736             boolean bound = mContext.bindServiceAsUser(systemUserServiceIntent,
737                     mUserToSystemServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
738             EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
739                     EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_BIND_SERVICE,
740                     sSystemServicesProxy.getProcessUser());
741             if (!bound) {
742                 // Retry after a fixed duration
743                 mHandler.postDelayed(new Runnable() {
744                     @Override
745                     public void run() {
746                         registerWithSystemUser();
747                     }
748                 }, BIND_TO_SYSTEM_USER_RETRY_DELAY);
749             }
750         } else {
751             runAndFlushOnConnectRunnables();
752         }
753     }
754 
755     /**
756      * Runs all the queued runnables after a service connection is made.
757      */
runAndFlushOnConnectRunnables()758     private void runAndFlushOnConnectRunnables() {
759         for (Runnable r : mOnConnectRunnables) {
760             r.run();
761         }
762         mOnConnectRunnables.clear();
763     }
764 
765     /**
766      * @return whether this device is provisioned and the current user is set up.
767      */
isUserSetup()768     private boolean isUserSetup() {
769         ContentResolver cr = mContext.getContentResolver();
770         return (Settings.Global.getInt(cr, Settings.Global.DEVICE_PROVISIONED, 0) != 0) &&
771                 (Settings.Secure.getInt(cr, Settings.Secure.USER_SETUP_COMPLETE, 0) != 0);
772     }
773 
774     /**
775      * Attempts to proxy the following action to the override recents package.
776      * @return whether the proxying was successful
777      */
proxyToOverridePackage(String action)778     private boolean proxyToOverridePackage(String action) {
779         if (mOverrideRecentsPackageName != null) {
780             Intent intent = new Intent(action);
781             intent.setPackage(mOverrideRecentsPackageName);
782             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
783             mContext.sendBroadcast(intent);
784             return true;
785         }
786         return false;
787     }
788 
789     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)790     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
791         pw.println("Recents");
792         pw.println("  currentUserId=" + SystemServicesProxy.getInstance(mContext).getCurrentUser());
793     }
794 }
795