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

Commit 0aeed083 authored by Ashish Kumar Gupta's avatar Ashish Kumar Gupta Committed by Android (Google) Code Review
Browse files

Merge "Adding unit test for UsbService anti-clobbering feature." into main

parents 65dc94c7 ae1ee0df
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);
    }
}