Home | History | Annotate | Download | only in system
      1 /*
      2  * Copyright (C) 2015 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.shared.system;
     18 
     19 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
     20 import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
     21 import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
     22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
     23 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
     24 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
     25 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
     26 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
     27 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
     28 
     29 import android.annotation.NonNull;
     30 import android.app.ActivityManager;
     31 import android.app.ActivityManager.RecentTaskInfo;
     32 import android.app.ActivityOptions;
     33 import android.app.AppGlobals;
     34 import android.app.IAssistDataReceiver;
     35 import android.app.WindowConfiguration.ActivityType;
     36 import android.content.ContentResolver;
     37 import android.content.Context;
     38 import android.content.Intent;
     39 import android.content.pm.ActivityInfo;
     40 import android.content.pm.ApplicationInfo;
     41 import android.content.pm.PackageManager;
     42 import android.content.pm.UserInfo;
     43 import android.graphics.Bitmap;
     44 import android.graphics.Rect;
     45 import android.os.Bundle;
     46 import android.os.Handler;
     47 import android.os.IBinder;
     48 import android.os.Looper;
     49 import android.os.RemoteException;
     50 import android.os.ServiceManager;
     51 import android.os.UserHandle;
     52 import android.provider.Settings;
     53 import android.util.Log;
     54 import android.view.IRecentsAnimationController;
     55 import android.view.IRecentsAnimationRunner;
     56 
     57 import android.view.RemoteAnimationTarget;
     58 
     59 import com.android.internal.app.IVoiceInteractionManagerService;
     60 import com.android.systemui.shared.recents.model.Task;
     61 import com.android.systemui.shared.recents.model.Task.TaskKey;
     62 import com.android.systemui.shared.recents.model.ThumbnailData;
     63 
     64 import java.util.ArrayList;
     65 import java.util.List;
     66 import java.util.function.Consumer;
     67 
     68 public class ActivityManagerWrapper {
     69 
     70     private static final String TAG = "ActivityManagerWrapper";
     71 
     72     private static final ActivityManagerWrapper sInstance = new ActivityManagerWrapper();
     73 
     74     // Should match the values in PhoneWindowManager
     75     public static final String CLOSE_SYSTEM_WINDOWS_REASON_RECENTS = "recentapps";
     76 
     77     private final PackageManager mPackageManager;
     78     private final BackgroundExecutor mBackgroundExecutor;
     79     private final TaskStackChangeListeners mTaskStackChangeListeners;
     80 
     81     private ActivityManagerWrapper() {
     82         final Context context = AppGlobals.getInitialApplication();
     83         mPackageManager = context.getPackageManager();
     84         mBackgroundExecutor = BackgroundExecutor.get();
     85         mTaskStackChangeListeners = new TaskStackChangeListeners(Looper.getMainLooper());
     86     }
     87 
     88     public static ActivityManagerWrapper getInstance() {
     89         return sInstance;
     90     }
     91 
     92     /**
     93      * @return the current user's id.
     94      */
     95     public int getCurrentUserId() {
     96         UserInfo ui;
     97         try {
     98             ui = ActivityManager.getService().getCurrentUser();
     99             return ui != null ? ui.id : 0;
    100         } catch (RemoteException e) {
    101             throw e.rethrowFromSystemServer();
    102         }
    103     }
    104 
    105     /**
    106      * @return the top running task (can be {@code null}).
    107      */
    108     public ActivityManager.RunningTaskInfo getRunningTask() {
    109         return getRunningTask(ACTIVITY_TYPE_RECENTS /* ignoreActivityType */);
    110     }
    111 
    112     public ActivityManager.RunningTaskInfo getRunningTask(@ActivityType int ignoreActivityType) {
    113         // Note: The set of running tasks from the system is ordered by recency
    114         try {
    115             List<ActivityManager.RunningTaskInfo> tasks =
    116                     ActivityManager.getService().getFilteredTasks(1, ignoreActivityType,
    117                             WINDOWING_MODE_PINNED /* ignoreWindowingMode */);
    118             if (tasks.isEmpty()) {
    119                 return null;
    120             }
    121             return tasks.get(0);
    122         } catch (RemoteException e) {
    123             return null;
    124         }
    125     }
    126 
    127     /**
    128      * @return a list of the recents tasks.
    129      */
    130     public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
    131         try {
    132             return ActivityManager.getService().getRecentTasks(numTasks,
    133                             RECENT_IGNORE_UNAVAILABLE, userId).getList();
    134         } catch (RemoteException e) {
    135             Log.e(TAG, "Failed to get recent tasks", e);
    136             return new ArrayList<>();
    137         }
    138     }
    139 
    140     /**
    141      * @return the task snapshot for the given {@param taskId}.
    142      */
    143     public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean reducedResolution) {
    144         ActivityManager.TaskSnapshot snapshot = null;
    145         try {
    146             snapshot = ActivityManager.getService().getTaskSnapshot(taskId, reducedResolution);
    147         } catch (RemoteException e) {
    148             Log.w(TAG, "Failed to retrieve task snapshot", e);
    149         }
    150         if (snapshot != null) {
    151             return new ThumbnailData(snapshot);
    152         } else {
    153             return new ThumbnailData();
    154         }
    155     }
    156 
    157     /**
    158      * @return the activity label, badging if necessary.
    159      */
    160     public String getBadgedActivityLabel(ActivityInfo info, int userId) {
    161         return getBadgedLabel(info.loadLabel(mPackageManager).toString(), userId);
    162     }
    163 
    164     /**
    165      * @return the application label, badging if necessary.
    166      */
    167     public String getBadgedApplicationLabel(ApplicationInfo appInfo, int userId) {
    168         return getBadgedLabel(appInfo.loadLabel(mPackageManager).toString(), userId);
    169     }
    170 
    171     /**
    172      * @return the content description for a given task, badging it if necessary.  The content
    173      * description joins the app and activity labels.
    174      */
    175     public String getBadgedContentDescription(ActivityInfo info, int userId,
    176             ActivityManager.TaskDescription td) {
    177         String activityLabel;
    178         if (td != null && td.getLabel() != null) {
    179             activityLabel = td.getLabel();
    180         } else {
    181             activityLabel = info.loadLabel(mPackageManager).toString();
    182         }
    183         String applicationLabel = info.applicationInfo.loadLabel(mPackageManager).toString();
    184         String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
    185         return applicationLabel.equals(activityLabel)
    186                 ? badgedApplicationLabel
    187                 : badgedApplicationLabel + " " + activityLabel;
    188     }
    189 
    190     /**
    191      * @return the given label for a user, badging if necessary.
    192      */
    193     private String getBadgedLabel(String label, int userId) {
    194         if (userId != UserHandle.myUserId()) {
    195             label = mPackageManager.getUserBadgedLabel(label, new UserHandle(userId)).toString();
    196         }
    197         return label;
    198     }
    199 
    200     /**
    201      * Starts the recents activity. The caller should manage the thread on which this is called.
    202      */
    203     public void startRecentsActivity(Intent intent, AssistDataReceiver assistDataReceiver,
    204             RecentsAnimationListener animationHandler, Consumer<Boolean> resultCallback,
    205             Handler resultCallbackHandler) {
    206         try {
    207             IAssistDataReceiver receiver = null;
    208             if (assistDataReceiver != null) {
    209                 receiver = new IAssistDataReceiver.Stub() {
    210                     public void onHandleAssistData(Bundle resultData) {
    211                         assistDataReceiver.onHandleAssistData(resultData);
    212                     }
    213                     public void onHandleAssistScreenshot(Bitmap screenshot) {
    214                         assistDataReceiver.onHandleAssistScreenshot(screenshot);
    215                     }
    216                 };
    217             }
    218             IRecentsAnimationRunner runner = null;
    219             if (animationHandler != null) {
    220                 runner = new IRecentsAnimationRunner.Stub() {
    221                     public void onAnimationStart(IRecentsAnimationController controller,
    222                             RemoteAnimationTarget[] apps, Rect homeContentInsets,
    223                             Rect minimizedHomeBounds) {
    224                         final RecentsAnimationControllerCompat controllerCompat =
    225                                 new RecentsAnimationControllerCompat(controller);
    226                         final RemoteAnimationTargetCompat[] appsCompat =
    227                                 RemoteAnimationTargetCompat.wrap(apps);
    228                         animationHandler.onAnimationStart(controllerCompat, appsCompat,
    229                                 homeContentInsets, minimizedHomeBounds);
    230                     }
    231 
    232                     public void onAnimationCanceled() {
    233                         animationHandler.onAnimationCanceled();
    234                     }
    235                 };
    236             }
    237             ActivityManager.getService().startRecentsActivity(intent, receiver, runner);
    238             if (resultCallback != null) {
    239                 resultCallbackHandler.post(new Runnable() {
    240                     @Override
    241                     public void run() {
    242                         resultCallback.accept(true);
    243                     }
    244                 });
    245             }
    246         } catch (Exception e) {
    247             if (resultCallback != null) {
    248                 resultCallbackHandler.post(new Runnable() {
    249                     @Override
    250                     public void run() {
    251                         resultCallback.accept(false);
    252                     }
    253                 });
    254             }
    255         }
    256     }
    257 
    258     /**
    259      * Cancels the remote recents animation started from {@link #startRecentsActivity}.
    260      */
    261     public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
    262         try {
    263             ActivityManager.getService().cancelRecentsAnimation(restoreHomeStackPosition);
    264         } catch (RemoteException e) {
    265             Log.e(TAG, "Failed to cancel recents animation", e);
    266         }
    267     }
    268 
    269     /**
    270      * Starts a task from Recents.
    271      *
    272      * @see {@link #startActivityFromRecentsAsync(TaskKey, ActivityOptions, int, int, Consumer, Handler)}
    273      */
    274     public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options,
    275             Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
    276         startActivityFromRecentsAsync(taskKey, options, WINDOWING_MODE_UNDEFINED,
    277                 ACTIVITY_TYPE_UNDEFINED, resultCallback, resultCallbackHandler);
    278     }
    279 
    280     /**
    281      * Starts a task from Recents.
    282      *
    283      * @param resultCallback The result success callback
    284      * @param resultCallbackHandler The handler to receive the result callback
    285      */
    286     public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options,
    287             int windowingMode, int activityType, Consumer<Boolean> resultCallback,
    288             Handler resultCallbackHandler) {
    289         if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
    290             // We show non-visible docked tasks in Recents, but we always want to launch
    291             // them in the fullscreen stack.
    292             if (options == null) {
    293                 options = ActivityOptions.makeBasic();
    294             }
    295             options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
    296         } else if (windowingMode != WINDOWING_MODE_UNDEFINED
    297                 || activityType != ACTIVITY_TYPE_UNDEFINED) {
    298             if (options == null) {
    299                 options = ActivityOptions.makeBasic();
    300             }
    301             options.setLaunchWindowingMode(windowingMode);
    302             options.setLaunchActivityType(activityType);
    303         }
    304         final ActivityOptions finalOptions = options;
    305 
    306         // Execute this from another thread such that we can do other things (like caching the
    307         // bitmap for the thumbnail) while AM is busy starting our activity.
    308         mBackgroundExecutor.submit(new Runnable() {
    309             @Override
    310             public void run() {
    311                 boolean result = false;
    312                 try {
    313                     result = startActivityFromRecents(taskKey.id, finalOptions);
    314                 } catch (Exception e) {
    315                     // Fall through
    316                 }
    317                 final boolean finalResult = result;
    318                 if (resultCallback != null) {
    319                     resultCallbackHandler.post(new Runnable() {
    320                         @Override
    321                         public void run() {
    322                             resultCallback.accept(finalResult);
    323                         }
    324                     });
    325                 }
    326             }
    327         });
    328     }
    329 
    330     /**
    331      * Starts a task from Recents synchronously.
    332      */
    333     public boolean startActivityFromRecents(int taskId, ActivityOptions options) {
    334         try {
    335             Bundle optsBundle = options == null ? null : options.toBundle();
    336             ActivityManager.getService().startActivityFromRecents(taskId, optsBundle);
    337             return true;
    338         } catch (Exception e) {
    339             return false;
    340         }
    341     }
    342 
    343     /**
    344      * Registers a task stack listener with the system.
    345      * This should be called on the main thread.
    346      */
    347     public void registerTaskStackListener(TaskStackChangeListener listener) {
    348         synchronized (mTaskStackChangeListeners) {
    349             mTaskStackChangeListeners.addListener(ActivityManager.getService(), listener);
    350         }
    351     }
    352 
    353     /**
    354      * Unregisters a task stack listener with the system.
    355      * This should be called on the main thread.
    356      */
    357     public void unregisterTaskStackListener(TaskStackChangeListener listener) {
    358         synchronized (mTaskStackChangeListeners) {
    359             mTaskStackChangeListeners.removeListener(listener);
    360         }
    361     }
    362 
    363     /**
    364      * Requests that the system close any open system windows (including other SystemUI).
    365      */
    366     public void closeSystemWindows(String reason) {
    367         mBackgroundExecutor.submit(new Runnable() {
    368             @Override
    369             public void run() {
    370                 try {
    371                     ActivityManager.getService().closeSystemDialogs(reason);
    372                 } catch (RemoteException e) {
    373                     Log.w(TAG, "Failed to close system windows", e);
    374                 }
    375             }
    376         });
    377     }
    378 
    379     /**
    380      * Removes a task by id.
    381      */
    382     public void removeTask(int taskId) {
    383         mBackgroundExecutor.submit(new Runnable() {
    384             @Override
    385             public void run() {
    386                 try {
    387                     ActivityManager.getService().removeTask(taskId);
    388                 } catch (RemoteException e) {
    389                     Log.w(TAG, "Failed to remove task=" + taskId, e);
    390                 }
    391             }
    392         });
    393     }
    394 
    395     /**
    396      * Cancels the current window transtion to/from Recents for the given task id.
    397      */
    398     public void cancelWindowTransition(int taskId) {
    399         try {
    400             ActivityManager.getService().cancelTaskWindowTransition(taskId);
    401         } catch (RemoteException e) {
    402             Log.w(TAG, "Failed to cancel window transition for task=" + taskId, e);
    403         }
    404     }
    405 
    406     /**
    407      * @return whether screen pinning is active.
    408      */
    409     public boolean isScreenPinningActive() {
    410         try {
    411             return ActivityManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED;
    412         } catch (RemoteException e) {
    413             return false;
    414         }
    415     }
    416 
    417     /**
    418      * @return whether screen pinning is enabled.
    419      */
    420     public boolean isScreenPinningEnabled() {
    421         final ContentResolver cr = AppGlobals.getInitialApplication().getContentResolver();
    422         return Settings.System.getInt(cr, Settings.System.LOCK_TO_APP_ENABLED, 0) != 0;
    423     }
    424 
    425     /**
    426      * @return whether there is currently a locked task (ie. in screen pinning).
    427      */
    428     public boolean isLockToAppActive() {
    429         try {
    430             return ActivityManager.getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE;
    431         } catch (RemoteException e) {
    432             return false;
    433         }
    434     }
    435 
    436     /**
    437      * Shows a voice session identified by {@code token}
    438      * @return true if the session was shown, false otherwise
    439      */
    440     public boolean showVoiceSession(IBinder token, Bundle args, int flags) {
    441         IVoiceInteractionManagerService service = IVoiceInteractionManagerService.Stub.asInterface(
    442                 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
    443         if (service == null) {
    444             return false;
    445         }
    446         try {
    447             return service.showSessionFromSession(token, args, flags);
    448         } catch (RemoteException e) {
    449             return false;
    450         }
    451     }
    452 }
    453