blob: 124c44192d37e0ba066dacc3b0474b264e7d99e6 [file] [log] [blame]
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -08001/**
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
Daniel Nishi422e7c32017-02-09 16:07:22 -080019import static android.content.Intent.EXTRA_USER;
20import static android.content.Intent.EXTRA_USER_ID;
21import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
22import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
23
Alexandra Gherghina7d748c02014-06-27 12:33:42 +010024import android.annotation.Nullable;
Amith Yamasaniae697552011-09-27 11:33:17 -070025import android.app.ActivityManager;
Amith Yamasani9627a8e2012-09-23 12:54:14 -070026import android.app.AlertDialog;
Sudheer Shankabc956302015-04-09 12:19:53 +010027import android.app.AppGlobals;
Amith Yamasani9627a8e2012-09-23 12:54:14 -070028import android.app.Dialog;
Fabrice Di Meglio769630c2014-04-24 14:48:48 -070029import android.app.Fragment;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +010030import android.app.IActivityManager;
Rubin Xu3231afe2016-08-24 10:15:07 +010031import android.app.KeyguardManager;
Sudheer Shankabc956302015-04-09 12:19:53 +010032import android.app.admin.DevicePolicyManager;
Daniel Nishi31027da2017-01-19 14:03:57 -080033import android.content.ActivityNotFoundException;
Fabrice Di Meglio8b2ea392015-01-23 19:03:22 -080034import android.content.ComponentName;
Amith Yamasani8d40fac2012-10-23 15:36:16 -070035import android.content.ContentResolver;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080036import android.content.Context;
Amith Yamasani9627a8e2012-09-23 12:54:14 -070037import android.content.DialogInterface;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080038import android.content.Intent;
Fabrice Di Meglio8b2ea392015-01-23 19:03:22 -080039import android.content.IntentFilter;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080040import android.content.pm.ApplicationInfo;
Sudheer Shankabc956302015-04-09 12:19:53 +010041import android.content.pm.IPackageManager;
Christopher Tatea08a2252015-07-01 16:52:43 -070042import android.content.pm.IntentFilterVerificationInfo;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080043import android.content.pm.PackageManager;
Jason Monk75199542016-05-06 15:09:32 -040044import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070045import android.content.pm.ResolveInfo;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070046import android.content.pm.UserInfo;
Anders Hammar1b2dd9032010-04-08 10:03:50 +020047import android.content.res.Resources;
Jason Monkb5aa73f2015-03-31 12:59:33 -040048import android.content.res.TypedArray;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070049import android.database.Cursor;
Amith Yamasanif34a85d2012-09-17 18:31:45 -070050import android.graphics.Bitmap;
51import android.graphics.BitmapFactory;
Jeff Sharkeyab508072016-10-11 14:25:22 -060052import android.hardware.fingerprint.FingerprintManager;
Amith Yamasanic06d4c42011-02-25 14:35:20 -080053import android.net.ConnectivityManager;
54import android.net.LinkProperties;
Jaewoong Jungc260e6d2016-10-13 14:21:52 -070055import android.net.Network;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070056import android.net.Uri;
Jaewoong Jungc260e6d2016-10-13 14:21:52 -070057import android.net.wifi.WifiManager;
Amith Yamasania4379d62011-07-22 10:34:58 -070058import android.os.BatteryManager;
Anders Hammar1b2dd9032010-04-08 10:03:50 +020059import android.os.Bundle;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +010060import android.os.IBinder;
Jason Monkb45e27b2015-05-20 13:35:43 -040061import android.os.INetworkManagementService;
Jason Monkbe941212016-05-27 12:58:53 -040062import android.os.Looper;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +010063import android.os.RemoteException;
Jason Monkb45e27b2015-05-20 13:35:43 -040064import android.os.ServiceManager;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070065import android.os.UserHandle;
66import android.os.UserManager;
Fabrice Di Megliodff3faa2015-02-27 11:14:11 -080067import android.os.storage.StorageManager;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070068import android.preference.PreferenceFrameLayout;
Amith Yamasani8d40fac2012-10-23 15:36:16 -070069import android.provider.ContactsContract.CommonDataKinds;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070070import android.provider.ContactsContract.Contacts;
Amith Yamasani8d40fac2012-10-23 15:36:16 -070071import android.provider.ContactsContract.Data;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070072import android.provider.ContactsContract.Profile;
Amith Yamasani8d40fac2012-10-23 15:36:16 -070073import android.provider.ContactsContract.RawContacts;
Udam Saini679f7ad2016-03-25 10:47:00 -070074import android.provider.Settings;
Andres Moralesce249fe2014-07-07 16:58:16 -070075import android.service.persistentdata.PersistentDataBlockManager;
Jason Monk39b46742015-09-10 15:52:51 -040076import android.support.v7.preference.Preference;
77import android.support.v7.preference.PreferenceGroup;
Jason Monk91e2f892016-02-23 15:31:09 -050078import android.support.v7.preference.PreferenceManager;
79import android.support.v7.preference.PreferenceScreen;
Amith Yamasani60133dd2010-09-11 14:17:31 -070080import android.telephony.TelephonyManager;
Julia Reynoldsce25af42015-07-08 16:56:31 -040081import android.text.Spannable;
82import android.text.SpannableString;
Anders Hammar1b2dd9032010-04-08 10:03:50 +020083import android.text.TextUtils;
Jason Monkb37e2882016-01-11 14:27:20 -050084import android.text.format.DateUtils;
Julia Reynoldsce25af42015-07-08 16:56:31 -040085import android.text.style.TtsSpan;
Christopher Tatea08a2252015-07-01 16:52:43 -070086import android.util.ArraySet;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +010087import android.util.Log;
Zoltan Szatmary-Bane5814ff2014-12-19 16:27:45 +000088import android.util.SparseArray;
Jason Monkdb4ed192015-12-11 16:48:31 -050089import android.util.TypedValue;
Zoltan Szatmary-Ban3af2e4c2014-12-19 17:17:23 +000090import android.view.LayoutInflater;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070091import android.view.View;
92import android.view.ViewGroup;
Jason Monkb5aa73f2015-03-31 12:59:33 -040093import android.view.animation.Animation;
94import android.view.animation.Animation.AnimationListener;
95import android.view.animation.AnimationUtils;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070096import android.widget.ListView;
97import android.widget.TabWidget;
Fan Zhangcc335d92016-09-29 14:37:14 -070098
Ricky Wai616342b2016-04-13 10:40:22 +010099import com.android.internal.app.UnlaunchableAppActivity;
Ricky Wai72500162016-06-07 16:54:25 +0100100import com.android.internal.util.ArrayUtils;
Alexandra Gherghinabc6e78f2014-09-03 10:22:09 +0100101import com.android.internal.util.UserIcons;
Rubin Xu3231afe2016-08-24 10:15:07 +0100102import com.android.internal.widget.LockPatternUtils;
Daisuke Miyakawaa2633d02010-09-15 20:09:12 -0700103
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700104import java.io.IOException;
105import java.io.InputStream;
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800106import java.net.InetAddress;
Alexandra Gherghina80e1f1b2014-07-31 14:56:33 +0100107import java.util.ArrayList;
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800108import java.util.Iterator;
Daisuke Miyakawaa2633d02010-09-15 20:09:12 -0700109import java.util.List;
Jean Chalard71ad1f42011-05-12 15:06:16 +0900110import java.util.Locale;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800111
Jason Monk27985e12016-01-08 14:13:05 -0500112public final class Utils extends com.android.settingslib.Utils {
113
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100114 private static final String TAG = "Settings";
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100115
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800116 /**
117 * Set the preference's title to the matching activity's label.
118 */
119 public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1;
120
121 /**
Shuhrat Dehkanov96577682012-10-03 12:24:07 +0900122 * The opacity level of a disabled icon.
123 */
124 public static final float DISABLED_ALPHA = 0.4f;
125
126 /**
Dianne Hackborn68f005f2014-06-18 18:29:12 -0700127 * Color spectrum to use to indicate badness. 0 is completely transparent (no data),
128 * 1 is most bad (red), the last value is least bad (green).
129 */
130 public static final int[] BADNESS_COLORS = new int[] {
131 0x00000000, 0xffc43828, 0xffe54918, 0xfff47b00,
132 0xfffabf2c, 0xff679e37, 0xff0a7f42
133 };
134
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100135 private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
136
Elliott Hughes7253df32014-09-02 17:10:14 -0700137 private static final int SECONDS_PER_MINUTE = 60;
138 private static final int SECONDS_PER_HOUR = 60 * 60;
139 private static final int SECONDS_PER_DAY = 24 * 60 * 60;
140
Jason Monkbeb171d2015-05-21 15:24:37 -0400141 public static final String OS_PKG = "os";
142
Zoltan Szatmary-Bane5814ff2014-12-19 16:27:45 +0000143 private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<Bitmap>();
144
Anders Hammar1b2dd9032010-04-08 10:03:50 +0200145 /**
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800146 * Finds a matching activity for a preference's intent. If a matching
147 * activity is not found, it will remove the preference.
Ying Wanga7188322010-01-04 18:45:10 -0800148 *
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800149 * @param context The context.
150 * @param parentPreferenceGroup The preference group that contains the
151 * preference whose intent is being resolved.
152 * @param preferenceKey The key of the preference whose intent is being
153 * resolved.
154 * @param flags 0 or one or more of
155 * {@link #UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY}
156 * .
157 * @return Whether an activity was found. If false, the preference was
158 * removed.
159 */
160 public static boolean updatePreferenceToSpecificActivityOrRemove(Context context,
161 PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags) {
Ying Wanga7188322010-01-04 18:45:10 -0800162
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800163 Preference preference = parentPreferenceGroup.findPreference(preferenceKey);
164 if (preference == null) {
165 return false;
166 }
Ying Wanga7188322010-01-04 18:45:10 -0800167
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800168 Intent intent = preference.getIntent();
169 if (intent != null) {
170 // Find the activity that is in the system image
171 PackageManager pm = context.getPackageManager();
172 List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
173 int listSize = list.size();
174 for (int i = 0; i < listSize; i++) {
175 ResolveInfo resolveInfo = list.get(i);
176 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
177 != 0) {
Ying Wanga7188322010-01-04 18:45:10 -0800178
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800179 // Replace the intent with this specific activity
180 preference.setIntent(new Intent().setClassName(
181 resolveInfo.activityInfo.packageName,
182 resolveInfo.activityInfo.name));
183
184 if ((flags & UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY) != 0) {
185 // Set the preference title to the activity's label
186 preference.setTitle(resolveInfo.loadLabel(pm));
187 }
Ying Wanga7188322010-01-04 18:45:10 -0800188
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800189 return true;
190 }
191 }
192 }
193
194 // Did not find a matching activity, so remove the preference
195 parentPreferenceGroup.removePreference(preference);
Ying Wanga7188322010-01-04 18:45:10 -0800196
Shuhrat Dehkanov7dc567a2012-04-23 01:59:56 +0900197 return false;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800198 }
Ying Wanga7188322010-01-04 18:45:10 -0800199
Anders Hammar1b2dd9032010-04-08 10:03:50 +0200200 /**
Benjamin Franz194300d2016-01-13 12:16:25 +0000201 * Returns the UserManager for a given context
202 *
203 * @throws IllegalStateException if no UserManager could be retrieved.
204 */
205 public static UserManager getUserManager(Context context) {
206 UserManager um = UserManager.get(context);
207 if (um == null) {
208 throw new IllegalStateException("Unable to load UserManager");
209 }
210 return um;
211 }
212
213 /**
Ying Wanga7188322010-01-04 18:45:10 -0800214 * Returns true if Monkey is running.
215 */
216 public static boolean isMonkeyRunning() {
Amith Yamasaniae697552011-09-27 11:33:17 -0700217 return ActivityManager.isUserAMonkey();
Ying Wanga7188322010-01-04 18:45:10 -0800218 }
Amith Yamasani60133dd2010-09-11 14:17:31 -0700219
220 /**
221 * Returns whether the device is voice-capable (meaning, it is also a phone).
222 */
223 public static boolean isVoiceCapable(Context context) {
224 TelephonyManager telephony =
225 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
226 return telephony != null && telephony.isVoiceCapable();
227 }
Amith Yamasani0f85c482011-02-23 17:19:11 -0800228
Robert Greenwalt8af88fb2011-08-31 11:17:47 -0700229 public static boolean isWifiOnly(Context context) {
230 ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
231 Context.CONNECTIVITY_SERVICE);
232 return (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
Amith Yamasani0f85c482011-02-23 17:19:11 -0800233 }
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800234
235 /**
236 * Returns the WIFI IP Addresses, if any, taking into account IPv4 and IPv6 style addresses.
237 * @param context the application context
Lorenzo Colitti769f0692013-08-01 17:30:07 +0900238 * @return the formatted and newline-separated IP addresses, or null if none.
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800239 */
240 public static String getWifiIpAddresses(Context context) {
Jaewoong Jungc260e6d2016-10-13 14:21:52 -0700241 WifiManager wifiManager = context.getSystemService(WifiManager.class);
242 Network currentNetwork = wifiManager.getCurrentNetwork();
243 if (currentNetwork != null) {
244 ConnectivityManager cm = (ConnectivityManager)
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800245 context.getSystemService(Context.CONNECTIVITY_SERVICE);
Jaewoong Jungc260e6d2016-10-13 14:21:52 -0700246 LinkProperties prop = cm.getLinkProperties(currentNetwork);
247 return formatIpAddresses(prop);
248 }
249 return null;
Amith Yamasani6822b742011-10-17 16:41:00 -0700250 }
251
252 /**
253 * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
254 * addresses.
Lorenzo Colitti769f0692013-08-01 17:30:07 +0900255 * @return the formatted and newline-separated IP addresses, or null if none.
Amith Yamasani6822b742011-10-17 16:41:00 -0700256 */
Lorenzo Colitti6eb6a902013-11-08 03:53:29 +0900257 public static String getDefaultIpAddresses(ConnectivityManager cm) {
Amith Yamasani6822b742011-10-17 16:41:00 -0700258 LinkProperties prop = cm.getActiveLinkProperties();
259 return formatIpAddresses(prop);
260 }
261
262 private static String formatIpAddresses(LinkProperties prop) {
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800263 if (prop == null) return null;
Lorenzo Colitti769f0692013-08-01 17:30:07 +0900264 Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800265 // If there are no entries, return null
266 if (!iter.hasNext()) return null;
267 // Concatenate all available addresses, comma separated
268 String addresses = "";
269 while (iter.hasNext()) {
270 addresses += iter.next().getHostAddress();
Lorenzo Colitti769f0692013-08-01 17:30:07 +0900271 if (iter.hasNext()) addresses += "\n";
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800272 }
273 return addresses;
274 }
Jean Chalard71ad1f42011-05-12 15:06:16 +0900275
276 public static Locale createLocaleFromString(String localeStr) {
277 // TODO: is there a better way to actually construct a locale that will match?
278 // The main problem is, on top of Java specs, locale.toString() and
279 // new Locale(locale.toString()).toString() do not return equal() strings in
280 // many cases, because the constructor takes the only string as the language
281 // code. So : new Locale("en", "US").toString() => "en_US"
282 // And : new Locale("en_US").toString() => "en_us"
283 if (null == localeStr)
284 return Locale.getDefault();
285 String[] brokenDownLocale = localeStr.split("_", 3);
286 // split may not return a 0-length array.
287 if (1 == brokenDownLocale.length) {
288 return new Locale(brokenDownLocale[0]);
289 } else if (2 == brokenDownLocale.length) {
290 return new Locale(brokenDownLocale[0], brokenDownLocale[1]);
291 } else {
292 return new Locale(brokenDownLocale[0], brokenDownLocale[1], brokenDownLocale[2]);
293 }
294 }
Amith Yamasania4379d62011-07-22 10:34:58 -0700295
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900296 public static boolean isBatteryPresent(Intent batteryChangedIntent) {
297 return batteryChangedIntent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
298 }
299
Amith Yamasania4379d62011-07-22 10:34:58 -0700300 public static String getBatteryPercentage(Intent batteryChangedIntent) {
Elliott Hughes7253df32014-09-02 17:10:14 -0700301 return formatPercentage(getBatteryLevel(batteryChangedIntent));
Dianne Hackborn525f2bd2014-04-29 11:24:06 -0700302 }
303
Jeff Sharkey97d07fa2012-11-30 12:36:53 -0800304 public static void forcePrepareCustomPreferencesList(
305 ViewGroup parent, View child, ListView list, boolean ignoreSidePadding) {
306 list.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
307 list.setClipToPadding(false);
308 prepareCustomPreferencesList(parent, child, list, ignoreSidePadding);
309 }
310
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700311 /**
312 * Prepare a custom preferences layout, moving padding to {@link ListView}
313 * when outside scrollbars are requested. Usually used to display
314 * {@link ListView} and {@link TabWidget} with correct padding.
315 */
Jeff Sharkey5d706792011-09-08 18:57:17 -0700316 public static void prepareCustomPreferencesList(
Jeff Sharkey97d07fa2012-11-30 12:36:53 -0800317 ViewGroup parent, View child, View list, boolean ignoreSidePadding) {
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700318 final boolean movePadding = list.getScrollBarStyle() == View.SCROLLBARS_OUTSIDE_OVERLAY;
Fabrice Di Meglio97a18c82014-07-18 19:12:36 -0700319 if (movePadding) {
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700320 final Resources res = list.getResources();
Amith Yamasani56f51a82013-08-05 10:07:23 -0700321 final int paddingSide = res.getDimensionPixelSize(R.dimen.settings_side_margin);
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700322 final int paddingBottom = res.getDimensionPixelSize(
323 com.android.internal.R.dimen.preference_fragment_padding_bottom);
Jeff Sharkey5d706792011-09-08 18:57:17 -0700324
Fabrice Di Meglio97a18c82014-07-18 19:12:36 -0700325 if (parent instanceof PreferenceFrameLayout) {
326 ((PreferenceFrameLayout.LayoutParams) child.getLayoutParams()).removeBorders = true;
327
328 final int effectivePaddingSide = ignoreSidePadding ? 0 : paddingSide;
329 list.setPaddingRelative(effectivePaddingSide, 0, effectivePaddingSide, paddingBottom);
330 } else {
331 list.setPaddingRelative(paddingSide, 0, paddingSide, paddingBottom);
332 }
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700333 }
334 }
Jeff Sharkeya83a24f2011-09-16 01:52:39 -0700335
Fabrice Di Meglio0f4a7792014-07-28 18:25:14 -0700336 public static void forceCustomPadding(View view, boolean additive) {
Fabrice Di Meglio38ba9a22014-07-18 19:58:50 -0700337 final Resources res = view.getResources();
338 final int paddingSide = res.getDimensionPixelSize(R.dimen.settings_side_margin);
Fabrice Di Meglio0f4a7792014-07-28 18:25:14 -0700339
340 final int paddingStart = paddingSide + (additive ? view.getPaddingStart() : 0);
341 final int paddingEnd = paddingSide + (additive ? view.getPaddingEnd() : 0);
Fabrice Di Meglio38ba9a22014-07-18 19:58:50 -0700342 final int paddingBottom = res.getDimensionPixelSize(
343 com.android.internal.R.dimen.preference_fragment_padding_bottom);
344
Fabrice Di Meglio0f4a7792014-07-28 18:25:14 -0700345 view.setPaddingRelative(paddingStart, 0, paddingEnd, paddingBottom);
Fabrice Di Meglio38ba9a22014-07-18 19:58:50 -0700346 }
347
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700348 /* Used by UserSettings as well. Call this on a non-ui thread. */
Sudheer Shanka71caf692016-04-14 16:50:14 -0700349 public static void copyMeProfilePhoto(Context context, UserInfo user) {
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700350 Uri contactUri = Profile.CONTENT_URI;
351
Sudheer Shanka71caf692016-04-14 16:50:14 -0700352 int userId = user != null ? user.id : UserHandle.myUserId();
353
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700354 InputStream avatarDataStream = Contacts.openContactPhotoInputStream(
355 context.getContentResolver(),
356 contactUri, true);
357 // If there's no profile photo, assign a default avatar
358 if (avatarDataStream == null) {
Sudheer Shanka71caf692016-04-14 16:50:14 -0700359 assignDefaultPhoto(context, userId);
Sudheer Shanka39975a62016-04-15 17:20:54 -0700360 return;
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700361 }
Sudheer Shanka39975a62016-04-15 17:20:54 -0700362
363 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
364 Bitmap icon = BitmapFactory.decodeStream(avatarDataStream);
365 um.setUserIcon(userId, icon);
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700366 try {
Amith Yamasanif34a85d2012-09-17 18:31:45 -0700367 avatarDataStream.close();
368 } catch (IOException ioe) { }
Sudheer Shanka71caf692016-04-14 16:50:14 -0700369 }
370
jackqdyulei8164c642016-12-27 10:15:27 -0800371 /**
372 * Assign the default photo to user with {@paramref userId}
373 * @param context used to get the {@link UserManager}
374 * @param userId used to get the icon bitmap
375 * @return true if assign photo successfully, false if failed
376 */
377 public static boolean assignDefaultPhoto(Context context, int userId) {
378 if (context == null) {
379 return false;
380 }
Sudheer Shanka71caf692016-04-14 16:50:14 -0700381 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
382 Bitmap bitmap = getDefaultUserIconAsBitmap(userId);
383 um.setUserIcon(userId, bitmap);
jackqdyulei8164c642016-12-27 10:15:27 -0800384
385 return true;
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700386 }
387
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700388 public static String getMeProfileName(Context context, boolean full) {
389 if (full) {
390 return getProfileDisplayName(context);
391 } else {
392 return getShorterNameIfPossible(context);
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700393 }
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700394 }
395
396 private static String getShorterNameIfPossible(Context context) {
397 final String given = getLocalProfileGivenName(context);
398 return !TextUtils.isEmpty(given) ? given : getProfileDisplayName(context);
399 }
400
401 private static String getLocalProfileGivenName(Context context) {
402 final ContentResolver cr = context.getContentResolver();
403
404 // Find the raw contact ID for the local ME profile raw contact.
405 final long localRowProfileId;
406 final Cursor localRawProfile = cr.query(
407 Profile.CONTENT_RAW_CONTACTS_URI,
408 new String[] {RawContacts._ID},
409 RawContacts.ACCOUNT_TYPE + " IS NULL AND " +
410 RawContacts.ACCOUNT_NAME + " IS NULL",
411 null, null);
412 if (localRawProfile == null) return null;
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700413
414 try {
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700415 if (!localRawProfile.moveToFirst()) {
416 return null;
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700417 }
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700418 localRowProfileId = localRawProfile.getLong(0);
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700419 } finally {
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700420 localRawProfile.close();
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700421 }
Amith Yamasani8d40fac2012-10-23 15:36:16 -0700422
423 // Find the structured name for the raw contact.
424 final Cursor structuredName = cr.query(
425 Profile.CONTENT_URI.buildUpon().appendPath(Contacts.Data.CONTENT_DIRECTORY).build(),
426 new String[] {CommonDataKinds.StructuredName.GIVEN_NAME,
427 CommonDataKinds.StructuredName.FAMILY_NAME},
428 Data.RAW_CONTACT_ID + "=" + localRowProfileId,
429 null, null);
430 if (structuredName == null) return null;
431
432 try {
433 if (!structuredName.moveToFirst()) {
434 return null;
435 }
436 String partialName = structuredName.getString(0);
437 if (TextUtils.isEmpty(partialName)) {
438 partialName = structuredName.getString(1);
439 }
440 return partialName;
441 } finally {
442 structuredName.close();
443 }
444 }
445
446 private static final String getProfileDisplayName(Context context) {
447 final ContentResolver cr = context.getContentResolver();
448 final Cursor profile = cr.query(Profile.CONTENT_URI,
449 new String[] {Profile.DISPLAY_NAME}, null, null, null);
450 if (profile == null) return null;
451
452 try {
453 if (!profile.moveToFirst()) {
454 return null;
455 }
456 return profile.getString(0);
457 } finally {
458 profile.close();
459 }
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700460 }
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700461
462 /** Not global warming, it's global change warning. */
463 public static Dialog buildGlobalChangeWarningDialog(final Context context, int titleResId,
464 final Runnable positiveAction) {
465 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
466 builder.setTitle(titleResId);
467 builder.setMessage(R.string.global_change_warning);
468 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
469 @Override
470 public void onClick(DialogInterface dialog, int which) {
471 positiveAction.run();
472 }
473 });
474 builder.setNegativeButton(android.R.string.cancel, null);
475
476 return builder.create();
477 }
478
479 public static boolean hasMultipleUsers(Context context) {
480 return ((UserManager) context.getSystemService(Context.USER_SERVICE))
481 .getUsers().size() > 1;
482 }
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700483
484 /**
485 * Start a new instance of the activity, showing only the given fragment.
486 * When launched in this mode, the given preference fragment will be instantiated and fill the
487 * entire activity.
488 *
489 * @param context The context.
Fabrice Di Meglio93b77b72014-05-17 00:01:07 +0000490 * @param fragmentName The name of the fragment to display.
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700491 * @param args Optional arguments to supply to the fragment.
Fabrice Di Meglioa9e77992014-06-09 12:52:24 -0700492 * @param resultTo Option fragment that should receive the result of the activity launch.
493 * @param resultRequestCode If resultTo is non-null, this is the request code in which
494 * to report the result.
495 * @param titleResId resource id for the String to display for the title of this set
496 * of preferences.
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700497 * @param title String to display for the title of this set of preferences.
Fan Zhangc6ca3142017-02-14 15:02:35 -0800498 * @param metricsCategory The current metricsCategory for logging source when fragment starts
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700499 */
Fabrice Di Meglio93b77b72014-05-17 00:01:07 +0000500 public static void startWithFragment(Context context, String fragmentName, Bundle args,
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100501 Fragment resultTo, int resultRequestCode, int titleResId,
Fan Zhangc6ca3142017-02-14 15:02:35 -0800502 CharSequence title, int metricsCategory) {
Fabrice Di Meglio0d643fd2014-06-16 20:11:27 -0700503 startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
Fan Zhangc6ca3142017-02-14 15:02:35 -0800504 null /* titleResPackageName */, titleResId, title, false /* not a shortcut */,
505 metricsCategory);
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100506 }
507
508 /**
509 * Start a new instance of the activity, showing only the given fragment.
510 * When launched in this mode, the given preference fragment will be instantiated and fill the
511 * entire activity.
512 *
513 * @param context The context.
514 * @param fragmentName The name of the fragment to display.
515 * @param args Optional arguments to supply to the fragment.
516 * @param resultTo Option fragment that should receive the result of the activity launch.
517 * @param resultRequestCode If resultTo is non-null, this is the request code in which
518 * to report the result.
519 * @param titleResPackageName Optional package name for the resource id of the title.
520 * @param titleResId resource id for the String to display for the title of this set
521 * of preferences.
522 * @param title String to display for the title of this set of preferences.
Fan Zhangc6ca3142017-02-14 15:02:35 -0800523 * @param metricsCategory The current metricsCategory for logging source when fragment starts
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100524 */
525 public static void startWithFragment(Context context, String fragmentName, Bundle args,
526 Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
Fan Zhangc6ca3142017-02-14 15:02:35 -0800527 CharSequence title, int metricsCategory) {
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100528 startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
Fan Zhangc6ca3142017-02-14 15:02:35 -0800529 titleResPackageName, titleResId, title, false /* not a shortcut */,
530 metricsCategory);
Fabrice Di Meglio0d643fd2014-06-16 20:11:27 -0700531 }
532
533 public static void startWithFragment(Context context, String fragmentName, Bundle args,
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100534 Fragment resultTo, int resultRequestCode, int titleResId,
Fan Zhangc6ca3142017-02-14 15:02:35 -0800535 CharSequence title, boolean isShortcut, int metricsCategory) {
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100536 Intent intent = onBuildStartFragmentIntent(context, fragmentName, args,
Fan Zhangc6ca3142017-02-14 15:02:35 -0800537 null /* titleResPackageName */, titleResId, title, isShortcut, metricsCategory);
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100538 if (resultTo == null) {
539 context.startActivity(intent);
540 } else {
Doris Ling36e953f2016-08-12 15:25:00 -0700541 resultTo.getActivity().startActivityForResult(intent, resultRequestCode);
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100542 }
543 }
544
545 public static void startWithFragment(Context context, String fragmentName, Bundle args,
546 Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
Fan Zhangc6ca3142017-02-14 15:02:35 -0800547 CharSequence title, boolean isShortcut, int metricsCategory) {
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100548 Intent intent = onBuildStartFragmentIntent(context, fragmentName, args, titleResPackageName,
Fan Zhangc6ca3142017-02-14 15:02:35 -0800549 titleResId, title, isShortcut, metricsCategory);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700550 if (resultTo == null) {
551 context.startActivity(intent);
552 } else {
553 resultTo.startActivityForResult(intent, resultRequestCode);
554 }
555 }
556
Zoltan Szatmary-Ban7a2ccf22014-09-18 10:26:11 +0100557 public static void startWithFragmentAsUser(Context context, String fragmentName, Bundle args,
Fan Zhangc6ca3142017-02-14 15:02:35 -0800558 int titleResId, CharSequence title, boolean isShortcut, int metricsCategory,
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100559 UserHandle userHandle) {
Victor Chang16da2aa2016-03-11 19:44:56 +0000560 // workaround to avoid crash in b/17523189
561 if (userHandle.getIdentifier() == UserHandle.myUserId()) {
Fan Zhangc6ca3142017-02-14 15:02:35 -0800562 startWithFragment(context, fragmentName, args, null, 0, titleResId, title, isShortcut,
563 metricsCategory);
Victor Chang16da2aa2016-03-11 19:44:56 +0000564 } else {
565 Intent intent = onBuildStartFragmentIntent(context, fragmentName, args,
Fan Zhangc6ca3142017-02-14 15:02:35 -0800566 null /* titleResPackageName */, titleResId, title, isShortcut, metricsCategory);
Victor Chang16da2aa2016-03-11 19:44:56 +0000567 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
568 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
569 context.startActivityAsUser(intent, userHandle);
570 }
Zoltan Szatmary-Ban7a2ccf22014-09-18 10:26:11 +0100571 }
572
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700573 /**
574 * Build an Intent to launch a new activity showing the selected fragment.
575 * The implementation constructs an Intent that re-launches the current activity with the
576 * appropriate arguments to display the fragment.
577 *
Fabrice Di Meglio0d643fd2014-06-16 20:11:27 -0700578 *
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700579 * @param context The Context.
Fabrice Di Meglio93b77b72014-05-17 00:01:07 +0000580 * @param fragmentName The name of the fragment to display.
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700581 * @param args Optional arguments to supply to the fragment.
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100582 * @param titleResPackageName Optional package name for the resource id of the title.
Fabrice Di Meglioa9e77992014-06-09 12:52:24 -0700583 * @param titleResId Optional title resource id to show for this item.
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700584 * @param title Optional title to show for this item.
Fabrice Di Meglio0d643fd2014-06-16 20:11:27 -0700585 * @param isShortcut tell if this is a Launcher Shortcut or not
Fan Zhangc6ca3142017-02-14 15:02:35 -0800586 * @param sourceMetricsCategory The context (source) from which an action is performed
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700587 * @return Returns an Intent that can be launched to display the given
588 * fragment.
589 */
Fabrice Di Meglio93b77b72014-05-17 00:01:07 +0000590 public static Intent onBuildStartFragmentIntent(Context context, String fragmentName,
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100591 Bundle args, String titleResPackageName, int titleResId, CharSequence title,
Fan Zhangc6ca3142017-02-14 15:02:35 -0800592 boolean isShortcut, int sourceMetricsCategory) {
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700593 Intent intent = new Intent(Intent.ACTION_MAIN);
594 intent.setClass(context, SubSettings.class);
Fabrice Di Meglio93b77b72014-05-17 00:01:07 +0000595 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, fragmentName);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700596 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
Alexandra Gherghina62464b82014-08-11 12:40:13 +0100597 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,
598 titleResPackageName);
Fabrice Di Meglioa9e77992014-06-09 12:52:24 -0700599 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleResId);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700600 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title);
Fabrice Di Meglio0d643fd2014-06-16 20:11:27 -0700601 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut);
Fan Zhangc6ca3142017-02-14 15:02:35 -0800602 intent.putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY, sourceMetricsCategory);
Fabrice Di Meglio769630c2014-04-24 14:48:48 -0700603 return intent;
604 }
Alexandra Gherghina3939cd72014-06-04 10:02:55 +0100605
606 /**
607 * Returns the managed profile of the current user or null if none found.
608 */
609 public static UserHandle getManagedProfile(UserManager userManager) {
610 List<UserHandle> userProfiles = userManager.getUserProfiles();
611 final int count = userProfiles.size();
612 for (int i = 0; i < count; i++) {
613 final UserHandle profile = userProfiles.get(i);
614 if (profile.getIdentifier() == userManager.getUserHandle()) {
615 continue;
616 }
617 final UserInfo userInfo = userManager.getUserInfo(profile.getIdentifier());
618 if (userInfo.isManagedProfile()) {
619 return profile;
620 }
621 }
622 return null;
623 }
624
625 /**
Clara Bayarri462cce12016-02-18 12:09:21 +0000626 * Retrieves the id for the given user's managed profile.
627 *
628 * @return the managed profile id or UserHandle.USER_NULL if there is none.
629 */
630 public static int getManagedProfileId(UserManager um, int parentUserId) {
Fyodor Kupolov4a4af5a2016-04-07 16:46:18 -0700631 int[] profileIds = um.getProfileIdsWithDisabled(parentUserId);
632 for (int profileId : profileIds) {
633 if (profileId != parentUserId) {
634 return profileId;
Clara Bayarri462cce12016-02-18 12:09:21 +0000635 }
636 }
637 return UserHandle.USER_NULL;
638 }
639
640 /**
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100641 * Returns the target user for a Settings activity.
Tony Mak8f41b9b2016-11-23 11:36:18 +0000642 * <p>
643 * User would be retrieved in this order:
644 * <ul>
645 * <li> If this activity is launched from other user, return that user id.
646 * <li> If this is launched from the Settings app in same user, return the user contained as an
647 * extra in the arguments or intent extras.
648 * <li> Otherwise, return UserHandle.myUserId().
649 * </ul>
650 * <p>
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100651 * Note: This is secure in the sense that it only returns a target user different to the current
652 * one if the app launching this activity is the Settings app itself, running in the same user
653 * or in one that is in the same profile group, or if the user id is provided by the system.
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100654 */
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100655 public static UserHandle getSecureTargetUser(IBinder activityToken,
Tony Mak8f41b9b2016-11-23 11:36:18 +0000656 UserManager um, @Nullable Bundle arguments, @Nullable Bundle intentExtras) {
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100657 UserHandle currentUser = new UserHandle(UserHandle.myUserId());
Sudheer Shankaacb1a612016-11-10 15:30:14 -0800658 IActivityManager am = ActivityManager.getService();
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100659 try {
660 String launchedFromPackage = am.getLaunchedFromPackage(activityToken);
661 boolean launchedFromSettingsApp = SETTINGS_PACKAGE_NAME.equals(launchedFromPackage);
662
663 UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId(
664 am.getLaunchedFromUid(activityToken)));
665 if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) {
666 // Check it's secure
667 if (isProfileOf(um, launchedFromUser)) {
668 return launchedFromUser;
669 }
670 }
Tony Mak8f41b9b2016-11-23 11:36:18 +0000671 UserHandle extrasUser = getUserHandleFromBundle(intentExtras);
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100672 if (extrasUser != null && !extrasUser.equals(currentUser)) {
673 // Check it's secure
674 if (launchedFromSettingsApp && isProfileOf(um, extrasUser)) {
675 return extrasUser;
676 }
677 }
Tony Mak8f41b9b2016-11-23 11:36:18 +0000678 UserHandle argumentsUser = getUserHandleFromBundle(arguments);
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100679 if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
680 // Check it's secure
681 if (launchedFromSettingsApp && isProfileOf(um, argumentsUser)) {
682 return argumentsUser;
683 }
684 }
685 } catch (RemoteException e) {
686 // Should not happen
687 Log.v(TAG, "Could not talk to activity manager.", e);
688 }
689 return currentUser;
Tony Mak8f41b9b2016-11-23 11:36:18 +0000690 }
691
692 /**
693 * Lookup both {@link Intent#EXTRA_USER} and {@link Intent#EXTRA_USER_ID} in the bundle
694 * and return the {@link UserHandle} object. Return {@code null} if nothing is found.
695 */
696 private static @Nullable UserHandle getUserHandleFromBundle(Bundle bundle) {
697 if (bundle == null) {
698 return null;
699 }
700 final UserHandle user = bundle.getParcelable(EXTRA_USER);
701 if (user != null) {
702 return user;
703 }
704 final int userId = bundle.getInt(EXTRA_USER_ID, -1);
705 if (userId != -1) {
706 return UserHandle.of(userId);
707 }
708 return null;
709 }
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100710
711 /**
712 * Returns the target user for a Settings activity.
713 *
714 * The target user can be either the current user, the user that launched this activity or
715 * the user contained as an extra in the arguments or intent extras.
716 *
717 * You should use {@link #getSecureTargetUser(IBinder, UserManager, Bundle, Bundle)} if
718 * possible.
719 *
720 * @see #getInsecureTargetUser(IBinder, Bundle, Bundle)
721 */
722 public static UserHandle getInsecureTargetUser(IBinder activityToken, @Nullable Bundle arguments,
723 @Nullable Bundle intentExtras) {
724 UserHandle currentUser = new UserHandle(UserHandle.myUserId());
Sudheer Shankaacb1a612016-11-10 15:30:14 -0800725 IActivityManager am = ActivityManager.getService();
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100726 try {
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100727 UserHandle launchedFromUser = new UserHandle(UserHandle.getUserId(
728 am.getLaunchedFromUid(activityToken)));
729 if (launchedFromUser != null && !launchedFromUser.equals(currentUser)) {
730 return launchedFromUser;
731 }
732 UserHandle extrasUser = intentExtras != null
733 ? (UserHandle) intentExtras.getParcelable(EXTRA_USER) : null;
734 if (extrasUser != null && !extrasUser.equals(currentUser)) {
735 return extrasUser;
736 }
737 UserHandle argumentsUser = arguments != null
738 ? (UserHandle) arguments.getParcelable(EXTRA_USER) : null;
739 if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
740 return argumentsUser;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100741 }
742 } catch (RemoteException e) {
743 // Should not happen
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100744 Log.v(TAG, "Could not talk to activity manager.", e);
745 return null;
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100746 }
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100747 return currentUser;
748 }
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100749
Alexandra Gherghina7d748c02014-06-27 12:33:42 +0100750 /**
751 * Returns true if the user provided is in the same profiles group as the current user.
752 */
753 private static boolean isProfileOf(UserManager um, UserHandle otherUser) {
754 if (um == null || otherUser == null) return false;
755 return (UserHandle.myUserId() == otherUser.getIdentifier())
756 || um.getUserProfiles().contains(otherUser);
Alexandra Gherghina1eb3f312014-06-10 14:01:10 +0100757 }
Amith Yamasani51c6dac2014-07-02 00:06:37 +0530758
Andres Moralesce249fe2014-07-07 16:58:16 -0700759
760 /**
761 * Returns whether or not this device is able to be OEM unlocked.
762 */
763 static boolean isOemUnlockEnabled(Context context) {
764 PersistentDataBlockManager manager =(PersistentDataBlockManager)
765 context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
766 return manager.getOemUnlockEnabled();
767 }
768
769 /**
770 * Allows enabling or disabling OEM unlock on this device. OEM unlocked
771 * devices allow users to flash other OSes to them.
772 */
773 static void setOemUnlockEnabled(Context context, boolean enabled) {
Steven Ng3fe14032016-04-27 15:28:40 +0100774 try {
775 PersistentDataBlockManager manager = (PersistentDataBlockManager)
776 context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
777 manager.setOemUnlockEnabled(enabled);
778 } catch (SecurityException e) {
779 Log.e(TAG, "Fail to set oem unlock.", e);
780 }
Andres Moralesce249fe2014-07-07 16:58:16 -0700781 }
Alexandra Gherghina95b86a52014-07-24 19:13:25 +0100782
783 /**
Fabrice Di Meglio22a2a492014-08-08 12:27:57 -0700784 * Return whether or not the user should have a SIM Cards option in Settings.
785 * TODO: Change back to returning true if count is greater than one after testing.
786 * TODO: See bug 16533525.
787 */
788 public static boolean showSimCardTile(Context context) {
789 final TelephonyManager tm =
790 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
791
PauloftheWestdbd03822014-12-04 11:56:35 -0800792 return tm.getSimCount() > 1;
Fabrice Di Meglio22a2a492014-08-08 12:27:57 -0700793 }
Dan Sandlerb58b5122014-09-02 18:31:49 +0200794
795 /**
Elliott Hughes7253df32014-09-02 17:10:14 -0700796 * Returns elapsed time for the given millis, in the following format:
797 * 2d 5h 40m 29s
798 * @param context the application context
799 * @param millis the elapsed time in milli seconds
800 * @param withSeconds include seconds?
801 * @return the formatted elapsed time
802 */
803 public static String formatElapsedTime(Context context, double millis, boolean withSeconds) {
804 StringBuilder sb = new StringBuilder();
805 int seconds = (int) Math.floor(millis / 1000);
806 if (!withSeconds) {
807 // Round up.
808 seconds += 30;
809 }
810
811 int days = 0, hours = 0, minutes = 0;
812 if (seconds >= SECONDS_PER_DAY) {
813 days = seconds / SECONDS_PER_DAY;
814 seconds -= days * SECONDS_PER_DAY;
815 }
816 if (seconds >= SECONDS_PER_HOUR) {
817 hours = seconds / SECONDS_PER_HOUR;
818 seconds -= hours * SECONDS_PER_HOUR;
819 }
820 if (seconds >= SECONDS_PER_MINUTE) {
821 minutes = seconds / SECONDS_PER_MINUTE;
822 seconds -= minutes * SECONDS_PER_MINUTE;
823 }
824 if (withSeconds) {
825 if (days > 0) {
826 sb.append(context.getString(R.string.battery_history_days,
827 days, hours, minutes, seconds));
828 } else if (hours > 0) {
829 sb.append(context.getString(R.string.battery_history_hours,
830 hours, minutes, seconds));
831 } else if (minutes > 0) {
832 sb.append(context.getString(R.string.battery_history_minutes, minutes, seconds));
833 } else {
834 sb.append(context.getString(R.string.battery_history_seconds, seconds));
835 }
836 } else {
837 if (days > 0) {
838 sb.append(context.getString(R.string.battery_history_days_no_seconds,
839 days, hours, minutes));
840 } else if (hours > 0) {
841 sb.append(context.getString(R.string.battery_history_hours_no_seconds,
842 hours, minutes));
843 } else {
844 sb.append(context.getString(R.string.battery_history_minutes_no_seconds, minutes));
845 }
846 }
847 return sb.toString();
848 }
PauloftheWest0a0daca2014-11-06 15:02:58 -0800849
850 /**
Amith Yamasani45f86232014-11-19 17:12:46 -0800851 * Queries for the UserInfo of a user. Returns null if the user doesn't exist (was removed).
852 * @param userManager Instance of UserManager
853 * @param checkUser The user to check the existence of.
854 * @return UserInfo of the user or null for non-existent user.
855 */
856 public static UserInfo getExistingUser(UserManager userManager, UserHandle checkUser) {
857 final List<UserInfo> users = userManager.getUsers(true /* excludeDying */);
858 final int checkUserId = checkUser.getIdentifier();
859 for (UserInfo user : users) {
860 if (user.id == checkUserId) {
861 return user;
862 }
863 }
864 return null;
865 }
866
Zoltan Szatmary-Ban3af2e4c2014-12-19 17:17:23 +0000867 public static View inflateCategoryHeader(LayoutInflater inflater, ViewGroup parent) {
868 final TypedArray a = inflater.getContext().obtainStyledAttributes(null,
869 com.android.internal.R.styleable.Preference,
870 com.android.internal.R.attr.preferenceCategoryStyle, 0);
871 final int resId = a.getResourceId(com.android.internal.R.styleable.Preference_layout,
872 0);
873 a.recycle();
874 return inflater.inflate(resId, parent, false);
875 }
876
Fabrice Di Megliodff3faa2015-02-27 11:14:11 -0800877 /**
878 * Return if we are running low on storage space or not.
879 *
880 * @param context The context
881 * @return true if we are running low on storage space
882 */
883 public static boolean isLowStorage(Context context) {
884 final StorageManager sm = StorageManager.from(context);
885 return (sm.getStorageBytesUntilLow(context.getFilesDir()) < 0);
886 }
887
Zoltan Szatmary-Bane5814ff2014-12-19 16:27:45 +0000888 /**
889 * Returns a default user icon (as a {@link Bitmap}) for the given user.
890 *
891 * Note that for guest users, you should pass in {@code UserHandle.USER_NULL}.
892 * @param userId the user id or {@code UserHandle.USER_NULL} for a non-user specific icon
893 */
894 public static Bitmap getDefaultUserIconAsBitmap(int userId) {
895 Bitmap bitmap = null;
896 // Try finding the corresponding bitmap in the dark bitmap cache
897 bitmap = sDarkDefaultUserBitmapCache.get(userId);
898 if (bitmap == null) {
899 bitmap = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(userId, false));
900 // Save it to cache
901 sDarkDefaultUserBitmapCache.put(userId, bitmap);
902 }
903 return bitmap;
904 }
Fabrice Di Meglio8b2ea392015-01-23 19:03:22 -0800905
Fabrice Di Meglio8b2ea392015-01-23 19:03:22 -0800906 public static boolean hasPreferredActivities(PackageManager pm, String packageName) {
907 // Get list of preferred activities
908 List<ComponentName> prefActList = new ArrayList<>();
909 // Intent list cannot be null. so pass empty list
910 List<IntentFilter> intentList = new ArrayList<>();
911 pm.getPreferredActivities(intentList, prefActList, packageName);
912 Log.d(TAG, "Have " + prefActList.size() + " number of activities in preferred list");
913 return prefActList.size() > 0;
914 }
915
Christopher Tatea08a2252015-07-01 16:52:43 -0700916 public static ArraySet<String> getHandledDomains(PackageManager pm, String packageName) {
917 List<IntentFilterVerificationInfo> iviList = pm.getIntentFilterVerifications(packageName);
918 List<IntentFilter> filters = pm.getAllIntentFilters(packageName);
919
920 ArraySet<String> result = new ArraySet<>();
921 if (iviList.size() > 0) {
922 for (IntentFilterVerificationInfo ivi : iviList) {
923 for (String host : ivi.getDomains()) {
924 result.add(host);
925 }
926 }
927 }
928 if (filters != null && filters.size() > 0) {
929 for (IntentFilter filter : filters) {
Christopher Tateddaa1422015-07-16 16:00:49 -0700930 if (filter.hasCategory(Intent.CATEGORY_BROWSABLE)
931 && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
932 filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) {
Christopher Tatea08a2252015-07-01 16:52:43 -0700933 result.addAll(filter.getHostsList());
934 }
935 }
936 }
937 return result;
938 }
939
Jason Monkb5aa73f2015-03-31 12:59:33 -0400940 public static void handleLoadingContainer(View loading, View doneLoading, boolean done,
941 boolean animate) {
942 setViewShown(loading, !done, animate);
943 setViewShown(doneLoading, done, animate);
944 }
945
946 private static void setViewShown(final View view, boolean shown, boolean animate) {
947 if (animate) {
948 Animation animation = AnimationUtils.loadAnimation(view.getContext(),
949 shown ? android.R.anim.fade_in : android.R.anim.fade_out);
950 if (shown) {
951 view.setVisibility(View.VISIBLE);
952 } else {
953 animation.setAnimationListener(new AnimationListener() {
954 @Override
955 public void onAnimationStart(Animation animation) {
956 }
957
958 @Override
959 public void onAnimationRepeat(Animation animation) {
960 }
961
962 @Override
963 public void onAnimationEnd(Animation animation) {
964 view.setVisibility(View.INVISIBLE);
965 }
966 });
967 }
968 view.startAnimation(animation);
969 } else {
970 view.clearAnimation();
971 view.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
972 }
973 }
Sudheer Shankabc956302015-04-09 12:19:53 +0100974
975 /**
976 * Returns the application info of the currently installed MDM package.
977 */
978 public static ApplicationInfo getAdminApplicationInfo(Context context, int profileId) {
979 DevicePolicyManager dpm =
980 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
981 ComponentName mdmPackage = dpm.getProfileOwnerAsUser(profileId);
982 if (mdmPackage == null) {
983 return null;
984 }
985 String mdmPackageName = mdmPackage.getPackageName();
986 try {
987 IPackageManager ipm = AppGlobals.getPackageManager();
988 ApplicationInfo mdmApplicationInfo =
989 ipm.getApplicationInfo(mdmPackageName, 0, profileId);
990 return mdmApplicationInfo;
991 } catch (RemoteException e) {
992 Log.e(TAG, "Error while retrieving application info for package " + mdmPackageName
993 + ", userId " + profileId, e);
994 return null;
995 }
996 }
Jason Monkb45e27b2015-05-20 13:35:43 -0400997
998 public static boolean isBandwidthControlEnabled() {
999 final INetworkManagementService netManager = INetworkManagementService.Stub
1000 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1001 try {
1002 return netManager.isBandwidthControlEnabled();
1003 } catch (RemoteException e) {
1004 return false;
1005 }
1006 }
Julia Reynoldsce25af42015-07-08 16:56:31 -04001007
1008 /**
1009 * Returns an accessible SpannableString.
1010 * @param displayText the text to display
1011 * @param accessibileText the text text-to-speech engines should read
1012 */
1013 public static SpannableString createAccessibleSequence(CharSequence displayText,
1014 String accessibileText) {
1015 SpannableString str = new SpannableString(displayText);
1016 str.setSpan(new TtsSpan.TextBuilder(accessibileText).build(), 0,
1017 displayText.length(),
1018 Spannable.SPAN_INCLUSIVE_INCLUSIVE);
1019 return str;
1020 }
Andres Morales7bdffd82015-08-04 16:55:00 -07001021
Clara Bayarrife432e82015-10-12 12:07:02 +01001022 /**
Benjamin Franz194300d2016-01-13 12:16:25 +00001023 * Returns the user id present in the bundle with {@link Intent#EXTRA_USER_ID} if it
Clara Bayarrife432e82015-10-12 12:07:02 +01001024 * belongs to the current user.
1025 *
1026 * @throws SecurityException if the given userId does not belong to the current user group.
1027 */
Benjamin Franz194300d2016-01-13 12:16:25 +00001028 public static int getUserIdFromBundle(Context context, Bundle bundle) {
Clara Bayarrife432e82015-10-12 12:07:02 +01001029 if (bundle == null) {
Benjamin Franz194300d2016-01-13 12:16:25 +00001030 return getCredentialOwnerUserId(context);
Clara Bayarrife432e82015-10-12 12:07:02 +01001031 }
Clara Bayarri6934a042015-10-14 11:07:35 +01001032 int userId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId());
Benjamin Franz194300d2016-01-13 12:16:25 +00001033 return enforceSameOwner(context, userId);
Clara Bayarrife432e82015-10-12 12:07:02 +01001034 }
1035
1036 /**
1037 * Returns the given user id if it belongs to the current user.
1038 *
1039 * @throws SecurityException if the given userId does not belong to the current user group.
1040 */
Benjamin Franz194300d2016-01-13 12:16:25 +00001041 public static int enforceSameOwner(Context context, int userId) {
Ricky Wai72500162016-06-07 16:54:25 +01001042 final UserManager um = getUserManager(context);
1043 final int[] profileIds = um.getProfileIdsWithDisabled(UserHandle.myUserId());
1044 if (ArrayUtils.contains(profileIds, userId)) {
1045 return userId;
Clara Bayarrife432e82015-10-12 12:07:02 +01001046 }
Ricky Wai72500162016-06-07 16:54:25 +01001047 throw new SecurityException("Given user id " + userId + " does not belong to user "
1048 + UserHandle.myUserId());
Clara Bayarrife432e82015-10-12 12:07:02 +01001049 }
1050
Benjamin Franz194300d2016-01-13 12:16:25 +00001051 /**
1052 * Returns the effective credential owner of the calling user.
1053 */
1054 public static int getCredentialOwnerUserId(Context context) {
1055 return getCredentialOwnerUserId(context, UserHandle.myUserId());
1056 }
1057
1058 /**
1059 * Returns the user id of the credential owner of the given user id.
1060 */
1061 public static int getCredentialOwnerUserId(Context context, int userId) {
1062 UserManager um = getUserManager(context);
1063 return um.getCredentialOwnerProfile(userId);
Andres Morales7bdffd82015-08-04 16:55:00 -07001064 }
Jason Monkdb4ed192015-12-11 16:48:31 -05001065
1066 public static int resolveResource(Context context, int attr) {
1067 TypedValue value = new TypedValue();
1068 context.getTheme().resolveAttribute(attr, value, true);
1069 return value.resourceId;
1070 }
Jason Monkb37e2882016-01-11 14:27:20 -05001071
1072 private static final StringBuilder sBuilder = new StringBuilder(50);
1073 private static final java.util.Formatter sFormatter = new java.util.Formatter(
1074 sBuilder, Locale.getDefault());
1075
1076 public static String formatDateRange(Context context, long start, long end) {
1077 final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
1078
1079 synchronized (sBuilder) {
1080 sBuilder.setLength(0);
1081 return DateUtils.formatDateRange(context, sFormatter, start, end, flags, null)
1082 .toString();
1083 }
1084 }
Jason Monk91e2f892016-02-23 15:31:09 -05001085
1086 public static List<String> getNonIndexable(int xml, Context context) {
Jason Monkbe941212016-05-27 12:58:53 -04001087 if (Looper.myLooper() == null) {
1088 // Hack to make sure Preferences can initialize. Prefs expect a looper, but
1089 // don't actually use it for the basic stuff here.
1090 Looper.prepare();
1091 }
Jason Monk9ee80cd2016-03-03 11:21:42 -05001092 final List<String> ret = new ArrayList<>();
Jason Monkbe941212016-05-27 12:58:53 -04001093 PreferenceManager manager = new PreferenceManager(context);
1094 PreferenceScreen screen = manager.inflateFromResource(context, xml, null);
1095 checkPrefs(screen, ret);
Jason Monk91e2f892016-02-23 15:31:09 -05001096
1097 return ret;
1098 }
1099
1100 private static void checkPrefs(PreferenceGroup group, List<String> ret) {
1101 if (group == null) return;
1102 for (int i = 0; i < group.getPreferenceCount(); i++) {
1103 Preference pref = group.getPreference(i);
1104 if (pref instanceof SelfAvailablePreference
1105 && !((SelfAvailablePreference) pref).isAvailable(group.getContext())) {
1106 ret.add(pref.getKey());
1107 if (pref instanceof PreferenceGroup) {
1108 addAll((PreferenceGroup) pref, ret);
1109 }
1110 } else if (pref instanceof PreferenceGroup) {
1111 checkPrefs((PreferenceGroup) pref, ret);
1112 }
1113 }
1114 }
1115
1116 private static void addAll(PreferenceGroup group, List<String> ret) {
1117 if (group == null) return;
1118 for (int i = 0; i < group.getPreferenceCount(); i++) {
1119 Preference pref = group.getPreference(i);
1120 ret.add(pref.getKey());
1121 if (pref instanceof PreferenceGroup) {
1122 addAll((PreferenceGroup) pref, ret);
1123 }
1124 }
1125 }
Udam Saini679f7ad2016-03-25 10:47:00 -07001126
1127 public static boolean isDeviceProvisioned(Context context) {
1128 return Settings.Global.getInt(context.getContentResolver(),
1129 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
1130 }
Ricky Wai616342b2016-04-13 10:40:22 +01001131
Bernard Chau88d523b2016-04-14 15:08:28 +01001132 public static boolean startQuietModeDialogIfNecessary(Context context, UserManager um,
Ricky Wai616342b2016-04-13 10:40:22 +01001133 int userId) {
1134 if (um.isQuietModeEnabled(UserHandle.of(userId))) {
1135 final Intent intent = UnlaunchableAppActivity.createInQuietModeDialogIntent(userId);
1136 context.startActivity(intent);
1137 return true;
1138 }
1139 return false;
1140 }
Sudheer Shankac3eb16e2016-04-21 12:51:43 -07001141
Rubin Xu3231afe2016-08-24 10:15:07 +01001142 public static boolean unlockWorkProfileIfNecessary(Context context, int userId) {
1143 try {
Sudheer Shankaacb1a612016-11-10 15:30:14 -08001144 if (!ActivityManager.getService().isUserRunning(userId,
Rubin Xu3231afe2016-08-24 10:15:07 +01001145 ActivityManager.FLAG_AND_LOCKED)) {
1146 return false;
1147 }
1148 } catch (RemoteException e) {
1149 return false;
1150 }
1151 if (!(new LockPatternUtils(context)).isSecure(userId)) {
1152 return false;
1153 }
Robin Leecccf3242017-02-10 15:32:49 +00001154 return confirmWorkProfileCredentials(context, userId);
1155 }
1156
1157 public static boolean confirmWorkProfileCredentialsIfNecessary(Context context, int userId) {
1158 KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
1159 if (!km.isDeviceLocked(userId)) {
1160 return false;
1161 }
1162 return confirmWorkProfileCredentials(context, userId);
1163 }
1164
1165 private static boolean confirmWorkProfileCredentials(Context context, int userId) {
Rubin Xu3231afe2016-08-24 10:15:07 +01001166 final KeyguardManager km = (KeyguardManager) context.getSystemService(
1167 Context.KEYGUARD_SERVICE);
1168 final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
1169 if (unlockIntent != null) {
1170 context.startActivity(unlockIntent);
1171 return true;
1172 } else {
1173 return false;
1174 }
Rubin Xu3231afe2016-08-24 10:15:07 +01001175 }
1176
Sudheer Shankac3eb16e2016-04-21 12:51:43 -07001177 public static CharSequence getApplicationLabel(Context context, String packageName) {
1178 try {
1179 final ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
1180 packageName,
1181 PackageManager.MATCH_DISABLED_COMPONENTS
Amith Yamasani21556cd2016-11-01 15:49:21 -07001182 | PackageManager.MATCH_ANY_USER);
Sudheer Shankac3eb16e2016-04-21 12:51:43 -07001183 return appInfo.loadLabel(context.getPackageManager());
1184 } catch (PackageManager.NameNotFoundException e) {
1185 Log.w(TAG, "Unable to find info for package: " + packageName);
1186 }
1187 return null;
1188 }
Jason Monk75199542016-05-06 15:09:32 -04001189
Jeff Sharkey4a8136b2016-07-27 12:53:34 -06001190 public static boolean isPackageDirectBootAware(Context context, String packageName) {
1191 try {
1192 final ApplicationInfo ai = context.getPackageManager().getApplicationInfo(
1193 packageName, 0);
1194 return ai.isDirectBootAware() || ai.isPartiallyDirectBootAware();
1195 } catch (NameNotFoundException ignored) {
1196 }
1197 return false;
1198 }
Andre Lago3e398c82016-07-25 14:12:28 +01001199
1200 /**
1201 * Returns a context created from the given context for the given user, or null if it fails
1202 */
1203 public static Context createPackageContextAsUser(Context context, int userId) {
1204 try {
1205 return context.createPackageContextAsUser(
1206 context.getPackageName(), 0 /* flags */, UserHandle.of(userId));
1207 } catch (PackageManager.NameNotFoundException e) {
1208 Log.e(TAG, "Failed to create user context", e);
1209 }
1210 return null;
1211 }
Jeff Sharkeyab508072016-10-11 14:25:22 -06001212
1213 public static FingerprintManager getFingerprintManagerOrNull(Context context) {
1214 if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
1215 return context.getSystemService(FingerprintManager.class);
1216 } else {
1217 return null;
1218 }
1219 }
Daniel Nishi31027da2017-01-19 14:03:57 -08001220
1221 /**
1222 * Launches an intent which may optionally have a user id defined.
1223 * @param fragment Fragment to use to launch the activity.
1224 * @param intent Intent to launch.
1225 */
1226 public static void launchIntent(Fragment fragment, Intent intent) {
1227 try {
1228 final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1);
1229
1230 if (userId == -1) {
1231 fragment.startActivity(intent);
1232 } else {
1233 fragment.getActivity().startActivityAsUser(intent, new UserHandle(userId));
1234 }
1235 } catch (ActivityNotFoundException e) {
1236 Log.w(TAG, "No activity found for " + intent);
1237 }
1238 }
1239
Christine Franks14782222017-01-23 16:44:02 -08001240 public static boolean isCarrierDemoUser(Context context) {
1241 final String carrierDemoModeSetting =
1242 context.getString(com.android.internal.R.string.config_carrierDemoModeSetting);
1243 return UserManager.isDeviceInDemoMode(context)
1244 && getUserManager(context).isDemoUser()
1245 && !TextUtils.isEmpty(carrierDemoModeSetting)
1246 && (Settings.Secure.getInt(context.getContentResolver(),
1247 carrierDemoModeSetting, 0) == 1);
1248 }
Daniel Nishidfed8a22017-01-19 16:32:41 -08001249
1250 /**
1251 * Returns if a given user is a profile of another user.
1252 * @param user The user whose profiles will be checked.
1253 * @param profile The (potential) profile.
1254 * @return if the profile is actually a profile
1255 */
1256 public static boolean isProfileOf(UserInfo user, UserInfo profile) {
1257 return user.id == profile.id ||
1258 (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
1259 && user.profileGroupId == profile.profileGroupId);
1260 }
Jeff Sharkey4a8136b2016-07-27 12:53:34 -06001261}