Home | History | Annotate | Download | only in recents
      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      */
    179     public IBinder getSystemUserCallbacks() {
    180         return mSystemToUserCallbacks;
    181     }
    182 
    183     public static RecentsTaskLoader getTaskLoader() {
    184         return sTaskLoader;
    185     }
    186 
    187 
    188     public static SystemServicesProxy getSystemServices() {
    189         return sSystemServicesProxy;
    190     }
    191 
    192     public static RecentsConfiguration getConfiguration() {
    193         return sConfiguration;
    194     }
    195 
    196     public static RecentsDebugFlags getDebugFlags() {
    197         return sDebugFlags;
    198     }
    199 
    200     @Override
    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
    240     public void onBootCompleted() {
    241         mImpl.onBootCompleted();
    242     }
    243 
    244     /**
    245      * Shows the Recents.
    246      */
    247     @Override
    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
    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
    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
    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
    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
    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 
    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 
    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
    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
    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
    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
    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      */
    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      */
    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      */
    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 
    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 
    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 
    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 
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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