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