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

Commit 60a54ee2 authored by George Chan's avatar George Chan
Browse files

Add UsbManagerInternal interface

This interface will be used by internal services such as AndroidAdvancedProtectionModeService (TBD.)

go/usb_aapm

Change-Id: I843f4957406f1a79a46a880e20978d4c6a6edab9
Test: atest
Flag: android.hardware.usb.flags.enable_usb_data_signal_staking_internal
Bug: 369382558
parent 8973771c
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -23,8 +23,10 @@ import android.util.ArraySet;
 *
 * External requesters are identified by UIDs.
 * Internal requesters are identified by a reason code enumerated in UsbManagerInternal.
 *
 * @hide
 */
public class UsbDataSignalDisableRequesters {
public final class UsbDataSignalDisableRequesters {
    final ArraySet<Integer> mExternalUids = new ArraySet<>();
    final ArraySet<Integer> mInternalReasons  = new ArraySet<>();

+48 −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 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;

  @Retention(RetentionPolicy.SOURCE)
  @IntDef(value = {OS_USB_DISABLE_REASON_AAPM})
  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
+32 −4
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;

import android.os.Binder;
import android.os.Bundle;
import android.os.Looper;
@@ -69,6 +70,7 @@ import com.android.internal.util.Preconditions;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;

@@ -223,6 +225,9 @@ public class UsbService extends IUsbManager.Stub {
        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
        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());
        }
    }

    // Ideally we should use the injector pattern so we wouldn't need this constructor  for test
@@ -238,6 +243,10 @@ public class UsbService extends IUsbManager.Stub {
        mUserManager = userManager;
        mSettingsManager = usbSettingsManager;
        mPermissionManager = new UsbPermissionManager(context, this);

        if(android.hardware.usb.flags.Flags.enableUsbDataSignalStakingInternal()) {
            LocalServices.addService(UsbManagerInternal.class, new UsbManagerInternalImpl());
        }
    }

    /**
@@ -905,7 +914,8 @@ 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(), false);
        return enableUsbDataInternal(portId, enable, operationId, callback,
            Binder.getCallingUid(), false);
    }

    /**
@@ -913,7 +923,8 @@ public class UsbService extends IUsbManager.Stub {
     *  1. UID of the app that requested USB data to be disabled if caller is external.
     *  2. Enumberated disable request reason if the caller is internal.
     *
     *  For internal requests, isInternalRequest should be set to true. Since internal requests all share the same UID, the request managed separately.
     *  For internal requests, isInternalRequest should be set to true. Since
     * internal requests all share the same UID, the request managed separately.
     */
    @VisibleForTesting
    boolean enableUsbDataInternal(String portId, boolean enable, int operationId,
@@ -999,7 +1010,8 @@ public class UsbService extends IUsbManager.Stub {
    @Override
    public void enableUsbDataWhileDocked(String portId, int operationId,
                                         IUsbOperationInternal callback) {
        enableUsbDataWhileDockedInternal(portId, operationId, callback, Binder.getCallingUid(), false);
        enableUsbDataWhileDockedInternal(portId, operationId, callback,
            Binder.getCallingUid(), false);
    }

    /**
@@ -1478,7 +1490,8 @@ public class UsbService extends IUsbManager.Stub {
        public void onUidRemoved(int uid) {
            synchronized (mUsbDisableRequesters) {
                for (String portId : mUsbDisableRequesters.keySet()) {
                    UsbDataSignalDisableRequesters disableRequesters = mUsbDisableRequesters.get(portId);
                    UsbDataSignalDisableRequesters disableRequesters =
                        mUsbDisableRequesters.get(portId);
                    if (disableRequesters != null) {
                        disableRequesters.mExternalUids.remove(uid);
                        if (disableRequesters.isEmpty()) {
@@ -1519,4 +1532,19 @@ public class UsbService extends IUsbManager.Stub {
            }
        }
    }

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

        @Override
        public UsbPort[] getPorts() {
            return mPortManager.getPorts();
        }
    }
}
+66 −15
Original line number Diff line number Diff line
@@ -18,6 +18,10 @@ package com.android.server.usb;

import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;

import static com.google.common.truth.Truth.assertThat;
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.mockito.ArgumentMatchers.any;
@@ -31,12 +35,15 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.usb.IUsbOperationInternal;
import android.hardware.usb.flags.Flags;
import android.hardware.usb.UsbPort;
import android.os.RemoteException;
import android.os.UserManager;
import android.platform.test.flag.junit.SetFlagsRule;

import androidx.test.runner.AndroidJUnit4;

import com.android.server.LocalServices;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -77,6 +84,8 @@ public class UsbServiceTest {

    private UsbService mUsbService;

    private UsbManagerInternal mUsbManagerInternal;

    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

@@ -84,13 +93,17 @@ public class UsbServiceTest {
    public void setUp() {
        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_USB_DATA_SIGNAL_STAKING);
        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_USB_DATA_SIGNAL_STAKING_INTERNAL);
        LocalServices.removeAllServicesForTest();
        MockitoAnnotations.initMocks(this);

        when(mUsbPortManager.enableUsbData(eq(TEST_PORT_ID), anyBoolean(), eq(TEST_TRANSACTION_ID),
                eq(mCallback), any())).thenReturn(true);
        when(mUsbPortManager.enableUsbData(eq(TEST_PORT_ID), anyBoolean(),
                 eq(TEST_TRANSACTION_ID), eq(mCallback), any())).thenReturn(true);

        mUsbService = new UsbService(mContext, mUsbPortManager, mUsbAlsaManager,
                mUserManager, mUsbSettingsManager);
        mUsbManagerInternal = LocalServices.getService(UsbManagerInternal.class);
        assertWithMessage("LocalServices.getService(UsbManagerInternal.class)")
            .that(mUsbManagerInternal).isNotNull();
    }

    private void assertToggleUsbSuccessfully(int requester, boolean enable,
@@ -127,7 +140,8 @@ public class UsbServiceTest {
    }

    /**
     * Verify enableUsbData successfully enables USB port without error given no other stakers
     * Verify enableUsbData successfully enables USB port without error given
     * no other stakers
     */
    @Test
    public void enableUsbWhenNoOtherStakers_successfullyEnable() {
@@ -145,7 +159,8 @@ public class UsbServiceTest {
    }

    /**
     * Verify enableUsbData successfully enables USB port when the last staker is removed
     * Verify enableUsbData successfully enables USB port when the last staker
     * is removed
     */
    @Test
    public void enableUsbByTheOnlyStaker_successfullyEnable() {
@@ -155,7 +170,8 @@ public class UsbServiceTest {
    }

    /**
     * Verify enableUsbDataWhileDockedInternal does not enable USB port if other stakers are present
     * Verify enableUsbDataWhileDockedInternal does not enable USB port if other
     * stakers are present
     */
    @Test
    public void enableUsbWhileDockedWhenThereAreOtherStakers_failsToEnable()
@@ -170,8 +186,8 @@ public class UsbServiceTest {
    }

    /**
     * Verify enableUsbDataWhileDockedInternal does enable USB port if other stakers are
     * not present
     * Verify enableUsbDataWhileDockedInternal does enable USB port if other
     * stakers are not present
     */
    @Test
    public void enableUsbWhileDockedWhenThereAreNoStakers_SuccessfullyEnable() {
@@ -184,7 +200,8 @@ public class UsbServiceTest {
    }

    /**
     * Verify enableUsbData successfully enables USB port without error given no other stakers for internal requests
     * Verify enableUsbData successfully enables USB port without error given no
     * other stakers for internal requests
     */
    @Test
    public void enableUsbWhenNoOtherStakers_forInternalRequest_successfullyEnable() {
@@ -192,37 +209,44 @@ public class UsbServiceTest {
    }

    /**
     * Verify enableUsbData does not enable USB port if other internal stakers are present for internal requests
     * Verify enableUsbData does not enable USB port if other internal stakers
     * are present for internal requests
     */
    @Test
    public void enableUsbPortWithOtherInternalStakers_forInternalRequest_failsToEnable() throws Exception {
    public void enableUsbPortWithOtherInternalStakers_forInternalRequest_failsToEnable()
        throws Exception {
        assertToggleUsbSuccessfully(TEST_INTERNAL_REQUESTER_REASON_1, false, true);

        assertToggleUsbFailed(TEST_INTERNAL_REQUESTER_REASON_2, true, true);
    }

    /**
     * Verify enableUsbData does not enable USB port if other external stakers are present for internal requests
     * Verify enableUsbData does not enable USB port if other external stakers
     * are present for internal requests
     */
    @Test
    public void enableUsbPortWithOtherExternalStakers_forInternalRequest_failsToEnable() throws Exception {
    public void enableUsbPortWithOtherExternalStakers_forInternalRequest_failsToEnable()
        throws Exception {
        assertToggleUsbSuccessfully(TEST_FIRST_CALLER_ID, false, false);

        assertToggleUsbFailed(TEST_INTERNAL_REQUESTER_REASON_2, true, true);
    }

    /**
     * Verify enableUsbData does not enable USB port if other internal stakers are present for external requests
     * Verify enableUsbData does not enable USB port if other internal stakers
     * are present for external requests
     */
    @Test
    public void enableUsbPortWithOtherInternalStakers_forExternalRequest_failsToEnable() throws Exception {
    public void enableUsbPortWithOtherInternalStakers_forExternalRequest_failsToEnable()
        throws Exception {
        assertToggleUsbSuccessfully(TEST_INTERNAL_REQUESTER_REASON_1, false, true);

        assertToggleUsbFailed(TEST_FIRST_CALLER_ID, true, false);
    }

    /**
     * Verify enableUsbData successfully enables USB port when the last staker is removed for internal requests
     * Verify enableUsbData successfully enables USB port when the last staker
     * is removed for internal requests
     */
    @Test
    public void enableUsbByTheOnlyStaker_forInternalRequest_successfullyEnable() {
@@ -230,4 +254,31 @@ 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);
    }

    @Test
    public void usbManagerInternal_enableUsbData_successfullyEnable() {
        boolean desiredEnableState = true;

        assertTrue(mUsbManagerInternal.enableUsbData(TEST_PORT_ID, desiredEnableState,
        TEST_TRANSACTION_ID, mCallback, TEST_INTERNAL_REQUESTER_REASON_1));

        verify(mUsbPortManager).enableUsbData(TEST_PORT_ID,
                desiredEnableState, TEST_TRANSACTION_ID, mCallback, null);
        verifyZeroInteractions(mCallback);
        clearInvocations(mUsbPortManager);
        clearInvocations(mCallback);
    }
}