/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm; import com.google.android.collect.Sets; import com.android.internal.util.Preconditions; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.content.ContentResolver; import android.content.Context; import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.util.Log; import android.util.Slog; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; import java.util.List; import java.util.Set; /** * Utility methods for user restrictions. * *
See {@link UserManagerService} for the method suffixes.
*/
public class UserRestrictionsUtils {
private static final String TAG = "UserRestrictionsUtils";
private UserRestrictionsUtils() {
}
private static Set The resulting {@link Bundle} is always writable. (i.e. it won't return
* {@link Bundle#EMPTY})
*/
public static @NonNull Bundle clone(@Nullable Bundle in) {
return (in != null) ? new Bundle(in) : new Bundle();
}
public static void merge(@NonNull Bundle dest, @Nullable Bundle in) {
Preconditions.checkNotNull(dest);
Preconditions.checkArgument(dest != in);
if (in == null) {
return;
}
for (String key : in.keySet()) {
if (in.getBoolean(key, false)) {
dest.putBoolean(key, true);
}
}
}
/**
* @return true if a restriction is settable by device owner.
*/
public static boolean canDeviceOwnerChange(String restriction) {
return !IMMUTABLE_BY_OWNERS.contains(restriction);
}
/**
* @return true if a restriction is settable by profile owner. Note it takes a user ID because
* some restrictions can be changed by PO only when it's running on the system user.
*/
public static boolean canProfileOwnerChange(String restriction, int userId) {
return !IMMUTABLE_BY_OWNERS.contains(restriction)
&& !(userId != UserHandle.USER_SYSTEM
&& DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction));
}
/**
* Takes restrictions that can be set by device owner, and sort them into what should be applied
* globally and what should be applied only on the current user.
*/
public static void sortToGlobalAndLocal(@Nullable Bundle in, @NonNull Bundle global,
@NonNull Bundle local) {
if (in == null || in.size() == 0) {
return;
}
for (String key : in.keySet()) {
if (!in.getBoolean(key)) {
continue;
}
if (DEVICE_OWNER_ONLY_RESTRICTIONS.contains(key) || GLOBAL_RESTRICTIONS.contains(key)) {
global.putBoolean(key, true);
} else {
local.putBoolean(key, true);
}
}
}
/**
* @return true if two Bundles contain the same user restriction.
* A null bundle and an empty bundle are considered to be equal.
*/
public static boolean areEqual(@Nullable Bundle a, @Nullable Bundle b) {
if (a == b) {
return true;
}
if (isEmpty(a)) {
return isEmpty(b);
}
if (isEmpty(b)) {
return false;
}
for (String key : a.keySet()) {
if (a.getBoolean(key) != b.getBoolean(key)) {
return false;
}
}
for (String key : b.keySet()) {
if (a.getBoolean(key) != b.getBoolean(key)) {
return false;
}
}
return true;
}
/**
* Takes a new use restriction set and the previous set, and apply the restrictions that have
* changed.
*
* Note this method is called by {@link UserManagerService} without holding any locks.
*/
public static void applyUserRestrictions(Context context, int userId,
Bundle newRestrictions, Bundle prevRestrictions) {
for (String key : USER_RESTRICTIONS) {
final boolean newValue = newRestrictions.getBoolean(key);
final boolean prevValue = prevRestrictions.getBoolean(key);
if (newValue != prevValue) {
applyUserRestriction(context, userId, key, newValue);
}
}
}
/**
* Apply each user restriction.
*
* See also {@link
* com.android.providers.settings.SettingsProvider#isGlobalOrSecureSettingRestrictedForUser},
* which should be in sync with this method.
*/
private static void applyUserRestriction(Context context, int userId, String key,
boolean newValue) {
if (UserManagerService.DBG) {
Log.d(TAG, "Applying user restriction: userId=" + userId
+ " key=" + key + " value=" + newValue);
}
// When certain restrictions are cleared, we don't update the system settings,
// because these settings are changeable on the Settings UI and we don't know the original
// value -- for example LOCATION_MODE might have been off already when the restriction was
// set, and in that case even if the restriction is lifted, changing it to ON would be
// wrong. So just don't do anything in such a case. If the user hopes to enable location
// later, they can do it on the Settings UI.
// WARNING: Remember that Settings.Global and Settings.Secure are changeable via adb.
// To prevent this from happening for a given user restriction, you have to add a check to
// SettingsProvider.isGlobalOrSecureSettingRestrictedForUser.
final ContentResolver cr = context.getContentResolver();
final long id = Binder.clearCallingIdentity();
try {
switch (key) {
case UserManager.DISALLOW_CONFIG_WIFI:
if (newValue) {
android.provider.Settings.Secure.putIntForUser(cr,
android.provider.Settings.Global
.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0, userId);
}
break;
case UserManager.DISALLOW_DATA_ROAMING:
if (newValue) {
// DISALLOW_DATA_ROAMING user restriction is set.
// Multi sim device.
SubscriptionManager subscriptionManager = new SubscriptionManager(context);
final List