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