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