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