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 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      */
    168     public IBinder getSystemUserCallbacks() {
    169         return mSystemToUserCallbacks;
    170     }
    171 
    172     public static RecentsTaskLoader getTaskLoader() {
    173         return sTaskLoader;
    174     }
    175 
    176     public static SystemServicesProxy getSystemServices() {
    177         return sSystemServicesProxy;
    178     }
    179 
    180     public static RecentsConfiguration getConfiguration() {
    181         return sConfiguration;
    182     }
    183 
    184     public static RecentsDebugFlags getDebugFlags() {
    185         return sDebugFlags;
    186     }
    187 
    188     @Override
    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
    233     public void onBootCompleted() {
    234         mImpl.onBootCompleted();
    235     }
    236 
    237     /**
    238      * Shows the Recents.
    239      */
    240     @Override
    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
    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
    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
    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
    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
    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 
    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 
    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
    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
    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
    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
    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      */
    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      */
    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      */
    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 
    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 
    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 
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    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