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