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