Home | History | Annotate | Download | only in misc
      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.misc;
     18 
     19 import android.app.ActivityManager;
     20 import android.app.ActivityManagerNative;
     21 import android.app.ActivityOptions;
     22 import android.app.AppGlobals;
     23 import android.app.IActivityContainer;
     24 import android.app.IActivityManager;
     25 import android.app.ITaskStackListener;
     26 import android.app.SearchManager;
     27 import android.appwidget.AppWidgetHost;
     28 import android.appwidget.AppWidgetManager;
     29 import android.appwidget.AppWidgetProviderInfo;
     30 import android.content.ComponentName;
     31 import android.content.ContentResolver;
     32 import android.content.Context;
     33 import android.content.Intent;
     34 import android.content.pm.ActivityInfo;
     35 import android.content.pm.IPackageManager;
     36 import android.content.pm.PackageManager;
     37 import android.content.pm.ResolveInfo;
     38 import android.content.res.Resources;
     39 import android.graphics.Bitmap;
     40 import android.graphics.BitmapFactory;
     41 import android.graphics.Canvas;
     42 import android.graphics.Color;
     43 import android.graphics.Paint;
     44 import android.graphics.Point;
     45 import android.graphics.PorterDuff;
     46 import android.graphics.PorterDuffXfermode;
     47 import android.graphics.Rect;
     48 import android.graphics.drawable.ColorDrawable;
     49 import android.graphics.drawable.Drawable;
     50 import android.os.Bundle;
     51 import android.os.Handler;
     52 import android.os.HandlerThread;
     53 import android.os.ParcelFileDescriptor;
     54 import android.os.RemoteException;
     55 import android.os.SystemProperties;
     56 import android.os.UserHandle;
     57 import android.os.UserManager;
     58 import android.provider.Settings;
     59 import android.util.Log;
     60 import android.util.MutableBoolean;
     61 import android.util.Pair;
     62 import android.util.SparseArray;
     63 import android.view.Display;
     64 import android.view.DisplayInfo;
     65 import android.view.SurfaceControl;
     66 import android.view.WindowManager;
     67 import android.view.accessibility.AccessibilityManager;
     68 
     69 import com.android.internal.app.AssistUtils;
     70 import com.android.systemui.Prefs;
     71 import com.android.systemui.R;
     72 import com.android.systemui.recents.Constants;
     73 import com.android.systemui.recents.Recents;
     74 import com.android.systemui.recents.RecentsAppWidgetHost;
     75 
     76 import java.io.IOException;
     77 import java.util.ArrayList;
     78 import java.util.Iterator;
     79 import java.util.List;
     80 import java.util.Random;
     81 
     82 /**
     83  * Acts as a shim around the real system services that we need to access data from, and provides
     84  * a point of injection when testing UI.
     85  */
     86 public class SystemServicesProxy {
     87     final static String TAG = "SystemServicesProxy";
     88 
     89     final static BitmapFactory.Options sBitmapOptions;
     90     final static HandlerThread sBgThread;
     91 
     92     static {
     93         sBgThread = new HandlerThread("Recents-SystemServicesProxy",
     94                 android.os.Process.THREAD_PRIORITY_BACKGROUND);
     95         sBgThread.start();
     96         sBitmapOptions = new BitmapFactory.Options();
     97         sBitmapOptions.inMutable = true;
     98     }
     99 
    100     AccessibilityManager mAccm;
    101     ActivityManager mAm;
    102     IActivityManager mIam;
    103     AppWidgetManager mAwm;
    104     PackageManager mPm;
    105     IPackageManager mIpm;
    106     AssistUtils mAssistUtils;
    107     WindowManager mWm;
    108     Display mDisplay;
    109     String mRecentsPackage;
    110     ComponentName mAssistComponent;
    111 
    112     Handler mBgThreadHandler;
    113 
    114     Bitmap mDummyIcon;
    115     int mDummyThumbnailWidth;
    116     int mDummyThumbnailHeight;
    117     Paint mBgProtectionPaint;
    118     Canvas mBgProtectionCanvas;
    119 
    120     /** Private constructor */
    121     public SystemServicesProxy(Context context) {
    122         mAccm = AccessibilityManager.getInstance(context);
    123         mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    124         mIam = ActivityManagerNative.getDefault();
    125         mAwm = AppWidgetManager.getInstance(context);
    126         mPm = context.getPackageManager();
    127         mIpm = AppGlobals.getPackageManager();
    128         mAssistUtils = new AssistUtils(context);
    129         mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    130         mDisplay = mWm.getDefaultDisplay();
    131         mRecentsPackage = context.getPackageName();
    132         mBgThreadHandler = new Handler(sBgThread.getLooper());
    133 
    134         // Get the dummy thumbnail width/heights
    135         Resources res = context.getResources();
    136         int wId = com.android.internal.R.dimen.thumbnail_width;
    137         int hId = com.android.internal.R.dimen.thumbnail_height;
    138         mDummyThumbnailWidth = res.getDimensionPixelSize(wId);
    139         mDummyThumbnailHeight = res.getDimensionPixelSize(hId);
    140 
    141         // Create the protection paints
    142         mBgProtectionPaint = new Paint();
    143         mBgProtectionPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
    144         mBgProtectionPaint.setColor(0xFFffffff);
    145         mBgProtectionCanvas = new Canvas();
    146 
    147         // Resolve the assist intent
    148         mAssistComponent = mAssistUtils.getAssistComponentForUser(UserHandle.myUserId());
    149 
    150         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
    151             // Create a dummy icon
    152             mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
    153             mDummyIcon.eraseColor(0xFF999999);
    154         }
    155     }
    156 
    157     /** Returns a list of the recents tasks */
    158     public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
    159             boolean isTopTaskHome) {
    160         if (mAm == null) return null;
    161 
    162         // If we are mocking, then create some recent tasks
    163         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
    164             ArrayList<ActivityManager.RecentTaskInfo> tasks =
    165                     new ArrayList<ActivityManager.RecentTaskInfo>();
    166             int count = Math.min(numLatestTasks, Constants.DebugFlags.App.SystemServicesProxyMockTaskCount);
    167             for (int i = 0; i < count; i++) {
    168                 // Create a dummy component name
    169                 int packageIndex = i % Constants.DebugFlags.App.SystemServicesProxyMockPackageCount;
    170                 ComponentName cn = new ComponentName("com.android.test" + packageIndex,
    171                         "com.android.test" + i + ".Activity");
    172                 String description = "" + i + " - " +
    173                         Long.toString(Math.abs(new Random().nextLong()), 36);
    174                 // Create the recent task info
    175                 ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
    176                 rti.id = rti.persistentId = i;
    177                 rti.baseIntent = new Intent();
    178                 rti.baseIntent.setComponent(cn);
    179                 rti.description = description;
    180                 rti.firstActiveTime = rti.lastActiveTime = i;
    181                 if (i % 2 == 0) {
    182                     rti.taskDescription = new ActivityManager.TaskDescription(description,
    183                         Bitmap.createBitmap(mDummyIcon),
    184                         0xFF000000 | (0xFFFFFF & new Random().nextInt()));
    185                 } else {
    186                     rti.taskDescription = new ActivityManager.TaskDescription();
    187                 }
    188                 tasks.add(rti);
    189             }
    190             return tasks;
    191         }
    192 
    193         // Remove home/recents/excluded tasks
    194         int minNumTasksToQuery = 10;
    195         int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
    196         List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery,
    197                 ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
    198                 ActivityManager.RECENT_IGNORE_UNAVAILABLE |
    199                 ActivityManager.RECENT_INCLUDE_PROFILES |
    200                 ActivityManager.RECENT_WITH_EXCLUDED, userId);
    201 
    202         // Break early if we can't get a valid set of tasks
    203         if (tasks == null) {
    204             return new ArrayList<>();
    205         }
    206 
    207         boolean isFirstValidTask = true;
    208         Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
    209         while (iter.hasNext()) {
    210             ActivityManager.RecentTaskInfo t = iter.next();
    211 
    212             // NOTE: The order of these checks happens in the expected order of the traversal of the
    213             // tasks
    214 
    215             // Check the first non-recents task, include this task even if it is marked as excluded
    216             // from recents if we are currently in the app.  In other words, only remove excluded
    217             // tasks if it is not the first active task.
    218             boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
    219                     == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
    220             if (isExcluded && (isTopTaskHome || !isFirstValidTask)) {
    221                 iter.remove();
    222                 continue;
    223             }
    224             isFirstValidTask = false;
    225         }
    226 
    227         return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
    228     }
    229 
    230     /** Returns a list of the running tasks */
    231     private List<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) {
    232         if (mAm == null) return null;
    233         return mAm.getRunningTasks(numTasks);
    234     }
    235 
    236     /** Returns the top task. */
    237     public ActivityManager.RunningTaskInfo getTopMostTask() {
    238         List<ActivityManager.RunningTaskInfo> tasks = getRunningTasks(1);
    239         if (tasks != null && !tasks.isEmpty()) {
    240             return tasks.get(0);
    241         }
    242         return null;
    243     }
    244 
    245     /** Returns whether the recents is currently running */
    246     public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
    247             MutableBoolean isHomeTopMost) {
    248         if (topTask != null) {
    249             ComponentName topActivity = topTask.topActivity;
    250 
    251             // Check if the front most activity is recents
    252             if (topActivity.getPackageName().equals(Recents.sRecentsPackage) &&
    253                     topActivity.getClassName().equals(Recents.sRecentsActivity)) {
    254                 if (isHomeTopMost != null) {
    255                     isHomeTopMost.value = false;
    256                 }
    257                 return true;
    258             }
    259 
    260             if (isHomeTopMost != null) {
    261                 isHomeTopMost.value = isInHomeStack(topTask.id);
    262             }
    263         }
    264         return false;
    265     }
    266 
    267     /** Get the bounds of a stack / task. */
    268     public Rect getTaskBounds(int stackId) {
    269         ActivityManager.StackInfo info = getAllStackInfos().get(stackId);
    270         if (info != null)
    271           return info.bounds;
    272         return new Rect();
    273     }
    274 
    275     /** Resize a given task. */
    276     public void resizeTask(int taskId, Rect bounds) {
    277         if (mIam == null) return;
    278 
    279         try {
    280             mIam.resizeTask(taskId, bounds);
    281         } catch (RemoteException e) {
    282             e.printStackTrace();
    283         }
    284     }
    285 
    286     /** Returns the stack info for all stacks. */
    287     public SparseArray<ActivityManager.StackInfo> getAllStackInfos() {
    288         if (mIam == null) return new SparseArray<ActivityManager.StackInfo>();
    289 
    290         try {
    291             SparseArray<ActivityManager.StackInfo> stacks =
    292                     new SparseArray<ActivityManager.StackInfo>();
    293             List<ActivityManager.StackInfo> infos = mIam.getAllStackInfos();
    294             int stackCount = infos.size();
    295             for (int i = 0; i < stackCount; i++) {
    296                 ActivityManager.StackInfo info = infos.get(i);
    297                 stacks.put(info.stackId, info);
    298             }
    299             return stacks;
    300         } catch (RemoteException e) {
    301             e.printStackTrace();
    302             return new SparseArray<ActivityManager.StackInfo>();
    303         }
    304     }
    305 
    306     /** Returns the focused stack id. */
    307     public int getFocusedStack() {
    308         if (mIam == null) return -1;
    309 
    310         try {
    311             return mIam.getFocusedStackId();
    312         } catch (RemoteException e) {
    313             e.printStackTrace();
    314             return -1;
    315         }
    316     }
    317 
    318     /** Returns whether the specified task is in the home stack */
    319     public boolean isInHomeStack(int taskId) {
    320         if (mAm == null) return false;
    321 
    322         // If we are mocking, then just return false
    323         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
    324             return false;
    325         }
    326 
    327         return mAm.isInHomeStack(taskId);
    328     }
    329 
    330     /** Returns the top task thumbnail for the given task id */
    331     public Bitmap getTaskThumbnail(int taskId) {
    332         if (mAm == null) return null;
    333 
    334         // If we are mocking, then just return a dummy thumbnail
    335         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
    336             Bitmap thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, mDummyThumbnailHeight,
    337                     Bitmap.Config.ARGB_8888);
    338             thumbnail.eraseColor(0xff333333);
    339             return thumbnail;
    340         }
    341 
    342         Bitmap thumbnail = SystemServicesProxy.getThumbnail(mAm, taskId);
    343         if (thumbnail != null) {
    344             thumbnail.setHasAlpha(false);
    345             // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
    346             // left pixel, then assume the whole thumbnail is transparent. Generally, proper
    347             // screenshots are always composed onto a bitmap that has no alpha.
    348             if (Color.alpha(thumbnail.getPixel(0, 0)) == 0) {
    349                 mBgProtectionCanvas.setBitmap(thumbnail);
    350                 mBgProtectionCanvas.drawRect(0, 0, thumbnail.getWidth(), thumbnail.getHeight(),
    351                         mBgProtectionPaint);
    352                 mBgProtectionCanvas.setBitmap(null);
    353                 Log.e(TAG, "Invalid screenshot detected from getTaskThumbnail()");
    354             }
    355         }
    356         return thumbnail;
    357     }
    358 
    359     /**
    360      * Returns a task thumbnail from the activity manager
    361      */
    362     public static Bitmap getThumbnail(ActivityManager activityManager, int taskId) {
    363         ActivityManager.TaskThumbnail taskThumbnail = activityManager.getTaskThumbnail(taskId);
    364         if (taskThumbnail == null) return null;
    365 
    366         Bitmap thumbnail = taskThumbnail.mainThumbnail;
    367         ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
    368         if (thumbnail == null && descriptor != null) {
    369             thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor(),
    370                     null, sBitmapOptions);
    371         }
    372         if (descriptor != null) {
    373             try {
    374                 descriptor.close();
    375             } catch (IOException e) {
    376             }
    377         }
    378         return thumbnail;
    379     }
    380 
    381     /** Moves a task to the front with the specified activity options. */
    382     public void moveTaskToFront(int taskId, ActivityOptions opts) {
    383         if (mAm == null) return;
    384         if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
    385 
    386         if (opts != null) {
    387             mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME,
    388                     opts.toBundle());
    389         } else {
    390             mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME);
    391         }
    392     }
    393 
    394     /** Removes the task */
    395     public void removeTask(final int taskId) {
    396         if (mAm == null) return;
    397         if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
    398 
    399         // Remove the task.
    400         mBgThreadHandler.post(new Runnable() {
    401             @Override
    402             public void run() {
    403                 mAm.removeTask(taskId);
    404             }
    405         });
    406     }
    407 
    408     /**
    409      * Returns the activity info for a given component name.
    410      *
    411      * @param cn The component name of the activity.
    412      * @param userId The userId of the user that this is for.
    413      */
    414     public ActivityInfo getActivityInfo(ComponentName cn, int userId) {
    415         if (mIpm == null) return null;
    416         if (Constants.DebugFlags.App.EnableSystemServicesProxy) return new ActivityInfo();
    417 
    418         try {
    419             return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId);
    420         } catch (RemoteException e) {
    421             e.printStackTrace();
    422             return null;
    423         }
    424     }
    425 
    426     /**
    427      * Returns the activity info for a given component name.
    428      *
    429      * @param cn The component name of the activity.
    430      */
    431     public ActivityInfo getActivityInfo(ComponentName cn) {
    432         if (mPm == null) return null;
    433         if (Constants.DebugFlags.App.EnableSystemServicesProxy) return new ActivityInfo();
    434 
    435         try {
    436             return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA);
    437         } catch (PackageManager.NameNotFoundException e) {
    438             e.printStackTrace();
    439             return null;
    440         }
    441     }
    442 
    443     /** Returns the activity label */
    444     public String getActivityLabel(ActivityInfo info) {
    445         if (mPm == null) return null;
    446 
    447         // If we are mocking, then return a mock label
    448         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
    449             return "Recent Task";
    450         }
    451 
    452         return info.loadLabel(mPm).toString();
    453     }
    454 
    455     /** Returns the application label */
    456     public String getApplicationLabel(Intent baseIntent, int userId) {
    457         if (mPm == null) return null;
    458 
    459         // If we are mocking, then return a mock label
    460         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
    461             return "Recent Task";
    462         }
    463 
    464         ResolveInfo ri = mPm.resolveActivityAsUser(baseIntent, 0, userId);
    465         CharSequence label = (ri != null) ? ri.loadLabel(mPm) : null;
    466         return (label != null) ? label.toString() : null;
    467     }
    468 
    469     /** Returns the content description for a given task */
    470     public String getContentDescription(Intent baseIntent, int userId, String activityLabel,
    471             Resources res) {
    472         String applicationLabel = getApplicationLabel(baseIntent, userId);
    473         if (applicationLabel == null) {
    474             return getBadgedLabel(activityLabel, userId);
    475         }
    476         String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
    477         return applicationLabel.equals(activityLabel) ? badgedApplicationLabel
    478                 : res.getString(R.string.accessibility_recents_task_header,
    479                         badgedApplicationLabel, activityLabel);
    480     }
    481 
    482     /**
    483      * Returns the activity icon for the ActivityInfo for a user, badging if
    484      * necessary.
    485      */
    486     public Drawable getActivityIcon(ActivityInfo info, int userId) {
    487         if (mPm == null) return null;
    488 
    489         // If we are mocking, then return a mock label
    490         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
    491             return new ColorDrawable(0xFF666666);
    492         }
    493 
    494         Drawable icon = info.loadIcon(mPm);
    495         return getBadgedIcon(icon, userId);
    496     }
    497 
    498     /**
    499      * Returns the given icon for a user, badging if necessary.
    500      */
    501     public Drawable getBadgedIcon(Drawable icon, int userId) {
    502         if (userId != UserHandle.myUserId()) {
    503             icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId));
    504         }
    505         return icon;
    506     }
    507 
    508     /**
    509      * Returns the given label for a user, badging if necessary.
    510      */
    511     public String getBadgedLabel(String label, int userId) {
    512         if (userId != UserHandle.myUserId()) {
    513             label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString();
    514         }
    515         return label;
    516     }
    517 
    518     /** Returns the package name of the home activity. */
    519     public String getHomeActivityPackageName() {
    520         if (mPm == null) return null;
    521         if (Constants.DebugFlags.App.EnableSystemServicesProxy) return null;
    522 
    523         ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
    524         ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities);
    525         if (defaultHomeActivity != null) {
    526             return defaultHomeActivity.getPackageName();
    527         } else if (homeActivities.size() == 1) {
    528             ResolveInfo info = homeActivities.get(0);
    529             if (info.activityInfo != null) {
    530                 return info.activityInfo.packageName;
    531             }
    532         }
    533         return null;
    534     }
    535 
    536     /**
    537      * Returns whether the foreground user is the owner.
    538      */
    539     public boolean isForegroundUserOwner() {
    540         if (mAm == null) return false;
    541 
    542         return mAm.getCurrentUser() == UserHandle.USER_OWNER;
    543     }
    544 
    545     /**
    546      * Returns the current search widget id.
    547      */
    548     public int getSearchAppWidgetId(Context context) {
    549         return Prefs.getInt(context, Prefs.Key.SEARCH_APP_WIDGET_ID, -1);
    550     }
    551 
    552     /**
    553      * Returns the current search widget info, binding a new one if necessary.
    554      */
    555     public AppWidgetProviderInfo getOrBindSearchAppWidget(Context context, AppWidgetHost host) {
    556         int searchWidgetId = Prefs.getInt(context, Prefs.Key.SEARCH_APP_WIDGET_ID, -1);
    557         AppWidgetProviderInfo searchWidgetInfo = mAwm.getAppWidgetInfo(searchWidgetId);
    558         AppWidgetProviderInfo resolvedSearchWidgetInfo = resolveSearchAppWidget();
    559 
    560         // Return the search widget info if it hasn't changed
    561         if (searchWidgetInfo != null && resolvedSearchWidgetInfo != null &&
    562                 searchWidgetInfo.provider.equals(resolvedSearchWidgetInfo.provider)) {
    563             if (Prefs.getString(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE, null) == null) {
    564                 Prefs.putString(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE,
    565                         searchWidgetInfo.provider.getPackageName());
    566             }
    567             return searchWidgetInfo;
    568         }
    569 
    570         // Delete the old widget
    571         if (searchWidgetId != -1) {
    572             host.deleteAppWidgetId(searchWidgetId);
    573         }
    574 
    575         // And rebind a new search widget
    576         if (resolvedSearchWidgetInfo != null) {
    577             Pair<Integer, AppWidgetProviderInfo> widgetInfo = bindSearchAppWidget(host,
    578                     resolvedSearchWidgetInfo);
    579             if (widgetInfo != null) {
    580                 Prefs.putInt(context, Prefs.Key.SEARCH_APP_WIDGET_ID, widgetInfo.first);
    581                 Prefs.putString(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE,
    582                         widgetInfo.second.provider.getPackageName());
    583                 return widgetInfo.second;
    584             }
    585         }
    586 
    587         // If we fall through here, then there is no resolved search widget, so clear the state
    588         Prefs.remove(context, Prefs.Key.SEARCH_APP_WIDGET_ID);
    589         Prefs.remove(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE);
    590         return null;
    591     }
    592 
    593     /**
    594      * Returns the first Recents widget from the same package as the global assist activity.
    595      */
    596     private AppWidgetProviderInfo resolveSearchAppWidget() {
    597         if (mAssistComponent == null) return null;
    598         List<AppWidgetProviderInfo> widgets = mAwm.getInstalledProviders(
    599                 AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
    600         for (AppWidgetProviderInfo info : widgets) {
    601             if (info.provider.getPackageName().equals(mAssistComponent.getPackageName())) {
    602                 return info;
    603             }
    604         }
    605         return null;
    606     }
    607 
    608     /**
    609      * Resolves and binds the search app widget that is to appear in the recents.
    610      */
    611     private Pair<Integer, AppWidgetProviderInfo> bindSearchAppWidget(AppWidgetHost host,
    612             AppWidgetProviderInfo resolvedSearchWidgetInfo) {
    613         if (mAwm == null) return null;
    614         if (mAssistComponent == null) return null;
    615 
    616         // Allocate a new widget id and try and bind the app widget (if that fails, then just skip)
    617         int searchWidgetId = host.allocateAppWidgetId();
    618         Bundle opts = new Bundle();
    619         opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
    620                 AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
    621         if (!mAwm.bindAppWidgetIdIfAllowed(searchWidgetId, resolvedSearchWidgetInfo.provider, opts)) {
    622             host.deleteAppWidgetId(searchWidgetId);
    623             return null;
    624         }
    625         return new Pair<>(searchWidgetId, resolvedSearchWidgetInfo);
    626     }
    627 
    628     /**
    629      * Returns whether touch exploration is currently enabled.
    630      */
    631     public boolean isTouchExplorationEnabled() {
    632         if (mAccm == null) return false;
    633 
    634         return mAccm.isEnabled() && mAccm.isTouchExplorationEnabled();
    635     }
    636 
    637     /**
    638      * Returns a global setting.
    639      */
    640     public int getGlobalSetting(Context context, String setting) {
    641         ContentResolver cr = context.getContentResolver();
    642         return Settings.Global.getInt(cr, setting, 0);
    643     }
    644 
    645     /**
    646      * Returns a system setting.
    647      */
    648     public int getSystemSetting(Context context, String setting) {
    649         ContentResolver cr = context.getContentResolver();
    650         return Settings.System.getInt(cr, setting, 0);
    651     }
    652 
    653     /**
    654      * Returns a system property.
    655      */
    656     public String getSystemProperty(String key) {
    657         return SystemProperties.get(key);
    658     }
    659 
    660     /**
    661      * Returns the window rect.
    662      */
    663     public Rect getWindowRect() {
    664         Rect windowRect = new Rect();
    665         if (mWm == null) return windowRect;
    666 
    667         Point p = new Point();
    668         mWm.getDefaultDisplay().getRealSize(p);
    669         windowRect.set(0, 0, p.x, p.y);
    670         return windowRect;
    671     }
    672 
    673     /** Starts an activity from recents. */
    674     public boolean startActivityFromRecents(Context context, int taskId, String taskName,
    675             ActivityOptions options) {
    676         if (mIam != null) {
    677             try {
    678                 mIam.startActivityFromRecents(taskId, options == null ? null : options.toBundle());
    679                 return true;
    680             } catch (Exception e) {
    681                 Console.logError(context,
    682                         context.getString(R.string.recents_launch_error_message, taskName));
    683             }
    684         }
    685         return false;
    686     }
    687 
    688     /** Starts an in-place animation on the front most application windows. */
    689     public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts) {
    690         if (mIam == null) return;
    691 
    692         try {
    693             mIam.startInPlaceAnimationOnFrontMostApplication(opts);
    694         } catch (Exception e) {
    695             e.printStackTrace();
    696         }
    697     }
    698 
    699     /** Registers a task stack listener with the system. */
    700     public void registerTaskStackListener(ITaskStackListener listener) {
    701         if (mIam == null) return;
    702 
    703         try {
    704             mIam.registerTaskStackListener(listener);
    705         } catch (Exception e) {
    706             e.printStackTrace();
    707         }
    708     }
    709 }
    710