Home | History | Annotate | Download | only in settings
      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.text.format.DateUtils.FORMAT_ABBREV_MONTH;
     22 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
     23 
     24 import android.annotation.Nullable;
     25 import android.app.ActivityManager;
     26 import android.app.AlertDialog;
     27 import android.app.AppGlobals;
     28 import android.app.AppOpsManager;
     29 import android.app.Dialog;
     30 import android.app.Fragment;
     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.DialogInterface;
     39 import android.content.Intent;
     40 import android.content.IntentFilter;
     41 import android.content.pm.ApplicationInfo;
     42 import android.content.pm.IPackageManager;
     43 import android.content.pm.IntentFilterVerificationInfo;
     44 import android.content.pm.PackageManager;
     45 import android.content.pm.PackageManager.NameNotFoundException;
     46 import android.content.pm.ResolveInfo;
     47 import android.content.pm.UserInfo;
     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.BitmapFactory;
     53 import android.hardware.fingerprint.FingerprintManager;
     54 import android.net.ConnectivityManager;
     55 import android.net.LinkProperties;
     56 import android.net.Network;
     57 import android.net.Uri;
     58 import android.net.wifi.WifiManager;
     59 import android.os.BatteryManager;
     60 import android.os.Bundle;
     61 import android.os.IBinder;
     62 import android.os.INetworkManagementService;
     63 import android.os.Looper;
     64 import android.os.RemoteException;
     65 import android.os.ServiceManager;
     66 import android.os.UserHandle;
     67 import android.os.UserManager;
     68 import android.os.storage.StorageManager;
     69 import android.os.storage.VolumeInfo;
     70 import android.preference.PreferenceFrameLayout;
     71 import android.provider.ContactsContract.CommonDataKinds;
     72 import android.provider.ContactsContract.Contacts;
     73 import android.provider.ContactsContract.Data;
     74 import android.provider.ContactsContract.Profile;
     75 import android.provider.ContactsContract.RawContacts;
     76 import android.provider.Settings;
     77 import android.support.annotation.StringRes;
     78 import android.support.v7.preference.Preference;
     79 import android.support.v7.preference.PreferenceGroup;
     80 import android.support.v7.preference.PreferenceManager;
     81 import android.support.v7.preference.PreferenceScreen;
     82 import android.telephony.TelephonyManager;
     83 import android.text.Spannable;
     84 import android.text.SpannableString;
     85 import android.text.SpannableStringBuilder;
     86 import android.text.Spanned;
     87 import android.text.TextUtils;
     88 import android.text.format.DateUtils;
     89 import android.text.style.TtsSpan;
     90 import android.util.ArraySet;
     91 import android.util.Log;
     92 import android.util.SparseArray;
     93 import android.util.TypedValue;
     94 import android.view.LayoutInflater;
     95 import android.view.View;
     96 import android.view.ViewGroup;
     97 import android.view.animation.Animation;
     98 import android.view.animation.Animation.AnimationListener;
     99 import android.view.animation.AnimationUtils;
    100 import android.widget.ListView;
    101 import android.widget.TabWidget;
    102 
    103 import com.android.internal.app.UnlaunchableAppActivity;
    104 import com.android.internal.util.ArrayUtils;
    105 import com.android.internal.util.UserIcons;
    106 import com.android.internal.widget.LockPatternUtils;
    107 import com.android.settings.password.FingerprintManagerWrapper;
    108 import com.android.settings.password.IFingerprintManager;
    109 
    110 import java.io.IOException;
    111 import java.io.InputStream;
    112 import java.net.InetAddress;
    113 import java.util.ArrayList;
    114 import java.util.Iterator;
    115 import java.util.List;
    116 import java.util.Locale;
    117 
    118 public final class Utils extends com.android.settingslib.Utils {
    119 
    120     private static final String TAG = "Settings";
    121 
    122     /**
    123      * Set the preference's title to the matching activity's label.
    124      */
    125     public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1;
    126 
    127     /**
    128      * The opacity level of a disabled icon.
    129      */
    130     public static final float DISABLED_ALPHA = 0.4f;
    131 
    132     /**
    133      * Color spectrum to use to indicate badness.  0 is completely transparent (no data),
    134      * 1 is most bad (red), the last value is least bad (green).
    135      */
    136     public static final int[] BADNESS_COLORS = new int[] {
    137             0x00000000, 0xffc43828, 0xffe54918, 0xfff47b00,
    138             0xfffabf2c, 0xff679e37, 0xff0a7f42
    139     };
    140 
    141     private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
    142 
    143     private static final int SECONDS_PER_MINUTE = 60;
    144     private static final int SECONDS_PER_HOUR = 60 * 60;
    145     private static final int SECONDS_PER_DAY = 24 * 60 * 60;
    146 
    147     public static final String OS_PKG = "os";
    148 
    149     private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<Bitmap>();
    150 
    151     /**
    152      * Finds a matching activity for a preference's intent. If a matching
    153      * activity is not found, it will remove the preference.
    154      *
    155      * @param context The context.
    156      * @param parentPreferenceGroup The preference group that contains the
    157      *            preference whose intent is being resolved.
    158      * @param preferenceKey The key of the preference whose intent is being
    159      *            resolved.
    160      * @param flags 0 or one or more of
    161      *            {@link #UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY}
    162      *            .
    163      * @return Whether an activity was found. If false, the preference was
    164      *         removed.
    165      */
    166     public static boolean updatePreferenceToSpecificActivityOrRemove(Context context,
    167             PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags) {
    168 
    169         Preference preference = parentPreferenceGroup.findPreference(preferenceKey);
    170         if (preference == null) {
    171             return false;
    172         }
    173 
    174         Intent intent = preference.getIntent();
    175         if (intent != null) {
    176             // Find the activity that is in the system image
    177             PackageManager pm = context.getPackageManager();
    178             List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
    179             int listSize = list.size();
    180             for (int i = 0; i < listSize; i++) {
    181                 ResolveInfo resolveInfo = list.get(i);
    182                 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
    183                         != 0) {
    184 
    185                     // Replace the intent with this specific activity
    186                     preference.setIntent(new Intent().setClassName(
    187                             resolveInfo.activityInfo.packageName,
    188                             resolveInfo.activityInfo.name));
    189 
    190                     if ((flags & UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY) != 0) {
    191                         // Set the preference title to the activity's label
    192                         preference.setTitle(resolveInfo.loadLabel(pm));
    193                     }
    194 
    195                     return true;
    196                 }
    197             }
    198         }
    199 
    200         // Did not find a matching activity, so remove the preference
    201         parentPreferenceGroup.removePreference(preference);
    202 
    203         return false;
    204     }
    205 
    206     /**
    207      * Returns the UserManager for a given context
    208      *
    209      * @throws IllegalStateException if no UserManager could be retrieved.
    210      */
    211     public static UserManager getUserManager(Context context) {
    212         UserManager um = UserManager.get(context);
    213         if (um == null) {
    214             throw new IllegalStateException("Unable to load UserManager");
    215         }
    216         return um;
    217     }
    218 
    219     /**
    220      * Returns true if Monkey is running.
    221      */
    222     public static boolean isMonkeyRunning() {
    223         return ActivityManager.isUserAMonkey();
    224     }
    225 
    226     /**
    227      * Returns whether the device is voice-capable (meaning, it is also a phone).
    228      */
    229     public static boolean isVoiceCapable(Context context) {
    230         TelephonyManager telephony =
    231                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    232         return telephony != null && telephony.isVoiceCapable();
    233     }
    234 
    235     public static boolean isWifiOnly(Context context) {
    236         ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
    237                 Context.CONNECTIVITY_SERVICE);
    238         return (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
    239     }
    240 
    241     /**
    242      * Returns the WIFI IP Addresses, if any, taking into account IPv4 and IPv6 style addresses.
    243      * @param context the application context
    244      * @return the formatted and newline-separated IP addresses, or null if none.
    245      */
    246     public static String getWifiIpAddresses(Context context) {
    247         WifiManager wifiManager = context.getSystemService(WifiManager.class);
    248         Network currentNetwork = wifiManager.getCurrentNetwork();
    249         if (currentNetwork != null) {
    250             ConnectivityManager cm = (ConnectivityManager)
    251                 context.getSystemService(Context.CONNECTIVITY_SERVICE);
    252             LinkProperties prop = cm.getLinkProperties(currentNetwork);
    253             return formatIpAddresses(prop);
    254         }
    255         return null;
    256     }
    257 
    258     /**
    259      * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
    260      * addresses.
    261      * @return the formatted and newline-separated IP addresses, or null if none.
    262      */
    263     public static String getDefaultIpAddresses(ConnectivityManager cm) {
    264         LinkProperties prop = cm.getActiveLinkProperties();
    265         return formatIpAddresses(prop);
    266     }
    267 
    268     private static String formatIpAddresses(LinkProperties prop) {
    269         if (prop == null) return null;
    270         Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
    271         // If there are no entries, return null
    272         if (!iter.hasNext()) return null;
    273         // Concatenate all available addresses, comma separated
    274         String addresses = "";
    275         while (iter.hasNext()) {
    276             addresses += iter.next().getHostAddress();
    277             if (iter.hasNext()) addresses += "\n";
    278         }
    279         return addresses;
    280     }
    281 
    282     public static Locale createLocaleFromString(String localeStr) {
    283         // TODO: is there a better way to actually construct a locale that will match?
    284         // The main problem is, on top of Java specs, locale.toString() and
    285         // new Locale(locale.toString()).toString() do not return equal() strings in
    286         // many cases, because the constructor takes the only string as the language
    287         // code. So : new Locale("en", "US").toString() => "en_US"
    288         // And : new Locale("en_US").toString() => "en_us"
    289         if (null == localeStr)
    290             return Locale.getDefault();
    291         String[] brokenDownLocale = localeStr.split("_", 3);
    292         // split may not return a 0-length array.
    293         if (1 == brokenDownLocale.length) {
    294             return new Locale(brokenDownLocale[0]);
    295         } else if (2 == brokenDownLocale.length) {
    296             return new Locale(brokenDownLocale[0], brokenDownLocale[1]);
    297         } else {
    298             return new Locale(brokenDownLocale[0], brokenDownLocale[1], brokenDownLocale[2]);
    299         }
    300     }
    301 
    302     public static boolean isBatteryPresent(Intent batteryChangedIntent) {
    303         return batteryChangedIntent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
    304     }
    305 
    306     public static String getBatteryPercentage(Intent batteryChangedIntent) {
    307         return formatPercentage(getBatteryLevel(batteryChangedIntent));
    308     }
    309 
    310     /**
    311      * Prepare a custom preferences layout, moving padding to {@link ListView}
    312      * when outside scrollbars are requested. Usually used to display
    313      * {@link ListView} and {@link TabWidget} with correct padding.
    314      */
    315     public static void prepareCustomPreferencesList(
    316             ViewGroup parent, View child, View list, boolean ignoreSidePadding) {
    317         final boolean movePadding = list.getScrollBarStyle() == View.SCROLLBARS_OUTSIDE_OVERLAY;
    318         if (movePadding) {
    319             final Resources res = list.getResources();
    320             final int paddingSide = res.getDimensionPixelSize(R.dimen.settings_side_margin);
    321             final int paddingBottom = res.getDimensionPixelSize(
    322                     com.android.internal.R.dimen.preference_fragment_padding_bottom);
    323 
    324             if (parent instanceof PreferenceFrameLayout) {
    325                 ((PreferenceFrameLayout.LayoutParams) child.getLayoutParams()).removeBorders = true;
    326 
    327                 final int effectivePaddingSide = ignoreSidePadding ? 0 : paddingSide;
    328                 list.setPaddingRelative(effectivePaddingSide, 0, effectivePaddingSide, paddingBottom);
    329             } else {
    330                 list.setPaddingRelative(paddingSide, 0, paddingSide, paddingBottom);
    331             }
    332         }
    333     }
    334 
    335     public static void forceCustomPadding(View view, boolean additive) {
    336         final Resources res = view.getResources();
    337         final int paddingSide = res.getDimensionPixelSize(R.dimen.settings_side_margin);
    338 
    339         final int paddingStart = paddingSide + (additive ? view.getPaddingStart() : 0);
    340         final int paddingEnd = paddingSide + (additive ? view.getPaddingEnd() : 0);
    341         final int paddingBottom = res.getDimensionPixelSize(
    342                 com.android.internal.R.dimen.preference_fragment_padding_bottom);
    343 
    344         view.setPaddingRelative(paddingStart, 0, paddingEnd, paddingBottom);
    345     }
    346 
    347     /* Used by UserSettings as well. Call this on a non-ui thread. */
    348     public static void copyMeProfilePhoto(Context context, UserInfo user) {
    349         Uri contactUri = Profile.CONTENT_URI;
    350 
    351         int userId = user != null ? user.id : UserHandle.myUserId();
    352 
    353         InputStream avatarDataStream = Contacts.openContactPhotoInputStream(
    354                     context.getContentResolver(),
    355                     contactUri, true);
    356         // If there's no profile photo, assign a default avatar
    357         if (avatarDataStream == null) {
    358             assignDefaultPhoto(context, userId);
    359             return;
    360         }
    361 
    362         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
    363         Bitmap icon = BitmapFactory.decodeStream(avatarDataStream);
    364         um.setUserIcon(userId, icon);
    365         try {
    366             avatarDataStream.close();
    367         } catch (IOException ioe) { }
    368     }
    369 
    370     /**
    371      * Assign the default photo to user with {@paramref userId}
    372      * @param context used to get the {@link UserManager}
    373      * @param userId  used to get the icon bitmap
    374      * @return true if assign photo successfully, false if failed
    375      */
    376     public static boolean assignDefaultPhoto(Context context, int userId) {
    377         if (context == null) {
    378             return false;
    379         }
    380         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
    381         Bitmap bitmap = getDefaultUserIconAsBitmap(userId);
    382         um.setUserIcon(userId, bitmap);
    383 
    384         return true;
    385     }
    386 
    387     public static String getMeProfileName(Context context, boolean full) {
    388         if (full) {
    389             return getProfileDisplayName(context);
    390         } else {
    391             return getShorterNameIfPossible(context);
    392         }
    393     }
    394 
    395     private static String getShorterNameIfPossible(Context context) {
    396         final String given = getLocalProfileGivenName(context);
    397         return !TextUtils.isEmpty(given) ? given : getProfileDisplayName(context);
    398     }
    399 
    400     private static String getLocalProfileGivenName(Context context) {
    401         final ContentResolver cr = context.getContentResolver();
    402 
    403         // Find the raw contact ID for the local ME profile raw contact.
    404         final long localRowProfileId;
    405         final Cursor localRawProfile = cr.query(
    406                 Profile.CONTENT_RAW_CONTACTS_URI,
    407                 new String[] {RawContacts._ID},
    408                 RawContacts.ACCOUNT_TYPE + " IS NULL AND " +
    409                         RawContacts.ACCOUNT_NAME + " IS NULL",
    410                 null, null);
    411         if (localRawProfile == null) return null;
    412 
    413         try {
    414             if (!localRawProfile.moveToFirst()) {
    415                 return null;
    416             }
    417             localRowProfileId = localRawProfile.getLong(0);
    418         } finally {
    419             localRawProfile.close();
    420         }
    421 
    422         // Find the structured name for the raw contact.
    423         final Cursor structuredName = cr.query(
    424                 Profile.CONTENT_URI.buildUpon().appendPath(Contacts.Data.CONTENT_DIRECTORY).build(),
    425                 new String[] {CommonDataKinds.StructuredName.GIVEN_NAME,
    426                     CommonDataKinds.StructuredName.FAMILY_NAME},
    427                 Data.RAW_CONTACT_ID + "=" + localRowProfileId,
    428                 null, null);
    429         if (structuredName == null) return null;
    430 
    431         try {
    432             if (!structuredName.moveToFirst()) {
    433                 return null;
    434             }
    435             String partialName = structuredName.getString(0);
    436             if (TextUtils.isEmpty(partialName)) {
    437                 partialName = structuredName.getString(1);
    438             }
    439             return partialName;
    440         } finally {
    441             structuredName.close();
    442         }
    443     }
    444 
    445     private static final String getProfileDisplayName(Context context) {
    446         final ContentResolver cr = context.getContentResolver();
    447         final Cursor profile = cr.query(Profile.CONTENT_URI,
    448                 new String[] {Profile.DISPLAY_NAME}, null, null, null);
    449         if (profile == null) return null;
    450 
    451         try {
    452             if (!profile.moveToFirst()) {
    453                 return null;
    454             }
    455             return profile.getString(0);
    456         } finally {
    457             profile.close();
    458         }
    459     }
    460 
    461     /** Not global warming, it's global change warning. */
    462     public static Dialog buildGlobalChangeWarningDialog(final Context context, int titleResId,
    463             final Runnable positiveAction) {
    464         final AlertDialog.Builder builder = new AlertDialog.Builder(context);
    465         builder.setTitle(titleResId);
    466         builder.setMessage(R.string.global_change_warning);
    467         builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
    468             @Override
    469             public void onClick(DialogInterface dialog, int which) {
    470                 positiveAction.run();
    471             }
    472         });
    473         builder.setNegativeButton(android.R.string.cancel, null);
    474 
    475         return builder.create();
    476     }
    477 
    478     public static boolean hasMultipleUsers(Context context) {
    479         return ((UserManager) context.getSystemService(Context.USER_SERVICE))
    480                 .getUsers().size() > 1;
    481     }
    482 
    483     /**
    484      * Start a new instance of the activity, showing only the given fragment.
    485      * When launched in this mode, the given preference fragment will be instantiated and fill the
    486      * entire activity.
    487      *
    488      * @param context The context.
    489      * @param fragmentName The name of the fragment to display.
    490      * @param args Optional arguments to supply to the fragment.
    491      * @param resultTo Option fragment that should receive the result of the activity launch.
    492      * @param resultRequestCode If resultTo is non-null, this is the request code in which
    493      *                          to report the result.
    494      * @param titleResId resource id for the String to display for the title of this set
    495      *                   of preferences.
    496      * @param title String to display for the title of this set of preferences.
    497      * @param metricsCategory The current metricsCategory for logging source when fragment starts
    498      */
    499     public static void startWithFragment(Context context, String fragmentName, Bundle args,
    500             Fragment resultTo, int resultRequestCode, int titleResId,
    501             CharSequence title, int metricsCategory) {
    502         startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
    503                 null /* titleResPackageName */, titleResId, title, false /* not a shortcut */,
    504                 metricsCategory);
    505     }
    506 
    507     /**
    508      * Start a new instance of the activity, showing only the given fragment.
    509      * When launched in this mode, the given preference fragment will be instantiated and fill the
    510      * entire activity.
    511      *
    512      * @param context The context.
    513      * @param fragmentName The name of the fragment to display.
    514      * @param args Optional arguments to supply to the fragment.
    515      * @param resultTo Option fragment that should receive the result of the activity launch.
    516      * @param resultRequestCode If resultTo is non-null, this is the request code in which
    517      *                          to report the result.
    518      * @param titleResPackageName Optional package name for the resource id of the title.
    519      * @param titleResId resource id for the String to display for the title of this set
    520      *                   of preferences.
    521      * @param title String to display for the title of this set of preferences.
    522      * @param metricsCategory The current metricsCategory for logging source when fragment starts
    523      */
    524     public static void startWithFragment(Context context, String fragmentName, Bundle args,
    525             Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
    526             CharSequence title, int metricsCategory) {
    527         startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
    528                 titleResPackageName, titleResId, title, false /* not a shortcut */,
    529                 metricsCategory);
    530     }
    531 
    532     public static void startWithFragment(Context context, String fragmentName, Bundle args,
    533             Fragment resultTo, int resultRequestCode, int titleResId,
    534             CharSequence title, boolean isShortcut, int metricsCategory) {
    535         Intent intent = onBuildStartFragmentIntent(context, fragmentName, args,
    536                 null /* titleResPackageName */, titleResId, title, isShortcut, metricsCategory);
    537         if (resultTo == null) {
    538             context.startActivity(intent);
    539         } else {
    540             resultTo.getActivity().startActivityForResult(intent, resultRequestCode);
    541         }
    542     }
    543 
    544     public static void startWithFragment(Context context, String fragmentName, Bundle args,
    545             Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
    546             CharSequence title, boolean isShortcut, int metricsCategory) {
    547         Intent intent = onBuildStartFragmentIntent(context, fragmentName, args, titleResPackageName,
    548                 titleResId, title, isShortcut, metricsCategory);
    549         if (resultTo == null) {
    550             context.startActivity(intent);
    551         } else {
    552             resultTo.startActivityForResult(intent, resultRequestCode);
    553         }
    554     }
    555 
    556     public static void startWithFragmentAsUser(Context context, String fragmentName, Bundle args,
    557             int titleResId, CharSequence title, boolean isShortcut, int metricsCategory,
    558             UserHandle userHandle) {
    559         // workaround to avoid crash in b/17523189
    560         if (userHandle.getIdentifier() == UserHandle.myUserId()) {
    561             startWithFragment(context, fragmentName, args, null, 0, titleResId, title, isShortcut,
    562                     metricsCategory);
    563         } else {
    564             Intent intent = onBuildStartFragmentIntent(context, fragmentName, args,
    565                     null /* titleResPackageName */, titleResId, title, isShortcut, metricsCategory);
    566             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    567             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
    568             context.startActivityAsUser(intent, userHandle);
    569         }
    570     }
    571 
    572     /**
    573      * Build an Intent to launch a new activity showing the selected fragment.
    574      * The implementation constructs an Intent that re-launches the current activity with the
    575      * appropriate arguments to display the fragment.
    576      *
    577      *
    578      * @param context The Context.
    579      * @param fragmentName The name of the fragment to display.
    580      * @param args Optional arguments to supply to the fragment.
    581      * @param titleResPackageName Optional package name for the resource id of the title.
    582      * @param titleResId Optional title resource id to show for this item.
    583      * @param title Optional title to show for this item.
    584      * @param isShortcut  tell if this is a Launcher Shortcut or not
    585      * @param sourceMetricsCategory The context (source) from which an action is performed
    586      * @return Returns an Intent that can be launched to display the given
    587      * fragment.
    588      */
    589     public static Intent onBuildStartFragmentIntent(Context context, String fragmentName,
    590             Bundle args, String titleResPackageName, int titleResId, CharSequence title,
    591             boolean isShortcut, int sourceMetricsCategory) {
    592         Intent intent = new Intent(Intent.ACTION_MAIN);
    593         intent.setClass(context, SubSettings.class);
    594         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, fragmentName);
    595         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
    596         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,
    597                 titleResPackageName);
    598         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleResId);
    599         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title);
    600         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut);
    601         intent.putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY, sourceMetricsCategory);
    602         return intent;
    603     }
    604 
    605     /**
    606      * Returns the managed profile of the current user or {@code null} if none is found or a profile
    607      * exists but it is disabled.
    608      */
    609     public static UserHandle getManagedProfile(UserManager userManager) {
    610         List<UserHandle> userProfiles = userManager.getUserProfiles();
    611         final int count = userProfiles.size();
    612         for (int i = 0; i < count; i++) {
    613             final UserHandle profile = userProfiles.get(i);
    614             if (profile.getIdentifier() == userManager.getUserHandle()) {
    615                 continue;
    616             }
    617             final UserInfo userInfo = userManager.getUserInfo(profile.getIdentifier());
    618             if (userInfo.isManagedProfile()) {
    619                 return profile;
    620             }
    621         }
    622         return null;
    623     }
    624 
    625     /**
    626      * Returns the managed profile of the current user or {@code null} if none is found. Unlike
    627      * {@link #getManagedProfile} this method returns enabled and disabled managed profiles.
    628      */
    629     public static UserHandle getManagedProfileWithDisabled(UserManager userManager) {
    630         // TODO: Call getManagedProfileId from here once Robolectric supports
    631         // API level 24 and UserManager.getProfileIdsWithDisabled can be Mocked (to avoid having
    632         // yet another implementation that loops over user profiles in this method). In the meantime
    633         // we need to use UserManager.getProfiles that is available on API 23 (the one currently
    634         // used for Settings Robolectric tests).
    635         final int myUserId = UserHandle.myUserId();
    636         List<UserInfo> profiles = userManager.getProfiles(myUserId);
    637         final int count = profiles.size();
    638         for (int i = 0; i < count; i++) {
    639             final UserInfo profile = profiles.get(i);
    640             if (profile.isManagedProfile()
    641                     && profile.getUserHandle().getIdentifier() != myUserId) {
    642                 return profile.getUserHandle();
    643             }
    644         }
    645         return null;
    646     }
    647 
    648     /**
    649      * Retrieves the id for the given user's managed profile.
    650      *
    651      * @return the managed profile id or UserHandle.USER_NULL if there is none.
    652      */
    653     public static int getManagedProfileId(UserManager um, int parentUserId) {
    654         int[] profileIds = um.getProfileIdsWithDisabled(parentUserId);
    655         for (int profileId : profileIds) {
    656             if (profileId != parentUserId) {
    657                 return profileId;
    658             }
    659         }
    660         return UserHandle.USER_NULL;
    661     }
    662 
    663     /**
    664      * Returns the target user for a Settings activity.
    665      * <p>
    666      * User would be retrieved in this order:
    667      * <ul>
    668      * <li> If this activity is launched from other user, return that user id.
    669      * <li> If this is launched from the Settings app in same user, return the user contained as an
    670      *      extra in the arguments or intent extras.
    671      * <li> Otherwise, return UserHandle.myUserId().
    672      * </ul>
    673      * <p>
    674      * Note: This is secure in the sense that it only returns a target user different to the current
    675      * one if the app launching this activity is the Settings app itself, running in the same user
    676      * or in one that is in the same profile group, or if the user id is provided by the system.
    677      */
    678     public static UserHandle getSecureTargetUser(IBinder activityToken,
    679             UserManager um, @Nullable Bundle arguments, @Nullable Bundle intentExtras) {
    680         UserHandle currentUser = new UserHandle(UserHandle.myUserId());
    681         IActivityManager am = ActivityManager.getService();
    682         try {
    683             String launchedFromPackage = am.getLaunchedFromPackage(activityToken);
    684             boolean launchedFromSettingsApp = SETTINGS_PACKAGE_NAME.equals(launchedFromPackage);
    685 
    686             UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId(
    687                     am.getLaunchedFromUid(activityToken)));
    688             if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) {
    689                 // Check it's secure
    690                 if (isProfileOf(um, launchedFromUser)) {
    691                     return launchedFromUser;
    692                 }
    693             }
    694             UserHandle extrasUser = getUserHandleFromBundle(intentExtras);
    695             if (extrasUser != null && !extrasUser.equals(currentUser)) {
    696                 // Check it's secure
    697                 if (launchedFromSettingsApp && isProfileOf(um, extrasUser)) {
    698                     return extrasUser;
    699                 }
    700             }
    701             UserHandle argumentsUser = getUserHandleFromBundle(arguments);
    702             if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
    703                 // Check it's secure
    704                 if (launchedFromSettingsApp && isProfileOf(um, argumentsUser)) {
    705                     return argumentsUser;
    706                 }
    707             }
    708         } catch (RemoteException e) {
    709             // Should not happen
    710             Log.v(TAG, "Could not talk to activity manager.", e);
    711         }
    712         return currentUser;
    713     }
    714 
    715     /**
    716      * Lookup both {@link Intent#EXTRA_USER} and {@link Intent#EXTRA_USER_ID} in the bundle
    717      * and return the {@link UserHandle} object. Return {@code null} if nothing is found.
    718      */
    719     private static @Nullable UserHandle getUserHandleFromBundle(Bundle bundle) {
    720         if (bundle == null) {
    721             return null;
    722         }
    723         final UserHandle user = bundle.getParcelable(EXTRA_USER);
    724         if (user != null) {
    725             return user;
    726         }
    727         final int userId = bundle.getInt(EXTRA_USER_ID, -1);
    728         if (userId != -1) {
    729             return UserHandle.of(userId);
    730         }
    731         return null;
    732     }
    733 
    734     /**
    735      * Returns the target user for a Settings activity.
    736      *
    737      * The target user can be either the current user, the user that launched this activity or
    738      * the user contained as an extra in the arguments or intent extras.
    739      *
    740      * You should use {@link #getSecureTargetUser(IBinder, UserManager, Bundle, Bundle)} if
    741      * possible.
    742      *
    743      * @see #getInsecureTargetUser(IBinder, Bundle, Bundle)
    744      */
    745    public static UserHandle getInsecureTargetUser(IBinder activityToken, @Nullable Bundle arguments,
    746            @Nullable Bundle intentExtras) {
    747        UserHandle currentUser = new UserHandle(UserHandle.myUserId());
    748        IActivityManager am = ActivityManager.getService();
    749        try {
    750            UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId(
    751                    am.getLaunchedFromUid(activityToken)));
    752            if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) {
    753                return launchedFromUser;
    754            }
    755            UserHandle extrasUser = intentExtras != null
    756                    ? (UserHandle) intentExtras.getParcelable(EXTRA_USER) : null;
    757            if (extrasUser != null && !extrasUser.equals(currentUser)) {
    758                return extrasUser;
    759            }
    760            UserHandle argumentsUser = arguments != null
    761                    ? (UserHandle) arguments.getParcelable(EXTRA_USER) : null;
    762            if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
    763                return argumentsUser;
    764            }
    765        } catch (RemoteException e) {
    766            // Should not happen
    767            Log.v(TAG, "Could not talk to activity manager.", e);
    768            return null;
    769        }
    770        return currentUser;
    771    }
    772 
    773    /**
    774     * Returns true if the user provided is in the same profiles group as the current user.
    775     */
    776    private static boolean isProfileOf(UserManager um, UserHandle otherUser) {
    777        if (um == null || otherUser == null) return false;
    778        return (UserHandle.myUserId() == otherUser.getIdentifier())
    779                || um.getUserProfiles().contains(otherUser);
    780    }
    781 
    782     /**
    783      * Return whether or not the user should have a SIM Cards option in Settings.
    784      * TODO: Change back to returning true if count is greater than one after testing.
    785      * TODO: See bug 16533525.
    786      */
    787     public static boolean showSimCardTile(Context context) {
    788         final TelephonyManager tm =
    789                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    790 
    791         return tm.getSimCount() > 1;
    792     }
    793 
    794     /**
    795      * Returns elapsed time for the given millis, in the following format:
    796      * 2d 5h 40m 29s
    797      * @param context the application context
    798      * @param millis the elapsed time in milli seconds
    799      * @param withSeconds include seconds?
    800      * @return the formatted elapsed time
    801      */
    802     public static CharSequence formatElapsedTime(Context context, double millis,
    803             boolean withSeconds) {
    804         SpannableStringBuilder sb = new SpannableStringBuilder();
    805         int seconds = (int) Math.floor(millis / 1000);
    806         if (!withSeconds) {
    807             // Round up.
    808             seconds += 30;
    809         }
    810 
    811         int days = 0, hours = 0, minutes = 0;
    812         if (seconds >= SECONDS_PER_DAY) {
    813             days = seconds / SECONDS_PER_DAY;
    814             seconds -= days * SECONDS_PER_DAY;
    815         }
    816         if (seconds >= SECONDS_PER_HOUR) {
    817             hours = seconds / SECONDS_PER_HOUR;
    818             seconds -= hours * SECONDS_PER_HOUR;
    819         }
    820         if (seconds >= SECONDS_PER_MINUTE) {
    821             minutes = seconds / SECONDS_PER_MINUTE;
    822             seconds -= minutes * SECONDS_PER_MINUTE;
    823         }
    824         if (withSeconds) {
    825             if (days > 0) {
    826                 sb.append(context.getString(R.string.battery_history_days,
    827                         days, hours, minutes, seconds));
    828             } else if (hours > 0) {
    829                 sb.append(context.getString(R.string.battery_history_hours,
    830                         hours, minutes, seconds));
    831             } else if (minutes > 0) {
    832                 sb.append(context.getString(R.string.battery_history_minutes, minutes, seconds));
    833             } else {
    834                 sb.append(context.getString(R.string.battery_history_seconds, seconds));
    835             }
    836         } else {
    837             if (days > 0) {
    838                 sb.append(context.getString(R.string.battery_history_days_no_seconds,
    839                         days, hours, minutes));
    840             } else if (hours > 0) {
    841                 sb.append(context.getString(R.string.battery_history_hours_no_seconds,
    842                         hours, minutes));
    843             } else {
    844                 sb.append(context.getString(R.string.battery_history_minutes_no_seconds, minutes));
    845 
    846                 // Add ttsSpan if it only have minute value, because it will be read as "meters"
    847                 TtsSpan ttsSpan = new TtsSpan.MeasureBuilder().setNumber(minutes)
    848                         .setUnit("minute").build();
    849                 sb.setSpan(ttsSpan, 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    850             }
    851         }
    852 
    853         return sb;
    854     }
    855 
    856     /**
    857      * Queries for the UserInfo of a user. Returns null if the user doesn't exist (was removed).
    858      * @param userManager Instance of UserManager
    859      * @param checkUser The user to check the existence of.
    860      * @return UserInfo of the user or null for non-existent user.
    861      */
    862     public static UserInfo getExistingUser(UserManager userManager, UserHandle checkUser) {
    863         final List<UserInfo> users = userManager.getUsers(true /* excludeDying */);
    864         final int checkUserId = checkUser.getIdentifier();
    865         for (UserInfo user : users) {
    866             if (user.id == checkUserId) {
    867                 return user;
    868             }
    869         }
    870         return null;
    871     }
    872 
    873     public static View inflateCategoryHeader(LayoutInflater inflater, ViewGroup parent) {
    874         final TypedArray a = inflater.getContext().obtainStyledAttributes(null,
    875                 com.android.internal.R.styleable.Preference,
    876                 com.android.internal.R.attr.preferenceCategoryStyle, 0);
    877         final int resId = a.getResourceId(com.android.internal.R.styleable.Preference_layout,
    878                 0);
    879         a.recycle();
    880         return inflater.inflate(resId, parent, false);
    881     }
    882 
    883     /**
    884      * Return if we are running low on storage space or not.
    885      *
    886      * @param context The context
    887      * @return true if we are running low on storage space
    888      */
    889     public static boolean isLowStorage(Context context) {
    890         final StorageManager sm = StorageManager.from(context);
    891         return (sm.getStorageBytesUntilLow(context.getFilesDir()) < 0);
    892     }
    893 
    894     /**
    895      * Returns a default user icon (as a {@link Bitmap}) for the given user.
    896      *
    897      * Note that for guest users, you should pass in {@code UserHandle.USER_NULL}.
    898      * @param userId the user id or {@code UserHandle.USER_NULL} for a non-user specific icon
    899      */
    900     public static Bitmap getDefaultUserIconAsBitmap(int userId) {
    901         Bitmap bitmap = null;
    902         // Try finding the corresponding bitmap in the dark bitmap cache
    903         bitmap = sDarkDefaultUserBitmapCache.get(userId);
    904         if (bitmap == null) {
    905             bitmap = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(userId, false));
    906             // Save it to cache
    907             sDarkDefaultUserBitmapCache.put(userId, bitmap);
    908         }
    909         return bitmap;
    910     }
    911 
    912     public static boolean hasPreferredActivities(PackageManager pm, String packageName) {
    913         // Get list of preferred activities
    914         List<ComponentName> prefActList = new ArrayList<>();
    915         // Intent list cannot be null. so pass empty list
    916         List<IntentFilter> intentList = new ArrayList<>();
    917         pm.getPreferredActivities(intentList, prefActList, packageName);
    918         Log.d(TAG, "Have " + prefActList.size() + " number of activities in preferred list");
    919         return prefActList.size() > 0;
    920     }
    921 
    922     public static ArraySet<String> getHandledDomains(PackageManager pm, String packageName) {
    923         List<IntentFilterVerificationInfo> iviList = pm.getIntentFilterVerifications(packageName);
    924         List<IntentFilter> filters = pm.getAllIntentFilters(packageName);
    925 
    926         ArraySet<String> result = new ArraySet<>();
    927         if (iviList.size() > 0) {
    928             for (IntentFilterVerificationInfo ivi : iviList) {
    929                 for (String host : ivi.getDomains()) {
    930                     result.add(host);
    931                 }
    932             }
    933         }
    934         if (filters != null && filters.size() > 0) {
    935             for (IntentFilter filter : filters) {
    936                 if (filter.hasCategory(Intent.CATEGORY_BROWSABLE)
    937                         && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
    938                                 filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) {
    939                     result.addAll(filter.getHostsList());
    940                 }
    941             }
    942         }
    943         return result;
    944     }
    945 
    946     public static void handleLoadingContainer(View loading, View doneLoading, boolean done,
    947             boolean animate) {
    948         setViewShown(loading, !done, animate);
    949         setViewShown(doneLoading, done, animate);
    950     }
    951 
    952     private static void setViewShown(final View view, boolean shown, boolean animate) {
    953         if (animate) {
    954             Animation animation = AnimationUtils.loadAnimation(view.getContext(),
    955                     shown ? android.R.anim.fade_in : android.R.anim.fade_out);
    956             if (shown) {
    957                 view.setVisibility(View.VISIBLE);
    958             } else {
    959                 animation.setAnimationListener(new AnimationListener() {
    960                     @Override
    961                     public void onAnimationStart(Animation animation) {
    962                     }
    963 
    964                     @Override
    965                     public void onAnimationRepeat(Animation animation) {
    966                     }
    967 
    968                     @Override
    969                     public void onAnimationEnd(Animation animation) {
    970                         view.setVisibility(View.INVISIBLE);
    971                     }
    972                 });
    973             }
    974             view.startAnimation(animation);
    975         } else {
    976             view.clearAnimation();
    977             view.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
    978         }
    979     }
    980 
    981     /**
    982      * Returns the application info of the currently installed MDM package.
    983      */
    984     public static ApplicationInfo getAdminApplicationInfo(Context context, int profileId) {
    985         DevicePolicyManager dpm =
    986                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
    987         ComponentName mdmPackage = dpm.getProfileOwnerAsUser(profileId);
    988         if (mdmPackage == null) {
    989             return null;
    990         }
    991         String mdmPackageName = mdmPackage.getPackageName();
    992         try {
    993             IPackageManager ipm = AppGlobals.getPackageManager();
    994             ApplicationInfo mdmApplicationInfo =
    995                     ipm.getApplicationInfo(mdmPackageName, 0, profileId);
    996             return mdmApplicationInfo;
    997         } catch (RemoteException e) {
    998             Log.e(TAG, "Error while retrieving application info for package " + mdmPackageName
    999                     + ", userId " + profileId, e);
   1000             return null;
   1001         }
   1002     }
   1003 
   1004     public static boolean isBandwidthControlEnabled() {
   1005         final INetworkManagementService netManager = INetworkManagementService.Stub
   1006                 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
   1007         try {
   1008             return netManager.isBandwidthControlEnabled();
   1009         } catch (RemoteException e) {
   1010             return false;
   1011         }
   1012     }
   1013 
   1014     /**
   1015      * Returns an accessible SpannableString.
   1016      * @param displayText the text to display
   1017      * @param accessibileText the text text-to-speech engines should read
   1018      */
   1019     public static SpannableString createAccessibleSequence(CharSequence displayText,
   1020             String accessibileText) {
   1021         SpannableString str = new SpannableString(displayText);
   1022         str.setSpan(new TtsSpan.TextBuilder(accessibileText).build(), 0,
   1023                 displayText.length(),
   1024                 Spannable.SPAN_INCLUSIVE_INCLUSIVE);
   1025         return str;
   1026     }
   1027 
   1028     /**
   1029      * Returns the user id present in the bundle with {@link Intent#EXTRA_USER_ID} if it
   1030      * belongs to the current user.
   1031      *
   1032      * @throws SecurityException if the given userId does not belong to the current user group.
   1033      */
   1034     public static int getUserIdFromBundle(Context context, Bundle bundle) {
   1035         if (bundle == null) {
   1036             return getCredentialOwnerUserId(context);
   1037         }
   1038         int userId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId());
   1039         return enforceSameOwner(context, userId);
   1040     }
   1041 
   1042     /**
   1043      * Returns the given user id if it belongs to the current user.
   1044      *
   1045      * @throws SecurityException if the given userId does not belong to the current user group.
   1046      */
   1047     public static int enforceSameOwner(Context context, int userId) {
   1048         final UserManager um = getUserManager(context);
   1049         final int[] profileIds = um.getProfileIdsWithDisabled(UserHandle.myUserId());
   1050         if (ArrayUtils.contains(profileIds, userId)) {
   1051             return userId;
   1052         }
   1053         throw new SecurityException("Given user id " + userId + " does not belong to user "
   1054                 + UserHandle.myUserId());
   1055     }
   1056 
   1057     /**
   1058      * Returns the effective credential owner of the calling user.
   1059      */
   1060     public static int getCredentialOwnerUserId(Context context) {
   1061         return getCredentialOwnerUserId(context, UserHandle.myUserId());
   1062     }
   1063 
   1064     /**
   1065      * Returns the user id of the credential owner of the given user id.
   1066      */
   1067     public static int getCredentialOwnerUserId(Context context, int userId) {
   1068         UserManager um = getUserManager(context);
   1069         return um.getCredentialOwnerProfile(userId);
   1070     }
   1071 
   1072     public static int resolveResource(Context context, int attr) {
   1073         TypedValue value = new TypedValue();
   1074         context.getTheme().resolveAttribute(attr, value, true);
   1075         return value.resourceId;
   1076     }
   1077 
   1078     private static final StringBuilder sBuilder = new StringBuilder(50);
   1079     private static final java.util.Formatter sFormatter = new java.util.Formatter(
   1080             sBuilder, Locale.getDefault());
   1081 
   1082     public static String formatDateRange(Context context, long start, long end) {
   1083         final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
   1084 
   1085         synchronized (sBuilder) {
   1086             sBuilder.setLength(0);
   1087             return DateUtils.formatDateRange(context, sFormatter, start, end, flags, null)
   1088                     .toString();
   1089         }
   1090     }
   1091 
   1092     public static List<String> getNonIndexable(int xml, Context context) {
   1093         if (Looper.myLooper() == null) {
   1094             // Hack to make sure Preferences can initialize.  Prefs expect a looper, but
   1095             // don't actually use it for the basic stuff here.
   1096             Looper.prepare();
   1097         }
   1098         final List<String> ret = new ArrayList<>();
   1099         PreferenceManager manager = new PreferenceManager(context);
   1100         PreferenceScreen screen = manager.inflateFromResource(context, xml, null);
   1101         checkPrefs(screen, ret);
   1102 
   1103         return ret;
   1104     }
   1105 
   1106     private static void checkPrefs(PreferenceGroup group, List<String> ret) {
   1107         if (group == null) return;
   1108         for (int i = 0; i < group.getPreferenceCount(); i++) {
   1109             Preference pref = group.getPreference(i);
   1110             if (pref instanceof SelfAvailablePreference
   1111                     && !((SelfAvailablePreference) pref).isAvailable(group.getContext())) {
   1112                 ret.add(pref.getKey());
   1113                 if (pref instanceof PreferenceGroup) {
   1114                     addAll((PreferenceGroup) pref, ret);
   1115                 }
   1116             } else if (pref instanceof PreferenceGroup) {
   1117                 checkPrefs((PreferenceGroup) pref, ret);
   1118             }
   1119         }
   1120     }
   1121 
   1122     private static void addAll(PreferenceGroup group, List<String> ret) {
   1123         if (group == null) return;
   1124         for (int i = 0; i < group.getPreferenceCount(); i++) {
   1125             Preference pref = group.getPreference(i);
   1126             ret.add(pref.getKey());
   1127             if (pref instanceof PreferenceGroup) {
   1128                 addAll((PreferenceGroup) pref, ret);
   1129             }
   1130         }
   1131     }
   1132 
   1133     public static boolean isDeviceProvisioned(Context context) {
   1134         return Settings.Global.getInt(context.getContentResolver(),
   1135                 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
   1136     }
   1137 
   1138     public static boolean startQuietModeDialogIfNecessary(Context context, UserManager um,
   1139             int userId) {
   1140         if (um.isQuietModeEnabled(UserHandle.of(userId))) {
   1141             final Intent intent = UnlaunchableAppActivity.createInQuietModeDialogIntent(userId);
   1142             context.startActivity(intent);
   1143             return true;
   1144         }
   1145         return false;
   1146     }
   1147 
   1148     public static boolean unlockWorkProfileIfNecessary(Context context, int userId) {
   1149         try {
   1150             if (!ActivityManager.getService().isUserRunning(userId,
   1151                     ActivityManager.FLAG_AND_LOCKED)) {
   1152                 return false;
   1153             }
   1154         } catch (RemoteException e) {
   1155             return false;
   1156         }
   1157         if (!(new LockPatternUtils(context)).isSecure(userId)) {
   1158             return false;
   1159         }
   1160         return confirmWorkProfileCredentials(context, userId);
   1161     }
   1162 
   1163     public static boolean confirmWorkProfileCredentialsIfNecessary(Context context, int userId) {
   1164         KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
   1165         if (!km.isDeviceLocked(userId)) {
   1166             return false;
   1167         }
   1168         return confirmWorkProfileCredentials(context, userId);
   1169     }
   1170 
   1171     private static boolean confirmWorkProfileCredentials(Context context, int userId) {
   1172         final KeyguardManager km = (KeyguardManager) context.getSystemService(
   1173                 Context.KEYGUARD_SERVICE);
   1174         final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
   1175         if (unlockIntent != null) {
   1176             context.startActivity(unlockIntent);
   1177             return true;
   1178         } else {
   1179             return false;
   1180         }
   1181     }
   1182 
   1183     public static CharSequence getApplicationLabel(Context context, String packageName) {
   1184         try {
   1185             final ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
   1186                     packageName,
   1187                     PackageManager.MATCH_DISABLED_COMPONENTS
   1188                     | PackageManager.MATCH_ANY_USER);
   1189             return appInfo.loadLabel(context.getPackageManager());
   1190         } catch (PackageManager.NameNotFoundException e) {
   1191             Log.w(TAG, "Unable to find info for package: " + packageName);
   1192         }
   1193         return null;
   1194     }
   1195 
   1196     public static boolean isPackageDirectBootAware(Context context, String packageName) {
   1197         try {
   1198             final ApplicationInfo ai = context.getPackageManager().getApplicationInfo(
   1199                     packageName, 0);
   1200             return ai.isDirectBootAware() || ai.isPartiallyDirectBootAware();
   1201         } catch (NameNotFoundException ignored) {
   1202         }
   1203         return false;
   1204     }
   1205 
   1206     /**
   1207      * Returns a context created from the given context for the given user, or null if it fails
   1208      */
   1209     public static Context createPackageContextAsUser(Context context, int userId) {
   1210         try {
   1211             return context.createPackageContextAsUser(
   1212                     context.getPackageName(), 0 /* flags */, UserHandle.of(userId));
   1213         } catch (PackageManager.NameNotFoundException e) {
   1214             Log.e(TAG, "Failed to create user context", e);
   1215         }
   1216         return null;
   1217     }
   1218 
   1219     public static FingerprintManager getFingerprintManagerOrNull(Context context) {
   1220         if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
   1221             return context.getSystemService(FingerprintManager.class);
   1222         } else {
   1223             return null;
   1224         }
   1225     }
   1226 
   1227     public static IFingerprintManager getFingerprintManagerWrapperOrNull(Context context) {
   1228         FingerprintManager fingerprintManager = getFingerprintManagerOrNull(context);
   1229         if (fingerprintManager != null) {
   1230             return new FingerprintManagerWrapper(fingerprintManager);
   1231         } else {
   1232             return null;
   1233         }
   1234     }
   1235 
   1236     /**
   1237      * Launches an intent which may optionally have a user id defined.
   1238      * @param fragment Fragment to use to launch the activity.
   1239      * @param intent Intent to launch.
   1240      */
   1241     public static void launchIntent(Fragment fragment, Intent intent) {
   1242         try {
   1243             final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1);
   1244 
   1245             if (userId == -1) {
   1246                 fragment.startActivity(intent);
   1247             } else {
   1248                 fragment.getActivity().startActivityAsUser(intent, new UserHandle(userId));
   1249             }
   1250         } catch (ActivityNotFoundException e) {
   1251             Log.w(TAG, "No activity found for " + intent);
   1252         }
   1253     }
   1254 
   1255     public static boolean isCarrierDemoUser(Context context) {
   1256         final String carrierDemoModeSetting =
   1257                 context.getString(com.android.internal.R.string.config_carrierDemoModeSetting);
   1258         return UserManager.isDeviceInDemoMode(context)
   1259                 && getUserManager(context).isDemoUser()
   1260                 && !TextUtils.isEmpty(carrierDemoModeSetting)
   1261                 && (Settings.Secure.getInt(context.getContentResolver(),
   1262                         carrierDemoModeSetting, 0) == 1);
   1263     }
   1264 
   1265     /**
   1266      * Returns if a given user is a profile of another user.
   1267      * @param user The user whose profiles will be checked.
   1268      * @param profile The (potential) profile.
   1269      * @return if the profile is actually a profile
   1270      */
   1271     public static boolean isProfileOf(UserInfo user, UserInfo profile) {
   1272         return user.id == profile.id ||
   1273                 (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
   1274                         && user.profileGroupId == profile.profileGroupId);
   1275     }
   1276 
   1277     /**
   1278      * Tries to initalize a volume with the given bundle. If it is a valid, private, and readable
   1279      * {@link VolumeInfo}, it is returned. If it is not valid, null is returned.
   1280      */
   1281     @Nullable
   1282     public static VolumeInfo maybeInitializeVolume(StorageManager sm, Bundle bundle) {
   1283         final String volumeId = bundle.getString(VolumeInfo.EXTRA_VOLUME_ID,
   1284                 VolumeInfo.ID_PRIVATE_INTERNAL);
   1285         VolumeInfo volume = sm.findVolumeById(volumeId);
   1286         return isVolumeValid(volume) ? volume : null;
   1287     }
   1288 
   1289     /**
   1290      * Return the resource id to represent the install status for an app
   1291      */
   1292     @StringRes
   1293     public static int getInstallationStatus(ApplicationInfo info) {
   1294         if ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
   1295             return R.string.not_installed;
   1296         }
   1297         return info.enabled ? R.string.installed : R.string.disabled;
   1298     }
   1299 
   1300     /**
   1301      * Control if other apps can display overlays. By default this is allowed. Be sure to
   1302      * re-enable overlays, as the effect is system-wide.
   1303      */
   1304     public static void setOverlayAllowed(Context context, IBinder token, boolean allowed) {
   1305         AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
   1306         if (appOpsManager != null) {
   1307             appOpsManager.setUserRestriction(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
   1308                     !allowed, token);
   1309             appOpsManager.setUserRestriction(AppOpsManager.OP_TOAST_WINDOW,
   1310                     !allowed, token);
   1311         }
   1312     }
   1313 
   1314 
   1315 
   1316     private static boolean isVolumeValid(VolumeInfo volume) {
   1317         return (volume != null) && (volume.getType() == VolumeInfo.TYPE_PRIVATE)
   1318                 && volume.isMountedReadable();
   1319     }
   1320 
   1321 }
   1322