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