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