Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 498e883d authored by Jeffrey Huang's avatar Jeffrey Huang Committed by Android (Google) Code Review
Browse files

Merge "Introduce VerifyAppsOverUsbPreferenceControllerV2"

parents f1c3e8f4 7b216bae
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -259,7 +259,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
        controllers.add(new DebugViewAttributesPreferenceController(context));
        controllers.add(new SelectDebugAppPreferenceController(context, fragment));
        controllers.add(new WaitForDebuggerPreferenceController(context));
        // verify apps over usb
        controllers.add(new VerifyAppsOverUsbPreferenceControllerV2(context));
        // logger buffer sizes
        // store logger data persistently on device
        controllers.add(new ConnectivityMonitorPreferenceControllerV2(context));
+3 −0
Original line number Diff line number Diff line
@@ -37,7 +37,10 @@ import java.util.List;

/**
 * Controller to manage the state of "Verify apps over USB" toggle.
 *
 * deprecated in favor of {@link VerifyAppsOverUsbPreferenceControllerV2}
 */
@Deprecated
public class VerifyAppsOverUsbPreferenceController extends AbstractPreferenceController implements
        PreferenceControllerMixin {
    private static final String VERIFY_APPS_OVER_USB_KEY = "verify_apps_over_usb";
+172 −0
Original line number Diff line number Diff line
/*
 * 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.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.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.wrapper.PackageManagerWrapper;

import java.util.List;

/**
 * Controller to manage the state of "Verify apps over USB" toggle.
 */
public class VerifyAppsOverUsbPreferenceControllerV2 extends
        DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
        AdbOnChangeListener {
    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;

    @VisibleForTesting
    static final int SETTING_VALUE_ON = 1;
    @VisibleForTesting
    static final int SETTING_VALUE_OFF = 0;

    /**
     * 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();

    // This field is accessed using reflection in the test, please keep name in sync.
    private final PackageManagerWrapper mPackageManager;

    public VerifyAppsOverUsbPreferenceControllerV2(Context context) {
        super(context);

        mPackageManager = new PackageManagerWrapper(context.getPackageManager());
    }

    @Override
    public boolean isAvailable() {
        return Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.PACKAGE_VERIFIER_SETTING_VISIBLE, 1 /* default */) > 0;
    }

    @Override
    public String getPreferenceKey() {
        return VERIFY_APPS_OVER_USB_KEY;
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        mPreference = (RestrictedSwitchPreference) screen.findPreference(getPreferenceKey());
    }

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        final boolean isEnabled = (Boolean) newValue;
        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB,
                isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
        return true;
    }

    @Override
    public void updateState(Preference preference) {
        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, SETTING_VALUE_ON)
                != SETTING_VALUE_OFF;
        mPreference.setChecked(checked);
    }

    @Override
    public void onAdbSettingChanged() {
        if (isAvailable()) {
            updateState(mPreference);
        }
    }

    @Override
    protected void onDeveloperOptionsSwitchEnabled() {
        updateState(mPreference);
    }

    @Override
    protected void onDeveloperOptionsSwitchDisabled() {
        // intentional no-op
        // We can rely on onAdbSettingChanged() to update this controller.
    }

    /**
     * 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,
                AdbPreferenceController.ADB_SETTING_OFF)
                == AdbPreferenceController.ADB_SETTING_OFF) {
            return false;
        }
        if (Settings.Global.getInt(cr, Settings.Global.PACKAGE_VERIFIER_ENABLE, SETTING_VALUE_ON)
                == SETTING_VALUE_OFF) {
            return false;
        } else {
            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 = mPackageManager.queryBroadcastReceivers(
                    verification, 0 /* flags */);
            if (receivers.size() == 0) {
                return false;
            }
        }
        return true;
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -47,6 +47,10 @@ import org.robolectric.util.ReflectionHelpers;
import java.util.Collections;
import java.util.List;

/**
 * deprecated in favor of {@link VerifyAppsOverUsbPreferenceControllerV2}
 */
@Deprecated
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class VerifyAppsOverUsbPreferenceControllerTest {
+211 −0
Original line number Diff line number Diff line
/*
 * 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 com.google.common.truth.Truth.assertThat;

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.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.support.v7.preference.PreferenceScreen;

import com.android.settings.TestConfig;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.wrapper.PackageManagerWrapper;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
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 VerifyAppsOverUsbPreferenceControllerV2Test {

    @Mock
    private PackageManagerWrapper mPackageManager;
    @Mock
    private PreferenceScreen mScreen;
    @Mock
    private RestrictedSwitchPreference mPreference;

    @Mock
    private VerifyAppsOverUsbPreferenceControllerV2.RestrictedLockUtilsDelegate
            mRestrictedLockUtilsDelegate;

    private Context mContext;
    private VerifyAppsOverUsbPreferenceControllerV2 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);
        mContext = RuntimeEnvironment.application;
        when(mScreen.findPreference(anyString())).thenReturn(mPreference);
        mController = new VerifyAppsOverUsbPreferenceControllerV2(mContext);
        ReflectionHelpers.setField(
                mController, "mRestrictedLockUtils", mRestrictedLockUtilsDelegate);
        ReflectionHelpers.setField(mController, "mPackageManager", mPackageManager);
        mController.displayPreference(mScreen);
    }

    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_settingEnabled_preferenceShouldBeChecked() {
        setupVerifyBroadcastReceivers(true);
        setupEnforcedAdmin(null);
        mGlobals.set(Global.ADB_ENABLED, 1 /* setting enabled */)
                .set(Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1 /* setting enabled */);
        mController.updateState(mPreference);
        verify(mPreference).setChecked(true);
    }

    @Test
    public void updateState_settingDisabled_preferenceShouldNotBeChecked() {
        setupVerifyBroadcastReceivers(true);
        setupEnforcedAdmin(null);
        mGlobals.set(Global.ADB_ENABLED, 1 /* setting enabled */)
                .set(Global.PACKAGE_VERIFIER_INCLUDE_ADB, 0 /* setting disabled */);
        mController.updateState(mPreference);
        verify(mPreference).setChecked(false);
    }

    @Test
    public void updateState_adbDisabled_preferenceShouldNotBeChecked() {
        setupVerifyBroadcastReceivers(true);
        setupEnforcedAdmin(null);
        mGlobals.set(Global.ADB_ENABLED, 0 /* setting disabled */)
                .set(Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1 /* setting enabled */);
        mController.updateState(mPreference);
        verify(mPreference).setChecked(false);
    }

    @Test
    public void updateState_verifierOff_preferenceShouldNotBeChecked() {
        setupVerifyBroadcastReceivers(true);
        setupEnforcedAdmin(null);
        mGlobals.set(Global.ADB_ENABLED, 1 /* setting enabled */)
                .set(Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1 /* setting enabled */)
                .set(Global.PACKAGE_VERIFIER_ENABLE, 0 /* setting disabled */);
        mController.updateState(mPreference);
        verify(mPreference).setChecked(false);
    }

    @Test
    public void updateState_noBroadcastReceivers_preferenceShouldNotBeChecked() {
        setupVerifyBroadcastReceivers(false);
        setupEnforcedAdmin(null);
        mGlobals.set(Global.ADB_ENABLED, 1 /* setting enabled */)
                .set(Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1 /* setting enabled */);
        mController.updateState(mPreference);
        verify(mPreference).setChecked(false);
    }

    @Test
    public void updateState_restrictedByAdmin_preferenceShouldBeDisabled() {
        setupVerifyBroadcastReceivers(true);
        final EnforcedAdmin admin = new EnforcedAdmin();
        setupEnforcedAdmin(admin);
        mGlobals.set(Global.ADB_ENABLED, 1 /* setting enabled */)
                .set(Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1 /* setting enabled */);
        mController.updateState(mPreference);
        verify(mPreference).setDisabledByAdmin(admin);
    }

    @Test
    public void isAvailable_verifierNotVisible_shouldReturnFalse() {
        setupVerifyBroadcastReceivers(true);
        mGlobals.set(Global.PACKAGE_VERIFIER_SETTING_VISIBLE, 0 /* setting disabled */);

        assertThat(mController.isAvailable()).isFalse();
    }

    @Test
    public void isAvailable_verifierVisible_shouldReturnTrue() {
        setupVerifyBroadcastReceivers(true);
        mGlobals.set(Global.PACKAGE_VERIFIER_SETTING_VISIBLE, 1 /* setting enabled */);

        assertThat(mController.isAvailable()).isTrue();
    }

    @Test
    public void onPreferenceChange_settingEnabled_shouldEnableUsbVerify() {
        mController.onPreferenceChange(mPreference, true /* new value */);

        final int mode = Settings.Global.getInt(mContext.getContentResolver(),
                android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, -1 /* default */);

        assertThat(mode).isEqualTo(VerifyAppsOverUsbPreferenceControllerV2.SETTING_VALUE_ON);
    }

    @Test
    public void onPreferenceChange_settingDisabled_shouldDisableUsbVerify() {
        mController.onPreferenceChange(mPreference, false /* new value */);

        final int mode = Settings.Global.getInt(mContext.getContentResolver(),
                android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, -1 /* default */);

        assertThat(mode).isEqualTo(VerifyAppsOverUsbPreferenceControllerV2.SETTING_VALUE_OFF);
    }

    @Test
    public void onDeveloperOptionsEnabled_shouldUpdateState() {
        mController = spy(mController);
        mController.onDeveloperOptionsSwitchEnabled();

        verify(mController).updateState(mPreference);
    }
}
 No newline at end of file