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