| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.systemui.recents; |
| |
| import android.app.ActivityManager; |
| import android.app.UiModeManager; |
| import android.content.BroadcastReceiver; |
| import android.content.ComponentName; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.ServiceConnection; |
| import android.content.pm.ActivityInfo; |
| import android.content.res.Configuration; |
| import android.graphics.Point; |
| import android.graphics.Rect; |
| import android.hardware.display.DisplayManager; |
| import android.os.Build; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.RemoteException; |
| import android.os.SystemClock; |
| import android.os.SystemProperties; |
| import android.os.UserHandle; |
| import android.provider.Settings; |
| import android.util.EventLog; |
| import android.util.Log; |
| import android.view.Display; |
| import android.widget.Toast; |
| |
| import com.android.internal.logging.MetricsLogger; |
| import com.android.internal.logging.MetricsProto.MetricsEvent; |
| import com.android.systemui.EventLogConstants; |
| import com.android.systemui.EventLogTags; |
| import com.android.systemui.Prefs; |
| import com.android.systemui.R; |
| import com.android.systemui.RecentsComponent; |
| import com.android.systemui.SystemUI; |
| import com.android.systemui.recents.events.EventBus; |
| import com.android.systemui.recents.events.activity.ConfigurationChangedEvent; |
| import com.android.systemui.recents.events.activity.DockedTopTaskEvent; |
| import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; |
| import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; |
| import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; |
| import com.android.systemui.recents.events.component.ShowUserToastEvent; |
| import com.android.systemui.recents.events.ui.RecentsDrawnEvent; |
| import com.android.systemui.recents.misc.SystemServicesProxy; |
| import com.android.systemui.recents.model.RecentsTaskLoader; |
| import com.android.systemui.recents.tv.RecentsTvImpl; |
| import com.android.systemui.stackdivider.Divider; |
| |
| import java.util.ArrayList; |
| |
| |
| /** |
| * An implementation of the SystemUI recents component, which supports both system and secondary |
| * users. |
| */ |
| public class Recents extends SystemUI |
| implements RecentsComponent { |
| |
| private final static String TAG = "Recents"; |
| private final static boolean DEBUG = false; |
| |
| public final static int EVENT_BUS_PRIORITY = 1; |
| public final static int BIND_TO_SYSTEM_USER_RETRY_DELAY = 5000; |
| public final static int RECENTS_GROW_TARGET_INVALID = -1; |
| |
| // Purely for experimentation |
| private final static String RECENTS_OVERRIDE_SYSPROP_KEY = "persist.recents_override_pkg"; |
| private final static String ACTION_SHOW_RECENTS = "com.android.systemui.recents.ACTION_SHOW"; |
| private final static String ACTION_HIDE_RECENTS = "com.android.systemui.recents.ACTION_HIDE"; |
| private final static String ACTION_TOGGLE_RECENTS = "com.android.systemui.recents.ACTION_TOGGLE"; |
| |
| private static final String COUNTER_WINDOW_SUPPORTED = "window_enter_supported"; |
| private static final String COUNTER_WINDOW_UNSUPPORTED = "window_enter_unsupported"; |
| private static final String COUNTER_WINDOW_INCOMPATIBLE = "window_enter_incompatible"; |
| |
| private static SystemServicesProxy sSystemServicesProxy; |
| private static RecentsDebugFlags sDebugFlags; |
| private static RecentsTaskLoader sTaskLoader; |
| private static RecentsConfiguration sConfiguration; |
| |
| // For experiments only, allows another package to handle recents if it is defined in the system |
| // properties. This is limited to show/toggle/hide, and does not tie into the ActivityManager, |
| // and does not reside in the home stack. |
| private String mOverrideRecentsPackageName; |
| |
| private Handler mHandler = new Handler(); |
| private RecentsImpl mImpl; |
| private int mDraggingInRecentsCurrentUser; |
| |
| // Only For system user, this is the callbacks instance we return to each secondary user |
| private RecentsSystemUser mSystemToUserCallbacks; |
| |
| // Only for secondary users, this is the callbacks instance provided by the system user to make |
| // calls back |
| private IRecentsSystemUserCallbacks mUserToSystemCallbacks; |
| |
| // The set of runnables to run after binding to the system user's service. |
| private final ArrayList<Runnable> mOnConnectRunnables = new ArrayList<>(); |
| |
| // Only for secondary users, this is the death handler for the binder from the system user |
| private final IBinder.DeathRecipient mUserToSystemCallbacksDeathRcpt = new IBinder.DeathRecipient() { |
| @Override |
| public void binderDied() { |
| mUserToSystemCallbacks = null; |
| EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION, |
| EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_UNBOUND, |
| sSystemServicesProxy.getProcessUser()); |
| |
| // Retry after a fixed duration |
| mHandler.postDelayed(new Runnable() { |
| @Override |
| public void run() { |
| registerWithSystemUser(); |
| } |
| }, BIND_TO_SYSTEM_USER_RETRY_DELAY); |
| } |
| }; |
| |
| // Only for secondary users, this is the service connection we use to connect to the system user |
| private final ServiceConnection mUserToSystemServiceConnection = new ServiceConnection() { |
| @Override |
| public void onServiceConnected(ComponentName name, IBinder service) { |
| if (service != null) { |
| mUserToSystemCallbacks = IRecentsSystemUserCallbacks.Stub.asInterface( |
| service); |
| EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION, |
| EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_BOUND, |
| sSystemServicesProxy.getProcessUser()); |
| |
| // Listen for system user's death, so that we can reconnect later |
| try { |
| service.linkToDeath(mUserToSystemCallbacksDeathRcpt, 0); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Lost connection to (System) SystemUI", e); |
| } |
| |
| // Run each of the queued runnables |
| runAndFlushOnConnectRunnables(); |
| } |
| |
| // Unbind ourselves now that we've registered our callbacks. The |
| // binder to the system user are still valid at this point. |
| mContext.unbindService(this); |
| } |
| |
| @Override |
| public void onServiceDisconnected(ComponentName name) { |
| // Do nothing |
| } |
| }; |
| |
| |
| private BroadcastReceiver mSystemUserUnlockedReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { |
| int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); |
| if (userId != UserHandle.USER_NULL) { |
| mImpl.onUserUnlocked(userId); |
| } |
| } |
| } |
| }; |
| |
| |
| /** |
| * Returns the callbacks interface that non-system users can call. |
| */ |
| public IBinder getSystemUserCallbacks() { |
| return mSystemToUserCallbacks; |
| } |
| |
| public static RecentsTaskLoader getTaskLoader() { |
| return sTaskLoader; |
| } |
| |
| public static SystemServicesProxy getSystemServices() { |
| return sSystemServicesProxy; |
| } |
| |
| public static RecentsConfiguration getConfiguration() { |
| return sConfiguration; |
| } |
| |
| public static RecentsDebugFlags getDebugFlags() { |
| return sDebugFlags; |
| } |
| |
| @Override |
| public void start() { |
| sDebugFlags = new RecentsDebugFlags(mContext); |
| sSystemServicesProxy = SystemServicesProxy.getInstance(mContext); |
| sTaskLoader = new RecentsTaskLoader(mContext); |
| sConfiguration = new RecentsConfiguration(mContext); |
| |
| UiModeManager uiModeManager = (UiModeManager) mContext. |
| getSystemService(Context.UI_MODE_SERVICE); |
| if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) { |
| mImpl = new RecentsTvImpl(mContext); |
| } else { |
| mImpl = new RecentsImpl(mContext); |
| } |
| |
| // Check if there is a recents override package |
| if ("userdebug".equals(Build.TYPE) || "eng".equals(Build.TYPE)) { |
| String cnStr = SystemProperties.get(RECENTS_OVERRIDE_SYSPROP_KEY); |
| if (!cnStr.isEmpty()) { |
| mOverrideRecentsPackageName = cnStr; |
| } |
| } |
| |
| // Register with the event bus |
| EventBus.getDefault().register(this, EVENT_BUS_PRIORITY); |
| EventBus.getDefault().register(sSystemServicesProxy, EVENT_BUS_PRIORITY); |
| EventBus.getDefault().register(sTaskLoader, EVENT_BUS_PRIORITY); |
| |
| // Due to the fact that RecentsActivity is per-user, we need to establish and interface for |
| // the system user's Recents component to pass events (like show/hide/toggleRecents) to the |
| // secondary user, and vice versa (like visibility change, screen pinning). |
| final int processUser = sSystemServicesProxy.getProcessUser(); |
| if (sSystemServicesProxy.isSystemUser(processUser)) { |
| // For the system user, initialize an instance of the interface that we can pass to the |
| // secondary user |
| mSystemToUserCallbacks = new RecentsSystemUser(mContext, mImpl); |
| |
| // Listen for user-unlocked to kick off preloading recents |
| IntentFilter filter = new IntentFilter(); |
| filter.addAction(Intent.ACTION_USER_UNLOCKED); |
| mContext.registerReceiverAsUser(mSystemUserUnlockedReceiver, UserHandle.SYSTEM, filter, |
| null /* permission */, null /* scheduler */); |
| } else { |
| // For the secondary user, bind to the primary user's service to get a persistent |
| // interface to register its implementation and to later update its state |
| registerWithSystemUser(); |
| } |
| putComponent(Recents.class, this); |
| |
| // Migrate the old stack active time if necessary, otherwise, it will already be managed |
| // when the tasks are loaded in the system. See TaskPersister.restoreTasksForUserLocked(). |
| long lastVisibleTaskActiveTime = Prefs.getLong(mContext, |
| Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, -1); |
| if (lastVisibleTaskActiveTime != -1) { |
| long uptime = SystemClock.elapsedRealtime(); |
| Settings.Secure.putLongForUser(mContext.getContentResolver(), |
| Settings.Secure.OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME, |
| uptime - Math.max(0, System.currentTimeMillis() - lastVisibleTaskActiveTime), |
| processUser); |
| Prefs.remove(mContext, Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME); |
| } |
| } |
| |
| @Override |
| public void onBootCompleted() { |
| mImpl.onBootCompleted(); |
| } |
| |
| /** |
| * Shows the Recents. |
| */ |
| @Override |
| public void showRecents(boolean triggeredFromAltTab, boolean fromHome) { |
| // Ensure the device has been provisioned before allowing the user to interact with |
| // recents |
| if (!isUserSetup()) { |
| return; |
| } |
| |
| if (proxyToOverridePackage(ACTION_SHOW_RECENTS)) { |
| return; |
| } |
| |
| int recentsGrowTarget = getComponent(Divider.class).getView().growsRecents(); |
| |
| int currentUser = sSystemServicesProxy.getCurrentUser(); |
| if (sSystemServicesProxy.isSystemUser(currentUser)) { |
| mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */, |
| true /* animate */, false /* reloadTasks */, fromHome, recentsGrowTarget); |
| } else { |
| if (mSystemToUserCallbacks != null) { |
| IRecentsNonSystemUserCallbacks callbacks = |
| mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); |
| if (callbacks != null) { |
| try { |
| callbacks.showRecents(triggeredFromAltTab, false /* draggingInRecents */, |
| true /* animate */, false /* reloadTasks */, fromHome, |
| recentsGrowTarget); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Callback failed", e); |
| } |
| } else { |
| Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Hides the Recents. |
| */ |
| @Override |
| public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { |
| // Ensure the device has been provisioned before allowing the user to interact with |
| // recents |
| if (!isUserSetup()) { |
| return; |
| } |
| |
| if (proxyToOverridePackage(ACTION_HIDE_RECENTS)) { |
| return; |
| } |
| |
| int currentUser = sSystemServicesProxy.getCurrentUser(); |
| if (sSystemServicesProxy.isSystemUser(currentUser)) { |
| mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey); |
| } else { |
| if (mSystemToUserCallbacks != null) { |
| IRecentsNonSystemUserCallbacks callbacks = |
| mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); |
| if (callbacks != null) { |
| try { |
| callbacks.hideRecents(triggeredFromAltTab, triggeredFromHomeKey); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Callback failed", e); |
| } |
| } else { |
| Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Toggles the Recents activity. |
| */ |
| @Override |
| public void toggleRecents(Display display) { |
| // Ensure the device has been provisioned before allowing the user to interact with |
| // recents |
| if (!isUserSetup()) { |
| return; |
| } |
| |
| if (proxyToOverridePackage(ACTION_TOGGLE_RECENTS)) { |
| return; |
| } |
| |
| int growTarget = getComponent(Divider.class).getView().growsRecents(); |
| |
| int currentUser = sSystemServicesProxy.getCurrentUser(); |
| if (sSystemServicesProxy.isSystemUser(currentUser)) { |
| mImpl.toggleRecents(growTarget); |
| } else { |
| if (mSystemToUserCallbacks != null) { |
| IRecentsNonSystemUserCallbacks callbacks = |
| mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); |
| if (callbacks != null) { |
| try { |
| callbacks.toggleRecents(growTarget); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Callback failed", e); |
| } |
| } else { |
| Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Preloads info for the Recents activity. |
| */ |
| @Override |
| public void preloadRecents() { |
| // Ensure the device has been provisioned before allowing the user to interact with |
| // recents |
| if (!isUserSetup()) { |
| return; |
| } |
| |
| int currentUser = sSystemServicesProxy.getCurrentUser(); |
| if (sSystemServicesProxy.isSystemUser(currentUser)) { |
| mImpl.preloadRecents(); |
| } else { |
| if (mSystemToUserCallbacks != null) { |
| IRecentsNonSystemUserCallbacks callbacks = |
| mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); |
| if (callbacks != null) { |
| try { |
| callbacks.preloadRecents(); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Callback failed", e); |
| } |
| } else { |
| Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void cancelPreloadingRecents() { |
| // Ensure the device has been provisioned before allowing the user to interact with |
| // recents |
| if (!isUserSetup()) { |
| return; |
| } |
| |
| int currentUser = sSystemServicesProxy.getCurrentUser(); |
| if (sSystemServicesProxy.isSystemUser(currentUser)) { |
| mImpl.cancelPreloadingRecents(); |
| } else { |
| if (mSystemToUserCallbacks != null) { |
| IRecentsNonSystemUserCallbacks callbacks = |
| mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); |
| if (callbacks != null) { |
| try { |
| callbacks.cancelPreloadingRecents(); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Callback failed", e); |
| } |
| } else { |
| Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds, |
| int metricsDockAction) { |
| // Ensure the device has been provisioned before allowing the user to interact with |
| // recents |
| if (!isUserSetup()) { |
| return false; |
| } |
| |
| Point realSize = new Point(); |
| if (initialBounds == null) { |
| mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY) |
| .getRealSize(realSize); |
| initialBounds = new Rect(0, 0, realSize.x, realSize.y); |
| } |
| |
| int currentUser = sSystemServicesProxy.getCurrentUser(); |
| SystemServicesProxy ssp = Recents.getSystemServices(); |
| ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask(); |
| boolean screenPinningActive = ssp.isScreenPinningActive(); |
| boolean isRunningTaskInHomeStack = runningTask != null && |
| SystemServicesProxy.isHomeStack(runningTask.stackId); |
| if (runningTask != null && !isRunningTaskInHomeStack && !screenPinningActive) { |
| logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode); |
| if (runningTask.isDockable) { |
| if (metricsDockAction != -1) { |
| MetricsLogger.action(mContext, metricsDockAction, |
| runningTask.topActivity.flattenToShortString()); |
| } |
| if (sSystemServicesProxy.isSystemUser(currentUser)) { |
| mImpl.dockTopTask(runningTask.id, dragMode, stackCreateMode, initialBounds); |
| } else { |
| if (mSystemToUserCallbacks != null) { |
| IRecentsNonSystemUserCallbacks callbacks = |
| mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); |
| if (callbacks != null) { |
| try { |
| callbacks.dockTopTask(runningTask.id, dragMode, stackCreateMode, |
| initialBounds); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Callback failed", e); |
| } |
| } else { |
| Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); |
| } |
| } |
| } |
| mDraggingInRecentsCurrentUser = currentUser; |
| return true; |
| } else { |
| EventBus.getDefault().send(new ShowUserToastEvent( |
| R.string.recents_incompatible_app_message, Toast.LENGTH_SHORT)); |
| return false; |
| } |
| } else { |
| return false; |
| } |
| } |
| |
| public static void logDockAttempt(Context ctx, ComponentName activity, int resizeMode) { |
| if (resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE) { |
| MetricsLogger.action(ctx, MetricsEvent.ACTION_WINDOW_DOCK_UNRESIZABLE, |
| activity.flattenToShortString()); |
| } |
| MetricsLogger.count(ctx, getMetricsCounterForResizeMode(resizeMode), 1); |
| } |
| |
| private static String getMetricsCounterForResizeMode(int resizeMode) { |
| switch (resizeMode) { |
| case ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE: |
| return COUNTER_WINDOW_UNSUPPORTED; |
| case ActivityInfo.RESIZE_MODE_RESIZEABLE: |
| case ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE: |
| return COUNTER_WINDOW_SUPPORTED; |
| default: |
| return COUNTER_WINDOW_INCOMPATIBLE; |
| } |
| } |
| |
| @Override |
| public void onDraggingInRecents(float distanceFromTop) { |
| if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) { |
| mImpl.onDraggingInRecents(distanceFromTop); |
| } else { |
| if (mSystemToUserCallbacks != null) { |
| IRecentsNonSystemUserCallbacks callbacks = |
| mSystemToUserCallbacks.getNonSystemUserRecentsForUser( |
| mDraggingInRecentsCurrentUser); |
| if (callbacks != null) { |
| try { |
| callbacks.onDraggingInRecents(distanceFromTop); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Callback failed", e); |
| } |
| } else { |
| Log.e(TAG, "No SystemUI callbacks found for user: " |
| + mDraggingInRecentsCurrentUser); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void onDraggingInRecentsEnded(float velocity) { |
| if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) { |
| mImpl.onDraggingInRecentsEnded(velocity); |
| } else { |
| if (mSystemToUserCallbacks != null) { |
| IRecentsNonSystemUserCallbacks callbacks = |
| mSystemToUserCallbacks.getNonSystemUserRecentsForUser( |
| mDraggingInRecentsCurrentUser); |
| if (callbacks != null) { |
| try { |
| callbacks.onDraggingInRecentsEnded(velocity); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Callback failed", e); |
| } |
| } else { |
| Log.e(TAG, "No SystemUI callbacks found for user: " |
| + mDraggingInRecentsCurrentUser); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void showNextAffiliatedTask() { |
| // Ensure the device has been provisioned before allowing the user to interact with |
| // recents |
| if (!isUserSetup()) { |
| return; |
| } |
| |
| mImpl.showNextAffiliatedTask(); |
| } |
| |
| @Override |
| public void showPrevAffiliatedTask() { |
| // Ensure the device has been provisioned before allowing the user to interact with |
| // recents |
| if (!isUserSetup()) { |
| return; |
| } |
| |
| mImpl.showPrevAffiliatedTask(); |
| } |
| |
| /** |
| * Updates on configuration change. |
| */ |
| public void onConfigurationChanged(Configuration newConfig) { |
| int currentUser = sSystemServicesProxy.getCurrentUser(); |
| if (sSystemServicesProxy.isSystemUser(currentUser)) { |
| mImpl.onConfigurationChanged(); |
| } else { |
| if (mSystemToUserCallbacks != null) { |
| IRecentsNonSystemUserCallbacks callbacks = |
| mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); |
| if (callbacks != null) { |
| try { |
| callbacks.onConfigurationChanged(); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Callback failed", e); |
| } |
| } else { |
| Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Handle Recents activity visibility changed. |
| */ |
| public final void onBusEvent(final RecentsVisibilityChangedEvent event) { |
| SystemServicesProxy ssp = Recents.getSystemServices(); |
| int processUser = ssp.getProcessUser(); |
| if (ssp.isSystemUser(processUser)) { |
| mImpl.onVisibilityChanged(event.applicationContext, event.visible); |
| } else { |
| postToSystemUser(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| mUserToSystemCallbacks.updateRecentsVisibility(event.visible); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Callback failed", e); |
| } |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Handle screen pinning request. |
| */ |
| public final void onBusEvent(final ScreenPinningRequestEvent event) { |
| int processUser = sSystemServicesProxy.getProcessUser(); |
| if (sSystemServicesProxy.isSystemUser(processUser)) { |
| mImpl.onStartScreenPinning(event.applicationContext, event.taskId); |
| } else { |
| postToSystemUser(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| mUserToSystemCallbacks.startScreenPinning(event.taskId); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Callback failed", e); |
| } |
| } |
| }); |
| } |
| } |
| |
| public final void onBusEvent(final RecentsDrawnEvent event) { |
| int processUser = sSystemServicesProxy.getProcessUser(); |
| if (!sSystemServicesProxy.isSystemUser(processUser)) { |
| postToSystemUser(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| mUserToSystemCallbacks.sendRecentsDrawnEvent(); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Callback failed", e); |
| } |
| } |
| }); |
| } |
| } |
| |
| public final void onBusEvent(final DockedTopTaskEvent event) { |
| int processUser = sSystemServicesProxy.getProcessUser(); |
| if (!sSystemServicesProxy.isSystemUser(processUser)) { |
| postToSystemUser(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| mUserToSystemCallbacks.sendDockingTopTaskEvent(event.dragMode, |
| event.initialRect); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Callback failed", e); |
| } |
| } |
| }); |
| } |
| } |
| |
| public final void onBusEvent(final RecentsActivityStartingEvent event) { |
| int processUser = sSystemServicesProxy.getProcessUser(); |
| if (!sSystemServicesProxy.isSystemUser(processUser)) { |
| postToSystemUser(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| mUserToSystemCallbacks.sendLaunchRecentsEvent(); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Callback failed", e); |
| } |
| } |
| }); |
| } |
| } |
| |
| public final void onBusEvent(ConfigurationChangedEvent event) { |
| // Update the configuration for the Recents component when the activity configuration |
| // changes as well |
| mImpl.onConfigurationChanged(); |
| } |
| |
| public final void onBusEvent(ShowUserToastEvent event) { |
| int currentUser = sSystemServicesProxy.getCurrentUser(); |
| if (sSystemServicesProxy.isSystemUser(currentUser)) { |
| mImpl.onShowCurrentUserToast(event.msgResId, event.msgLength); |
| } else { |
| if (mSystemToUserCallbacks != null) { |
| IRecentsNonSystemUserCallbacks callbacks = |
| mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); |
| if (callbacks != null) { |
| try { |
| callbacks.showCurrentUserToast(event.msgResId, event.msgLength); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Callback failed", e); |
| } |
| } else { |
| Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Attempts to register with the system user. |
| */ |
| private void registerWithSystemUser() { |
| final int processUser = sSystemServicesProxy.getProcessUser(); |
| postToSystemUser(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| mUserToSystemCallbacks.registerNonSystemUserCallbacks( |
| new RecentsImplProxy(mImpl), processUser); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Failed to register", e); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Runs the runnable in the system user's Recents context, connecting to the service if |
| * necessary. |
| */ |
| private void postToSystemUser(final Runnable onConnectRunnable) { |
| mOnConnectRunnables.add(onConnectRunnable); |
| if (mUserToSystemCallbacks == null) { |
| Intent systemUserServiceIntent = new Intent(); |
| systemUserServiceIntent.setClass(mContext, RecentsSystemUserService.class); |
| boolean bound = mContext.bindServiceAsUser(systemUserServiceIntent, |
| mUserToSystemServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM); |
| EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION, |
| EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_BIND_SERVICE, |
| sSystemServicesProxy.getProcessUser()); |
| if (!bound) { |
| // Retry after a fixed duration |
| mHandler.postDelayed(new Runnable() { |
| @Override |
| public void run() { |
| registerWithSystemUser(); |
| } |
| }, BIND_TO_SYSTEM_USER_RETRY_DELAY); |
| } |
| } else { |
| runAndFlushOnConnectRunnables(); |
| } |
| } |
| |
| /** |
| * Runs all the queued runnables after a service connection is made. |
| */ |
| private void runAndFlushOnConnectRunnables() { |
| for (Runnable r : mOnConnectRunnables) { |
| r.run(); |
| } |
| mOnConnectRunnables.clear(); |
| } |
| |
| /** |
| * @return whether this device is provisioned and the current user is set up. |
| */ |
| private boolean isUserSetup() { |
| ContentResolver cr = mContext.getContentResolver(); |
| return (Settings.Global.getInt(cr, Settings.Global.DEVICE_PROVISIONED, 0) != 0) && |
| (Settings.Secure.getInt(cr, Settings.Secure.USER_SETUP_COMPLETE, 0) != 0); |
| } |
| |
| /** |
| * Attempts to proxy the following action to the override recents package. |
| * @return whether the proxying was successful |
| */ |
| private boolean proxyToOverridePackage(String action) { |
| if (mOverrideRecentsPackageName != null) { |
| Intent intent = new Intent(action); |
| intent.setPackage(mOverrideRecentsPackageName); |
| intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); |
| mContext.sendBroadcast(intent); |
| return true; |
| } |
| return false; |
| } |
| } |