diff --git a/core/api/test-current.txt b/core/api/test-current.txt index f8a6af1a5c60ee791b5cfc60ba1a31e732cfd1a3..46585d71d357a8c5021a06b75a62fb7528c5e860 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1840,6 +1840,14 @@ package android.hardware.soundtrigger { } +package android.hardware.usb { + + public final class UsbPort { + method @FlaggedApi("android.hardware.usb.flags.enable_is_mode_change_supported_api") @RequiresPermission(android.Manifest.permission.MANAGE_USB) public boolean isModeChangeSupported(); + } + +} + package android.inputmethodservice { public abstract class AbstractInputMethodService extends android.window.WindowProviderService implements android.view.KeyEvent.Callback { diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index 09f5f36187d45de59fba231af821c546b43ec4e9..c0e506b05a64e746fab5563e0b18fcd9ea64fb51 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -186,6 +186,10 @@ interface IUsbManager /* Gets the status of the specified USB port. */ UsbPortStatus getPortStatus(in String portId); + /* Returns if the specified USB port supports mode change. */ + @EnforcePermission("MANAGE_USB") + boolean isModeChangeSupported(in String portId); + /* Sets the port's current role. */ void setPortRoles(in String portId, int powerRole, int dataRole); diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 81a023451f16e581d1c42db901e9946a1be54d44..41f344a03e77810aa420d7a12b26a7842c8e3d19 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -1473,6 +1473,21 @@ public class UsbManager { } } + /** + * Checks if the given port supports mode change. Should only be called by + * {@link UsbPort#isModeChangeSupported}. + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_USB) + boolean isModeChangeSupported(UsbPort port) { + try { + return mService.isModeChangeSupported(port.getId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Should only be called by {@link UsbPort#setRoles}. * diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java index 8f0149b39b9a21cd40926717cc51dc4c791500b5..03398c45e3b20dc88f263222cc3135ba6f2740ae 100644 --- a/core/java/android/hardware/usb/UsbPort.java +++ b/core/java/android/hardware/usb/UsbPort.java @@ -65,11 +65,14 @@ import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_S import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.CheckResult; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.hardware.usb.flags.Flags; import android.hardware.usb.UsbOperationInternal; import android.hardware.usb.V1_0.Constants; import android.os.Binder; @@ -374,6 +377,19 @@ public final class UsbPort { return mUsbManager.getPortStatus(this); } + /** + * Returns whether this USB port supports mode change + * + * @return true if mode change is supported. + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.MANAGE_USB) + @FlaggedApi(Flags.FLAG_ENABLE_IS_MODE_CHANGE_SUPPORTED_API) + public boolean isModeChangeSupported() { + return mUsbManager.isModeChangeSupported(this); + } + /** * Queries USB Port to see if the port is capable of identifying * non compliant USB power source/cable/accessory. diff --git a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig index cc56a311e9a43cde1731985202197c1da4079e64..a4956311995cbe1832a23b5a6cb97aa408d797f5 100644 --- a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig +++ b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig @@ -6,3 +6,10 @@ flag { description: "Feature flag for the api to check if a port is PD compliant" bug: "323470419" } + +flag { + name: "enable_is_mode_change_supported_api" + namespace: "usb" + description: "Feature flag for the api to check if a port supports mode change" + bug: "323470419" +} diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java index fc7c6a6d0258c000c93f7004771ff352ee9af722..55a89239b8642daec56e6dd81517be3686041b83 100644 --- a/services/usb/java/com/android/server/usb/UsbPortManager.java +++ b/services/usb/java/com/android/server/usb/UsbPortManager.java @@ -318,6 +318,16 @@ public class UsbPortManager implements IBinder.DeathRecipient { } } + /** + * Returns true if the provided port supports changing its mode. + */ + public boolean isModeChangeSupported(String portId) { + synchronized (mLock) { + final PortInfo portInfo = mPorts.get(portId); + return portInfo != null ? portInfo.mCanChangeMode : false; + } + } + /** * Enables/disables contaminant detection. * diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index fb13b33a30cea45b86400408f282e376a6fcd350..530a39e8b53e4509482f45650a2e5d7327947635 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -781,6 +781,20 @@ public class UsbService extends IUsbManager.Stub { } } + @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_USB) + @Override + public boolean isModeChangeSupported(String portId) { + isModeChangeSupported_enforcePermission(); + Objects.requireNonNull(portId, "portId must not be null"); + + final long ident = Binder.clearCallingIdentity(); + try { + return mPortManager != null ? mPortManager.isModeChangeSupported(portId) : false; + } finally { + Binder.restoreCallingIdentity(ident); + } + } + @Override public void setPortRoles(String portId, int powerRole, int dataRole) { Objects.requireNonNull(portId, "portId must not be null"); diff --git a/tests/UsbManagerTests/src/android/hardware/usb/UsbPortTest.java b/tests/UsbManagerTests/src/android/hardware/usb/UsbPortTest.java new file mode 100644 index 0000000000000000000000000000000000000000..afd141935d08e1e200d84a4329edb248c6d54599 --- /dev/null +++ b/tests/UsbManagerTests/src/android/hardware/usb/UsbPortTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2024 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 android.hardware.usb; + +import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE; +import static android.hardware.usb.UsbPortStatus.MODE_NONE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.hardware.usb.flags.Flags; +import android.os.RemoteException; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; + +import androidx.test.InstrumentationRegistry; + +import com.google.testing.junit.testparameterinjector.TestParameter; +import com.google.testing.junit.testparameterinjector.TestParameterInjector; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for {@link android.hardware.usb.UsbPortStatus} */ +@RunWith(TestParameterInjector.class) +public class UsbPortTest { + + private IUsbManager mMockUsbService; + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + private UsbManager mUsbManager; + + @Before + public void setUp() throws Exception { + mMockUsbService = mock(IUsbManager.class); + mUsbManager = new UsbManager(InstrumentationRegistry.getContext(), mMockUsbService); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_IS_MODE_CHANGE_SUPPORTED_API) + public void testIsModeSupported(@TestParameter boolean isModeChangeSupported) + throws RemoteException { + String testPortId = "port-1"; + when(mMockUsbService.isModeChangeSupported(testPortId)).thenReturn(isModeChangeSupported); + UsbPort usbPort = new UsbPort( + mUsbManager, + testPortId, + MODE_NONE, + CONTAMINANT_PROTECTION_NONE, + false /* supportsEnableContaminantPresenceProtection= */ , + false /* supportsEnableContaminantPresenceDetection= */); + + assertThat(usbPort.isModeChangeSupported()).isEqualTo(isModeChangeSupported); + } +}