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

Commit 40a1562d authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add support for USB4/Thunderbolt tunnel management" into main

parents 24a62e64 77117eb9
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -231,4 +231,7 @@ interface IUsbManager
            "@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_USB)")
    void unregisterForDisplayPortEvents(IDisplayPortAltModeInfoListener listener);

    /* Enable/disable PCI tunnels for USB4 and Thunderbolt connections. */
    @EnforcePermission("MANAGE_USB")
    void enablePciTunnels(boolean enable);
}
+1 −0
Original line number Diff line number Diff line
@@ -437,6 +437,7 @@ system_java_library {
    required: [
        "libukey2_jni_shared",
        "core.protolog.pb",
        "libusb4_policies_jni",
    ],
    lint: {
        baseline_filename: "lint-baseline.xml",
+4 −0
Original line number Diff line number Diff line
@@ -28,6 +28,10 @@ java_library_static {
        "android.hardware.usb.gadget-V1-java",
    ],

    required: [
        "libusb4_policies_jni",
    ],

    static_libs: [
        "android.hardware.usb-V1.0-java",
        "android.hardware.usb-V1.1-java",
+179 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.server.usb;

import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

/**
 * Usb4Manager manages USB4 related functionalities such as setting mode preferences to prioritize
 * Thunderbolt and authorizing PCI tunnels.
 */
public class Usb4Manager {
    private static final String TAG = Usb4Manager.class.getSimpleName();

    private final Context mContext;
    private final UserManager mUserManager;

    @GuardedBy("mLock")
    private @UserIdInt int mCurrentUserId;

    @GuardedBy("mLock")
    private boolean mIsScreenLocked;

    private final Object mLock = new Object();

    /**
     * Native methods for the Usb4Manager.
     *
     * <p> This class exists to make testing easier. The native methods are loaded in the
     * init(), and can be mocked in tests.
     */
    public static class Usb4ManagerNative {
        private Usb4Manager mUsb4Manager;

        public Usb4ManagerNative() {
        }

        void init(Usb4Manager usb4Manager) {
            mUsb4Manager = usb4Manager;
            System.loadLibrary("usb4_policies_jni");
            mUsb4Manager.nativeInit();
        }

        void enablePciTunnels(boolean enable) {
            mUsb4Manager.enablePciTunnels(enable);
        }

        void updateLockState(boolean locked) {
            mUsb4Manager.updateLockState(locked);
        }

        void updateLoggedInState(boolean loggedIn, long userId) {
            mUsb4Manager.updateLoggedInState(loggedIn, userId);
        }
    }

    private Usb4ManagerNative mUsb4ManagerNative;

    @VisibleForTesting
    public Usb4Manager(
            Context context, UserManager userManager, Usb4ManagerNative usb4ManagerNative) {
        mContext = context;
        mUserManager = userManager;
        mUsb4ManagerNative = usb4ManagerNative;

        // Default to the system user.
        mCurrentUserId = UserHandle.USER_SYSTEM;

        // Initialize the native methods.
        mUsb4ManagerNative.init(this);

        // When the flag is set, default to allowing pci tunnels.
        // Call the native method directly (bypassing user checks).
        if (com.android.server.usb.flags.Flags.defaultAllowPciTunnels()) {
            mUsb4ManagerNative.enablePciTunnels(true);
        }
    }

    public Usb4Manager(Context context, UserManager userManager) {
        this(context, userManager, new Usb4ManagerNative());
    }

    /**
     * Enable or disable PCI tunnels.
     *
     * @param enable true to enable PCI tunnels, false to disable PCI tunnels.
     * @throws IllegalStateException if the currently logged in user is not full or admin.
     */
    @SuppressLint("AndroidFrameworkRequiresPermission") // TODO(b/440646300)
    public void onEnablePciTunnels(boolean enable) {
        Slog.d(TAG, "enablePciTunnels: " + enable);

        synchronized (mLock) {
            UserInfo userInfo = mUserManager.getUserInfo(mCurrentUserId);
            if (userInfo == null) {
                Slog.e(TAG, "enablePciTunnels: UserInfo is null");
                return;
            }

            if (!userInfo.isFull() || !userInfo.isAdmin()) {
                Slog.e(TAG, "enablePciTunnels: User is not full or admin");
                throw new IllegalStateException("User is not full or admin");
            }
        }

        mUsb4ManagerNative.enablePciTunnels(enable);
    }

    /**
     * Update the screen locked state.
     *
     * @param locked True when the screen is locked, false otherwise.
     */
    public void onUpdateScreenLockedState(boolean locked) {
        Slog.d(TAG, "updateLockState: " + locked);

        synchronized (mLock) {
            mIsScreenLocked = locked;
        }

        mUsb4ManagerNative.updateLockState(locked);
    }

    /**
     * Update the logged in state of a user.
     *
     * <p>Note: Changes to the logged in state of the system user are ignored.
     *
     * @param loggedIn True when a user logs in, False when a user is stopped.
     * @param userId User that is the target of this state change.
     */
    public void onUpdateLoggedInState(boolean loggedIn, int userId) {
        Slog.d(TAG, "updateLoggedInState: " + loggedIn + " " + userId);

        synchronized (mLock) {
            // Update the current user id to the logged in user.
            if (loggedIn) {
                mCurrentUserId = userId;
            }
        }

        if (userId == UserHandle.USER_SYSTEM) {
            Slog.d(TAG, "updateLoggedInState: User is system user. Do nothing.");
            return;
        }

        mUsb4ManagerNative.updateLoggedInState(loggedIn, userId);
    }

    /**
     * Native methods for the Usb4Manager. Called by Usb4ManagerNative for testing.
     */
    native void nativeInit();
    native void enablePciTunnels(boolean enable);
    native void updateLockState(boolean locked);
    native void updateLoggedInState(boolean loggedIn, long userId);
}
+68 −11
Original line number Diff line number Diff line
@@ -18,6 +18,9 @@ package com.android.server.usb;

import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN;
import static android.hardware.usb.DisplayPortAltModeInfo.LINK_TRAINING_STATUS_UNKNOWN;
import static android.hardware.usb.InternalUsbDataSignalDisableReason.USB_DISABLE_REASON_APM;
import static android.hardware.usb.InternalUsbDataSignalDisableReason.USB_DISABLE_REASON_ENTERPRISE;
import static android.hardware.usb.InternalUsbDataSignalDisableReason.USB_DISABLE_REASON_LOCKDOWN_MODE;
import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
@@ -26,15 +29,13 @@ import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
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 static android.hardware.usb.InternalUsbDataSignalDisableReason.USB_DISABLE_REASON_LOCKDOWN_MODE;
import static android.hardware.usb.InternalUsbDataSignalDisableReason.USB_DISABLE_REASON_APM;
import static android.hardware.usb.InternalUsbDataSignalDisableReason.USB_DISABLE_REASON_ENTERPRISE;

import android.hardware.usb.IUsbManagerInternal;
import static android.os.Binder.clearCallingIdentity;
import static android.os.Binder.restoreCallingIdentity;

import android.annotation.NonNull;
import android.annotation.IntDef;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
@@ -45,17 +46,18 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.usb.IDisplayPortAltModeInfoListener;
import android.hardware.usb.IUsbManager;
import android.hardware.usb.IUsbManagerInternal;
import android.hardware.usb.IUsbOperationInternal;
import android.hardware.usb.UsbOperationInternal;
import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbOperationInternal;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;

import android.os.Binder;
import android.os.Bundle;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -87,15 +89,12 @@ 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.Set;
import java.util.concurrent.CompletableFuture;

import java.util.concurrent.atomic.AtomicInteger;

/**
@@ -163,7 +162,9 @@ public class UsbService extends IUsbManager.Stub {
    private UsbDeviceManager mDeviceManager;
    private UsbHostManager mHostManager;
    private UsbPortManager mPortManager;
    private Usb4Manager mUsb4Manager;
    private final UsbAlsaManager mAlsaManager;
    private KeyguardManager.KeyguardLockedStateListener mKeyguardLockedStateListener;

    private final UsbSettingsManager mSettingsManager;
    private final UsbPermissionManager mPermissionManager;
@@ -206,6 +207,9 @@ public class UsbService extends IUsbManager.Stub {
        mSettingsManager = new UsbSettingsManager(context, this);
        mPermissionManager = new UsbPermissionManager(context, this);
        mAlsaManager = new UsbAlsaManager(context);
        if (com.android.server.usb.flags.Flags.enableUsb4()) {
            mUsb4Manager = new Usb4Manager(context, mUserManager);
        }

        final PackageManager pm = mContext.getPackageManager();
        if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
@@ -265,6 +269,31 @@ public class UsbService extends IUsbManager.Stub {
        }
    }

    @SuppressLint("AndroidFrameworkRequiresPermission") // TODO(b/440646300)
    private void registerForKeyguardCallbacks() {
        mKeyguardLockedStateListener = this::onScreenLockChange;
        // Register with keyguard to send locked state events to the listener initialized above
        try {
            final KeyguardManager keyguardManager =
                    mContext.getSystemService(KeyguardManager.class);
            Slog.d(TAG, "Adding keyguard locked state listener");
            keyguardManager.addKeyguardLockedStateListener(
                    new HandlerExecutor(FgThread.getHandler()), mKeyguardLockedStateListener);

            /* Set initial state of the listener */
            onScreenLockChange(keyguardManager.isKeyguardLocked());
        } catch (Exception e) {
            Slog.e(TAG, "Error adding keyguard locked listener ", e);
        }
    }

    private void onScreenLockChange(boolean locked) {
        Slog.d(TAG, "Handling onScreenLockChange: " + locked);
        if (mUsb4Manager != null) {
            mUsb4Manager.onUpdateScreenLockedState(locked);
        }
    }

    /**
     * Set new {@link #mCurrentUserId} and propagate it to other modules.
     *
@@ -285,6 +314,9 @@ public class UsbService extends IUsbManager.Stub {
            if (mDeviceManager != null) {
                mDeviceManager.setCurrentUser(newUserId, settings);
            }
            if (mUsb4Manager != null) {
                mUsb4Manager.onUpdateLoggedInState(true, newUserId);
            }
        }
    }

@@ -295,8 +327,14 @@ public class UsbService extends IUsbManager.Stub {
     */
    private void onStopUser(@NonNull UserHandle stoppedUser) {
        mSettingsManager.remove(stoppedUser);

        if (mUsb4Manager != null) {
            mUsb4Manager.onUpdateLoggedInState(false, stoppedUser.getIdentifier());
        }
    }



    public void systemReady() {
        mAlsaManager.systemReady();

@@ -309,6 +347,11 @@ public class UsbService extends IUsbManager.Stub {
        if (mPortManager != null) {
            mPortManager.systemReady();
        }

        if (com.android.server.usb.flags.Flags.enableUsb4()
                || com.android.server.usb.flags.Flags.enableUsbAuthorization()) {
            registerForKeyguardCallbacks();
        }
    }

    public void bootCompleted() {
@@ -1211,6 +1254,20 @@ public class UsbService extends IUsbManager.Stub {
        }
    }

    @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_USB)
    @Override
    public void enablePciTunnels(boolean enable) {
        enablePciTunnels_enforcePermission();

        final long ident = Binder.clearCallingIdentity();
        try {
            if (mUsb4Manager != null) {
                mUsb4Manager.onEnablePciTunnels(enable);
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    @NeverCompile // Avoid size overhead of debugging code.
    @Override
Loading