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