• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16 
17 package com.android.settings;
18 
19 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PASSWORD;
20 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PATTERN;
21 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PIN;
22 import static android.content.Intent.EXTRA_USER;
23 import static android.content.Intent.EXTRA_USER_ID;
24 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
25 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
26 
27 import android.app.ActionBar;
28 import android.app.Activity;
29 import android.app.ActivityManager;
30 import android.app.AppGlobals;
31 import android.app.IActivityManager;
32 import android.app.KeyguardManager;
33 import android.app.admin.DevicePolicyManager;
34 import android.content.ActivityNotFoundException;
35 import android.content.ComponentName;
36 import android.content.ContentResolver;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.content.IntentFilter;
40 import android.content.pm.ApplicationInfo;
41 import android.content.pm.IPackageManager;
42 import android.content.pm.IntentFilterVerificationInfo;
43 import android.content.pm.PackageManager;
44 import android.content.pm.PackageManager.NameNotFoundException;
45 import android.content.pm.ResolveInfo;
46 import android.content.pm.UserInfo;
47 import android.content.pm.UserProperties;
48 import android.content.res.Configuration;
49 import android.content.res.Resources;
50 import android.content.res.TypedArray;
51 import android.database.Cursor;
52 import android.graphics.Bitmap;
53 import android.graphics.Canvas;
54 import android.graphics.drawable.AdaptiveIconDrawable;
55 import android.graphics.drawable.BitmapDrawable;
56 import android.graphics.drawable.Drawable;
57 import android.graphics.drawable.VectorDrawable;
58 import android.hardware.face.FaceManager;
59 import android.hardware.fingerprint.FingerprintManager;
60 import android.net.ConnectivityManager;
61 import android.net.LinkAddress;
62 import android.net.LinkProperties;
63 import android.net.Network;
64 import android.net.wifi.WifiManager;
65 import android.os.BatteryManager;
66 import android.os.Binder;
67 import android.os.Build;
68 import android.os.Bundle;
69 import android.os.IBinder;
70 import android.os.INetworkManagementService;
71 import android.os.RemoteException;
72 import android.os.ServiceManager;
73 import android.os.UserHandle;
74 import android.os.UserManager;
75 import android.os.storage.StorageManager;
76 import android.os.storage.VolumeInfo;
77 import android.preference.PreferenceFrameLayout;
78 import android.provider.ContactsContract.CommonDataKinds;
79 import android.provider.ContactsContract.Contacts;
80 import android.provider.ContactsContract.Data;
81 import android.provider.ContactsContract.Profile;
82 import android.provider.ContactsContract.RawContacts;
83 import android.telephony.SubscriptionManager;
84 import android.telephony.TelephonyManager;
85 import android.text.Spannable;
86 import android.text.SpannableString;
87 import android.text.TextUtils;
88 import android.text.format.DateUtils;
89 import android.text.style.TtsSpan;
90 import android.util.ArraySet;
91 import android.util.IconDrawableFactory;
92 import android.util.Log;
93 import android.view.LayoutInflater;
94 import android.view.View;
95 import android.view.ViewGroup;
96 import android.widget.EditText;
97 import android.widget.ListView;
98 import android.widget.TabWidget;
99 
100 import androidx.annotation.ColorInt;
101 import androidx.annotation.NonNull;
102 import androidx.annotation.Nullable;
103 import androidx.annotation.VisibleForTesting;
104 import androidx.core.graphics.drawable.IconCompat;
105 import androidx.core.graphics.drawable.RoundedBitmapDrawable;
106 import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
107 import androidx.fragment.app.Fragment;
108 import androidx.lifecycle.Lifecycle;
109 import androidx.preference.Preference;
110 import androidx.preference.PreferenceGroup;
111 
112 import com.android.internal.app.UnlaunchableAppActivity;
113 import com.android.internal.util.ArrayUtils;
114 import com.android.internal.widget.LockPatternUtils;
115 import com.android.settings.dashboard.profileselector.ProfileFragmentBridge;
116 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
117 import com.android.settings.password.ChooseLockSettingsHelper;
118 import com.android.settingslib.widget.ActionBarShadowController;
119 import com.android.settingslib.widget.AdaptiveIcon;
120 
121 import java.util.Iterator;
122 import java.util.List;
123 import java.util.Locale;
124 import java.util.Set;
125 
126 public final class Utils extends com.android.settingslib.Utils {
127 
128     private static final String TAG = "Settings";
129 
130     public static final String FILE_PROVIDER_AUTHORITY = "com.android.settings.files";
131 
132     /**
133      * Set the preference's title to the matching activity's label.
134      */
135     public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1;
136 
137     public static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
138 
139     public static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui";
140 
141     public static final String OS_PKG = "os";
142 
143     /**
144      * Whether to disable the new device identifier access restrictions.
145      */
146     public static final String PROPERTY_DEVICE_IDENTIFIER_ACCESS_RESTRICTIONS_DISABLED =
147             "device_identifier_access_restrictions_disabled";
148 
149     /**
150      * Whether to show the Permissions Hub.
151      */
152     public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
153 
154     /**
155      * Whether to show location indicators.
156      */
157     public static final String PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled";
158 
159     /**
160      * Whether to show location indicator settings in developer options.
161      */
162     public static final String PROPERTY_LOCATION_INDICATOR_SETTINGS_ENABLED =
163             "location_indicator_settings_enabled";
164 
165     /** Whether or not app hibernation is enabled on the device **/
166     public static final String PROPERTY_APP_HIBERNATION_ENABLED = "app_hibernation_enabled";
167 
168     /** Whether or not app hibernation targets apps that target a pre-S SDK **/
169     public static final String PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS =
170             "app_hibernation_targets_pre_s_apps";
171 
172     /**
173      * Whether or not Cloned Apps menu is available in Apps page. Default is false.
174      */
175     public static final String PROPERTY_CLONED_APPS_ENABLED = "cloned_apps_enabled";
176 
177     /**
178      * Whether or not Delete All App Clones sub-menu is available in the Cloned Apps page.
179      * Default is false.
180      */
181     public static final String PROPERTY_DELETE_ALL_APP_CLONES_ENABLED =
182             "delete_all_app_clones_enabled";
183 
184     /**
185      * Finds a matching activity for a preference's intent. If a matching
186      * activity is not found, it will remove the preference.
187      *
188      * @param context The context.
189      * @param parentPreferenceGroup The preference group that contains the
190      *            preference whose intent is being resolved.
191      * @param preferenceKey The key of the preference whose intent is being
192      *            resolved.
193      * @param flags 0 or one or more of
194      *            {@link #UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY}
195      *            .
196      * @return Whether an activity was found. If false, the preference was
197      *         removed.
198      */
updatePreferenceToSpecificActivityOrRemove(Context context, PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags)199     public static boolean updatePreferenceToSpecificActivityOrRemove(Context context,
200             PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags) {
201 
202         final Preference preference = parentPreferenceGroup.findPreference(preferenceKey);
203         if (preference == null) {
204             return false;
205         }
206 
207         final Intent intent = preference.getIntent();
208         if (intent != null) {
209             // Find the activity that is in the system image
210             final PackageManager pm = context.getPackageManager();
211             final List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
212             final int listSize = list.size();
213             for (int i = 0; i < listSize; i++) {
214                 final ResolveInfo resolveInfo = list.get(i);
215                 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
216                         != 0) {
217 
218                     // Replace the intent with this specific activity
219                     preference.setIntent(new Intent().setClassName(
220                             resolveInfo.activityInfo.packageName,
221                             resolveInfo.activityInfo.name));
222 
223                     if ((flags & UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY) != 0) {
224                         // Set the preference title to the activity's label
225                         preference.setTitle(resolveInfo.loadLabel(pm));
226                     }
227 
228                     return true;
229                 }
230             }
231         }
232 
233         // Did not find a matching activity, so remove the preference
234         parentPreferenceGroup.removePreference(preference);
235 
236         return false;
237     }
238 
239     /**
240      * Returns true if Monkey is running.
241      */
isMonkeyRunning()242     public static boolean isMonkeyRunning() {
243         return ActivityManager.isUserAMonkey();
244     }
245 
246     /**
247      * Returns whether the device is voice-capable (meaning, it is also a phone).
248      */
isVoiceCapable(Context context)249     public static boolean isVoiceCapable(Context context) {
250         final TelephonyManager telephony =
251                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
252         return telephony != null && telephony.isVoiceCapable();
253     }
254 
255     /**
256      * Returns the WIFI IP Addresses, if any, taking into account IPv4 and IPv6 style addresses.
257      * @param context the application context
258      * @return the formatted and newline-separated IP addresses, or null if none.
259      */
getWifiIpAddresses(Context context)260     public static String getWifiIpAddresses(Context context) {
261         final WifiManager wifiManager = context.getSystemService(WifiManager.class);
262         final Network currentNetwork = wifiManager.getCurrentNetwork();
263         if (currentNetwork != null) {
264             final ConnectivityManager cm = (ConnectivityManager)
265                 context.getSystemService(Context.CONNECTIVITY_SERVICE);
266             final LinkProperties prop = cm.getLinkProperties(currentNetwork);
267             return formatIpAddresses(prop);
268         }
269         return null;
270     }
271 
formatIpAddresses(LinkProperties prop)272     private static String formatIpAddresses(LinkProperties prop) {
273         if (prop == null) return null;
274         final Iterator<LinkAddress> iter = prop.getAllLinkAddresses().iterator();
275         // If there are no entries, return null
276         if (!iter.hasNext()) return null;
277         // Concatenate all available addresses, comma separated
278         String addresses = "";
279         while (iter.hasNext()) {
280             addresses += iter.next().getAddress().getHostAddress();
281             if (iter.hasNext()) addresses += "\n";
282         }
283         return addresses;
284     }
285 
createLocaleFromString(String localeStr)286     public static Locale createLocaleFromString(String localeStr) {
287         // TODO: is there a better way to actually construct a locale that will match?
288         // The main problem is, on top of Java specs, locale.toString() and
289         // new Locale(locale.toString()).toString() do not return equal() strings in
290         // many cases, because the constructor takes the only string as the language
291         // code. So : new Locale("en", "US").toString() => "en_US"
292         // And : new Locale("en_US").toString() => "en_us"
293         if (null == localeStr)
294             return Locale.getDefault();
295         final String[] brokenDownLocale = localeStr.split("_", 3);
296         // split may not return a 0-length array.
297         if (1 == brokenDownLocale.length) {
298             return new Locale(brokenDownLocale[0]);
299         } else if (2 == brokenDownLocale.length) {
300             return new Locale(brokenDownLocale[0], brokenDownLocale[1]);
301         } else {
302             return new Locale(brokenDownLocale[0], brokenDownLocale[1], brokenDownLocale[2]);
303         }
304     }
305 
isBatteryPresent(Intent batteryChangedIntent)306     public static boolean isBatteryPresent(Intent batteryChangedIntent) {
307         return batteryChangedIntent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
308     }
309 
310     /**
311      * Return true if battery is present.
312      */
isBatteryPresent(Context context)313     public static boolean isBatteryPresent(Context context) {
314         Intent batteryBroadcast = context.registerReceiver(null /* receiver */,
315                 new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
316         return isBatteryPresent(batteryBroadcast);
317     }
318 
getBatteryPercentage(Intent batteryChangedIntent)319     public static String getBatteryPercentage(Intent batteryChangedIntent) {
320         return formatPercentage(getBatteryLevel(batteryChangedIntent));
321     }
322 
323     /**
324      * Prepare a custom preferences layout, moving padding to {@link ListView}
325      * when outside scrollbars are requested. Usually used to display
326      * {@link ListView} and {@link TabWidget} with correct padding.
327      */
prepareCustomPreferencesList( ViewGroup parent, View child, View list, boolean ignoreSidePadding)328     public static void prepareCustomPreferencesList(
329             ViewGroup parent, View child, View list, boolean ignoreSidePadding) {
330         final boolean movePadding = list.getScrollBarStyle() == View.SCROLLBARS_OUTSIDE_OVERLAY;
331         if (movePadding) {
332             final Resources res = list.getResources();
333             final int paddingBottom = res.getDimensionPixelSize(
334                     com.android.internal.R.dimen.preference_fragment_padding_bottom);
335 
336             if (parent instanceof PreferenceFrameLayout) {
337                 ((PreferenceFrameLayout.LayoutParams) child.getLayoutParams()).removeBorders = true;
338             }
339             list.setPaddingRelative(0 /* start */, 0 /* top */, 0 /* end */, paddingBottom);
340         }
341     }
342 
forceCustomPadding(View view, boolean additive)343     public static void forceCustomPadding(View view, boolean additive) {
344         final Resources res = view.getResources();
345 
346         final int paddingStart = additive ? view.getPaddingStart() : 0;
347         final int paddingEnd = additive ? view.getPaddingEnd() : 0;
348         final int paddingBottom = res.getDimensionPixelSize(
349                 com.android.internal.R.dimen.preference_fragment_padding_bottom);
350 
351         view.setPaddingRelative(paddingStart, 0, paddingEnd, paddingBottom);
352     }
353 
getMeProfileName(Context context, boolean full)354     public static String getMeProfileName(Context context, boolean full) {
355         if (full) {
356             return getProfileDisplayName(context);
357         } else {
358             return getShorterNameIfPossible(context);
359         }
360     }
361 
getShorterNameIfPossible(Context context)362     private static String getShorterNameIfPossible(Context context) {
363         final String given = getLocalProfileGivenName(context);
364         return !TextUtils.isEmpty(given) ? given : getProfileDisplayName(context);
365     }
366 
getLocalProfileGivenName(Context context)367     private static String getLocalProfileGivenName(Context context) {
368         final ContentResolver cr = context.getContentResolver();
369 
370         // Find the raw contact ID for the local ME profile raw contact.
371         final long localRowProfileId;
372         final Cursor localRawProfile = cr.query(
373                 Profile.CONTENT_RAW_CONTACTS_URI,
374                 new String[] {RawContacts._ID},
375                 RawContacts.ACCOUNT_TYPE + " IS NULL AND " +
376                         RawContacts.ACCOUNT_NAME + " IS NULL",
377                 null, null);
378         if (localRawProfile == null) return null;
379 
380         try {
381             if (!localRawProfile.moveToFirst()) {
382                 return null;
383             }
384             localRowProfileId = localRawProfile.getLong(0);
385         } finally {
386             localRawProfile.close();
387         }
388 
389         // Find the structured name for the raw contact.
390         final Cursor structuredName = cr.query(
391                 Profile.CONTENT_URI.buildUpon().appendPath(Contacts.Data.CONTENT_DIRECTORY).build(),
392                 new String[] {CommonDataKinds.StructuredName.GIVEN_NAME,
393                     CommonDataKinds.StructuredName.FAMILY_NAME},
394                 Data.RAW_CONTACT_ID + "=" + localRowProfileId,
395                 null, null);
396         if (structuredName == null) return null;
397 
398         try {
399             if (!structuredName.moveToFirst()) {
400                 return null;
401             }
402             String partialName = structuredName.getString(0);
403             if (TextUtils.isEmpty(partialName)) {
404                 partialName = structuredName.getString(1);
405             }
406             return partialName;
407         } finally {
408             structuredName.close();
409         }
410     }
411 
getProfileDisplayName(Context context)412     private static final String getProfileDisplayName(Context context) {
413         final ContentResolver cr = context.getContentResolver();
414         final Cursor profile = cr.query(Profile.CONTENT_URI,
415                 new String[] {Profile.DISPLAY_NAME}, null, null, null);
416         if (profile == null) return null;
417 
418         try {
419             if (!profile.moveToFirst()) {
420                 return null;
421             }
422             return profile.getString(0);
423         } finally {
424             profile.close();
425         }
426     }
427 
hasMultipleUsers(Context context)428     public static boolean hasMultipleUsers(Context context) {
429         return context.getSystemService(UserManager.class)
430                 .getUsers().size() > 1;
431     }
432 
433     /**
434      * Returns the managed profile of the current user or {@code null} if none is found or a profile
435      * exists but it is disabled.
436      */
getManagedProfile(UserManager userManager)437     public static UserHandle getManagedProfile(UserManager userManager) {
438         final List<UserHandle> userProfiles = userManager.getUserProfiles();
439         for (UserHandle profile : userProfiles) {
440             if (profile.getIdentifier() == userManager.getProcessUserId()) {
441                 continue;
442             }
443             final UserInfo userInfo = userManager.getUserInfo(profile.getIdentifier());
444             if (userInfo.isManagedProfile()) {
445                 return profile;
446             }
447         }
448         return null;
449     }
450 
451     /**
452      * Returns the managed profile of the current user or {@code null} if none is found. Unlike
453      * {@link #getManagedProfile} this method returns enabled and disabled managed profiles.
454      */
getManagedProfileWithDisabled(UserManager userManager)455     public static UserHandle getManagedProfileWithDisabled(UserManager userManager) {
456         return getManagedProfileWithDisabled(userManager, UserHandle.myUserId());
457     }
458 
459     /**
460      * Returns the managed profile of the given user or {@code null} if none is found. Unlike
461      * {@link #getManagedProfile} this method returns enabled and disabled managed profiles.
462      */
getManagedProfileWithDisabled(UserManager um, int parentUserId)463     private static UserHandle getManagedProfileWithDisabled(UserManager um, int parentUserId) {
464         final List<UserInfo> profiles = um.getProfiles(parentUserId);
465         final int count = profiles.size();
466         for (int i = 0; i < count; i++) {
467             final UserInfo profile = profiles.get(i);
468             if (profile.isManagedProfile()
469                     && profile.getUserHandle().getIdentifier() != parentUserId) {
470                 return profile.getUserHandle();
471             }
472         }
473         return null;
474     }
475 
476     /**
477      * Retrieves the id for the given user's managed profile.
478      * Unlike {@link #getManagedProfile} this method returns enabled and disabled managed profiles.
479      *
480      * @return the managed profile id or UserHandle.USER_NULL if there is none.
481      */
getManagedProfileId(UserManager um, int parentUserId)482     public static int getManagedProfileId(UserManager um, int parentUserId) {
483         final UserHandle profile = getManagedProfileWithDisabled(um, parentUserId);
484         if (profile != null) {
485             return profile.getIdentifier();
486         }
487         return UserHandle.USER_NULL;
488     }
489 
490     /** Returns user ID of current user, throws IllegalStateException if it's not available. */
getCurrentUserId(UserManager userManager, boolean isWorkProfile)491     public static int getCurrentUserId(UserManager userManager, boolean isWorkProfile)
492             throws IllegalStateException {
493         if (isWorkProfile) {
494             final UserHandle managedUserHandle = getManagedProfile(userManager);
495             if (managedUserHandle == null) {
496                 throw new IllegalStateException("Work profile user ID is not available.");
497             }
498             return managedUserHandle.getIdentifier();
499         }
500         return UserHandle.myUserId();
501     }
502 
503     /**
504      * Returns the target user for a Settings activity.
505      * <p>
506      * User would be retrieved in this order:
507      * <ul>
508      * <li> If this activity is launched from other user, return that user id.
509      * <li> If this is launched from the Settings app in same user, return the user contained as an
510      *      extra in the arguments or intent extras.
511      * <li> Otherwise, return UserHandle.myUserId().
512      * </ul>
513      * <p>
514      * Note: This is secure in the sense that it only returns a target user different to the current
515      * one if the app launching this activity is the Settings app itself, running in the same user
516      * or in one that is in the same profile group, or if the user id is provided by the system.
517      */
getSecureTargetUser(IBinder activityToken, UserManager um, @Nullable Bundle arguments, @Nullable Bundle intentExtras)518     public static UserHandle getSecureTargetUser(IBinder activityToken,
519             UserManager um, @Nullable Bundle arguments, @Nullable Bundle intentExtras) {
520         final UserHandle currentUser = new UserHandle(UserHandle.myUserId());
521         final IActivityManager am = ActivityManager.getService();
522         try {
523             final String launchedFromPackage = am.getLaunchedFromPackage(activityToken);
524             final boolean launchedFromSettingsApp =
525                     SETTINGS_PACKAGE_NAME.equals(launchedFromPackage);
526 
527             final UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId(
528                     am.getLaunchedFromUid(activityToken)));
529             if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) {
530                 // Check it's secure
531                 if (isProfileOf(um, launchedFromUser)) {
532                     return launchedFromUser;
533                 }
534             }
535             final UserHandle extrasUser = getUserHandleFromBundle(intentExtras);
536             if (extrasUser != null && !extrasUser.equals(currentUser)) {
537                 // Check it's secure
538                 if (launchedFromSettingsApp && isProfileOf(um, extrasUser)) {
539                     return extrasUser;
540                 }
541             }
542             final UserHandle argumentsUser = getUserHandleFromBundle(arguments);
543             if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
544                 // Check it's secure
545                 if (launchedFromSettingsApp && isProfileOf(um, argumentsUser)) {
546                     return argumentsUser;
547                 }
548             }
549         } catch (RemoteException e) {
550             // Should not happen
551             Log.v(TAG, "Could not talk to activity manager.", e);
552         }
553         return currentUser;
554     }
555 
556     /**
557      * Lookup both {@link Intent#EXTRA_USER} and {@link Intent#EXTRA_USER_ID} in the bundle
558      * and return the {@link UserHandle} object. Return {@code null} if nothing is found.
559      */
getUserHandleFromBundle(Bundle bundle)560     private static @Nullable UserHandle getUserHandleFromBundle(Bundle bundle) {
561         if (bundle == null) {
562             return null;
563         }
564         final UserHandle user = bundle.getParcelable(EXTRA_USER);
565         if (user != null) {
566             return user;
567         }
568         final int userId = bundle.getInt(EXTRA_USER_ID, -1);
569         if (userId != -1) {
570             return UserHandle.of(userId);
571         }
572         return null;
573     }
574 
575    /**
576     * Returns true if the user provided is in the same profiles group as the current user.
577     */
isProfileOf(UserManager um, UserHandle otherUser)578    private static boolean isProfileOf(UserManager um, UserHandle otherUser) {
579        if (um == null || otherUser == null) return false;
580        return (UserHandle.myUserId() == otherUser.getIdentifier())
581                || um.getUserProfiles().contains(otherUser);
582    }
583 
584     /**
585      * Queries for the UserInfo of a user. Returns null if the user doesn't exist (was removed).
586      * @param userManager Instance of UserManager
587      * @param checkUser The user to check the existence of.
588      * @return UserInfo of the user or null for non-existent user.
589      */
getExistingUser(UserManager userManager, UserHandle checkUser)590     public static UserInfo getExistingUser(UserManager userManager, UserHandle checkUser) {
591         final List<UserInfo> users = userManager.getAliveUsers();
592         final int checkUserId = checkUser.getIdentifier();
593         for (UserInfo user : users) {
594             if (user.id == checkUserId) {
595                 return user;
596             }
597         }
598         return null;
599     }
600 
inflateCategoryHeader(LayoutInflater inflater, ViewGroup parent)601     public static View inflateCategoryHeader(LayoutInflater inflater, ViewGroup parent) {
602         final TypedArray a = inflater.getContext().obtainStyledAttributes(null,
603                 com.android.internal.R.styleable.Preference,
604                 com.android.internal.R.attr.preferenceCategoryStyle, 0);
605         final int resId = a.getResourceId(com.android.internal.R.styleable.Preference_layout,
606                 0);
607         a.recycle();
608         return inflater.inflate(resId, parent, false);
609     }
610 
611     /** Gets all the domains that the given package could handled. */
612     @NonNull
getHandledDomains(PackageManager pm, String packageName)613     public static Set<String> getHandledDomains(PackageManager pm, String packageName) {
614         final List<IntentFilterVerificationInfo> iviList =
615                 pm.getIntentFilterVerifications(packageName);
616         final List<IntentFilter> filters = pm.getAllIntentFilters(packageName);
617 
618         final ArraySet<String> result = new ArraySet<>();
619         if (iviList != null && iviList.size() > 0) {
620             for (IntentFilterVerificationInfo ivi : iviList) {
621                 result.addAll(ivi.getDomains());
622             }
623         }
624         if (filters != null && filters.size() > 0) {
625             for (IntentFilter filter : filters) {
626                 if (filter.hasCategory(Intent.CATEGORY_BROWSABLE)
627                         && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
628                                 filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) {
629                     result.addAll(filter.getHostsList());
630                 }
631             }
632         }
633         return result;
634     }
635 
636     /**
637      * Returns the application info of the currently installed MDM package.
638      */
getAdminApplicationInfo(Context context, int profileId)639     public static ApplicationInfo getAdminApplicationInfo(Context context, int profileId) {
640         final DevicePolicyManager dpm =
641                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
642         final ComponentName mdmPackage = dpm.getProfileOwnerAsUser(profileId);
643         if (mdmPackage == null) {
644             return null;
645         }
646         final String mdmPackageName = mdmPackage.getPackageName();
647         try {
648             final IPackageManager ipm = AppGlobals.getPackageManager();
649             final ApplicationInfo mdmApplicationInfo =
650                     ipm.getApplicationInfo(mdmPackageName, 0, profileId);
651             return mdmApplicationInfo;
652         } catch (RemoteException e) {
653             Log.e(TAG, "Error while retrieving application info for package " + mdmPackageName
654                     + ", userId " + profileId, e);
655             return null;
656         }
657     }
658 
isBandwidthControlEnabled()659     public static boolean isBandwidthControlEnabled() {
660         final INetworkManagementService netManager = INetworkManagementService.Stub
661                 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
662         try {
663             return netManager.isBandwidthControlEnabled();
664         } catch (RemoteException e) {
665             return false;
666         }
667     }
668 
669     /**
670      * Returns an accessible SpannableString.
671      * @param displayText the text to display
672      * @param accessibileText the text text-to-speech engines should read
673      */
createAccessibleSequence(CharSequence displayText, String accessibileText)674     public static SpannableString createAccessibleSequence(CharSequence displayText,
675             String accessibileText) {
676         final SpannableString str = new SpannableString(displayText);
677         str.setSpan(new TtsSpan.TextBuilder(accessibileText).build(), 0,
678                 displayText.length(),
679                 Spannable.SPAN_INCLUSIVE_INCLUSIVE);
680         return str;
681     }
682 
683     /**
684      * Returns the user id present in the bundle with
685      * {@link Intent#EXTRA_USER_ID} if it belongs to the current user.
686      *
687      * @throws SecurityException if the given userId does not belong to the
688      *             current user group.
689      */
getUserIdFromBundle(Context context, Bundle bundle)690     public static int getUserIdFromBundle(Context context, Bundle bundle) {
691         return getUserIdFromBundle(context, bundle, false);
692     }
693 
694     /**
695      * Returns the user id present in the bundle with
696      * {@link Intent#EXTRA_USER_ID} if it belongs to the current user.
697      *
698      * @param isInternal indicating if the caller is "internal" to the system,
699      *            meaning we're willing to trust extras like
700      *            {@link ChooseLockSettingsHelper#EXTRA_KEY_ALLOW_ANY_USER}.
701      * @throws SecurityException if the given userId does not belong to the
702      *             current user group.
703      */
getUserIdFromBundle(Context context, Bundle bundle, boolean isInternal)704     public static int getUserIdFromBundle(Context context, Bundle bundle, boolean isInternal) {
705         if (bundle == null) {
706             return getCredentialOwnerUserId(context);
707         }
708         final boolean allowAnyUser = isInternal
709                 && bundle.getBoolean(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, false);
710         final int userId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId());
711         if (userId == LockPatternUtils.USER_FRP) {
712             return allowAnyUser ? userId : checkUserOwnsFrpCredential(context, userId);
713         }
714         if (userId == LockPatternUtils.USER_REPAIR_MODE) {
715             enforceRepairModeActive(context);
716             // any users can exit repair mode
717             return userId;
718         }
719         return allowAnyUser ? userId : enforceSameOwner(context, userId);
720     }
721 
722     /**
723      * Returns the given user id if the current user owns frp credential.
724      *
725      * @throws SecurityException if the current user do not own the frp credential.
726      */
727     @VisibleForTesting
checkUserOwnsFrpCredential(Context context, int userId)728     static int checkUserOwnsFrpCredential(Context context, int userId) {
729         final UserManager um = context.getSystemService(UserManager.class);
730         if (LockPatternUtils.userOwnsFrpCredential(context,
731                 um.getUserInfo(UserHandle.myUserId()))) {
732             return userId;
733         }
734         throw new SecurityException("Current user id " + UserHandle.myUserId()
735                 + " does not own frp credential.");
736     }
737 
738     /**
739      * Throws {@link SecurityException} if repair mode is not active on the device.
740      */
enforceRepairModeActive(Context context)741     private static void enforceRepairModeActive(Context context) {
742         if (LockPatternUtils.isRepairModeActive(context)) {
743             return;
744         }
745         throw new SecurityException("Repair mode is not active on the device.");
746     }
747 
748     /**
749      * Returns the given user id if it belongs to the current user.
750      *
751      * @throws SecurityException if the given userId does not belong to the current user group.
752      */
enforceSameOwner(Context context, int userId)753     public static int enforceSameOwner(Context context, int userId) {
754         final UserManager um = context.getSystemService(UserManager.class);
755         final int[] profileIds = um.getProfileIdsWithDisabled(UserHandle.myUserId());
756         if (ArrayUtils.contains(profileIds, userId)) {
757             return userId;
758         }
759         throw new SecurityException("Given user id " + userId + " does not belong to user "
760                 + UserHandle.myUserId());
761     }
762 
763     /**
764      * Returns the effective credential owner of the calling user.
765      */
getCredentialOwnerUserId(Context context)766     public static int getCredentialOwnerUserId(Context context) {
767         return getCredentialOwnerUserId(context, UserHandle.myUserId());
768     }
769 
770     /**
771      * Returns the user id of the credential owner of the given user id.
772      */
getCredentialOwnerUserId(Context context, int userId)773     public static int getCredentialOwnerUserId(Context context, int userId) {
774         final UserManager um = context.getSystemService(UserManager.class);
775         return um.getCredentialOwnerProfile(userId);
776     }
777 
778     /**
779      * Returns the credential type of the given user id.
780      */
getCredentialType(Context context, int userId)781     public static @LockPatternUtils.CredentialType int getCredentialType(Context context,
782             int userId) {
783         final LockPatternUtils lpu = new LockPatternUtils(context);
784         return lpu.getCredentialTypeForUser(userId);
785     }
786 
787     /**
788      * Returns the confirmation credential string of the given user id.
789      */
getConfirmCredentialStringForUser(@onNull Context context, int userId, @LockPatternUtils.CredentialType int credentialType)790     @Nullable public static String getConfirmCredentialStringForUser(@NonNull Context context,
791              int userId, @LockPatternUtils.CredentialType int credentialType) {
792         final int effectiveUserId = UserManager.get(context).getCredentialOwnerProfile(userId);
793         final boolean isEffectiveUserManagedProfile = UserManager.get(context)
794                 .isManagedProfile(effectiveUserId);
795         final DevicePolicyManager devicePolicyManager = context
796                 .getSystemService(DevicePolicyManager.class);
797         switch (credentialType) {
798             case LockPatternUtils.CREDENTIAL_TYPE_PIN:
799                 if (isEffectiveUserManagedProfile) {
800                     return devicePolicyManager.getResources().getString(WORK_PROFILE_CONFIRM_PIN,
801                             () -> context.getString(
802                                     R.string.lockpassword_confirm_your_pin_generic_profile));
803                 }
804 
805                 return context.getString(R.string.lockpassword_confirm_your_pin_generic);
806             case LockPatternUtils.CREDENTIAL_TYPE_PATTERN:
807                 if (isEffectiveUserManagedProfile) {
808                     return devicePolicyManager.getResources().getString(
809                             WORK_PROFILE_CONFIRM_PATTERN,
810                             () -> context.getString(
811                                     R.string.lockpassword_confirm_your_pattern_generic_profile));
812                 }
813 
814                 return context.getString(R.string.lockpassword_confirm_your_pattern_generic);
815             case LockPatternUtils.CREDENTIAL_TYPE_PASSWORD:
816                 if (isEffectiveUserManagedProfile) {
817                     return devicePolicyManager.getResources().getString(
818                             WORK_PROFILE_CONFIRM_PASSWORD,
819                             () -> context.getString(
820                                     R.string.lockpassword_confirm_your_password_generic_profile));
821                 }
822 
823                 return context.getString(R.string.lockpassword_confirm_your_password_generic);
824         }
825         return null;
826     }
827 
828     private static final StringBuilder sBuilder = new StringBuilder(50);
829     private static final java.util.Formatter sFormatter = new java.util.Formatter(
830             sBuilder, Locale.getDefault());
831 
formatDateRange(Context context, long start, long end)832     public static String formatDateRange(Context context, long start, long end) {
833         final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
834 
835         synchronized (sBuilder) {
836             sBuilder.setLength(0);
837             return DateUtils.formatDateRange(context, sFormatter, start, end, flags, null)
838                     .toString();
839         }
840     }
841 
startQuietModeDialogIfNecessary(Context context, UserManager um, int userId)842     public static boolean startQuietModeDialogIfNecessary(Context context, UserManager um,
843             int userId) {
844         if (um.isQuietModeEnabled(UserHandle.of(userId))) {
845             final Intent intent = UnlaunchableAppActivity.createInQuietModeDialogIntent(userId);
846             context.startActivity(intent);
847             return true;
848         }
849         return false;
850     }
851 
unlockWorkProfileIfNecessary(Context context, int userId)852     public static boolean unlockWorkProfileIfNecessary(Context context, int userId) {
853         try {
854             if (!ActivityManager.getService().isUserRunning(userId,
855                     ActivityManager.FLAG_AND_LOCKED)) {
856                 return false;
857             }
858         } catch (RemoteException e) {
859             return false;
860         }
861         if (!(new LockPatternUtils(context)).isSecure(userId)) {
862             return false;
863         }
864         return confirmWorkProfileCredentials(context, userId);
865     }
866 
confirmWorkProfileCredentials(Context context, int userId)867     private static boolean confirmWorkProfileCredentials(Context context, int userId) {
868         final KeyguardManager km = (KeyguardManager) context.getSystemService(
869                 Context.KEYGUARD_SERVICE);
870         final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
871         if (unlockIntent != null) {
872             context.startActivity(unlockIntent);
873             return true;
874         } else {
875             return false;
876         }
877     }
878 
879     /** Gets the application label of the given package name. */
880     @Nullable
getApplicationLabel(Context context, @NonNull String packageName)881     public static CharSequence getApplicationLabel(Context context, @NonNull String packageName) {
882         try {
883             final ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
884                     packageName,
885                     PackageManager.MATCH_DISABLED_COMPONENTS
886                     | PackageManager.MATCH_ANY_USER);
887             return appInfo.loadLabel(context.getPackageManager());
888         } catch (PackageManager.NameNotFoundException e) {
889             Log.e(TAG, "Unable to find info for package: " + packageName);
890         }
891         return null;
892     }
893 
isPackageDirectBootAware(Context context, String packageName)894     public static boolean isPackageDirectBootAware(Context context, String packageName) {
895         try {
896             final ApplicationInfo ai = context.getPackageManager().getApplicationInfo(
897                     packageName, 0);
898             return ai.isDirectBootAware() || ai.isPartiallyDirectBootAware();
899         } catch (NameNotFoundException ignored) {
900         }
901         return false;
902     }
903 
904     /**
905      * Returns a context created from the given context for the given user, or null if it fails
906      */
createPackageContextAsUser(Context context, int userId)907     public static Context createPackageContextAsUser(Context context, int userId) {
908         try {
909             return context.createPackageContextAsUser(
910                     context.getPackageName(), 0 /* flags */, UserHandle.of(userId));
911         } catch (PackageManager.NameNotFoundException e) {
912             Log.e(TAG, "Failed to create user context", e);
913         }
914         return null;
915     }
916 
getFingerprintManagerOrNull(Context context)917     public static FingerprintManager getFingerprintManagerOrNull(Context context) {
918         if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
919             return (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
920         } else {
921             return null;
922         }
923     }
924 
hasFingerprintHardware(Context context)925     public static boolean hasFingerprintHardware(Context context) {
926         final FingerprintManager fingerprintManager = getFingerprintManagerOrNull(context);
927         return fingerprintManager != null && fingerprintManager.isHardwareDetected();
928     }
929 
getFaceManagerOrNull(Context context)930     public static FaceManager getFaceManagerOrNull(Context context) {
931         if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
932             return (FaceManager) context.getSystemService(Context.FACE_SERVICE);
933         } else {
934             return null;
935         }
936     }
937 
hasFaceHardware(Context context)938     public static boolean hasFaceHardware(Context context) {
939         final FaceManager faceManager = getFaceManagerOrNull(context);
940         return faceManager != null && faceManager.isHardwareDetected();
941     }
942 
943     /**
944      * Return true if the device supports multiple biometrics authentications.
945      */
isMultipleBiometricsSupported(Context context)946     public static boolean isMultipleBiometricsSupported(Context context) {
947         return hasFingerprintHardware(context) && hasFaceHardware(context);
948     }
949 
950     /**
951      * Launches an intent which may optionally have a user id defined.
952      * @param fragment Fragment to use to launch the activity.
953      * @param intent Intent to launch.
954      */
launchIntent(Fragment fragment, Intent intent)955     public static void launchIntent(Fragment fragment, Intent intent) {
956         try {
957             final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1);
958 
959             if (userId == -1) {
960                 fragment.startActivity(intent);
961             } else {
962                 fragment.getActivity().startActivityAsUser(intent, new UserHandle(userId));
963             }
964         } catch (ActivityNotFoundException e) {
965             Log.w(TAG, "No activity found for " + intent);
966         }
967     }
968 
isDemoUser(Context context)969     public static boolean isDemoUser(Context context) {
970         return UserManager.isDeviceInDemoMode(context)
971                 && context.getSystemService(UserManager.class).isDemoUser();
972     }
973 
getDeviceOwnerComponent(Context context)974     public static ComponentName getDeviceOwnerComponent(Context context) {
975         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
976                 Context.DEVICE_POLICY_SERVICE);
977         return dpm.getDeviceOwnerComponentOnAnyUser();
978     }
979 
980     /**
981      * Returns if a given user is a profile of another user.
982      * @param user The user whose profiles wibe checked.
983      * @param profile The (potential) profile.
984      * @return if the profile is actually a profile
985      */
isProfileOf(UserInfo user, UserInfo profile)986     public static boolean isProfileOf(UserInfo user, UserInfo profile) {
987         return user.id == profile.id ||
988                 (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
989                         && user.profileGroupId == profile.profileGroupId);
990     }
991 
992     /**
993      * Tries to initalize a volume with the given bundle. If it is a valid, private, and readable
994      * {@link VolumeInfo}, it is returned. If it is not valid, null is returned.
995      */
996     @Nullable
maybeInitializeVolume(StorageManager sm, Bundle bundle)997     public static VolumeInfo maybeInitializeVolume(StorageManager sm, Bundle bundle) {
998         final String volumeId = bundle.getString(VolumeInfo.EXTRA_VOLUME_ID,
999                 VolumeInfo.ID_PRIVATE_INTERNAL);
1000         final VolumeInfo volume = sm.findVolumeById(volumeId);
1001         return isVolumeValid(volume) ? volume : null;
1002     }
1003 
1004     /**
1005      * Return {@code true} if the supplied package is device owner or profile owner of at
1006      * least one user.
1007      * @param userManager used to get profile owner app for each user
1008      * @param devicePolicyManager used to check whether it is device owner app
1009      * @param packageName package to check about
1010      */
isProfileOrDeviceOwner(UserManager userManager, DevicePolicyManager devicePolicyManager, String packageName)1011     public static boolean isProfileOrDeviceOwner(UserManager userManager,
1012             DevicePolicyManager devicePolicyManager, String packageName) {
1013         final List<UserInfo> userInfos = userManager.getUsers();
1014         if (devicePolicyManager.isDeviceOwnerAppOnAnyUser(packageName)) {
1015             return true;
1016         }
1017         for (int i = 0, size = userInfos.size(); i < size; i++) {
1018             final ComponentName cn = devicePolicyManager
1019                     .getProfileOwnerAsUser(userInfos.get(i).id);
1020             if (cn != null && cn.getPackageName().equals(packageName)) {
1021                 return true;
1022             }
1023         }
1024         return false;
1025     }
1026 
1027     /**
1028      * Return {@code true} if the supplied package is the device owner or profile owner of a
1029      * given user.
1030      *
1031      * @param devicePolicyManager used to check whether it is device owner and profile owner app
1032      * @param packageName         package to check about
1033      * @param userId              the if of the relevant user
1034      */
isProfileOrDeviceOwner(DevicePolicyManager devicePolicyManager, String packageName, int userId)1035     public static boolean isProfileOrDeviceOwner(DevicePolicyManager devicePolicyManager,
1036             String packageName, int userId) {
1037         if ((devicePolicyManager.getDeviceOwnerUserId() == userId)
1038                 && devicePolicyManager.isDeviceOwnerApp(packageName)) {
1039             return true;
1040         }
1041         final ComponentName cn = devicePolicyManager.getProfileOwnerAsUser(userId);
1042         if (cn != null && cn.getPackageName().equals(packageName)) {
1043             return true;
1044         }
1045         return false;
1046     }
1047 
isVolumeValid(VolumeInfo volume)1048     private static boolean isVolumeValid(VolumeInfo volume) {
1049         return (volume != null) && (volume.getType() == VolumeInfo.TYPE_PRIVATE)
1050                 && volume.isMountedReadable();
1051     }
1052 
setEditTextCursorPosition(EditText editText)1053     public static void setEditTextCursorPosition(EditText editText) {
1054         editText.setSelection(editText.getText().length());
1055     }
1056 
1057     /**
1058      * Gets the adaptive icon with a drawable that wrapped with an adaptive background using {@code
1059      * backgroundColor} if it is not a {@link AdaptiveIconDrawable}
1060      *
1061      * If the given {@code icon} is too big, it will be auto scaled down to to avoid crashing
1062      * Settings.
1063      */
getAdaptiveIcon(Context context, Drawable icon, @ColorInt int backgroundColor)1064     public static Drawable getAdaptiveIcon(Context context, Drawable icon,
1065             @ColorInt int backgroundColor) {
1066         Drawable adaptiveIcon = getSafeIcon(icon);
1067 
1068         if (!(adaptiveIcon instanceof AdaptiveIconDrawable)) {
1069             adaptiveIcon = new AdaptiveIcon(context, adaptiveIcon);
1070             ((AdaptiveIcon) adaptiveIcon).setBackgroundColor(backgroundColor);
1071         }
1072 
1073         return adaptiveIcon;
1074     }
1075 
1076     /**
1077      * Gets the icon with a drawable that is scaled down to to avoid crashing Settings if it's too
1078      * big and not a {@link VectorDrawable}.
1079      */
getSafeIcon(Drawable icon)1080     public static Drawable getSafeIcon(Drawable icon) {
1081         Drawable safeIcon = icon;
1082 
1083         if ((icon != null) && !(icon instanceof VectorDrawable)) {
1084             safeIcon = getSafeDrawable(icon,
1085                     /* MAX_DRAWABLE_SIZE */ 600, /* MAX_DRAWABLE_SIZE */ 600);
1086         }
1087 
1088         return safeIcon;
1089     }
1090 
1091     /**
1092      * Gets a drawable with a limited size to avoid crashing Settings if it's too big.
1093      *
1094      * @param original original drawable, typically an app icon.
1095      * @param maxWidth maximum width, in pixels.
1096      * @param maxHeight maximum height, in pixels.
1097      */
getSafeDrawable(Drawable original, int maxWidth, int maxHeight)1098     private static Drawable getSafeDrawable(Drawable original, int maxWidth, int maxHeight) {
1099         final int actualWidth = original.getMinimumWidth();
1100         final int actualHeight = original.getMinimumHeight();
1101 
1102         if (actualWidth <= maxWidth && actualHeight <= maxHeight) {
1103             return original;
1104         }
1105 
1106         final float scaleWidth = ((float) maxWidth) / actualWidth;
1107         final float scaleHeight = ((float) maxHeight) / actualHeight;
1108         final float scale = Math.min(scaleWidth, scaleHeight);
1109         final int width = (int) (actualWidth * scale);
1110         final int height = (int) (actualHeight * scale);
1111 
1112         final Bitmap bitmap;
1113         if (original instanceof BitmapDrawable) {
1114             bitmap = Bitmap.createScaledBitmap(((BitmapDrawable) original).getBitmap(), width,
1115                     height, false);
1116         } else {
1117             bitmap = createBitmap(original, width, height);
1118         }
1119         return new BitmapDrawable(null, bitmap);
1120     }
1121 
1122     /**
1123      * Create an Icon pointing to a drawable.
1124      */
createIconWithDrawable(Drawable drawable)1125     public static IconCompat createIconWithDrawable(Drawable drawable) {
1126         Bitmap bitmap;
1127         if (drawable instanceof BitmapDrawable) {
1128             bitmap = ((BitmapDrawable)drawable).getBitmap();
1129         } else {
1130             final int width = drawable.getIntrinsicWidth();
1131             final int height = drawable.getIntrinsicHeight();
1132             bitmap = createBitmap(drawable,
1133                     width > 0 ? width : 1,
1134                     height > 0 ? height : 1);
1135         }
1136         return IconCompat.createWithBitmap(bitmap);
1137     }
1138 
1139     /**
1140      * Creates a drawable with specified width and height.
1141      */
createBitmap(Drawable drawable, int width, int height)1142     public static Bitmap createBitmap(Drawable drawable, int width, int height) {
1143         final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
1144         final Canvas canvas = new Canvas(bitmap);
1145         drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
1146         drawable.draw(canvas);
1147         return bitmap;
1148     }
1149 
1150     /**
1151      * Get the {@link Drawable} that represents the app icon
1152      */
getBadgedIcon(IconDrawableFactory iconDrawableFactory, PackageManager packageManager, String packageName, int userId)1153     public static Drawable getBadgedIcon(IconDrawableFactory iconDrawableFactory,
1154             PackageManager packageManager, String packageName, int userId) {
1155         try {
1156             final ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser(
1157                     packageName, PackageManager.GET_META_DATA, userId);
1158             return iconDrawableFactory.getBadgedIcon(appInfo, userId);
1159         } catch (PackageManager.NameNotFoundException e) {
1160             return packageManager.getDefaultActivityIcon();
1161         }
1162     }
1163 
1164     /** Returns true if the current package is installed & enabled. */
isPackageEnabled(Context context, String packageName)1165     public static boolean isPackageEnabled(Context context, String packageName) {
1166         try {
1167             return context.getPackageManager().getApplicationInfo(packageName, 0).enabled;
1168         } catch (Exception e) {
1169             Log.e(TAG, "Error while retrieving application info for package " + packageName, e);
1170         }
1171         return false;
1172     }
1173 
1174     /** Get {@link Resources} by subscription id if subscription id is valid. */
getResourcesForSubId(Context context, int subId)1175     public static Resources getResourcesForSubId(Context context, int subId) {
1176         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
1177             return SubscriptionManager.getResourcesForSubId(context, subId);
1178         } else {
1179             return context.getResources();
1180         }
1181     }
1182 
1183     /**
1184      * Returns true if SYSTEM_ALERT_WINDOW permission is available.
1185      * Starting from Q, SYSTEM_ALERT_WINDOW is disabled on low ram phones.
1186      */
isSystemAlertWindowEnabled(Context context)1187     public static boolean isSystemAlertWindowEnabled(Context context) {
1188         // SYSTEM_ALERT_WINDOW is disabled on on low ram devices starting from Q
1189         ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
1190         return !(am.isLowRamDevice() && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q));
1191     }
1192 
1193     /**
1194      * Adds a shadow appear/disappear animation to action bar scroll.
1195      *
1196      * <p/>
1197      * This method must be called after {@link Fragment#onCreate(Bundle)}.
1198      */
setActionBarShadowAnimation(Activity activity, Lifecycle lifecycle, View scrollView)1199     public static void setActionBarShadowAnimation(Activity activity, Lifecycle lifecycle,
1200             View scrollView) {
1201         if (activity == null) {
1202             Log.w(TAG, "No activity, cannot style actionbar.");
1203             return;
1204         }
1205         final ActionBar actionBar = activity.getActionBar();
1206         if (actionBar == null) {
1207             Log.w(TAG, "No actionbar, cannot style actionbar.");
1208             return;
1209         }
1210         actionBar.setElevation(0);
1211 
1212         if (lifecycle != null && scrollView != null) {
1213             ActionBarShadowController.attachToView(activity, lifecycle, scrollView);
1214         }
1215     }
1216 
1217     /**
1218      * Return correct target fragment based on argument
1219      *
1220      * @param activity     the activity target fragment will be launched.
1221      * @param fragmentName initial target fragment name.
1222      * @param args         fragment launch arguments.
1223      */
getTargetFragment(Activity activity, String fragmentName, Bundle args)1224     public static Fragment getTargetFragment(Activity activity, String fragmentName, Bundle args) {
1225         Fragment f = null;
1226         final boolean isPersonal = args != null ? args.getInt(ProfileSelectFragment.EXTRA_PROFILE)
1227                 == ProfileSelectFragment.ProfileType.PERSONAL : false;
1228         final boolean isWork = args != null ? args.getInt(ProfileSelectFragment.EXTRA_PROFILE)
1229                 == ProfileSelectFragment.ProfileType.WORK : false;
1230         try {
1231             if (isNewTabNeeded(activity)
1232                     && ProfileFragmentBridge.FRAGMENT_MAP.get(fragmentName) != null
1233                     && !isWork && !isPersonal) {
1234                 f = Fragment.instantiate(activity,
1235                         ProfileFragmentBridge.FRAGMENT_MAP.get(fragmentName), args);
1236             } else {
1237                 f = Fragment.instantiate(activity, fragmentName, args);
1238             }
1239         } catch (Exception e) {
1240             Log.e(TAG, "Unable to get target fragment", e);
1241         }
1242         return f;
1243     }
1244 
1245     /**
1246      * Checks if a new tab is needed or not for any user profile associated with the context user.
1247      *
1248      * <p> Checks if any user has the property {@link UserProperties#SHOW_IN_SETTINGS_SEPARATE} set.
1249      */
isNewTabNeeded(Activity activity)1250     public static boolean isNewTabNeeded(Activity activity) {
1251         UserManager userManager = activity.getSystemService(UserManager.class);
1252         List<UserHandle> profiles = userManager.getUserProfiles();
1253         for (UserHandle userHandle : profiles) {
1254             UserProperties userProperties = userManager.getUserProperties(userHandle);
1255             if (userProperties.getShowInSettings()
1256                     == UserProperties.SHOW_IN_SETTINGS_SEPARATE) {
1257                 return true;
1258             }
1259         }
1260         return false;
1261     }
1262 
1263     /**
1264      * Returns true if current binder uid is Settings Intelligence.
1265      */
isSettingsIntelligence(Context context)1266     public static boolean isSettingsIntelligence(Context context) {
1267         final int callingUid = Binder.getCallingUid();
1268         final String callingPackage = context.getPackageManager().getPackagesForUid(callingUid)[0];
1269         final boolean isSettingsIntelligence = TextUtils.equals(callingPackage,
1270                 context.getString(R.string.config_settingsintelligence_package_name));
1271         return isSettingsIntelligence;
1272     }
1273 
1274     /**
1275      * Returns true if the night mode is enabled.
1276      */
isNightMode(Context context)1277     public static boolean isNightMode(Context context) {
1278         final int currentNightMode =
1279                 context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
1280         return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
1281     }
1282 
1283     /**
1284      * Returns a bitmap with rounded corner.
1285      *
1286      * @param context application context.
1287      * @param source bitmap to apply round corner.
1288      * @param cornerRadius corner radius value.
1289      */
convertCornerRadiusBitmap(@onNull Context context, @NonNull Bitmap source, @NonNull float cornerRadius)1290     public static Bitmap convertCornerRadiusBitmap(@NonNull Context context,
1291             @NonNull Bitmap source, @NonNull float cornerRadius) {
1292         final Bitmap roundedBitmap = Bitmap.createBitmap(source.getWidth(), source.getHeight(),
1293                 Bitmap.Config.ARGB_8888);
1294         final RoundedBitmapDrawable drawable =
1295                 RoundedBitmapDrawableFactory.create(context.getResources(), source);
1296         drawable.setAntiAlias(true);
1297         drawable.setCornerRadius(cornerRadius);
1298         final Canvas canvas = new Canvas(roundedBitmap);
1299         drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
1300         drawable.draw(canvas);
1301         return roundedBitmap;
1302     }
1303 
1304     /**
1305      * Returns the color of homepage preference icons.
1306      */
1307     @ColorInt
getHomepageIconColor(Context context)1308     public static int getHomepageIconColor(Context context) {
1309         return getColorAttrDefaultColor(context, android.R.attr.textColorPrimary);
1310     }
1311 
1312     /**
1313      * Returns the highlight color of homepage preference icons.
1314      */
1315     @ColorInt
getHomepageIconColorHighlight(Context context)1316     public static int getHomepageIconColorHighlight(Context context) {
1317         return context.getColor(R.color.accent_select_primary_text);
1318     }
1319 
1320     /**
1321      * Returns user id of clone profile if present, else returns -1.
1322      */
getCloneUserId(Context context)1323     public static int getCloneUserId(Context context) {
1324         UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
1325         for (UserHandle userHandle : userManager.getUserProfiles()) {
1326             if (userManager.getUserInfo(userHandle.getIdentifier()).isCloneProfile()) {
1327                 return userHandle.getIdentifier();
1328             }
1329         }
1330         return -1;
1331     }
1332 
1333     /**
1334      * Returns if the current user is able to use Dreams.
1335      */
canCurrentUserDream(Context context)1336     public static boolean canCurrentUserDream(Context context) {
1337         final UserHandle mainUser = context.getSystemService(UserManager.class).getMainUser();
1338         if (mainUser == null) {
1339             return false;
1340         }
1341         return context.createContextAsUser(mainUser, 0).getSystemService(UserManager.class)
1342                .isUserForeground();
1343     }
1344 
1345     /**
1346      * Returns if dreams are available to the current user.
1347      */
areDreamsAvailableToCurrentUser(Context context)1348     public static boolean areDreamsAvailableToCurrentUser(Context context) {
1349         final boolean dreamsSupported = context.getResources().getBoolean(
1350                 com.android.internal.R.bool.config_dreamsSupported);
1351         final boolean dreamsOnlyEnabledForDockUser = context.getResources().getBoolean(
1352                 com.android.internal.R.bool.config_dreamsOnlyEnabledForDockUser);
1353         return dreamsSupported && (!dreamsOnlyEnabledForDockUser || canCurrentUserDream(context));
1354     }
1355 
1356 }
1357