Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ae1ee0df authored by George Chan's avatar George Chan
Browse files

Adding unit test for UsbService anti-clobbering feature.

The UsbService needs further refactor to better accommodate tests in
order to the service comprehensively. This change only focuses on the
anti-clobber mechanism.

Test: atest UsbTests
Bug: 304579252
Change-Id: I27d40dbc8fc50c27cb9023e7d70b90fb905f1680
parent d31aca2f
Loading
Loading
Loading
Loading
+38 −4
Original line number Diff line number Diff line
@@ -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.
     *
@@ -887,6 +903,15 @@ public class UsbService extends IUsbManager.Stub {
    @Override
    public boolean enableUsbData(String portId, boolean enable, int operationId,
                                 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) {
@@ -951,6 +976,15 @@ public class UsbService extends IUsbManager.Stub {
    @Override
    public void enableUsbDataWhileDocked(String portId, int operationId,
                                         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) {
+2 −0
Original line number Diff line number Diff line
@@ -36,6 +36,8 @@ android_test {
        "services.usb",
        "truth",
        "UsbManagerTestLib",
        "android.hardware.usb.flags-aconfig-java",
        "flag-junit",
    ],
    jni_libs: [
        // Required for ExtendedMockito
+174 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}