UserRestrictionsUtils.java revision 36eb7a13709073111e2397c9f542cb6f64f3f2b9
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.pm; 18 19import com.google.android.collect.Sets; 20 21import com.android.internal.util.Preconditions; 22 23import android.annotation.NonNull; 24import android.annotation.Nullable; 25import android.app.ActivityManager; 26import android.app.ActivityManagerNative; 27import android.content.ContentResolver; 28import android.content.Context; 29import android.net.Uri; 30import android.os.Binder; 31import android.os.Bundle; 32import android.os.RemoteException; 33import android.os.SystemProperties; 34import android.os.UserHandle; 35import android.os.UserManager; 36import android.service.persistentdata.PersistentDataBlockManager; 37import android.telephony.SubscriptionInfo; 38import android.telephony.SubscriptionManager; 39import android.util.Log; 40import android.util.Slog; 41 42import org.xmlpull.v1.XmlPullParser; 43import org.xmlpull.v1.XmlSerializer; 44 45import java.io.IOException; 46import java.io.PrintWriter; 47import java.util.List; 48import java.util.Set; 49 50/** 51 * Utility methods for user restrictions. 52 * 53 * <p>See {@link UserManagerService} for the method suffixes. 54 */ 55public class UserRestrictionsUtils { 56 private static final String TAG = "UserRestrictionsUtils"; 57 58 private UserRestrictionsUtils() { 59 } 60 61 private static Set<String> newSetWithUniqueCheck(String[] strings) { 62 final Set<String> ret = Sets.newArraySet(strings); 63 64 // Make sure there's no overlap. 65 Preconditions.checkState(ret.size() == strings.length); 66 return ret; 67 } 68 69 public static final Set<String> USER_RESTRICTIONS = newSetWithUniqueCheck(new String[] { 70 UserManager.DISALLOW_CONFIG_WIFI, 71 UserManager.DISALLOW_MODIFY_ACCOUNTS, 72 UserManager.DISALLOW_INSTALL_APPS, 73 UserManager.DISALLOW_UNINSTALL_APPS, 74 UserManager.DISALLOW_SHARE_LOCATION, 75 UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, 76 UserManager.DISALLOW_CONFIG_BLUETOOTH, 77 UserManager.DISALLOW_USB_FILE_TRANSFER, 78 UserManager.DISALLOW_CONFIG_CREDENTIALS, 79 UserManager.DISALLOW_REMOVE_USER, 80 UserManager.DISALLOW_DEBUGGING_FEATURES, 81 UserManager.DISALLOW_CONFIG_VPN, 82 UserManager.DISALLOW_CONFIG_TETHERING, 83 UserManager.DISALLOW_NETWORK_RESET, 84 UserManager.DISALLOW_FACTORY_RESET, 85 UserManager.DISALLOW_ADD_USER, 86 UserManager.ENSURE_VERIFY_APPS, 87 UserManager.DISALLOW_CONFIG_CELL_BROADCASTS, 88 UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, 89 UserManager.DISALLOW_APPS_CONTROL, 90 UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, 91 UserManager.DISALLOW_UNMUTE_MICROPHONE, 92 UserManager.DISALLOW_ADJUST_VOLUME, 93 UserManager.DISALLOW_OUTGOING_CALLS, 94 UserManager.DISALLOW_SMS, 95 UserManager.DISALLOW_FUN, 96 UserManager.DISALLOW_CREATE_WINDOWS, 97 UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE, 98 UserManager.DISALLOW_OUTGOING_BEAM, 99 UserManager.DISALLOW_WALLPAPER, 100 UserManager.DISALLOW_SAFE_BOOT, 101 UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, 102 UserManager.DISALLOW_RECORD_AUDIO, 103 UserManager.DISALLOW_CAMERA, 104 UserManager.DISALLOW_RUN_IN_BACKGROUND, 105 UserManager.DISALLOW_DATA_ROAMING, 106 UserManager.DISALLOW_SET_USER_ICON, 107 UserManager.DISALLOW_SET_WALLPAPER, 108 UserManager.DISALLOW_OEM_UNLOCK 109 }); 110 111 /** 112 * Set of user restriction which we don't want to persist. 113 */ 114 private static final Set<String> NON_PERSIST_USER_RESTRICTIONS = Sets.newArraySet( 115 UserManager.DISALLOW_RECORD_AUDIO 116 ); 117 118 /** 119 * User restrictions that can not be set by profile owners. 120 */ 121 private static final Set<String> DEVICE_OWNER_ONLY_RESTRICTIONS = Sets.newArraySet( 122 UserManager.DISALLOW_USB_FILE_TRANSFER, 123 UserManager.DISALLOW_CONFIG_TETHERING, 124 UserManager.DISALLOW_NETWORK_RESET, 125 UserManager.DISALLOW_FACTORY_RESET, 126 UserManager.DISALLOW_ADD_USER, 127 UserManager.DISALLOW_CONFIG_CELL_BROADCASTS, 128 UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, 129 UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, 130 UserManager.DISALLOW_SMS, 131 UserManager.DISALLOW_FUN, 132 UserManager.DISALLOW_SAFE_BOOT, 133 UserManager.DISALLOW_CREATE_WINDOWS, 134 UserManager.DISALLOW_DATA_ROAMING 135 ); 136 137 /** 138 * User restrictions that can't be changed by device owner or profile owner. 139 */ 140 private static final Set<String> IMMUTABLE_BY_OWNERS = Sets.newArraySet( 141 UserManager.DISALLOW_RECORD_AUDIO, 142 UserManager.DISALLOW_WALLPAPER, 143 UserManager.DISALLOW_OEM_UNLOCK 144 ); 145 146 /** 147 * Special user restrictions that can be applied to a user as well as to all users globally, 148 * depending on callers. When device owner sets them, they'll be applied to all users. 149 */ 150 private static final Set<String> GLOBAL_RESTRICTIONS = Sets.newArraySet( 151 UserManager.DISALLOW_ADJUST_VOLUME, 152 UserManager.DISALLOW_RUN_IN_BACKGROUND, 153 UserManager.DISALLOW_UNMUTE_MICROPHONE 154 ); 155 156 /** 157 * Throws {@link IllegalArgumentException} if the given restriction name is invalid. 158 */ 159 public static boolean isValidRestriction(@NonNull String restriction) { 160 if (!USER_RESTRICTIONS.contains(restriction)) { 161 Slog.e(TAG, "Unknown restriction: " + restriction); 162 return false; 163 } 164 return true; 165 } 166 167 public static void writeRestrictions(@NonNull XmlSerializer serializer, 168 @Nullable Bundle restrictions, @NonNull String tag) throws IOException { 169 if (restrictions == null) { 170 return; 171 } 172 173 serializer.startTag(null, tag); 174 for (String key : restrictions.keySet()) { 175 if (NON_PERSIST_USER_RESTRICTIONS.contains(key)) { 176 continue; // Don't persist. 177 } 178 if (USER_RESTRICTIONS.contains(key)) { 179 if (restrictions.getBoolean(key)) { 180 serializer.attribute(null, key, "true"); 181 } 182 continue; 183 } 184 Log.w(TAG, "Unknown user restriction detected: " + key); 185 } 186 serializer.endTag(null, tag); 187 } 188 189 public static void readRestrictions(XmlPullParser parser, Bundle restrictions) 190 throws IOException { 191 for (String key : USER_RESTRICTIONS) { 192 final String value = parser.getAttributeValue(null, key); 193 if (value != null) { 194 restrictions.putBoolean(key, Boolean.parseBoolean(value)); 195 } 196 } 197 } 198 199 /** 200 * @return {@code in} itself when it's not null, or an empty bundle (which can writable). 201 */ 202 public static Bundle nonNull(@Nullable Bundle in) { 203 return in != null ? in : new Bundle(); 204 } 205 206 public static boolean isEmpty(@Nullable Bundle in) { 207 return (in == null) || (in.size() == 0); 208 } 209 210 /** 211 * Creates a copy of the {@code in} Bundle. If {@code in} is null, it'll return an empty 212 * bundle. 213 * 214 * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return 215 * {@link Bundle#EMPTY}) 216 */ 217 public static @NonNull Bundle clone(@Nullable Bundle in) { 218 return (in != null) ? new Bundle(in) : new Bundle(); 219 } 220 221 public static void merge(@NonNull Bundle dest, @Nullable Bundle in) { 222 Preconditions.checkNotNull(dest); 223 Preconditions.checkArgument(dest != in); 224 if (in == null) { 225 return; 226 } 227 for (String key : in.keySet()) { 228 if (in.getBoolean(key, false)) { 229 dest.putBoolean(key, true); 230 } 231 } 232 } 233 234 /** 235 * @return true if a restriction is settable by device owner. 236 */ 237 public static boolean canDeviceOwnerChange(String restriction) { 238 return !IMMUTABLE_BY_OWNERS.contains(restriction); 239 } 240 241 /** 242 * @return true if a restriction is settable by profile owner. Note it takes a user ID because 243 * some restrictions can be changed by PO only when it's running on the system user. 244 */ 245 public static boolean canProfileOwnerChange(String restriction, int userId) { 246 return !IMMUTABLE_BY_OWNERS.contains(restriction) 247 && !(userId != UserHandle.USER_SYSTEM 248 && DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction)); 249 } 250 251 /** 252 * Takes restrictions that can be set by device owner, and sort them into what should be applied 253 * globally and what should be applied only on the current user. 254 */ 255 public static void sortToGlobalAndLocal(@Nullable Bundle in, @NonNull Bundle global, 256 @NonNull Bundle local) { 257 if (in == null || in.size() == 0) { 258 return; 259 } 260 for (String key : in.keySet()) { 261 if (!in.getBoolean(key)) { 262 continue; 263 } 264 if (DEVICE_OWNER_ONLY_RESTRICTIONS.contains(key) || GLOBAL_RESTRICTIONS.contains(key)) { 265 global.putBoolean(key, true); 266 } else { 267 local.putBoolean(key, true); 268 } 269 } 270 } 271 272 /** 273 * @return true if two Bundles contain the same user restriction. 274 * A null bundle and an empty bundle are considered to be equal. 275 */ 276 public static boolean areEqual(@Nullable Bundle a, @Nullable Bundle b) { 277 if (a == b) { 278 return true; 279 } 280 if (isEmpty(a)) { 281 return isEmpty(b); 282 } 283 if (isEmpty(b)) { 284 return false; 285 } 286 for (String key : a.keySet()) { 287 if (a.getBoolean(key) != b.getBoolean(key)) { 288 return false; 289 } 290 } 291 for (String key : b.keySet()) { 292 if (a.getBoolean(key) != b.getBoolean(key)) { 293 return false; 294 } 295 } 296 return true; 297 } 298 299 /** 300 * Takes a new use restriction set and the previous set, and apply the restrictions that have 301 * changed. 302 * 303 * <p>Note this method is called by {@link UserManagerService} without holding any locks. 304 */ 305 public static void applyUserRestrictions(Context context, int userId, 306 Bundle newRestrictions, Bundle prevRestrictions) { 307 for (String key : USER_RESTRICTIONS) { 308 final boolean newValue = newRestrictions.getBoolean(key); 309 final boolean prevValue = prevRestrictions.getBoolean(key); 310 311 if (newValue != prevValue) { 312 applyUserRestriction(context, userId, key, newValue); 313 } 314 } 315 } 316 317 /** 318 * Apply each user restriction. 319 * 320 * <p>See also {@link 321 * com.android.providers.settings.SettingsProvider#isGlobalOrSecureSettingRestrictedForUser}, 322 * which should be in sync with this method. 323 */ 324 private static void applyUserRestriction(Context context, int userId, String key, 325 boolean newValue) { 326 if (UserManagerService.DBG) { 327 Log.d(TAG, "Applying user restriction: userId=" + userId 328 + " key=" + key + " value=" + newValue); 329 } 330 // When certain restrictions are cleared, we don't update the system settings, 331 // because these settings are changeable on the Settings UI and we don't know the original 332 // value -- for example LOCATION_MODE might have been off already when the restriction was 333 // set, and in that case even if the restriction is lifted, changing it to ON would be 334 // wrong. So just don't do anything in such a case. If the user hopes to enable location 335 // later, they can do it on the Settings UI. 336 // WARNING: Remember that Settings.Global and Settings.Secure are changeable via adb. 337 // To prevent this from happening for a given user restriction, you have to add a check to 338 // SettingsProvider.isGlobalOrSecureSettingRestrictedForUser. 339 340 final ContentResolver cr = context.getContentResolver(); 341 final long id = Binder.clearCallingIdentity(); 342 try { 343 switch (key) { 344 case UserManager.DISALLOW_CONFIG_WIFI: 345 if (newValue) { 346 android.provider.Settings.Secure.putIntForUser(cr, 347 android.provider.Settings.Global 348 .WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0, userId); 349 } 350 break; 351 case UserManager.DISALLOW_DATA_ROAMING: 352 if (newValue) { 353 // DISALLOW_DATA_ROAMING user restriction is set. 354 355 // Multi sim device. 356 SubscriptionManager subscriptionManager = new SubscriptionManager(context); 357 final List<SubscriptionInfo> subscriptionInfoList = 358 subscriptionManager.getActiveSubscriptionInfoList(); 359 if (subscriptionInfoList != null) { 360 for (SubscriptionInfo subInfo : subscriptionInfoList) { 361 android.provider.Settings.Global.putStringForUser(cr, 362 android.provider.Settings.Global.DATA_ROAMING 363 + subInfo.getSubscriptionId(), "0", userId); 364 } 365 } 366 367 // Single sim device. 368 android.provider.Settings.Global.putStringForUser(cr, 369 android.provider.Settings.Global.DATA_ROAMING, "0", userId); 370 } 371 break; 372 case UserManager.DISALLOW_SHARE_LOCATION: 373 if (newValue) { 374 android.provider.Settings.Secure.putIntForUser(cr, 375 android.provider.Settings.Secure.LOCATION_MODE, 376 android.provider.Settings.Secure.LOCATION_MODE_OFF, 377 userId); 378 } 379 break; 380 case UserManager.DISALLOW_DEBUGGING_FEATURES: 381 if (newValue) { 382 // Only disable adb if changing for system user, since it is global 383 // TODO: should this be admin user? 384 if (userId == UserHandle.USER_SYSTEM) { 385 android.provider.Settings.Global.putStringForUser(cr, 386 android.provider.Settings.Global.ADB_ENABLED, "0", 387 userId); 388 } 389 } 390 break; 391 case UserManager.ENSURE_VERIFY_APPS: 392 if (newValue) { 393 android.provider.Settings.Global.putStringForUser( 394 context.getContentResolver(), 395 android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, "1", 396 userId); 397 android.provider.Settings.Global.putStringForUser( 398 context.getContentResolver(), 399 android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1", 400 userId); 401 } 402 break; 403 case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES: 404 if (newValue) { 405 android.provider.Settings.Secure.putIntForUser(cr, 406 android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS, 0, 407 userId); 408 } 409 break; 410 case UserManager.DISALLOW_RUN_IN_BACKGROUND: 411 if (newValue) { 412 int currentUser = ActivityManager.getCurrentUser(); 413 if (currentUser != userId && userId != UserHandle.USER_SYSTEM) { 414 try { 415 ActivityManagerNative.getDefault().stopUser(userId, false, null); 416 } catch (RemoteException e) { 417 throw e.rethrowAsRuntimeException(); 418 } 419 } 420 } 421 break; 422 case UserManager.DISALLOW_SAFE_BOOT: 423 // Unlike with the other restrictions, we want to propagate the new value to 424 // the system settings even if it is false. The other restrictions modify 425 // settings which could be manually changed by the user from the Settings app 426 // after the policies enforcing these restrictions have been revoked, so we 427 // leave re-setting of those settings to the user. 428 android.provider.Settings.Global.putInt( 429 context.getContentResolver(), 430 android.provider.Settings.Global.SAFE_BOOT_DISALLOWED, 431 newValue ? 1 : 0); 432 break; 433 case UserManager.DISALLOW_FACTORY_RESET: 434 case UserManager.DISALLOW_OEM_UNLOCK: 435 if (newValue) { 436 PersistentDataBlockManager manager = (PersistentDataBlockManager) context 437 .getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE); 438 if (manager != null && manager.getOemUnlockEnabled()) { 439 manager.setOemUnlockEnabled(false); 440 } 441 } 442 } 443 } finally { 444 Binder.restoreCallingIdentity(id); 445 } 446 } 447 448 public static void dumpRestrictions(PrintWriter pw, String prefix, Bundle restrictions) { 449 boolean noneSet = true; 450 if (restrictions != null) { 451 for (String key : restrictions.keySet()) { 452 if (restrictions.getBoolean(key, false)) { 453 pw.println(prefix + key); 454 noneSet = false; 455 } 456 } 457 if (noneSet) { 458 pw.println(prefix + "none"); 459 } 460 } else { 461 pw.println(prefix + "null"); 462 } 463 } 464} 465