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