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

Commit e05005b1 authored by Evan Severson's avatar Evan Severson
Browse files

Separate Usb permissions from Usb settings

Test: Manual
Change-Id: I4c6b1c309ad7204a4475838abded47c595ff331e
parent 51e47a54
Loading
Loading
Loading
Loading
+14 −14
Original line number Diff line number Diff line
@@ -250,7 +250,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
    }

    public UsbDeviceManager(Context context, UsbAlsaManager alsaManager,
            UsbSettingsManager settingsManager) {
            UsbSettingsManager settingsManager, UsbPermissionManager permissionManager) {
        mContext = context;
        mContentResolver = context.getContentResolver();
        PackageManager pm = mContext.getPackageManager();
@@ -284,13 +284,13 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
             * Initialze the legacy UsbHandler
             */
            mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(), mContext, this,
                    alsaManager, settingsManager);
                    alsaManager, permissionManager);
        } else {
            /**
             * Initialize HAL based UsbHandler
             */
            mHandler = new UsbHandlerHal(FgThread.get().getLooper(), mContext, this,
                    alsaManager, settingsManager);
                    alsaManager, permissionManager);
        }

        if (nativeIsStartRequested()) {
@@ -468,7 +468,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser

        private final Context mContext;
        private final UsbAlsaManager mUsbAlsaManager;
        private final UsbSettingsManager mSettingsManager;
        private final UsbPermissionManager mPermissionManager;
        private NotificationManager mNotificationManager;

        protected long mScreenUnlockedFunctions;
@@ -489,12 +489,12 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
        protected static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";

        UsbHandler(Looper looper, Context context, UsbDeviceManager deviceManager,
                UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) {
                UsbAlsaManager alsaManager, UsbPermissionManager permissionManager) {
            super(looper);
            mContext = context;
            mUsbDeviceManager = deviceManager;
            mUsbAlsaManager = alsaManager;
            mSettingsManager = settingsManager;
            mPermissionManager = permissionManager;
            mContentResolver = context.getContentResolver();

            mCurrentUser = ActivityManager.getCurrentUser();
@@ -625,7 +625,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
                // successfully entered accessory mode
                String[] accessoryStrings = mUsbDeviceManager.getAccessoryStrings();
                if (accessoryStrings != null) {
                    UsbSerialReader serialReader = new UsbSerialReader(mContext, mSettingsManager,
                    UsbSerialReader serialReader = new UsbSerialReader(mContext, mPermissionManager,
                            accessoryStrings[UsbAccessory.SERIAL_STRING]);

                    mCurrentAccessory = new UsbAccessory(
@@ -663,7 +663,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser

            if (mCurrentAccessory != null) {
                if (mBootCompleted) {
                    mSettingsManager.usbAccessoryRemoved(mCurrentAccessory);
                    mPermissionManager.usbAccessoryRemoved(mCurrentAccessory);
                }
                mCurrentAccessory = null;
            }
@@ -1343,8 +1343,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
        private boolean mUsbDataUnlocked;

        UsbHandlerLegacy(Looper looper, Context context, UsbDeviceManager deviceManager,
                UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) {
            super(looper, context, deviceManager, alsaManager, settingsManager);
                UsbAlsaManager alsaManager, UsbPermissionManager permissionManager) {
            super(looper, context, deviceManager, alsaManager, permissionManager);
            try {
                readOemUsbOverrideConfig(context);
                // Restore default functions.
@@ -1738,8 +1738,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
        protected boolean mCurrentUsbFunctionsRequested;

        UsbHandlerHal(Looper looper, Context context, UsbDeviceManager deviceManager,
                UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) {
            super(looper, context, deviceManager, alsaManager, settingsManager);
                UsbAlsaManager alsaManager, UsbPermissionManager permissionManager) {
            super(looper, context, deviceManager, alsaManager, permissionManager);
            try {
                ServiceNotification serviceNotification = new ServiceNotification();

@@ -1977,7 +1977,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
     * @param uid Uid of the caller
     */
    public ParcelFileDescriptor openAccessory(UsbAccessory accessory,
            UsbUserSettingsManager settings, int uid) {
            UsbUserPermissionManager permissions, int uid) {
        UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
        if (currentAccessory == null) {
            throw new IllegalArgumentException("no accessory attached");
@@ -1988,7 +1988,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
                    + currentAccessory;
            throw new IllegalArgumentException(error);
        }
        settings.checkPermission(accessory, uid);
        permissions.checkPermission(accessory, uid);
        return nativeOpenAccessory();
    }

+12 −10
Original line number Diff line number Diff line
@@ -65,7 +65,7 @@ public class UsbHostManager {
    private final String[] mHostBlacklist;

    private final UsbAlsaManager mUsbAlsaManager;
    private final UsbSettingsManager mSettingsManager;
    private final UsbPermissionManager mPermissionManager;

    private final Object mLock = new Object();
    @GuardedBy("mLock")
@@ -232,13 +232,13 @@ public class UsbHostManager {
     * UsbHostManager
     */
    public UsbHostManager(Context context, UsbAlsaManager alsaManager,
            UsbSettingsManager settingsManager) {
            UsbPermissionManager permissionManager) {
        mContext = context;

        mHostBlacklist = context.getResources().getStringArray(
                com.android.internal.R.array.config_usbHostBlacklist);
        mUsbAlsaManager = alsaManager;
        mSettingsManager = settingsManager;
        mPermissionManager = permissionManager;
        String deviceConnectionHandler = context.getResources().getString(
                com.android.internal.R.string.config_UsbDeviceConnectionHandling_component);
        if (!TextUtils.isEmpty(deviceConnectionHandler)) {
@@ -393,8 +393,8 @@ public class UsbHostManager {
                addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADDEVICE,
                        parser.getRawDescriptors());
            } else {
                UsbSerialReader serialNumberReader = new UsbSerialReader(mContext, mSettingsManager,
                        newDeviceBuilder.serialNumber);
                UsbSerialReader serialNumberReader = new UsbSerialReader(mContext,
                        mPermissionManager, newDeviceBuilder.serialNumber);
                UsbDevice newDevice = newDeviceBuilder.build(serialNumberReader);
                serialNumberReader.setDevice(newDevice);

@@ -444,7 +444,7 @@ public class UsbHostManager {
            if (device != null) {
                Slog.d(TAG, "Removed device at " + deviceAddress + ": " + device.getProductName());
                mUsbAlsaManager.usbDeviceRemoved(deviceAddress);
                mSettingsManager.usbDeviceRemoved(device);
                mPermissionManager.usbDeviceRemoved(device);
                getCurrentUserSettings().usbDeviceRemoved(device);
                ConnectionRecord current = mConnected.get(deviceAddress);
                // Tracking
@@ -484,9 +484,11 @@ public class UsbHostManager {
        }
    }

    /* Opens the specified USB device */
    public ParcelFileDescriptor openDevice(String deviceAddress, UsbUserSettingsManager settings,
            String packageName, int uid) {
    /**
     *  Opens the specified USB device
     */
    public ParcelFileDescriptor openDevice(String deviceAddress,
            UsbUserPermissionManager permissions, String packageName, int uid) {
        synchronized (mLock) {
            if (isBlackListed(deviceAddress)) {
                throw new SecurityException("USB device is on a restricted bus");
@@ -498,7 +500,7 @@ public class UsbHostManager {
                        "device " + deviceAddress + " does not exist or is restricted");
            }

            settings.checkPermission(device, packageName, uid);
            permissions.checkPermission(device, packageName, uid);
            return nativeOpenDevice(deviceAddress);
        }
    }
+50 −185
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 * Copyright (C) 2019 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.
@@ -17,230 +17,95 @@
package com.android.server.usb;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.Binder;
import android.os.Process;
import android.os.UserHandle;
import android.service.usb.UsbSettingsAccessoryPermissionProto;
import android.service.usb.UsbSettingsDevicePermissionProto;
import android.service.usb.UsbUserSettingsManagerProto;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.dump.DualDumpOutputStream;

import java.util.HashMap;

/**
 * UsbPermissionManager manages usb device or accessory access permissions.
 *
 * @hide
 */
class UsbPermissionManager {
    private static final String LOG_TAG = UsbPermissionManager.class.getSimpleName();
    private static final boolean DEBUG = false;

    @GuardedBy("mLock")
    /** Temporary mapping USB device name to list of UIDs with permissions for the device*/
    private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
            new HashMap<>();
    @GuardedBy("mLock")
    /** Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory*/
    private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
            new HashMap<>();
    /** Context to be used by this module */
    private final @NonNull Context mContext;

    private final UserHandle mUser;
    private final boolean mDisablePermissionDialogs;
    /** Map from user id to {@link UsbUserPermissionManager} for the user */
    @GuardedBy("mPermissionsByUser")
    private final SparseArray<UsbUserPermissionManager> mPermissionsByUser = new SparseArray<>();

    private final Object mLock = new Object();
    final UsbService mUsbService;

    UsbPermissionManager(@NonNull Context context, @NonNull UserHandle user) {
        mUser = user;
        mDisablePermissionDialogs = context.getResources().getBoolean(
                com.android.internal.R.bool.config_disableUsbPermissionDialogs);
    UsbPermissionManager(@NonNull Context context,
            @NonNull UsbService usbService) {
        mContext = context;
        mUsbService = usbService;
    }

    /**
     * Removes access permissions of all packages for the USB accessory.
     *
     * @param accessory to remove permissions for
     */
    void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
        synchronized (mLock) {
            mAccessoryPermissionMap.remove(accessory);
        }
    @NonNull UsbUserPermissionManager getPermissionsForUser(@UserIdInt int userId) {
        synchronized (mPermissionsByUser) {
            UsbUserPermissionManager permissions = mPermissionsByUser.get(userId);
            if (permissions == null) {
                permissions = new UsbUserPermissionManager(mContext, UserHandle.of(userId),
                        mUsbService.getSettingsForUser(userId));
                mPermissionsByUser.put(userId, permissions);
            }

    /**
     * Removes access permissions of all packages for the USB device.
     *
     * @param device to remove permissions for
     */
    void removeDevicePermissions(@NonNull UsbDevice device) {
        synchronized (mLock) {
            mDevicePermissionMap.remove(device.getDeviceName());
            return permissions;
        }
    }

    /**
     * Grants permission for USB device without showing system dialog for package with uid.
     *
     * @param device to grant permission for
     * @param uid to grant permission for
     */
    void grantDevicePermission(@NonNull UsbDevice device, int uid) {
        synchronized (mLock) {
            String deviceName = device.getDeviceName();
            SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
            if (uidList == null) {
                uidList = new SparseBooleanArray(1);
                mDevicePermissionMap.put(deviceName, uidList);
            }
            uidList.put(uid, true);
    void remove(@NonNull UserHandle userToRemove) {
        synchronized (mPermissionsByUser) {
            mPermissionsByUser.remove(userToRemove.getIdentifier());
        }
    }

    /**
     * Grants permission for USB accessory without showing system dialog for package with uid.
     * Remove temporary access permission and broadcast that a device was removed.
     *
     * @param accessory to grant permission for
     * @param uid to grant permission for
     * @param device The device that is removed
     */
    void grantAccessoryPermission(@NonNull UsbAccessory accessory, int uid) {
        synchronized (mLock) {
            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
            if (uidList == null) {
                uidList = new SparseBooleanArray(1);
                mAccessoryPermissionMap.put(accessory, uidList);
            }
            uidList.put(uid, true);
    void usbDeviceRemoved(@NonNull UsbDevice device) {
        synchronized (mPermissionsByUser) {
            for (int i = 0; i < mPermissionsByUser.size(); i++) {
                // clear temporary permissions for the device
                mPermissionsByUser.valueAt(i).removeDevicePermissions(device);
            }
        }

    /**
     * Returns true if package with uid has permission to access the device.
     *
     * @param device to check permission for
     * @param uid to check permission for
     * @return {@code true} if package with uid has permission
     */
    boolean hasPermission(@NonNull UsbDevice device, int uid) {
        synchronized (mLock) {
            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
                return true;
            }
            SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
            if (uidList == null) {
                return false;
            }
            return uidList.get(uid);
        }
    }
        Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        intent.putExtra(UsbManager.EXTRA_DEVICE, device);

    /**
     * Returns true if caller has permission to access the accessory.
     *
     * @param accessory to check permission for
     * @param uid to check permission for
     * @return {@code true} if caller has permssion
     */
    boolean hasPermission(@NonNull UsbAccessory accessory, int uid) {
        synchronized (mLock) {
            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
                return true;
            }
            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
            if (uidList == null) {
                return false;
            }
            return uidList.get(uid);
        if (DEBUG) {
            Slog.d(LOG_TAG, "usbDeviceRemoved, sending " + intent);
        }
        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
    }

    /**
     * Creates UI dialog to request permission for the given package to access the device
     * or accessory.
     * Remove temporary access permission and broadcast that a accessory was removed.
     *
     * @param device The USB device attached
     * @param accessory The USB accessory attached
     * @param canBeDefault Whether the calling pacakge can set as default handler
     * of the USB device or accessory
     * @param packageName The package name of the calling package
     * @param uid The uid of the calling package
     * @param userContext The context to start the UI dialog
     * @param pi PendingIntent for returning result
     * @param accessory The accessory that is removed
     */
    void requestPermissionDialog(@Nullable UsbDevice device,
                                 @Nullable UsbAccessory accessory,
                                 boolean canBeDefault,
                                 @NonNull String packageName,
                                 int uid,
                                 @NonNull Context userContext,
                                 @NonNull PendingIntent pi) {
        long identity = Binder.clearCallingIdentity();
        Intent intent = new Intent();
        if (device != null) {
            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
        } else {
            intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
        }
        intent.putExtra(Intent.EXTRA_INTENT, pi);
        intent.putExtra(Intent.EXTRA_UID, uid);
        intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
        intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
        intent.setClassName("com.android.systemui",
                "com.android.systemui.usb.UsbPermissionActivity");
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        try {
            userContext.startActivityAsUser(intent, mUser);
        } catch (ActivityNotFoundException e) {
            Slog.e(LOG_TAG, "unable to start UsbPermissionActivity");
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    void usbAccessoryRemoved(@NonNull UsbAccessory accessory) {
        synchronized (mPermissionsByUser) {
            for (int i = 0; i < mPermissionsByUser.size(); i++) {
                // clear temporary permissions for the accessory
                mPermissionsByUser.valueAt(i).removeAccessoryPermissions(accessory);
            }

    void dump(@NonNull DualDumpOutputStream dump) {
        synchronized (mLock) {
            for (String deviceName : mDevicePermissionMap.keySet()) {
                long devicePermissionToken = dump.start("device_permissions",
                        UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);

                dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);

                SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
                int count = uidList.size();
                for (int i = 0; i < count; i++) {
                    dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
        }

                dump.end(devicePermissionToken);
            }

            for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
                long accessoryPermissionToken = dump.start("accessory_permissions",
                        UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);

                dump.write("accessory_description",
                        UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
                        accessory.getDescription());

                SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
                int count = uidList.size();
                for (int i = 0; i < count; i++) {
                    dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
        Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
    }

                dump.end(accessoryPermissionToken);
            }
        }
    }
}
+7 −6
Original line number Diff line number Diff line
@@ -783,7 +783,7 @@ class UsbProfileGroupSettingsManager {
            return;
        }

        mSettingsManager.getSettingsForUser(UserHandle.getUserId(appInfo.uid))
        mSettingsManager.mUsbService.getPermissionsForUser(UserHandle.getUserId(appInfo.uid))
                .grantDevicePermission(device, appInfo.uid);

        Intent activityIntent = new Intent(intent);
@@ -844,14 +844,15 @@ class UsbProfileGroupSettingsManager {
        }

        if (defaultActivity != null) {
            UsbUserSettingsManager defaultRIUserSettings = mSettingsManager.getSettingsForUser(
            UsbUserPermissionManager defaultRIUserPermissions =
                    mSettingsManager.mUsbService.getPermissionsForUser(
                            UserHandle.getUserId(defaultActivity.applicationInfo.uid));
            // grant permission for default activity
            if (device != null) {
                defaultRIUserSettings.
                        grantDevicePermission(device, defaultActivity.applicationInfo.uid);
                defaultRIUserPermissions
                        .grantDevicePermission(device, defaultActivity.applicationInfo.uid);
            } else if (accessory != null) {
                defaultRIUserSettings.grantAccessoryPermission(accessory,
                defaultRIUserPermissions.grantAccessoryPermission(accessory,
                        defaultActivity.applicationInfo.uid);
            }

+8 −7
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ import com.android.internal.util.ArrayUtils;
class UsbSerialReader extends IUsbSerialReader.Stub {
    private final @Nullable String mSerialNumber;
    private final @NonNull Context mContext;
    private final @NonNull UsbSettingsManager mSettingsManager;
    private final @NonNull UsbPermissionManager mPermissionManager;

    private Object mDevice;

@@ -51,10 +51,10 @@ class UsbSerialReader extends IUsbSerialReader.Stub {
     * @param settingsManager The USB settings manager
     * @param serialNumber The serial number that might be read
     */
    UsbSerialReader(@NonNull Context context, @NonNull UsbSettingsManager settingsManager,
    UsbSerialReader(@NonNull Context context, @NonNull UsbPermissionManager permissionManager,
            @Nullable String serialNumber) {
        mContext = context;
        mSettingsManager = settingsManager;
        mPermissionManager = permissionManager;
        mSerialNumber = serialNumber;
    }

@@ -89,13 +89,14 @@ class UsbSerialReader extends IUsbSerialReader.Stub {
                if (packageTargetSdkVersion >= Build.VERSION_CODES.Q) {
                    if (mContext.checkPermission(android.Manifest.permission.MANAGE_USB, pid, uid)
                            == PackageManager.PERMISSION_DENIED) {
                        UsbUserSettingsManager settings = mSettingsManager.getSettingsForUser(
                                UserHandle.getUserId(uid));

                        int userId = UserHandle.getUserId(uid);
                        if (mDevice instanceof UsbDevice) {
                            settings.checkPermission((UsbDevice) mDevice, packageName, uid);
                            mPermissionManager.getPermissionsForUser(userId)
                                    .checkPermission((UsbDevice) mDevice, packageName, uid);
                        } else {
                            settings.checkPermission((UsbAccessory) mDevice, uid);
                            mPermissionManager.getPermissionsForUser(userId)
                                    .checkPermission((UsbAccessory) mDevice, uid);
                        }
                    }
                }
Loading