From cf9eee0bb516edec9b252a1364ff03fa1056f272 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Tue, 25 Jun 2024 20:15:37 +0000 Subject: [PATCH] Exclude webcam and MIDI USB preferences from requiring auth challenge Webcam / MIDI don't transfer any persistent data to the host device, so it is okay to not guard it by an auth challenge. Auth challenge for webcam use increases friction and reduces usability. Bug: 349370229 Flag: com.android.settings.flags.exclude_webcam_auth_challenge Test: Check when the flag is enabled, webcam / MIDI doesn't require auth Test: Settings robolectric tests Change-Id: Id4c97a635a4c0a9ed14f88fbdda2743e2371dd10 Signed-off-by: Jayant Chowdhary --- ..._connecteddevice_flag_declarations.aconfig | 10 +++ .../usb/UsbDetailsFunctionsController.java | 76 +++++++++++-------- .../UsbDetailsFunctionsControllerTest.java | 29 +++++++ 3 files changed, 85 insertions(+), 30 deletions(-) diff --git a/aconfig/settings_connecteddevice_flag_declarations.aconfig b/aconfig/settings_connecteddevice_flag_declarations.aconfig index 693e3985af8..b2550965371 100644 --- a/aconfig/settings_connecteddevice_flag_declarations.aconfig +++ b/aconfig/settings_connecteddevice_flag_declarations.aconfig @@ -38,3 +38,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "exclude_webcam_auth_challenge" + namespace: "safety_center" + description: "Gates whether to exclude webcam from USB preferences auth challenge." + bug: "349370229" + metadata { + purpose: PURPOSE_BUGFIX + } +} \ No newline at end of file diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java index 04fab7d7d57..ae9dbeb7a4b 100644 --- a/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java +++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java @@ -31,6 +31,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.widget.SelectorWithWidgetPreference; import java.util.LinkedHashMap; @@ -130,39 +131,54 @@ public class UsbDetailsFunctionsController extends UsbDetailsController @Override public void onRadioButtonClicked(SelectorWithWidgetPreference preference) { - requireAuthAndExecute(() -> { - final long function = UsbBackend.usbFunctionsFromString(preference.getKey()); - final long previousFunction = mUsbBackend.getCurrentFunctions(); - if (DEBUG) { - Log.d(TAG, "onRadioButtonClicked() function : " + function + ", toString() : " - + UsbManager.usbFunctionsToString(function) + ", previousFunction : " - + previousFunction + ", toString() : " - + UsbManager.usbFunctionsToString(previousFunction)); - } - if (function != previousFunction && !Utils.isMonkeyRunning() - && !isClickEventIgnored(function, previousFunction)) { - mPreviousFunction = previousFunction; + final long function = UsbBackend.usbFunctionsFromString(preference.getKey()); + if (isAuthRequired(function)) { + requireAuthAndExecute(()->handleRadioButtonClicked(preference, function)); + } else { + handleRadioButtonClicked(preference, function); + } + } - //Update the UI in advance to make it looks smooth - final SelectorWithWidgetPreference prevPref = - (SelectorWithWidgetPreference) mProfilesContainer.findPreference( - UsbBackend.usbFunctionsToString(mPreviousFunction)); - if (prevPref != null) { - prevPref.setChecked(false); - preference.setChecked(true); - } + private void handleRadioButtonClicked(SelectorWithWidgetPreference preference, long function) { + final long previousFunction = mUsbBackend.getCurrentFunctions(); + if (DEBUG) { + Log.d(TAG, "onRadioButtonClicked() function : " + function + ", toString() : " + + UsbManager.usbFunctionsToString(function) + ", previousFunction : " + + previousFunction + ", toString() : " + + UsbManager.usbFunctionsToString(previousFunction)); + } + if (function != previousFunction && !Utils.isMonkeyRunning() + && !isClickEventIgnored(function, previousFunction)) { + mPreviousFunction = previousFunction; - if (function == UsbManager.FUNCTION_RNDIS || function == UsbManager.FUNCTION_NCM) { - // We need to have entitlement check for usb tethering, so use API in - // TetheringManager. - mTetheringManager.startTethering( - TetheringManager.TETHERING_USB, new HandlerExecutor(mHandler), - mOnStartTetheringCallback); - } else { - mUsbBackend.setCurrentFunctions(function); - } + //Update the UI in advance to make it looks smooth + final SelectorWithWidgetPreference prevPref = + (SelectorWithWidgetPreference) mProfilesContainer.findPreference( + UsbBackend.usbFunctionsToString(mPreviousFunction)); + if (prevPref != null) { + prevPref.setChecked(false); + preference.setChecked(true); } - }); + + if (function == UsbManager.FUNCTION_RNDIS || function == UsbManager.FUNCTION_NCM) { + // We need to have entitlement check for usb tethering, so use API in + // TetheringManager. + mTetheringManager.startTethering( + TetheringManager.TETHERING_USB, new HandlerExecutor(mHandler), + mOnStartTetheringCallback); + } else { + mUsbBackend.setCurrentFunctions(function); + } + } + } + + private boolean isAuthRequired(long function) { + if (!Flags.excludeWebcamAuthChallenge()) { + return true; + } + // Since webcam and MIDI don't transfer any persistent data over USB + // don't require authentication. + return !(function == UsbManager.FUNCTION_UVC || function == UsbManager.FUNCTION_MIDI); } private boolean isClickEventIgnored(long function, long previousFunction) { diff --git a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsControllerTest.java index eea4f52388b..9427bbe2838 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsControllerTest.java @@ -35,6 +35,8 @@ import android.content.Context; import android.hardware.usb.UsbManager; import android.net.TetheringManager; import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import androidx.fragment.app.FragmentActivity; import androidx.preference.PreferenceCategory; @@ -48,6 +50,7 @@ import com.android.settingslib.widget.SelectorWithWidgetPreference; import org.junit.Before; import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -82,6 +85,8 @@ public class UsbDetailsFunctionsControllerTest { private FragmentActivity mActivity; @Mock private TetheringManager mTetheringManager; + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Before public void setUp() { @@ -349,6 +354,30 @@ public class UsbDetailsFunctionsControllerTest { assertThat(mFragment.isUserAuthenticated()).isTrue(); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_EXCLUDE_WEBCAM_AUTH_CHALLENGE) + public void onRadioButtonClicked_webcamNoAuthNeeded() { + mRadioButtonPreference.setKey(UsbBackend.usbFunctionsToString(UsbManager.FUNCTION_UVC)); + doReturn(UsbManager.FUNCTION_MTP).when(mUsbBackend).getCurrentFunctions(); + setAuthPassesAutomatically(); + + mDetailsFunctionsController.onRadioButtonClicked(mRadioButtonPreference); + + assertThat(mFragment.isUserAuthenticated()).isFalse(); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_EXCLUDE_WEBCAM_AUTH_CHALLENGE) + public void onRadioButtonClicked_MidiNoAuthNeeded() { + mRadioButtonPreference.setKey(UsbBackend.usbFunctionsToString(UsbManager.FUNCTION_MIDI)); + doReturn(UsbManager.FUNCTION_MTP).when(mUsbBackend).getCurrentFunctions(); + setAuthPassesAutomatically(); + + mDetailsFunctionsController.onRadioButtonClicked(mRadioButtonPreference); + + assertThat(mFragment.isUserAuthenticated()).isFalse(); + } + private void setAuthPassesAutomatically() { Shadows.shadowOf(mContext.getSystemService(KeyguardManager.class)) .setIsKeyguardSecure(false);