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