Deprecating secure setting install_non_market_apps

Apps targetting Android O or higher should use the new api
canRequestPackageInstalls in package manager. The secure setting
INSTALL_NON_MARKET_APPS which was used is set to 1 to make the
change transparent to apps who are already querying for this setting's
value.

Test: adb shell am instrument -e class\
'com.android.providers.settings.InstallNonMarketAppsDeprecationTest' -w\
'com.android.providers.setting.test/android.support.test.runner.AndroidJUnitRunner'

Bug: 33947615

Change-Id: Ie38d130bfccd022483a566270fce071acbdb00b7
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 7206127..136f17e 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -38,7 +38,7 @@
 
     <bool name="def_bluetooth_on">true</bool>
     <bool name="def_wifi_display_on">false</bool>
-    <bool name="def_install_non_market_apps">false</bool>
+    <bool name="def_install_non_market_apps">true</bool>
     <bool name="def_package_verifier_enable">true</bool>
     <!-- Comma-separated list of location providers.
          Network location is off by default because it requires
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 8be9243..2299794 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2735,7 +2735,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 137;
+            private static final int SETTINGS_VERSION = 138;
 
             private final int mUserId;
 
@@ -3187,6 +3187,18 @@
 
                     currentVersion = 137;
                 }
+                if (currentVersion == 137) {
+                    // Version 138: Settings.Secure#INSTALL_NON_MARKET_APPS is deprecated and its
+                    // default value set to 1. The user can no longer change the value of this
+                    // setting through the UI.
+                    final SettingsState secureSetting = getSecureSettingsLocked(userId);
+                    if (!mUserManager.hasUserRestriction(
+                            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, UserHandle.of(userId))) {
+                        secureSetting.insertSettingLocked(Settings.Secure.INSTALL_NON_MARKET_APPS,
+                                "1", null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+                    currentVersion = 138;
+                }
 
                 if (currentVersion != newVersion) {
                     Slog.wtf("SettingsProvider", "warning: upgrading settings database to version "
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java
new file mode 100644
index 0000000..51e4373
--- /dev/null
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2017 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.providers.settings;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.SystemClock;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.List;
+
+@LargeTest
+public class InstallNonMarketAppsDeprecationTest extends BaseSettingsProviderTest {
+
+    private static final String TAG = InstallNonMarketAppsDeprecationTest.class.getSimpleName();
+    private static final long USER_RESTRICTION_CHANGE_TIMEOUT = 5000;
+
+    private UserManager mUm;
+    private boolean mHasUserRestriction;
+    private List<UserInfo> mCurrentUsers;
+
+    private String waitTillValueChanges(String errorMessage, String oldValue) {
+        boolean interrupted = false;
+        final long startTime = SystemClock.uptimeMillis();
+        String newValue = getSetting(SETTING_TYPE_SECURE, Settings.Secure.INSTALL_NON_MARKET_APPS);
+        while (newValue.equals(oldValue) && SystemClock.uptimeMillis() <= (startTime
+                + USER_RESTRICTION_CHANGE_TIMEOUT)) {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException exc) {
+                interrupted = true;
+            }
+            newValue = getSetting(SETTING_TYPE_SECURE, Settings.Secure.INSTALL_NON_MARKET_APPS);
+        }
+        if (interrupted) {
+            Thread.currentThread().interrupt();
+        }
+        assertFalse(errorMessage, oldValue.equals(newValue));
+        return newValue;
+    }
+
+    private String getSecureSettingForUserViaShell(int userId) throws IOException {
+        StringBuilder sb = new StringBuilder("settings get --user ");
+        sb.append(userId + " secure ");
+        sb.append(Settings.Secure.INSTALL_NON_MARKET_APPS);
+        BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(
+                InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+                        sb.toString()).getFileDescriptor())));
+        String line = reader.readLine();
+        return line.trim();
+    }
+
+    @Before
+    public void setUp() {
+        mUm = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+        mHasUserRestriction = mUm.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
+        mCurrentUsers = mUm.getUsers();
+    }
+
+    @Test
+    public void testValueDefaults() throws Exception {
+        if (mHasUserRestriction) {
+            // Default values don't apply when user restriction is set. Pass.
+            Log.w(TAG, "User restriction for unknown sources set. Skipping testValueDefaults test");
+            return;
+        }
+        String value = getSetting(SETTING_TYPE_SECURE, Settings.Secure.INSTALL_NON_MARKET_APPS);
+        assertEquals("install_non_market_apps should be 1", value, "1");
+
+        setSettingViaShell(SETTING_TYPE_SECURE, Settings.Secure.INSTALL_NON_MARKET_APPS, "0",
+                false);
+        resetSettingsViaShell(SETTING_TYPE_SECURE, Settings.RESET_MODE_TRUSTED_DEFAULTS);
+
+        value = getSetting(SETTING_TYPE_SECURE, Settings.Secure.INSTALL_NON_MARKET_APPS);
+        assertEquals("install_non_market_apps not reset to 1", value, "1");
+    }
+
+    @Test
+    public void testValueForNewUser() throws Exception {
+        UserInfo newUser = mUm.createUser("TEST_USER", 0);
+        String value = getSecureSettingForUserViaShell(newUser.id);
+        assertEquals("install_non_market_apps should be 1 for a new user", value, "1");
+    }
+
+    @Test
+    public void testValueRespectsUserRestriction() {
+        String value = getSetting(SETTING_TYPE_SECURE, Settings.Secure.INSTALL_NON_MARKET_APPS);
+        assertEquals(value, mHasUserRestriction ? "0" : "1");
+
+        mUm.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, !mHasUserRestriction);
+        value = waitTillValueChanges(
+                "Changing user restriction did not change the value of install_non_market_apps",
+                value);
+        assertTrue("Invalid value", value.equals("1") || value.equals("0"));
+
+        mUm.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, mHasUserRestriction);
+        value = waitTillValueChanges(
+                "Changing user restriction did not change the value of install_non_market_apps",
+                value);
+        assertTrue("Invalid value", value.equals("1") || value.equals("0"));
+    }
+
+    @After
+    public void tearDown() {
+        if (mUm.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)
+                != mHasUserRestriction) {
+            mUm.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+                    mHasUserRestriction);
+        }
+        mUm.getUsers().forEach(user -> {
+            if (!mCurrentUsers.contains(user)) {
+                mUm.removeUser(user.id);
+            }
+        });
+    }
+}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index d301463..0a1574b 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -474,11 +474,12 @@
                     }
                     break;
                 case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES:
-                    if (newValue) {
-                        android.provider.Settings.Secure.putIntForUser(cr,
-                                android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS, 0,
-                                userId);
-                    }
+                    // Since Android O, the secure setting is not available to be changed by the
+                    // user. Hence, when the restriction is cleared, we need to reset the state of
+                    // the setting to its default value which is now 1.
+                    android.provider.Settings.Secure.putIntForUser(cr,
+                            android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS,
+                            newValue ? 0 : 1, userId);
                     break;
                 case UserManager.DISALLOW_RUN_IN_BACKGROUND:
                     if (newValue) {