OSDN Git Service

Disable "Verify apps over usb" according to user restriction.
authorPavel Grafov <pgrafov@google.com>
Wed, 26 Apr 2017 10:54:13 +0000 (11:54 +0100)
committerPavel Grafov <pgrafov@google.com>
Wed, 26 Apr 2017 10:54:13 +0000 (11:54 +0100)
This is ag/2124876 which fails to automerge oc-dev->master, so
sending as a separate change. Will add Merged-In to the original CL
before submitting to oc-dev.

Bug: 37158436
Test: make ROBOTEST_FILTER=VerifyAppsOverUsbPreferenceControllerTest RunSettingsRoboTests
Change-Id: I52d40f4176ecd660c621ec056649c94fc29f2d4b

res/xml/development_prefs.xml
src/com/android/settings/development/DevelopmentSettings.java
src/com/android/settings/development/VerifyAppsOverUsbPreferenceController.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/development/VerifyAppsOverUsbPreferenceControllerTest.java [new file with mode: 0644]

index bec8863..0089ed7 100644 (file)
             android:title="@string/wait_for_debugger"
             android:summary="@string/wait_for_debugger_summary"/>
 
-        <SwitchPreference
+        <com.android.settingslib.RestrictedSwitchPreference
             android:key="verify_apps_over_usb"
             android:title="@string/verify_apps_over_usb_title"
             android:summary="@string/verify_apps_over_usb_summary"/>
index ea0adb9..0b18987 100644 (file)
@@ -40,7 +40,6 @@ import android.content.pm.ApplicationInfo;
 import android.content.pm.IShortcutService;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.hardware.usb.IUsbManager;
 import android.hardware.usb.UsbManager;
@@ -140,7 +139,6 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
     private static final String DEBUG_APP_KEY = "debug_app";
     private static final String WAIT_FOR_DEBUGGER_KEY = "wait_for_debugger";
     private static final String MOCK_LOCATION_APP_KEY = "mock_location_app";
-    private static final String VERIFY_APPS_OVER_USB_KEY = "verify_apps_over_usb";
     private static final String DEBUG_VIEW_ATTRIBUTES = "debug_view_attributes";
     private static final String FORCE_ALLOW_ON_EXTERNAL_KEY = "force_allow_on_external";
     private static final String STRICT_MODE_KEY = "strict_mode";
@@ -226,8 +224,6 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
 
     private static final String SHOW_ALL_ANRS_KEY = "show_all_anrs";
 
-    private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
-
     private static final String TERMINAL_APP_PACKAGE = "com.android.terminal";
 
     private static final String KEY_CONVERT_FBE = "convert_to_file_encryption";
@@ -274,7 +270,7 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
     private Preference mMockLocationAppPref;
 
     private SwitchPreference mWaitForDebugger;
-    private SwitchPreference mVerifyAppsOverUsb;
+    private VerifyAppsOverUsbPreferenceController mVerifyAppsOverUsbController;
     private SwitchPreference mWifiDisplayCertification;
     private SwitchPreference mWifiVerboseLogging;
     private SwitchPreference mWifiAggressiveHandover;
@@ -391,6 +387,7 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
         mBugReportInPowerController = new BugReportInPowerPreferenceController(getActivity());
         mTelephonyMonitorController = new TelephonyMonitorPreferenceController(getActivity());
         mWebViewAppPrefController = new WebViewAppPreferenceController(getActivity());
+        mVerifyAppsOverUsbController = new VerifyAppsOverUsbPreferenceController(getActivity());
 
         setIfOnlyAvailableForAdmins(true);
         if (isUiRestricted() || !Utils.isDeviceProvisioned(getActivity())) {
@@ -451,14 +448,8 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
         mMockLocationAppPref = findPreference(MOCK_LOCATION_APP_KEY);
         mAllPrefs.add(mMockLocationAppPref);
 
-        mVerifyAppsOverUsb = findAndInitSwitchPref(VERIFY_APPS_OVER_USB_KEY);
-        if (!showVerifierSetting()) {
-            if (debugDebuggingCategory != null) {
-                debugDebuggingCategory.removePreference(mVerifyAppsOverUsb);
-            } else {
-                mVerifyAppsOverUsb.setEnabled(false);
-            }
-        }
+        mVerifyAppsOverUsbController.displayPreference(getPreferenceScreen());
+
         mStrictMode = findAndInitSwitchPref(STRICT_MODE_KEY);
         mPointerLocation = findAndInitSwitchPref(POINTER_LOCATION_KEY);
         mShowTouches = findAndInitSwitchPref(SHOW_TOUCHES_KEY);
@@ -793,7 +784,7 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
         updateImmediatelyDestroyActivitiesOptions();
         updateAppProcessLimitOptions();
         updateShowAllANRsOptions();
-        updateVerifyAppsOverUsbOptions();
+        mVerifyAppsOverUsbController.updatePreference();
         updateOtaDisableAutomaticUpdateOptions();
         updateBugreportOptions();
         updateForceRtlOptions();
@@ -993,19 +984,6 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
         }
     }
 
-    private void updateVerifyAppsOverUsbOptions() {
-        updateSwitchPreference(mVerifyAppsOverUsb,
-                Settings.Global.getInt(getActivity().getContentResolver(),
-                        Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0);
-        mVerifyAppsOverUsb.setEnabled(enableVerifierSetting());
-    }
-
-    private void writeVerifyAppsOverUsbOptions() {
-        Settings.Global.putInt(getActivity().getContentResolver(),
-                Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB,
-                mVerifyAppsOverUsb.isChecked() ? 1 : 0);
-    }
-
     private void updateOtaDisableAutomaticUpdateOptions() {
         // We use the "disabled status" in code, but show the opposite text
         // "Automatic system updates" on screen. So a value 0 indicates the
@@ -1024,31 +1002,6 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
                 mOtaDisableAutomaticUpdate.isChecked() ? 0 : 1);
     }
 
-    private boolean enableVerifierSetting() {
-        final ContentResolver cr = getActivity().getContentResolver();
-        if (Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) == 0) {
-            return false;
-        }
-        if (Settings.Global.getInt(cr, Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 0) {
-            return false;
-        } else {
-            final PackageManager pm = getActivity().getPackageManager();
-            final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
-            verification.setType(PACKAGE_MIME_TYPE);
-            verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-            final List<ResolveInfo> receivers = pm.queryBroadcastReceivers(verification, 0);
-            if (receivers.size() == 0) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private boolean showVerifierSetting() {
-        return Settings.Global.getInt(getActivity().getContentResolver(),
-                Settings.Global.PACKAGE_VERIFIER_SETTING_VISIBLE, 1) > 0;
-    }
-
     private static boolean showEnableOemUnlockPreference(Context context) {
         return context.getSystemService(Context.OEM_LOCK_SERVICE) != null;
     }
@@ -2414,6 +2367,10 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
             return true;
         }
 
+        if (mVerifyAppsOverUsbController.handlePreferenceTreeClick(preference)) {
+            return true;
+        }
+
         if (preference == mEnableAdb) {
             if (mEnableAdb.isChecked()) {
                 mDialogClicked = false;
@@ -2428,8 +2385,7 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
             } else {
                 Settings.Global.putInt(getActivity().getContentResolver(),
                         Settings.Global.ADB_ENABLED, 0);
-                mVerifyAppsOverUsb.setEnabled(false);
-                mVerifyAppsOverUsb.setChecked(false);
+                mVerifyAppsOverUsbController.updatePreference();
                 updateBugreportOptions();
             }
         } else if (preference == mClearAdbKeys) {
@@ -2479,8 +2435,6 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
             startActivityForResult(intent, RESULT_DEBUG_APP);
         } else if (preference == mWaitForDebugger) {
             writeDebuggerOptions();
-        } else if (preference == mVerifyAppsOverUsb) {
-            writeVerifyAppsOverUsbOptions();
         } else if (preference == mOtaDisableAutomaticUpdate) {
             writeOtaDisableAutomaticUpdateOptions();
         } else if (preference == mStrictMode) {
@@ -2628,8 +2582,7 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
                 mDialogClicked = true;
                 Settings.Global.putInt(getActivity().getContentResolver(),
                         Settings.Global.ADB_ENABLED, 1);
-                mVerifyAppsOverUsb.setEnabled(true);
-                updateVerifyAppsOverUsbOptions();
+                mVerifyAppsOverUsbController.updatePreference();
                 updateBugreportOptions();
             } else {
                 // Reset the toggle
diff --git a/src/com/android/settings/development/VerifyAppsOverUsbPreferenceController.java b/src/com/android/settings/development/VerifyAppsOverUsbPreferenceController.java
new file mode 100644 (file)
index 0000000..c0bb0c7
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * 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.settings.development;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.core.PreferenceController;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import java.util.List;
+
+/**
+ * Controller to manage the state of "Verify apps over USB" toggle.
+ */
+public class VerifyAppsOverUsbPreferenceController extends PreferenceController {
+    private static final String VERIFY_APPS_OVER_USB_KEY = "verify_apps_over_usb";
+    private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
+
+    private RestrictedSwitchPreference mPreference;
+
+    /**
+     * Class for indirection of RestrictedLockUtils for testing purposes. It would be nice to mock
+     * the appropriate methods in UserManager instead but they aren't accessible.
+     */
+    @VisibleForTesting
+    class RestrictedLockUtilsDelegate {
+        public EnforcedAdmin checkIfRestrictionEnforced(
+                Context context, String userRestriction, int userId) {
+            return RestrictedLockUtils.checkIfRestrictionEnforced(context, userRestriction, userId);
+        }
+    }
+    // NB: This field is accessed using reflection in the test, please keep name in sync.
+    private final RestrictedLockUtilsDelegate mRestrictedLockUtils =
+            new RestrictedLockUtilsDelegate();
+
+    VerifyAppsOverUsbPreferenceController(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        if (isAvailable()) {
+            mPreference = (RestrictedSwitchPreference)
+                    screen.findPreference(VERIFY_APPS_OVER_USB_KEY);
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.PACKAGE_VERIFIER_SETTING_VISIBLE, 1) > 0;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return VERIFY_APPS_OVER_USB_KEY;
+    }
+
+    /** Saves the settings value when it is toggled. */
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (VERIFY_APPS_OVER_USB_KEY.equals(preference.getKey())) {
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, mPreference.isChecked() ? 1 : 0);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Checks whether the toggle should be enabled depending on whether verify apps over USB is
+     * possible currently. If ADB is disabled or if package verifier does not exist, the toggle
+     * should be disabled.
+     */
+    private boolean shouldBeEnabled() {
+        final ContentResolver cr = mContext.getContentResolver();
+        if (Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) == 0) {
+            return false;
+        }
+        if (Settings.Global.getInt(cr, Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 0) {
+            return false;
+        } else {
+            final PackageManager pm = mContext.getPackageManager();
+            final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
+            verification.setType(PACKAGE_MIME_TYPE);
+            verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            final List<ResolveInfo> receivers = pm.queryBroadcastReceivers(verification, 0);
+            if (receivers.size() == 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Updates position, enabled status and maybe admin message.
+     */
+    public void updatePreference() {
+        if (!isAvailable()) {
+            return;
+        }
+
+        if (!shouldBeEnabled()) {
+            mPreference.setChecked(false);
+            mPreference.setDisabledByAdmin(null);
+            mPreference.setEnabled(false);
+            return;
+        }
+
+        final EnforcedAdmin enforcingAdmin = mRestrictedLockUtils.checkIfRestrictionEnforced(
+                        mContext, UserManager.ENSURE_VERIFY_APPS, UserHandle.myUserId());
+        if (enforcingAdmin != null) {
+            mPreference.setChecked(true);
+            mPreference.setDisabledByAdmin(enforcingAdmin);
+            return;
+        }
+
+        mPreference.setEnabled(true);
+        final boolean checked = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
+        mPreference.setChecked(checked);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/VerifyAppsOverUsbPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/VerifyAppsOverUsbPreferenceControllerTest.java
new file mode 100644 (file)
index 0000000..d9c17bc
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * 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.settings.development;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.UserManager;
+import android.provider.Settings.Global;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class VerifyAppsOverUsbPreferenceControllerTest {
+
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private RestrictedSwitchPreference mPreference;
+
+    @Mock
+    private VerifyAppsOverUsbPreferenceController.RestrictedLockUtilsDelegate
+            mRestrictedLockUtilsDelegate;
+
+    private Context mContext;
+    private VerifyAppsOverUsbPreferenceController mController;
+
+    /** Convenience class for setting global int settings. */
+    class GlobalSetter {
+        public GlobalSetter set(String setting, int value) {
+            Global.putInt(mContext.getContentResolver(), setting, value);
+            return this;
+        }
+    }
+    private final GlobalSetter mGlobals = new GlobalSetter();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        final ShadowApplication shadowContext = ShadowApplication.getInstance();
+        mContext = spy(shadowContext.getApplicationContext());
+        when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        mController = new VerifyAppsOverUsbPreferenceController(mContext);
+        ReflectionHelpers.setField(
+                mController, "mRestrictedLockUtils", mRestrictedLockUtilsDelegate);
+    }
+
+    private void setupVerifyBroadcastReceivers(boolean nonEmpty) {
+        final List<ResolveInfo> resolveInfos = nonEmpty
+                ? Collections.singletonList(mock(ResolveInfo.class))
+                : Collections.<ResolveInfo>emptyList();
+        when(mPackageManager.queryBroadcastReceivers((Intent) any(), anyInt()))
+                .thenReturn(resolveInfos);
+    }
+
+    private void setupEnforcedAdmin(EnforcedAdmin result) {
+        when(mRestrictedLockUtilsDelegate.checkIfRestrictionEnforced(
+                (Context) any(), anyString(), anyInt())).thenReturn(result);
+    }
+
+    @Test
+    public void updateState_preferenceCheckedWhenSettingIsOn() {
+        setupVerifyBroadcastReceivers(true);
+        setupEnforcedAdmin(null);
+        mGlobals.set(Global.ADB_ENABLED, 1).set(Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1);
+        mController.displayPreference(mScreen);
+        mController.updatePreference();
+        verify(mPreference).setChecked(true);
+    }
+
+    @Test
+    public void updateState_preferenceUncheckedWhenSettingIsOff() {
+        setupVerifyBroadcastReceivers(true);
+        setupEnforcedAdmin(null);
+        mGlobals.set(Global.ADB_ENABLED, 1).set(Global.PACKAGE_VERIFIER_INCLUDE_ADB, 0);
+        mController.displayPreference(mScreen);
+        mController.updatePreference();
+        verify(mPreference).setChecked(false);
+    }
+
+    @Test
+    public void updateState_preferenceUncheckedWhenNoAdb() {
+        setupVerifyBroadcastReceivers(true);
+        setupEnforcedAdmin(null);
+        mGlobals.set(Global.ADB_ENABLED, 0).set(Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1);
+        mController.displayPreference(mScreen);
+        mController.updatePreference();
+        verify(mPreference).setChecked(false);
+    }
+
+    @Test
+    public void updateState_preferenceUncheckedWhenVerifierIsOff() {
+        setupVerifyBroadcastReceivers(true);
+        setupEnforcedAdmin(null);
+        mGlobals.set(Global.ADB_ENABLED, 1)
+                .set(Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1)
+                .set(Global.PACKAGE_VERIFIER_ENABLE, 0);
+        mController.displayPreference(mScreen);
+        mController.updatePreference();
+        verify(mPreference).setChecked(false);
+    }
+
+    @Test
+    public void updateState_preferenceUncheckedWhenNoVerifyBroadcastReceivers() {
+        setupVerifyBroadcastReceivers(false);
+        setupEnforcedAdmin(null);
+        mGlobals.set(Global.ADB_ENABLED, 1)
+                .set(Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1);
+        mController.displayPreference(mScreen);
+        mController.updatePreference();
+        verify(mPreference).setChecked(false);
+    }
+
+    @Test
+    public void updateState_preferenceDisabledWhenRestrictedByAdmin() {
+        setupVerifyBroadcastReceivers(true);
+        final EnforcedAdmin admin = new EnforcedAdmin();
+        setupEnforcedAdmin(admin);
+        mGlobals.set(Global.ADB_ENABLED, 1)
+                .set(Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1);
+        mController.displayPreference(mScreen);
+        mController.updatePreference();
+        verify(mPreference).setDisabledByAdmin(admin);
+    }
+
+    @Test
+    public void updateState_preferenceRemovedWhenVerifierSettingsVisibleIsOff() {
+        setupVerifyBroadcastReceivers(true);
+        mGlobals.set(Global.PACKAGE_VERIFIER_SETTING_VISIBLE, 0);
+        when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
+        when(mScreen.getPreferenceCount()).thenReturn(1);
+        when(mScreen.getPreference(anyInt())).thenReturn(mPreference);
+        mController.displayPreference(mScreen);
+        verify(mScreen).removePreference(mPreference);
+    }
+}
\ No newline at end of file