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