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

Commit bff492ba authored by Irem Uguz's avatar Irem Uguz
Browse files

Add policy transparency UI to USB connections

Update the preferences for USB connected devices to be of
RestrictedSelectorWithWidgetPreference class to display user restriction
on the UI transparently.

Bug: 330128955
Test: Manual test by device owner provisioning on TestDPC and setting
USB file transfer and config tethering disallowed. Then check Settings.
See recording in http://screencast/cast/NDc5MDMyMjY1ODgwMzcxMnxjNzhhMTQ0Ny02ZA
Flag: EXEMPT bugfix

Change-Id: Ie229a6e68caafec42b80439ac3eb2f45398d23c6
parent 225beade
Loading
Loading
Loading
Loading
+18 −17
Original line number Diff line number Diff line
@@ -47,9 +47,7 @@ public class UsbBackend {
    static final int PD_ROLE_SWAP_TIMEOUT_MS = 4000;
    static final int NONPD_ROLE_SWAP_TIMEOUT_MS = 15000;

    private final boolean mFileTransferRestricted;
    private final boolean mFileTransferRestrictedBySystem;
    private final boolean mTetheringRestricted;
    private final boolean mTetheringRestrictedBySystem;
    private final boolean mMidiSupported;
    private final boolean mTetheringSupported;
@@ -71,9 +69,7 @@ public class UsbBackend {
    public UsbBackend(Context context, UserManager userManager) {
        mUsbManager = context.getSystemService(UsbManager.class);

        mFileTransferRestricted = isUsbFileTransferRestricted(userManager);
        mFileTransferRestrictedBySystem = isUsbFileTransferRestrictedBySystem(userManager);
        mTetheringRestricted = isUsbTetheringRestricted(userManager);
        mTetheringRestrictedBySystem = isUsbTetheringRestrictedBySystem(userManager);
        mUVCEnabled = isUvcEnabled();
        mIsAdminUser = userManager.isAdminUser();
@@ -105,7 +101,7 @@ public class UsbBackend {
                || (!mTetheringSupported && (functions & UsbManager.FUNCTION_RNDIS) != 0)) {
            return false;
        }
        return !(areFunctionDisallowed(functions) || areFunctionsDisallowedBySystem(functions)
        return !(areFunctionsDisallowedBySystem(functions)
                || areFunctionsDisallowedByNonAdminUser(functions));
    }

@@ -183,12 +179,23 @@ public class UsbBackend {
        return Integer.parseInt(role);
    }

    private static boolean isUsbFileTransferRestricted(UserManager userManager) {
        return userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
    /**
     * Returns the user restriction that's applied to {@code function} if it exists. Returns null if
     * there isn't a user restriction associated.
     *
     * @param function the USB function to check restriction for
     * @return user restriction that's associated withe the function
     */
    @Nullable
    public static String maybeGetUserRestriction(long function) {
        if ((function & UsbManager.FUNCTION_MTP) != 0
                || (function & UsbManager.FUNCTION_PTP) != 0) {
            return UserManager.DISALLOW_USB_FILE_TRANSFER;
        }

    private static boolean isUsbTetheringRestricted(UserManager userManager) {
        return userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
        if ((function & UsbManager.FUNCTION_RNDIS) != 0) {
            return UserManager.DISALLOW_CONFIG_TETHERING;
        }
        return null;
    }

    private static boolean isUsbFileTransferRestrictedBySystem(UserManager userManager) {
@@ -205,12 +212,6 @@ public class UsbBackend {
        return UsbManager.isUvcSupportEnabled();
    }

    private boolean areFunctionDisallowed(long functions) {
        return (mFileTransferRestricted && ((functions & UsbManager.FUNCTION_MTP) != 0
                || (functions & UsbManager.FUNCTION_PTP) != 0))
                || (mTetheringRestricted && ((functions & UsbManager.FUNCTION_RNDIS) != 0));
    }

    private boolean areFunctionsDisallowedBySystem(long functions) {
        return (mFileTransferRestrictedBySystem && ((functions & UsbManager.FUNCTION_MTP) != 0
                || (functions & UsbManager.FUNCTION_PTP) != 0))
+6 −2
Original line number Diff line number Diff line
@@ -35,9 +35,9 @@ import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.development.DeveloperOptionAwareMixin;
import com.android.settings.widget.RadioButtonPickerFragment;
import com.android.settingslib.RestrictedSelectorWithWidgetPreference;
import com.android.settingslib.widget.CandidateInfo;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.SelectorWithWidgetPreference;

import com.google.android.collect.Lists;

@@ -220,11 +220,15 @@ public class UsbDefaultFragment extends RadioButtonPickerFragment implements
    private void refresh(long functions) {
        final PreferenceScreen screen = getPreferenceScreen();
        for (long option : UsbDetailsFunctionsController.FUNCTIONS_MAP.keySet()) {
            final SelectorWithWidgetPreference pref =
            final RestrictedSelectorWithWidgetPreference pref =
                    screen.findPreference(UsbBackend.usbFunctionsToString(option));
            if (pref != null) {
                final boolean isSupported = mUsbBackend.areFunctionsSupported(option);
                pref.setEnabled(isSupported);
                String userRestriction = mUsbBackend.maybeGetUserRestriction(option);
                if (userRestriction != null) {
                    pref.checkRestrictionAndSetDisabled(userRestriction);
                }
                if (isSupported) {
                    if (functions == UsbManager.FUNCTION_NCM) {
                        pref.setChecked(UsbManager.FUNCTION_RNDIS == option);
+20 −11
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.flags.Flags;
import com.android.settingslib.RestrictedSelectorWithWidgetPreference;
import com.android.settingslib.widget.SelectorWithWidgetPreference;

import java.util.LinkedHashMap;
@@ -82,13 +83,11 @@ public class UsbDetailsFunctionsController extends UsbDetailsController
                /* powerRole */ 0, /* dataRole */ 0);
    }

    /**
     * Gets a switch preference for the particular option, creating it if needed.
     */
    private SelectorWithWidgetPreference getProfilePreference(String key, int titleId) {
        SelectorWithWidgetPreference pref = mProfilesContainer.findPreference(key);
    /** Gets a switch preference for the particular option, creating it if needed. */
    private RestrictedSelectorWithWidgetPreference getProfilePreference(String key, int titleId) {
        RestrictedSelectorWithWidgetPreference pref = mProfilesContainer.findPreference(key);
        if (pref == null) {
            pref = new SelectorWithWidgetPreference(mProfilesContainer.getContext());
            pref = new RestrictedSelectorWithWidgetPreference(mProfilesContainer.getContext());
            pref.setKey(key);
            pref.setTitle(titleId);
            pref.setSingleLineTitle(false);
@@ -110,10 +109,11 @@ public class UsbDetailsFunctionsController extends UsbDetailsController
            // Functions are only available in device mode
            mProfilesContainer.setEnabled(true);
        }
        SelectorWithWidgetPreference pref;
        RestrictedSelectorWithWidgetPreference pref;
        for (long option : FUNCTIONS_MAP.keySet()) {
            int title = FUNCTIONS_MAP.get(option);
            pref = getProfilePreference(UsbBackend.usbFunctionsToString(option), title);
            checkUserRestrictions(option, pref);
            // Only show supported options
            if (mUsbBackend.areFunctionsSupported(option)) {
                if (isAccessoryMode(functions)) {
@@ -152,8 +152,9 @@ public class UsbDetailsFunctionsController extends UsbDetailsController
            mPreviousFunction = previousFunction;

            // Update the UI in advance to make it looks smooth
            final SelectorWithWidgetPreference prevPref =
                    (SelectorWithWidgetPreference) mProfilesContainer.findPreference(
            final RestrictedSelectorWithWidgetPreference prevPref =
                    (RestrictedSelectorWithWidgetPreference)
                            mProfilesContainer.findPreference(
                                    UsbBackend.usbFunctionsToString(mPreviousFunction));
            if (prevPref != null) {
                prevPref.setChecked(false);
@@ -189,6 +190,14 @@ public class UsbDetailsFunctionsController extends UsbDetailsController
        return (function & UsbManager.FUNCTION_ACCESSORY) != 0;
    }

    private void checkUserRestrictions(long option, RestrictedSelectorWithWidgetPreference pref) {
        String userRestriction = UsbBackend.maybeGetUserRestriction(option);
        if (userRestriction == null) {
            return;
        }
        pref.checkRestrictionAndSetDisabled(userRestriction);
    }

    @Override
    public boolean isAvailable() {
        return !Utils.isMonkeyRunning();
+45 −1
Original line number Diff line number Diff line
@@ -157,7 +157,7 @@ public class UsbBackendTest {
    }

    @Test
    public void areFunctionsSupported_fileTransferDisallowed_shouldReturnFalse() {
    public void areFunctionsSupported_fileTransferDisallowedByBaseRestriction_shouldReturnFalse() {
        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER))
                .thenReturn(true);
        when(mUserManager.hasBaseUserRestriction(
@@ -169,6 +169,19 @@ public class UsbBackendTest {
        assertThat(usbBackend.areFunctionsSupported(UsbManager.FUNCTION_MTP)).isFalse();
    }

    @Test
    public void areFunctionsSupported_fileTransferDisallowedByAdminRestriction_shouldReturnTrue() {
        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER))
                .thenReturn(true);
        when(mUserManager.hasBaseUserRestriction(
                eq(UserManager.DISALLOW_USB_FILE_TRANSFER), any(UserHandle.class)))
                .thenReturn(false);

        final UsbBackend usbBackend = new UsbBackend(mContext, mUserManager);

        assertThat(usbBackend.areFunctionsSupported(UsbManager.FUNCTION_MTP)).isTrue();
    }

    @Test
    public void areFunctionsSupported_fileTransferAllowed_shouldReturnTrue() {
        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER))
@@ -201,4 +214,35 @@ public class UsbBackendTest {
        assertThat(usbBackend.areFunctionsDisallowedByNonAdminUser(
                UsbManager.FUNCTION_RNDIS)).isTrue();
    }

    @Test
    public void maybeGetUserRestriction_functionMtp_returnsFileTransferRestriction() {
        final UsbBackend usbBackend = new UsbBackend(mContext, mUserManager);

        assertThat(usbBackend.maybeGetUserRestriction(UsbManager.FUNCTION_MTP)).isEqualTo(
                UserManager.DISALLOW_USB_FILE_TRANSFER);
    }

    @Test
    public void maybeGetUserRestriction_functionPtp_returnsFileTransferRestriction() {
        final UsbBackend usbBackend = new UsbBackend(mContext, mUserManager);

        assertThat(usbBackend.maybeGetUserRestriction(UsbManager.FUNCTION_PTP)).isEqualTo(
                UserManager.DISALLOW_USB_FILE_TRANSFER);
    }

    @Test
    public void maybeGetUserRestriction_functionRndis_returnsTetheringRestriction() {
        final UsbBackend usbBackend = new UsbBackend(mContext, mUserManager);

        assertThat(usbBackend.maybeGetUserRestriction(UsbManager.FUNCTION_RNDIS)).isEqualTo(
                UserManager.DISALLOW_CONFIG_TETHERING);
    }

    @Test
    public void maybeGetUserRestriction_functionUvc_returnsNull() {
        final UsbBackend usbBackend = new UsbBackend(mContext, mUserManager);

        assertThat(usbBackend.maybeGetUserRestriction(UsbManager.FUNCTION_UVC)).isNull();
    }
}
+46 −0
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@ import android.app.KeyguardManager;
import android.content.Context;
import android.hardware.usb.UsbManager;
import android.net.TetheringManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -44,6 +46,8 @@ import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;

import com.android.settings.flags.Flags;
import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settings.testutils.shadow.ShadowUtils;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
@@ -67,6 +71,8 @@ import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
        com.android.settings.testutils.shadow.ShadowFragment.class,
        ShadowUserManager.class,
        ShadowDevicePolicyManager.class,
})
public class UsbDetailsFunctionsControllerTest {

@@ -192,6 +198,34 @@ public class UsbDetailsFunctionsControllerTest {
        assertThat(prefs.get(1).isChecked()).isTrue();
    }

    @Test
    public void displayRefresh_fileTransferRestricted_shouldDisablePref() {
        when(mUsbBackend.areFunctionsSupported(anyLong())).thenReturn(true);
        setUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);

        mDetailsFunctionsController.refresh(true, UsbManager.FUNCTION_MTP, POWER_ROLE_SINK,
                DATA_ROLE_DEVICE);
        List<SelectorWithWidgetPreference> prefs = getRadioPreferences();

        assertThat(prefs.get(0).getKey())
                .isEqualTo(UsbBackend.usbFunctionsToString(UsbManager.FUNCTION_MTP));
        assertThat(prefs.get(0).isEnabled()).isFalse();
    }

    @Test
    public void displayRefresh_tetheringRestricted_shouldDisablePref() {
        when(mUsbBackend.areFunctionsSupported(anyLong())).thenReturn(true);
        setUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);

        mDetailsFunctionsController.refresh(true, UsbManager.FUNCTION_NCM, POWER_ROLE_SINK,
                DATA_ROLE_DEVICE);
        List<SelectorWithWidgetPreference> prefs = getRadioPreferences();

        assertThat(prefs.get(1).getKey())
                .isEqualTo(UsbBackend.usbFunctionsToString(UsbManager.FUNCTION_RNDIS));
        assertThat(prefs.get(1).isEnabled()).isFalse();
    }

    @Test
    public void onClickMtp_noneEnabled_shouldEnableMtp() {
        when(mUsbBackend.areFunctionsSupported(anyLong())).thenReturn(true);
@@ -265,6 +299,18 @@ public class UsbDetailsFunctionsControllerTest {
        return result;
    }

    private void setUserRestriction(String userRestriction) {
        // For RestrictedLockUtils.checkIfRestrictionEnforced
        final int userId = UserHandle.myUserId();
        List<UserManager.EnforcingUser> enforcingUsers = new ArrayList<>();
        enforcingUsers.add(
                new UserManager.EnforcingUser(userId, UserManager.RESTRICTION_SOURCE_DEVICE_OWNER));
        ShadowUserManager.getShadow().setUserRestrictionSources(
                userRestriction,
                UserHandle.of(userId),
                enforcingUsers);
    }

    @Test
    public void onRadioButtonClicked_functionRndis_startTetheringInvoked() {
        mRadioButtonPreference.setKey(UsbBackend.usbFunctionsToString(UsbManager.FUNCTION_RNDIS));