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
17package com.android.settings;
18
19import android.annotation.Nullable;
20import android.app.ActivityManager;
21import android.app.ActivityManagerNative;
22import android.app.AlertDialog;
23import android.app.AppGlobals;
24import android.app.Dialog;
25import android.app.Fragment;
26import android.app.IActivityManager;
27import android.app.admin.DevicePolicyManager;
28import android.content.ComponentName;
29import android.content.ContentResolver;
30import android.content.Context;
31import android.content.DialogInterface;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.pm.ApplicationInfo;
35import android.content.pm.IPackageManager;
36import android.content.pm.IntentFilterVerificationInfo;
37import android.content.pm.PackageInfo;
38import android.content.pm.PackageManager;
39import android.content.pm.PackageManager.NameNotFoundException;
40import android.content.pm.ResolveInfo;
41import android.content.pm.Signature;
42import android.content.pm.UserInfo;
43import android.content.res.Resources;
44import android.content.res.Resources.NotFoundException;
45import android.content.res.TypedArray;
46import android.database.Cursor;
47import android.graphics.Bitmap;
48import android.graphics.BitmapFactory;
49import android.graphics.drawable.Drawable;
50import android.hardware.usb.IUsbManager;
51import android.net.ConnectivityManager;
52import android.net.LinkProperties;
53import android.net.Uri;
54import android.os.BatteryManager;
55import android.os.Bundle;
56import android.os.IBinder;
57import android.os.INetworkManagementService;
58import android.os.RemoteException;
59import android.os.ServiceManager;
60import android.os.UserHandle;
61import android.os.UserManager;
62import android.os.storage.StorageManager;
63import android.preference.Preference;
64import android.preference.PreferenceFrameLayout;
65import android.preference.PreferenceGroup;
66import android.provider.ContactsContract.CommonDataKinds;
67import android.provider.ContactsContract.Contacts;
68import android.provider.ContactsContract.Data;
69import android.provider.ContactsContract.Profile;
70import android.provider.ContactsContract.RawContacts;
71import android.service.persistentdata.PersistentDataBlockManager;
72import android.telephony.TelephonyManager;
73import android.text.Spannable;
74import android.text.SpannableString;
75import android.text.TextUtils;
76import android.text.style.TtsSpan;
77import android.util.ArraySet;
78import android.util.Log;
79import android.util.SparseArray;
80import android.view.LayoutInflater;
81import android.view.View;
82import android.view.ViewGroup;
83import android.view.animation.Animation;
84import android.view.animation.Animation.AnimationListener;
85import android.view.animation.AnimationUtils;
86import android.widget.ListView;
87import android.widget.TabWidget;
88
89import com.android.internal.util.UserIcons;
90import com.android.settings.UserAdapter.UserDetails;
91import com.android.settings.dashboard.DashboardTile;
92import com.android.settings.drawable.CircleFramedDrawable;
93import com.android.settingslib.applications.ApplicationsState;
94
95import java.io.IOException;
96import java.io.InputStream;
97import java.net.InetAddress;
98import java.text.NumberFormat;
99import java.util.ArrayList;
100import java.util.Iterator;
101import java.util.List;
102import java.util.Locale;
103
104import static android.content.Intent.EXTRA_USER;
105
106public 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        // consider a visible current link-handling state to be any explicitly designated behavior
1150        boolean hasDomainURLsPreference =
1151                status != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
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