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