diff options
4 files changed, 104 insertions, 87 deletions
diff --git a/core/java/android/hardware/usb/IUsbManagerInternal.aidl b/core/java/android/hardware/usb/IUsbManagerInternal.aidl new file mode 100644 index 000000000000..32479d449f24 --- /dev/null +++ b/core/java/android/hardware/usb/IUsbManagerInternal.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2025 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 android.hardware.usb.IUsbOperationInternal; + +/** @hide */ +interface IUsbManagerInternal { + + /* Disable/enable USB data on a port for System Service callers. */ + boolean enableUsbDataSignal(boolean enable, int disableReason); +} diff --git a/services/usb/java/com/android/server/usb/UsbManagerInternal.java b/services/usb/java/com/android/server/usb/UsbManagerInternal.java deleted file mode 100644 index 31c5986c45b8..000000000000 --- a/services/usb/java/com/android/server/usb/UsbManagerInternal.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 com.android.server.usb; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.hardware.usb.IUsbOperationInternal; -import android.hardware.usb.UsbPort; -import android.util.ArraySet; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * UsbManagerInternal provides internal APIs for the UsbService to - * reduce IPC overhead costs and support internal USB data signal stakers. - * - * @hide Only for use within the system server. - */ -public abstract class UsbManagerInternal { - - public static final int OS_USB_DISABLE_REASON_AAPM = 0; - public static final int OS_USB_DISABLE_REASON_LOCKDOWN_MODE = 1; - - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = {OS_USB_DISABLE_REASON_AAPM, - OS_USB_DISABLE_REASON_LOCKDOWN_MODE}) - public @interface OsUsbDisableReason { - } - - public abstract boolean enableUsbData(String portId, boolean enable, - int operationId, IUsbOperationInternal callback, @OsUsbDisableReason int disableReason); - - public abstract UsbPort[] getPorts(); - -}
\ No newline at end of file diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index 4395b76d91cc..7808b2e318b2 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -27,7 +27,10 @@ import static android.hardware.usb.UsbPortStatus.MODE_UFP; import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK; import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE; +import android.hardware.usb.IUsbManagerInternal; + import android.annotation.NonNull; +import android.annotation.IntDef; import android.annotation.UserIdInt; import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; @@ -80,12 +83,16 @@ import dalvik.annotation.optimization.NeverCompile; import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; + /** * UsbService manages all USB related state, including both host and device support. * Host related events and calls are delegated to UsbHostManager, and device related @@ -93,6 +100,15 @@ import java.util.concurrent.CompletableFuture; */ public class UsbService extends IUsbManager.Stub { + public static final int OS_USB_DISABLE_REASON_AAPM = 0; + public static final int OS_USB_DISABLE_REASON_LOCKDOWN_MODE = 1; + + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = {OS_USB_DISABLE_REASON_AAPM, + OS_USB_DISABLE_REASON_LOCKDOWN_MODE}) + public @interface OsUsbDisableReason { + } + public static class Lifecycle extends SystemService { private UsbService mUsbService; private final CompletableFuture<Void> mOnStartFinished = new CompletableFuture<>(); @@ -227,7 +243,7 @@ public class UsbService extends IUsbManager.Stub { filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); mContext.registerReceiverAsUser(receiver, UserHandle.ALL, filter, null, null); if(android.hardware.usb.flags.Flags.enableUsbDataSignalStakingInternal()) { - LocalServices.addService(UsbManagerInternal.class, new UsbManagerInternalImpl()); + LocalServices.addService(IUsbManagerInternal.class, new UsbManagerInternalImpl()); } } @@ -246,7 +262,7 @@ public class UsbService extends IUsbManager.Stub { mPermissionManager = new UsbPermissionManager(context, this); if(android.hardware.usb.flags.Flags.enableUsbDataSignalStakingInternal()) { - LocalServices.addService(UsbManagerInternal.class, new UsbManagerInternalImpl()); + LocalServices.addService(IUsbManagerInternal.class, new UsbManagerInternalImpl()); } } @@ -1536,24 +1552,30 @@ public class UsbService extends IUsbManager.Stub { enableUsbDataInternal(port.getId(), !lockDownTriggeredByUser, STRONG_AUTH_OPERATION_ID, new IUsbOperationInternal.Default(), - UsbManagerInternal.OS_USB_DISABLE_REASON_LOCKDOWN_MODE, + OS_USB_DISABLE_REASON_LOCKDOWN_MODE, true); } } } - private class UsbManagerInternalImpl extends UsbManagerInternal { - @Override - public boolean enableUsbData(String portId, boolean enable, - int operationId, IUsbOperationInternal callback, - @OsUsbDisableReason int disableReason) { - return enableUsbDataInternal(portId, enable, operationId, callback, - disableReason, true); - } + private class UsbManagerInternalImpl extends IUsbManagerInternal.Stub { + private static final AtomicInteger sUsbOperationCount = new AtomicInteger(); @Override - public UsbPort[] getPorts() { - return mPortManager.getPorts(); + public boolean enableUsbDataSignal(boolean enable, + @OsUsbDisableReason int disableReason) { + boolean result = true; + int operationId = sUsbOperationCount.incrementAndGet() + disableReason; + for (UsbPort port : mPortManager.getPorts()) { + boolean success = enableUsbDataInternal(port.getId(), enable, operationId, + new IUsbOperationInternal.Default(), disableReason, true); + if(!success) { + Slog.e(TAG, "enableUsbDataInternal failed to change USB port " + + port.getId() + "state to " + enable); + } + result &= success; + } + return result; } } } diff --git a/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java index 51d57f0a0de9..f5d4b0c5e345 100644 --- a/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java +++ b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java @@ -24,15 +24,20 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.content.Context; +import android.hardware.usb.IUsbManagerInternal; import android.hardware.usb.IUsbOperationInternal; import android.hardware.usb.flags.Flags; import android.hardware.usb.UsbPort; @@ -70,7 +75,9 @@ public class UsbServiceTest { @Mock private IUsbOperationInternal mCallback; - private static final String TEST_PORT_ID = "123"; + private static final String TEST_PORT_ID = "1"; + + private static final String TEST_PORT_ID_2 = "2"; private static final int TEST_TRANSACTION_ID = 1; @@ -84,7 +91,7 @@ public class UsbServiceTest { private UsbService mUsbService; - private UsbManagerInternal mUsbManagerInternal; + private IUsbManagerInternal mIUsbManagerInternal; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -101,9 +108,9 @@ public class UsbServiceTest { mUsbService = new UsbService(mContext, mUsbPortManager, mUsbAlsaManager, mUserManager, mUsbSettingsManager); - mUsbManagerInternal = LocalServices.getService(UsbManagerInternal.class); - assertWithMessage("LocalServices.getService(UsbManagerInternal.class)") - .that(mUsbManagerInternal).isNotNull(); + mIUsbManagerInternal = LocalServices.getService(IUsbManagerInternal.class); + assertWithMessage("LocalServices.getService(IUsbManagerInternal.class)") + .that(mIUsbManagerInternal).isNotNull(); } private void assertToggleUsbSuccessfully(int requester, boolean enable, @@ -255,30 +262,42 @@ public class UsbServiceTest { assertToggleUsbSuccessfully(TEST_INTERNAL_REQUESTER_REASON_1, true, false); } - /** - * Verify USB Manager internal calls mPortManager to get UsbPorts - */ @Test - public void usbManagerInternal_getPorts_callsPortManager() { - when(mUsbPortManager.getPorts()).thenReturn(new UsbPort[] {}); - - UsbPort[] ports = mUsbManagerInternal.getPorts(); - - verify(mUsbPortManager).getPorts(); - assertEquals(ports.length, 0); + public void usbManagerInternal_enableUsbDataSignal_successfullyEnabled() { + assertTrue(runInternalUsbDataSignalTest(true, true, true)); } @Test - public void usbManagerInternal_enableUsbData_successfullyEnable() { - boolean desiredEnableState = true; + public void usbManagerInternal_enableUsbDataSignal_successfullyDisabled() { + assertTrue(runInternalUsbDataSignalTest(false, true, true)); + } - assertTrue(mUsbManagerInternal.enableUsbData(TEST_PORT_ID, desiredEnableState, - TEST_TRANSACTION_ID, mCallback, TEST_INTERNAL_REQUESTER_REASON_1)); + @Test + public void usbManagerInternal_enableUsbDataSignal_returnsFalseIfOnePortFails() { + assertFalse(runInternalUsbDataSignalTest(true, true, false)); + } - verify(mUsbPortManager).enableUsbData(TEST_PORT_ID, - desiredEnableState, TEST_TRANSACTION_ID, mCallback, null); - verifyZeroInteractions(mCallback); - clearInvocations(mUsbPortManager); - clearInvocations(mCallback); + private boolean runInternalUsbDataSignalTest(boolean desiredEnableState, boolean portOneSuccess, + boolean portTwoSuccess) { + UsbPort port = mock(UsbPort.class); + UsbPort port2 = mock(UsbPort.class); + when(port.getId()).thenReturn(TEST_PORT_ID); + when(port2.getId()).thenReturn(TEST_PORT_ID_2); + when(mUsbPortManager.getPorts()).thenReturn(new UsbPort[] { port, port2 }); + when(mUsbPortManager.enableUsbData(eq(TEST_PORT_ID), + eq(desiredEnableState), anyInt(), any(IUsbOperationInternal.class), isNull())) + .thenReturn(portOneSuccess); + when(mUsbPortManager.enableUsbData(eq(TEST_PORT_ID_2), + eq(desiredEnableState), anyInt(), any(IUsbOperationInternal.class), isNull())) + .thenReturn(portTwoSuccess); + try { + boolean result = mIUsbManagerInternal.enableUsbDataSignal(desiredEnableState, + TEST_INTERNAL_REQUESTER_REASON_1); + clearInvocations(mUsbPortManager); + return result; + } catch(RemoteException e) { + fail("RemoteException thrown when calling enableUsbDataSignal"); + return false; + } } } |