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