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.settingslib.applications; 18 19 import android.app.Application; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.content.pm.ApplicationInfo; 25 import android.content.pm.PackageInfo; 26 import android.content.pm.PackageManager; 27 import android.content.pm.ResolveInfo; 28 import android.graphics.drawable.Drawable; 29 import android.hardware.usb.IUsbManager; 30 import android.net.Uri; 31 import android.os.Environment; 32 import android.os.RemoteException; 33 import android.os.SystemProperties; 34 import android.os.UserHandle; 35 import android.os.UserManager; 36 import android.text.TextUtils; 37 import android.util.Log; 38 39 import com.android.settingslib.R; 40 import com.android.settingslib.Utils; 41 import com.android.settingslib.applications.instantapps.InstantAppDataProvider; 42 import com.android.settingslib.utils.ThreadUtils; 43 44 import java.util.ArrayList; 45 import java.util.List; 46 47 public class AppUtils { 48 private static final String TAG = "AppUtils"; 49 50 /** 51 * This should normally only be set in robolectric tests, to avoid getting a method not found 52 * exception when calling the isInstantApp method of the ApplicationInfo class, because 53 * robolectric does not yet have an implementation of it. 54 */ 55 private static InstantAppDataProvider sInstantAppDataProvider = null; 56 57 private static final Intent sBrowserIntent; 58 59 static { 60 sBrowserIntent = new Intent() 61 .setAction(Intent.ACTION_VIEW) 62 .addCategory(Intent.CATEGORY_BROWSABLE) 63 .setData(Uri.parse("http:")); 64 } 65 getLaunchByDefaultSummary(ApplicationsState.AppEntry appEntry, IUsbManager usbManager, PackageManager pm, Context context)66 public static CharSequence getLaunchByDefaultSummary(ApplicationsState.AppEntry appEntry, 67 IUsbManager usbManager, PackageManager pm, Context context) { 68 String packageName = appEntry.info.packageName; 69 boolean hasPreferred = hasPreferredActivities(pm, packageName) 70 || hasUsbDefaults(usbManager, packageName); 71 int status = pm.getIntentVerificationStatusAsUser(packageName, UserHandle.myUserId()); 72 // consider a visible current link-handling state to be any explicitly designated behavior 73 boolean hasDomainURLsPreference = 74 status != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; 75 return context.getString(hasPreferred || hasDomainURLsPreference 76 ? R.string.launch_defaults_some 77 : R.string.launch_defaults_none); 78 } 79 hasUsbDefaults(IUsbManager usbManager, String packageName)80 public static boolean hasUsbDefaults(IUsbManager usbManager, String packageName) { 81 try { 82 if (usbManager != null) { 83 return usbManager.hasDefaults(packageName, UserHandle.myUserId()); 84 } 85 } catch (RemoteException e) { 86 Log.e(TAG, "mUsbManager.hasDefaults", e); 87 } 88 return false; 89 } 90 hasPreferredActivities(PackageManager pm, String packageName)91 public static boolean hasPreferredActivities(PackageManager pm, String packageName) { 92 // Get list of preferred activities 93 List<ComponentName> prefActList = new ArrayList<>(); 94 // Intent list cannot be null. so pass empty list 95 List<IntentFilter> intentList = new ArrayList<>(); 96 pm.getPreferredActivities(intentList, prefActList, packageName); 97 Log.d(TAG, "Have " + prefActList.size() + " number of activities in preferred list"); 98 return prefActList.size() > 0; 99 } 100 101 /** 102 * Returns a boolean indicating whether the given package should be considered an instant app 103 */ isInstant(ApplicationInfo info)104 public static boolean isInstant(ApplicationInfo info) { 105 if (sInstantAppDataProvider != null) { 106 if (sInstantAppDataProvider.isInstantApp(info)) { 107 return true; 108 } 109 } else if (info.isInstantApp()) { 110 return true; 111 } 112 113 // For debugging/testing, we support setting the following property to a comma-separated 114 // list of search terms (typically, but not necessarily, full package names) to match 115 // against the package names of the app. 116 String propVal = SystemProperties.get("settingsdebug.instant.packages"); 117 if (propVal != null && !propVal.isEmpty() && info.packageName != null) { 118 String[] searchTerms = propVal.split(","); 119 if (searchTerms != null) { 120 for (String term : searchTerms) { 121 if (info.packageName.contains(term)) { 122 return true; 123 } 124 } 125 } 126 } 127 return false; 128 } 129 130 /** Returns the label for a given package. */ getApplicationLabel( PackageManager packageManager, String packageName)131 public static CharSequence getApplicationLabel( 132 PackageManager packageManager, String packageName) { 133 return com.android.settingslib.utils.applications.AppUtils 134 .getApplicationLabel(packageManager, packageName); 135 } 136 137 /** 138 * Returns a boolean indicating whether the given package is a hidden system module 139 */ isHiddenSystemModule(Context context, String packageName)140 public static boolean isHiddenSystemModule(Context context, String packageName) { 141 return ApplicationsState.getInstance((Application) context.getApplicationContext()) 142 .isHiddenModule(packageName); 143 } 144 145 /** 146 * Returns a boolean indicating whether a given package is a system module. 147 */ isSystemModule(Context context, String packageName)148 public static boolean isSystemModule(Context context, String packageName) { 149 return ApplicationsState.getInstance((Application) context.getApplicationContext()) 150 .isSystemModule(packageName); 151 } 152 153 /** 154 * Returns a boolean indicating whether a given package is a mainline module. 155 */ isMainlineModule(PackageManager pm, String packageName)156 public static boolean isMainlineModule(PackageManager pm, String packageName) { 157 // Check if the package is listed among the system modules. 158 try { 159 pm.getModuleInfo(packageName, 0 /* flags */); 160 return true; 161 } catch (PackageManager.NameNotFoundException e) { 162 //pass 163 } 164 165 try { 166 final PackageInfo pkg = pm.getPackageInfo(packageName, 0 /* flags */); 167 // Check if the package is contained in an APEX. There is no public API to properly 168 // check whether a given APK package comes from an APEX registered as module. 169 // Therefore we conservatively assume that any package scanned from an /apex path is 170 // a system package. 171 return pkg.applicationInfo.sourceDir.startsWith( 172 Environment.getApexDirectory().getAbsolutePath()); 173 } catch (PackageManager.NameNotFoundException e) { 174 return false; 175 } 176 } 177 178 /** 179 * Returns a content description of an app name which distinguishes a personal app from a 180 * work app for accessibility purpose. 181 * If the app is in a work profile, then add a "work" prefix to the app name. 182 */ getAppContentDescription(Context context, String packageName, int userId)183 public static String getAppContentDescription(Context context, String packageName, 184 int userId) { 185 return com.android.settingslib.utils.applications.AppUtils.getAppContentDescription(context, 186 packageName, userId); 187 } 188 189 /** 190 * Returns a boolean indicating whether a given package is a browser app. 191 * 192 * An app is a "browser" if it has an activity resolution that wound up 193 * marked with the 'handleAllWebDataURI' flag. 194 */ isBrowserApp(Context context, String packageName, int userId)195 public static boolean isBrowserApp(Context context, String packageName, int userId) { 196 sBrowserIntent.setPackage(packageName); 197 final List<ResolveInfo> list = context.getPackageManager().queryIntentActivitiesAsUser( 198 sBrowserIntent, PackageManager.MATCH_ALL, userId); 199 for (ResolveInfo info : list) { 200 if (info.activityInfo != null && info.handleAllWebDataURI) { 201 return true; 202 } 203 } 204 return false; 205 } 206 207 /** 208 * Returns a boolean indicating whether a given package is a default browser. 209 * 210 * @param packageName a given package. 211 * @return true if the given package is default browser. 212 */ isDefaultBrowser(Context context, String packageName)213 public static boolean isDefaultBrowser(Context context, String packageName) { 214 final String defaultBrowserPackage = 215 context.getPackageManager().getDefaultBrowserPackageNameAsUser( 216 UserHandle.myUserId()); 217 return TextUtils.equals(packageName, defaultBrowserPackage); 218 } 219 220 /** 221 * Get the app icon by app entry. 222 * 223 * @param context caller's context 224 * @param appEntry AppEntry of ApplicationsState 225 * @return app icon of the app entry 226 */ getIcon(Context context, ApplicationsState.AppEntry appEntry)227 public static Drawable getIcon(Context context, ApplicationsState.AppEntry appEntry) { 228 if (appEntry == null || appEntry.info == null) { 229 return null; 230 } 231 232 final AppIconCacheManager appIconCacheManager = AppIconCacheManager.getInstance(); 233 final String packageName = appEntry.info.packageName; 234 final int uid = appEntry.info.uid; 235 236 Drawable icon = appIconCacheManager.get(packageName, uid); 237 if (icon == null) { 238 if (appEntry.apkFile != null && appEntry.apkFile.exists()) { 239 icon = Utils.getBadgedIcon(context, appEntry.info); 240 appIconCacheManager.put(packageName, uid, icon); 241 } else { 242 setAppEntryMounted(appEntry, /* mounted= */ false); 243 icon = context.getDrawable( 244 com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon); 245 } 246 } else if (!appEntry.mounted && appEntry.apkFile != null && appEntry.apkFile.exists()) { 247 // If the app wasn't mounted but is now mounted, reload its icon. 248 setAppEntryMounted(appEntry, /* mounted= */ true); 249 icon = Utils.getBadgedIcon(context, appEntry.info); 250 appIconCacheManager.put(packageName, uid, icon); 251 } 252 253 return icon; 254 } 255 256 /** 257 * Get the app icon from cache by app entry. 258 * 259 * @param appEntry AppEntry of ApplicationsState 260 * @return app icon of the app entry 261 */ getIconFromCache(ApplicationsState.AppEntry appEntry)262 public static Drawable getIconFromCache(ApplicationsState.AppEntry appEntry) { 263 return appEntry == null || appEntry.info == null ? null 264 : AppIconCacheManager.getInstance().get( 265 appEntry.info.packageName, 266 appEntry.info.uid); 267 } 268 269 /** 270 * Preload the top N icons of app entry list. 271 * 272 * @param context caller's context 273 * @param appEntries AppEntry list of ApplicationsState 274 * @param number the number of Top N icons of the appEntries 275 */ preloadTopIcons(Context context, ArrayList<ApplicationsState.AppEntry> appEntries, int number)276 public static void preloadTopIcons(Context context, 277 ArrayList<ApplicationsState.AppEntry> appEntries, int number) { 278 if (appEntries == null || appEntries.isEmpty() || number <= 0) { 279 return; 280 } 281 282 for (int i = 0; i < Math.min(appEntries.size(), number); i++) { 283 final ApplicationsState.AppEntry entry = appEntries.get(i); 284 ThreadUtils.postOnBackgroundThread(() -> { 285 getIcon(context, entry); 286 }); 287 } 288 } 289 290 /** 291 * Returns a boolean indicating whether this app is installed or not. 292 * 293 * @param appEntry AppEntry of ApplicationsState. 294 * @return true if the app is in installed state. 295 */ isAppInstalled(ApplicationsState.AppEntry appEntry)296 public static boolean isAppInstalled(ApplicationsState.AppEntry appEntry) { 297 if (appEntry == null || appEntry.info == null) { 298 return false; 299 } 300 return (appEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0; 301 } 302 setAppEntryMounted(ApplicationsState.AppEntry appEntry, boolean mounted)303 private static void setAppEntryMounted(ApplicationsState.AppEntry appEntry, boolean mounted) { 304 if (appEntry.mounted != mounted) { 305 synchronized (appEntry) { 306 appEntry.mounted = mounted; 307 } 308 } 309 } 310 311 /** 312 * Returns clone user profile id if present. Returns -1 if not present. 313 */ getCloneUserId(Context context)314 public static int getCloneUserId(Context context) { 315 UserManager userManager = context.getSystemService(UserManager.class); 316 for (UserHandle userHandle : userManager.getUserProfiles()) { 317 if (userManager.getUserInfo(userHandle.getIdentifier()).isCloneProfile()) { 318 return userHandle.getIdentifier(); 319 } 320 } 321 return -1; 322 } 323 } 324