diff options
| author | 2024-04-04 08:03:08 +0000 | |
|---|---|---|
| committer | 2024-04-04 08:03:08 +0000 | |
| commit | 0aeed08397a19fac2fff51b801c713fb1bdd5d0b (patch) | |
| tree | 83fec65b781df04ec7dce8edd75199ae249836a9 | |
| parent | 65dc94c7e4e207568180e629f993fd25e1117cd2 (diff) | |
| parent | ae1ee0df6f3acfa5e955fcff410085597f3120b0 (diff) | |
Merge "Adding unit test for UsbService anti-clobbering feature." into main
| -rw-r--r-- | services/usb/java/com/android/server/usb/UsbService.java | 42 | ||||
| -rw-r--r-- | tests/UsbTests/Android.bp | 2 | ||||
| -rw-r--r-- | tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java | 174 |
3 files changed, 214 insertions, 4 deletions
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index 9b5612eacc13..9470c0a944c2 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -60,6 +60,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; @@ -222,6 +223,21 @@ public class UsbService extends IUsbManager.Stub { mContext.registerReceiverAsUser(receiver, UserHandle.ALL, filter, null, null); } + // Ideally we should use the injector pattern so we wouldn't need this constructor for test + @VisibleForTesting + UsbService(Context context, + UsbPortManager usbPortManager, + UsbAlsaManager usbAlsaManager, + UserManager userManager, + UsbSettingsManager usbSettingsManager) { + mContext = context; + mPortManager = usbPortManager; + mAlsaManager = usbAlsaManager; + mUserManager = userManager; + mSettingsManager = usbSettingsManager; + mPermissionManager = new UsbPermissionManager(context, this); + } + /** * Set new {@link #mCurrentUserId} and propagate it to other modules. * @@ -886,7 +902,16 @@ public class UsbService extends IUsbManager.Stub { @Override public boolean enableUsbData(String portId, boolean enable, int operationId, - IUsbOperationInternal callback) { + IUsbOperationInternal callback) { + return enableUsbDataInternal(portId, enable, operationId, callback, Binder.getCallingUid()); + } + + /** + * Internal function abstracted for testing with callerUid + */ + @VisibleForTesting + boolean enableUsbDataInternal(String portId, boolean enable, int operationId, + IUsbOperationInternal callback, int callerUid) { Objects.requireNonNull(portId, "enableUsbData: portId must not be null. opId:" + operationId); Objects.requireNonNull(callback, "enableUsbData: callback must not be null. opId:" @@ -894,7 +919,7 @@ public class UsbService extends IUsbManager.Stub { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); if (android.hardware.usb.flags.Flags.enableUsbDataSignalStaking()) { - if (!shouldUpdateUsbSignaling(portId, enable, Binder.getCallingUid())) { + if (!shouldUpdateUsbSignaling(portId, enable, callerUid)) { try { callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL); } catch (RemoteException e) { @@ -950,7 +975,16 @@ public class UsbService extends IUsbManager.Stub { @Override public void enableUsbDataWhileDocked(String portId, int operationId, - IUsbOperationInternal callback) { + IUsbOperationInternal callback) { + enableUsbDataWhileDockedInternal(portId, operationId, callback, Binder.getCallingUid()); + } + + /** + * Internal function abstracted for testing with callerUid + */ + @VisibleForTesting + void enableUsbDataWhileDockedInternal(String portId, int operationId, + IUsbOperationInternal callback, int callerUid) { Objects.requireNonNull(portId, "enableUsbDataWhileDocked: portId must not be null. opId:" + operationId); Objects.requireNonNull(callback, @@ -959,7 +993,7 @@ public class UsbService extends IUsbManager.Stub { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); if (android.hardware.usb.flags.Flags.enableUsbDataSignalStaking()) { - if (!shouldUpdateUsbSignaling(portId, true, Binder.getCallingUid())) { + if (!shouldUpdateUsbSignaling(portId, true, callerUid)) { try { callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL); } catch (RemoteException e) { diff --git a/tests/UsbTests/Android.bp b/tests/UsbTests/Android.bp index 92c271165ad7..95a4c91bb5de 100644 --- a/tests/UsbTests/Android.bp +++ b/tests/UsbTests/Android.bp @@ -36,6 +36,8 @@ android_test { "services.usb", "truth", "UsbManagerTestLib", + "android.hardware.usb.flags-aconfig-java", + "flag-junit", ], jni_libs: [ // Required for ExtendedMockito diff --git a/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java new file mode 100644 index 000000000000..b506d74d6500 --- /dev/null +++ b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java @@ -0,0 +1,174 @@ +/* + * 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 static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +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.IUsbOperationInternal; +import android.hardware.usb.flags.Flags; +import android.os.RemoteException; +import android.os.UserManager; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link com.android.server.usb.UsbService} + */ +@RunWith(AndroidJUnit4.class) +public class UsbServiceTest { + + @Mock + private Context mContext; + @Mock + private UsbPortManager mUsbPortManager; + @Mock + private UsbAlsaManager mUsbAlsaManager; + @Mock + private UserManager mUserManager; + @Mock + private UsbSettingsManager mUsbSettingsManager; + @Mock + private IUsbOperationInternal mIUsbOperationInternal; + + private static final String TEST_PORT_ID = "123"; + + private static final int TEST_TRANSACTION_ID = 1; + + private static final int TEST_FIRST_CALLER_ID = 1000; + + private static final int TEST_SECOND_CALLER_ID = 2000; + + private UsbService mUsbService; + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mUsbService = new UsbService(mContext, mUsbPortManager, mUsbAlsaManager, mUserManager, + mUsbSettingsManager); + } + + /** + * Verify enableUsbData successfully disables USB port without error + */ + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_USB_DATA_SIGNAL_STAKING) + public void usbPort_SuccessfullyDisabled() { + boolean enableState = false; + when(mUsbPortManager.enableUsbData(TEST_PORT_ID, enableState, TEST_TRANSACTION_ID, + mIUsbOperationInternal, null)).thenReturn(true); + + assertTrue(mUsbService.enableUsbDataInternal(TEST_PORT_ID, enableState, + TEST_TRANSACTION_ID, mIUsbOperationInternal, TEST_FIRST_CALLER_ID)); + + verify(mUsbPortManager, times(1)).enableUsbData(TEST_PORT_ID, + enableState, TEST_TRANSACTION_ID, mIUsbOperationInternal, null); + verifyZeroInteractions(mIUsbOperationInternal); + } + + /** + * Verify enableUsbData successfully enables USB port without error given no other stakers + */ + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_USB_DATA_SIGNAL_STAKING) + public void usbPortWhenNoOtherStakers_SuccessfullyEnabledUsb() { + boolean enableState = true; + when(mUsbPortManager.enableUsbData(TEST_PORT_ID, enableState, TEST_TRANSACTION_ID, + mIUsbOperationInternal, null)) + .thenReturn(true); + + assertTrue(mUsbService.enableUsbDataInternal(TEST_PORT_ID, enableState, + TEST_TRANSACTION_ID, mIUsbOperationInternal, TEST_FIRST_CALLER_ID)); + verifyZeroInteractions(mIUsbOperationInternal); + } + + /** + * Verify enableUsbData does not enable USB port if other stakers are present + */ + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_USB_DATA_SIGNAL_STAKING) + public void usbPortWithOtherStakers_DoesNotToEnableUsb() throws RemoteException { + mUsbService.enableUsbDataInternal(TEST_PORT_ID, false, TEST_TRANSACTION_ID, + mIUsbOperationInternal, TEST_FIRST_CALLER_ID); + clearInvocations(mUsbPortManager); + + assertFalse(mUsbService.enableUsbDataInternal(TEST_PORT_ID, true, + TEST_TRANSACTION_ID, mIUsbOperationInternal, TEST_SECOND_CALLER_ID)); + + verifyZeroInteractions(mUsbPortManager); + verify(mIUsbOperationInternal).onOperationComplete(USB_OPERATION_ERROR_INTERNAL); + } + + /** + * Verify enableUsbDataWhileDockedInternal does not enable USB port if other stakers are present + */ + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_USB_DATA_SIGNAL_STAKING) + public void enableUsbWhileDockedWhenThereAreOtherStakers_DoesNotEnableUsb() + throws RemoteException { + mUsbService.enableUsbDataInternal(TEST_PORT_ID, false, TEST_TRANSACTION_ID, + mIUsbOperationInternal, TEST_FIRST_CALLER_ID); + + mUsbService.enableUsbDataWhileDockedInternal(TEST_PORT_ID, 0, + mIUsbOperationInternal, TEST_SECOND_CALLER_ID); + + verify(mUsbPortManager, never()).enableUsbDataWhileDocked(any(), + anyLong(), any(), any()); + verify(mIUsbOperationInternal).onOperationComplete(USB_OPERATION_ERROR_INTERNAL); + } + + /** + * Verify enableUsbDataWhileDockedInternal does enable USB port if other stakers are + * not present + */ + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_USB_DATA_SIGNAL_STAKING) + public void enableUsbWhileDockedWhenThereAreNoStakers_SuccessfullyEnableUsb() + throws RemoteException { + mUsbService.enableUsbDataWhileDockedInternal(TEST_PORT_ID, TEST_TRANSACTION_ID, + mIUsbOperationInternal, TEST_SECOND_CALLER_ID); + + verify(mUsbPortManager, times(1)) + .enableUsbDataWhileDocked(TEST_PORT_ID, TEST_TRANSACTION_ID, + mIUsbOperationInternal, null); + verifyZeroInteractions(mIUsbOperationInternal); + } +} |