Starting from O, install_non_market_apps is deprecated and will not be
checked by the package installer. Device admin apps should be using the
user restriction instead.
Since on managed profiles, the default value blocked install from
unknown sources, the system will set the user restriction on behalf of
the profile owners (if the profile has one).
For non-managed profiles, the user had access to the settings to change
the value of install_non_market_apps. So going forward, any request to
change it's value by dpm#setSecureSetting in such users is going to be
ignored.
Test: Manually tested that:
1. For a profile with PO, when install_non_market_apps was set to 0,
user restriction is set on upgrade
2. For a profile with PO, when install_non_market_apps was set to 1,
user restriction is not set on upgrade
3. After upgrade, newly created managed profiles with PO have user
restriction set
Bug:
33947615
Change-Id: I063e9ee608b52086ffdf8ed2b24e2928574c58cd
* The settings that can be updated by a profile or device owner with this method are:
* <ul>
* <li>{@link Settings.Secure#DEFAULT_INPUT_METHOD}</li>
- * <li>{@link Settings.Secure#INSTALL_NON_MARKET_APPS}</li>
* <li>{@link Settings.Secure#SKIP_FIRST_USE_HINTS}</li>
* </ul>
* <p>
* <li>{@link Settings.Secure#LOCATION_MODE}</li>
* </ul>
*
+ * <strong>Note: Starting from Android O, apps should no longer call this method with the
+ * setting {@link android.provider.Settings.Secure#INSTALL_NON_MARKET_APPS}, which is
+ * deprecated. Instead, device owners or profile owners should use the restriction
+ * {@link UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES}.
+ * If any app targeting {@link android.os.Build.VERSION_CODES#O} or higher calls this method
+ * with {@link android.provider.Settings.Secure#INSTALL_NON_MARKET_APPS},
+ * an {@link UnsupportedOperationException} is thrown.
+ * </strong>
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param setting The name of the setting to update.
* @param value The value to update the setting to.
public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
/**
+ * A flag to tell {@link com.android.server.devicepolicy.DevicePolicyManagerService} that
+ * the default for {@link #INSTALL_NON_MARKET_APPS} is reversed for this user on OTA. So it
+ * can set the restriction {@link android.os.UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES}
+ * on behalf of the profile owner if needed to make the change transparent for profile
+ * owners.
+ *
+ * @hide
+ */
+ public static final String UNKNOWN_SOURCES_DEFAULT_REVERSED =
+ "unknown_sources_default_reversed";
+
+ /**
* Comma-separated list of location providers that activities may access. Do not rely on
* this value being present in settings.db or on ContentObserver notifications on the
* corresponding Uri.
<bool name="def_bluetooth_on">true</bool>
<bool name="def_wifi_display_on">false</bool>
- <bool name="def_install_non_market_apps">true</bool>
+ <bool name="def_install_non_market_apps">false</bool>
<bool name="def_package_verifier_enable">true</bool>
<!-- Comma-separated list of location providers.
Network location is off by default because it requires
// setting through the UI.
final SettingsState secureSetting = getSecureSettingsLocked(userId);
if (!mUserManager.hasUserRestriction(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, UserHandle.of(userId))) {
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, UserHandle.of(userId))
+ && secureSetting.getSettingLocked(
+ Settings.Secure.INSTALL_NON_MARKET_APPS).getValue().equals("0")) {
+
secureSetting.insertSettingLocked(Settings.Secure.INSTALL_NON_MARKET_APPS,
"1", null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ // For managed profiles with profile owners, DevicePolicyManagerService
+ // may want to set the user restriction in this case
+ secureSetting.insertSettingLocked(
+ Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, "1", null, true,
+ SettingsState.SYSTEM_PACKAGE_NAME);
}
currentVersion = 138;
}
import android.content.Context;
import android.content.pm.UserInfo;
+import android.os.Process;
import android.os.SystemClock;
import android.os.UserManager;
import android.provider.Settings;
private UserManager mUm;
private boolean mHasUserRestriction;
+ private boolean mSystemSetUserRestriction;
private List<UserInfo> mCurrentUsers;
private String waitTillValueChanges(String errorMessage, String oldValue) {
public void setUp() {
mUm = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
mHasUserRestriction = mUm.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
+ mSystemSetUserRestriction = mUm.getUserRestrictionSource(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle())
+ == UserManager.RESTRICTION_SOURCE_SYSTEM;
mCurrentUsers = mUm.getUsers();
}
String value = getSetting(SETTING_TYPE_SECURE, Settings.Secure.INSTALL_NON_MARKET_APPS);
assertEquals(value, mHasUserRestriction ? "0" : "1");
+ if (mHasUserRestriction && !mSystemSetUserRestriction) {
+ // User restriction set by device policy. This case should be covered in DO/PO related
+ // tests. Pass.
+ Log.w(TAG, "User restriction set by PO/DO. Skipping testValueRespectsUserRestriction");
+ return;
+ }
+
mUm.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, !mHasUserRestriction);
value = waitTillValueChanges(
"Changing user restriction did not change the value of install_non_market_apps",
@After
public void tearDown() {
- if (mUm.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)
- != mHasUserRestriction) {
+ if (!mHasUserRestriction || mSystemSetUserRestriction) {
+ // The test may have modified the user restriction state. Restore it.
mUm.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
mHasUserRestriction);
}
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
+import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
}
}
+ private void ensureUnknownSourcesRestrictionForProfileOwners() {
+ synchronized (this) {
+ for (int userId : mOwners.getProfileOwnerKeys().toArray(new Integer[0])) {
+ if (!mUserManager.isManagedProfile(userId) ||
+ Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId) == 0) {
+ continue;
+ }
+ setUserRestrictionOnBehalfOfProfileOwnerLocked(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userId);
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId);
+ }
+ }
+ }
+
+ private void setUserRestrictionOnBehalfOfProfileOwnerLocked(String userRestrictionKey,
+ int userId) {
+ if (UserRestrictionsUtils.isValidRestriction(userRestrictionKey) &&
+ UserRestrictionsUtils.canProfileOwnerChange(userRestrictionKey, userId)) {
+ ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
+ if (profileOwner == null) {
+ return;
+ }
+ Bundle restrictions = profileOwner.ensureUserRestrictions();
+ restrictions.putBoolean(userRestrictionKey, true);
+ saveUserRestrictionsLocked(userId);
+ }
+ }
+
private void onLockSettingsReady() {
getUserData(UserHandle.USER_SYSTEM);
loadOwners();
cleanUpOldUsers();
-
+ ensureUnknownSourcesRestrictionForProfileOwners();
onStartUser(UserHandle.USER_SYSTEM);
// Register an observer for watching for user setup complete.
mOwners.writeProfileOwner(userHandle);
Slog.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle);
+ if (mUserManager.isManagedProfile(userHandle)) {
+ setUserRestrictionOnBehalfOfProfileOwnerLocked(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userHandle);
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userHandle);
+ }
return true;
}
}
throw new SecurityException(String.format(
"Permission denial: Profile owners cannot update %1$s", setting));
}
-
+ if (setting.equals(Settings.Secure.INSTALL_NON_MARKET_APPS)) {
+ if (getTargetSdk(who.getPackageName(), callingUserId) >= Build.VERSION_CODES.O) {
+ throw new UnsupportedOperationException(Settings.Secure.INSTALL_NON_MARKET_APPS
+ + " is deprecated. Please use the user restriction "
+ + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES + " instead.");
+ }
+ if (!mUserManager.isManagedProfile(callingUserId)) {
+ Slog.e(LOG_TAG, "Ignoring setSecureSetting request for "
+ + setting + ". User restriction "
+ + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES
+ + " should be used instead.");
+ } else {
+ try {
+ setUserRestriction(who, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ (Integer.parseInt(value) == 0) ? true : false);
+ } catch (NumberFormatException exc) {
+ Slog.e(LOG_TAG, "Invalid value: " + value + " for setting " + setting);
+ }
+ }
+ return;
+ }
long id = mInjector.binderClearCallingIdentity();
try {
mInjector.settingsSecurePutStringForUser(setting, value, callingUserId);