2 * Copyright (C) 2007 Google Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
17 package com.android.settings;
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;
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;
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;
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;
118 public final class Utils extends com.android.settingslib.Utils {
120 private static final String TAG = "Settings";
123 * Set the preference's title to the matching activity's label.
125 public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1;
128 * The opacity level of a disabled icon.
130 public static final float DISABLED_ALPHA = 0.4f;
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).
136 public static final int[] BADNESS_COLORS = new int[] {
137 0x00000000, 0xffc43828, 0xffe54918, 0xfff47b00,
138 0xfffabf2c, 0xff679e37, 0xff0a7f42
141 private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
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;
147 public static final String OS_PKG = "os";
149 private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<Bitmap>();
152 * Finds a matching activity for a preference's intent. If a matching
153 * activity is not found, it will remove the preference.
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
160 * @param flags 0 or one or more of
161 * {@link #UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY}
163 * @return Whether an activity was found. If false, the preference was
166 public static boolean updatePreferenceToSpecificActivityOrRemove(Context context,
167 PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags) {
169 Preference preference = parentPreferenceGroup.findPreference(preferenceKey);
170 if (preference == null) {
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)
185 // Replace the intent with this specific activity
186 preference.setIntent(new Intent().setClassName(
187 resolveInfo.activityInfo.packageName,
188 resolveInfo.activityInfo.name));
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));
200 // Did not find a matching activity, so remove the preference
201 parentPreferenceGroup.removePreference(preference);
207 * Returns the UserManager for a given context
209 * @throws IllegalStateException if no UserManager could be retrieved.
211 public static UserManager getUserManager(Context context) {
212 UserManager um = UserManager.get(context);
214 throw new IllegalStateException("Unable to load UserManager");
220 * Returns true if Monkey is running.
222 public static boolean isMonkeyRunning() {
223 return ActivityManager.isUserAMonkey();
227 * Returns whether the device is voice-capable (meaning, it is also a phone).
229 public static boolean isVoiceCapable(Context context) {
230 TelephonyManager telephony =
231 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
232 return telephony != null && telephony.isVoiceCapable();
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);
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.
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);
259 * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
261 * @return the formatted and newline-separated IP addresses, or null if none.
263 public static String getDefaultIpAddresses(ConnectivityManager cm) {
264 LinkProperties prop = cm.getActiveLinkProperties();
265 return formatIpAddresses(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";
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]);
298 return new Locale(brokenDownLocale[0], brokenDownLocale[1], brokenDownLocale[2]);
302 public static boolean isBatteryPresent(Intent batteryChangedIntent) {
303 return batteryChangedIntent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
306 public static String getBatteryPercentage(Intent batteryChangedIntent) {
307 return formatPercentage(getBatteryLevel(batteryChangedIntent));
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.
315 public static void prepareCustomPreferencesList(
316 ViewGroup parent, View child, View list, boolean ignoreSidePadding) {
317 final boolean movePadding = list.getScrollBarStyle() == View.SCROLLBARS_OUTSIDE_OVERLAY;
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);
324 if (parent instanceof PreferenceFrameLayout) {
325 ((PreferenceFrameLayout.LayoutParams) child.getLayoutParams()).removeBorders = true;
327 final int effectivePaddingSide = ignoreSidePadding ? 0 : paddingSide;
328 list.setPaddingRelative(effectivePaddingSide, 0, effectivePaddingSide, paddingBottom);
330 list.setPaddingRelative(paddingSide, 0, paddingSide, paddingBottom);
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);
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);
344 view.setPaddingRelative(paddingStart, 0, paddingEnd, paddingBottom);
347 /* Used by UserSettings as well. Call this on a non-ui thread. */
348 public static void copyMeProfilePhoto(Context context, UserInfo user) {
349 Uri contactUri = Profile.CONTENT_URI;
351 int userId = user != null ? user.id : UserHandle.myUserId();
353 InputStream avatarDataStream = Contacts.openContactPhotoInputStream(
354 context.getContentResolver(),
356 // If there's no profile photo, assign a default avatar
357 if (avatarDataStream == null) {
358 assignDefaultPhoto(context, userId);
362 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
363 Bitmap icon = BitmapFactory.decodeStream(avatarDataStream);
364 um.setUserIcon(userId, icon);
366 avatarDataStream.close();
367 } catch (IOException ioe) { }
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
376 public static boolean assignDefaultPhoto(Context context, int userId) {
377 if (context == null) {
380 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
381 Bitmap bitmap = getDefaultUserIconAsBitmap(userId);
382 um.setUserIcon(userId, bitmap);
387 public static String getMeProfileName(Context context, boolean full) {
389 return getProfileDisplayName(context);
391 return getShorterNameIfPossible(context);
395 private static String getShorterNameIfPossible(Context context) {
396 final String given = getLocalProfileGivenName(context);
397 return !TextUtils.isEmpty(given) ? given : getProfileDisplayName(context);
400 private static String getLocalProfileGivenName(Context context) {
401 final ContentResolver cr = context.getContentResolver();
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",
411 if (localRawProfile == null) return null;
414 if (!localRawProfile.moveToFirst()) {
417 localRowProfileId = localRawProfile.getLong(0);
419 localRawProfile.close();
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,
429 if (structuredName == null) return null;
432 if (!structuredName.moveToFirst()) {
435 String partialName = structuredName.getString(0);
436 if (TextUtils.isEmpty(partialName)) {
437 partialName = structuredName.getString(1);
441 structuredName.close();
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;
452 if (!profile.moveToFirst()) {
455 return profile.getString(0);
461 /** Not global warming, it's global change warning. */
462 public static Dialog buildGlobalChangeWarningDialog(final Context context, int titleResId,
463 final Runnable positiveAction) {
464 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
465 builder.setTitle(titleResId);
466 builder.setMessage(R.string.global_change_warning);
467 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
469 public void onClick(DialogInterface dialog, int which) {
470 positiveAction.run();
473 builder.setNegativeButton(android.R.string.cancel, null);
475 return builder.create();
478 public static boolean hasMultipleUsers(Context context) {
479 return ((UserManager) context.getSystemService(Context.USER_SERVICE))
480 .getUsers().size() > 1;
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
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
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
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 */,
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
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
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
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 */,
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);
540 resultTo.getActivity().startActivityForResult(intent, resultRequestCode);
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);
552 resultTo.startActivityForResult(intent, resultRequestCode);
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,
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);
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.
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
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);
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.
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()) {
617 final UserInfo userInfo = userManager.getUserInfo(profile.getIdentifier());
618 if (userInfo.isManagedProfile()) {
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.
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();
649 * Retrieves the id for the given user's managed profile.
651 * @return the managed profile id or UserHandle.USER_NULL if there is none.
653 public static int getManagedProfileId(UserManager um, int parentUserId) {
654 int[] profileIds = um.getProfileIdsWithDisabled(parentUserId);
655 for (int profileId : profileIds) {
656 if (profileId != parentUserId) {
660 return UserHandle.USER_NULL;
664 * Returns the target user for a Settings activity.
666 * User would be retrieved in this order:
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().
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.
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();
683 String launchedFromPackage = am.getLaunchedFromPackage(activityToken);
684 boolean launchedFromSettingsApp = SETTINGS_PACKAGE_NAME.equals(launchedFromPackage);
686 UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId(
687 am.getLaunchedFromUid(activityToken)));
688 if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) {
690 if (isProfileOf(um, launchedFromUser)) {
691 return launchedFromUser;
694 UserHandle extrasUser = getUserHandleFromBundle(intentExtras);
695 if (extrasUser != null && !extrasUser.equals(currentUser)) {
697 if (launchedFromSettingsApp && isProfileOf(um, extrasUser)) {
701 UserHandle argumentsUser = getUserHandleFromBundle(arguments);
702 if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
704 if (launchedFromSettingsApp && isProfileOf(um, argumentsUser)) {
705 return argumentsUser;
708 } catch (RemoteException e) {
710 Log.v(TAG, "Could not talk to activity manager.", e);
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.
719 private static @Nullable UserHandle getUserHandleFromBundle(Bundle bundle) {
720 if (bundle == null) {
723 final UserHandle user = bundle.getParcelable(EXTRA_USER);
727 final int userId = bundle.getInt(EXTRA_USER_ID, -1);
729 return UserHandle.of(userId);
735 * Returns the target user for a Settings activity.
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.
740 * You should use {@link #getSecureTargetUser(IBinder, UserManager, Bundle, Bundle)} if
743 * @see #getInsecureTargetUser(IBinder, Bundle, Bundle)
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();
750 UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId(
751 am.getLaunchedFromUid(activityToken)));
752 if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) {
753 return launchedFromUser;
755 UserHandle extrasUser = intentExtras != null
756 ? (UserHandle) intentExtras.getParcelable(EXTRA_USER) : null;
757 if (extrasUser != null && !extrasUser.equals(currentUser)) {
760 UserHandle argumentsUser = arguments != null
761 ? (UserHandle) arguments.getParcelable(EXTRA_USER) : null;
762 if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
763 return argumentsUser;
765 } catch (RemoteException e) {
767 Log.v(TAG, "Could not talk to activity manager.", e);
774 * Returns true if the user provided is in the same profiles group as the current user.
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);
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.
787 public static boolean showSimCardTile(Context context) {
788 final TelephonyManager tm =
789 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
791 return tm.getSimCount() > 1;
795 * Returns elapsed time for the given millis, in the following format:
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
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);
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;
816 if (seconds >= SECONDS_PER_HOUR) {
817 hours = seconds / SECONDS_PER_HOUR;
818 seconds -= hours * SECONDS_PER_HOUR;
820 if (seconds >= SECONDS_PER_MINUTE) {
821 minutes = seconds / SECONDS_PER_MINUTE;
822 seconds -= minutes * SECONDS_PER_MINUTE;
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));
834 sb.append(context.getString(R.string.battery_history_seconds, seconds));
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,
844 sb.append(context.getString(R.string.battery_history_minutes_no_seconds, minutes));
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);
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.
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) {
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,
880 return inflater.inflate(resId, parent, false);
884 * Return if we are running low on storage space or not.
886 * @param context The context
887 * @return true if we are running low on storage space
889 public static boolean isLowStorage(Context context) {
890 final StorageManager sm = StorageManager.from(context);
891 return (sm.getStorageBytesUntilLow(context.getFilesDir()) < 0);
895 * Returns a default user icon (as a {@link Bitmap}) for the given user.
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
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));
907 sDarkDefaultUserBitmapCache.put(userId, bitmap);
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;
922 public static ArraySet<String> getHandledDomains(PackageManager pm, String packageName) {
923 List<IntentFilterVerificationInfo> iviList = pm.getIntentFilterVerifications(packageName);
924 List<IntentFilter> filters = pm.getAllIntentFilters(packageName);
926 ArraySet<String> result = new ArraySet<>();
927 if (iviList.size() > 0) {
928 for (IntentFilterVerificationInfo ivi : iviList) {
929 for (String host : ivi.getDomains()) {
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());
946 public static void handleLoadingContainer(View loading, View doneLoading, boolean done,
948 setViewShown(loading, !done, animate);
949 setViewShown(doneLoading, done, animate);
952 private static void setViewShown(final View view, boolean shown, boolean animate) {
954 Animation animation = AnimationUtils.loadAnimation(view.getContext(),
955 shown ? android.R.anim.fade_in : android.R.anim.fade_out);
957 view.setVisibility(View.VISIBLE);
959 animation.setAnimationListener(new AnimationListener() {
961 public void onAnimationStart(Animation animation) {
965 public void onAnimationRepeat(Animation animation) {
969 public void onAnimationEnd(Animation animation) {
970 view.setVisibility(View.INVISIBLE);
974 view.startAnimation(animation);
976 view.clearAnimation();
977 view.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
982 * Returns the application info of the currently installed MDM package.
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) {
991 String mdmPackageName = mdmPackage.getPackageName();
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);
1004 public static boolean isBandwidthControlEnabled() {
1005 final INetworkManagementService netManager = INetworkManagementService.Stub
1006 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1008 return netManager.isBandwidthControlEnabled();
1009 } catch (RemoteException e) {
1015 * Returns an accessible SpannableString.
1016 * @param displayText the text to display
1017 * @param accessibileText the text text-to-speech engines should read
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);
1029 * Returns the user id present in the bundle with {@link Intent#EXTRA_USER_ID} if it
1030 * belongs to the current user.
1032 * @throws SecurityException if the given userId does not belong to the current user group.
1034 public static int getUserIdFromBundle(Context context, Bundle bundle) {
1035 if (bundle == null) {
1036 return getCredentialOwnerUserId(context);
1038 int userId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId());
1039 return enforceSameOwner(context, userId);
1043 * Returns the given user id if it belongs to the current user.
1045 * @throws SecurityException if the given userId does not belong to the current user group.
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)) {
1053 throw new SecurityException("Given user id " + userId + " does not belong to user "
1054 + UserHandle.myUserId());
1058 * Returns the effective credential owner of the calling user.
1060 public static int getCredentialOwnerUserId(Context context) {
1061 return getCredentialOwnerUserId(context, UserHandle.myUserId());
1065 * Returns the user id of the credential owner of the given user id.
1067 public static int getCredentialOwnerUserId(Context context, int userId) {
1068 UserManager um = getUserManager(context);
1069 return um.getCredentialOwnerProfile(userId);
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;
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());
1082 public static String formatDateRange(Context context, long start, long end) {
1083 final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
1085 synchronized (sBuilder) {
1086 sBuilder.setLength(0);
1087 return DateUtils.formatDateRange(context, sFormatter, start, end, flags, null)
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.
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);
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);
1116 } else if (pref instanceof PreferenceGroup) {
1117 checkPrefs((PreferenceGroup) pref, 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);
1133 public static boolean isDeviceProvisioned(Context context) {
1134 return Settings.Global.getInt(context.getContentResolver(),
1135 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
1138 public static boolean startQuietModeDialogIfNecessary(Context context, UserManager um,
1140 if (um.isQuietModeEnabled(UserHandle.of(userId))) {
1141 final Intent intent = UnlaunchableAppActivity.createInQuietModeDialogIntent(userId);
1142 context.startActivity(intent);
1148 public static boolean unlockWorkProfileIfNecessary(Context context, int userId) {
1150 if (!ActivityManager.getService().isUserRunning(userId,
1151 ActivityManager.FLAG_AND_LOCKED)) {
1154 } catch (RemoteException e) {
1157 if (!(new LockPatternUtils(context)).isSecure(userId)) {
1160 return confirmWorkProfileCredentials(context, userId);
1163 public static boolean confirmWorkProfileCredentialsIfNecessary(Context context, int userId) {
1164 KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
1165 if (!km.isDeviceLocked(userId)) {
1168 return confirmWorkProfileCredentials(context, 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);
1183 public static CharSequence getApplicationLabel(Context context, String packageName) {
1185 final ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
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);
1196 public static boolean isPackageDirectBootAware(Context context, String packageName) {
1198 final ApplicationInfo ai = context.getPackageManager().getApplicationInfo(
1200 return ai.isDirectBootAware() || ai.isPartiallyDirectBootAware();
1201 } catch (NameNotFoundException ignored) {
1207 * Returns a context created from the given context for the given user, or null if it fails
1209 public static Context createPackageContextAsUser(Context context, int userId) {
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);
1219 public static FingerprintManager getFingerprintManagerOrNull(Context context) {
1220 if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
1221 return context.getSystemService(FingerprintManager.class);
1227 public static IFingerprintManager getFingerprintManagerWrapperOrNull(Context context) {
1228 FingerprintManager fingerprintManager = getFingerprintManagerOrNull(context);
1229 if (fingerprintManager != null) {
1230 return new FingerprintManagerWrapper(fingerprintManager);
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.
1241 public static void launchIntent(Fragment fragment, Intent intent) {
1243 final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1);
1246 fragment.startActivity(intent);
1248 fragment.getActivity().startActivityAsUser(intent, new UserHandle(userId));
1250 } catch (ActivityNotFoundException e) {
1251 Log.w(TAG, "No activity found for " + intent);
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);
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
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);
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.
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;
1290 * Return the resource id to represent the install status for an app
1293 public static int getInstallationStatus(ApplicationInfo info) {
1294 if ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
1295 return R.string.not_installed;
1297 return info.enabled ? R.string.installed : R.string.disabled;
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.
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,
1309 appOpsManager.setUserRestriction(AppOpsManager.OP_TOAST_WINDOW,
1316 private static boolean isVolumeValid(VolumeInfo volume) {
1317 return (volume != null) && (volume.getType() == VolumeInfo.TYPE_PRIVATE)
1318 && volume.isMountedReadable();