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);