All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.usb.UsbUserPermissionManager Maven / Gradle / Ivy

/*
 * Copyright (C) 2018 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.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.hardware.SensorPrivacyManager.Sensors;
import android.hardware.SensorPrivacyManagerInternal;
import android.hardware.usb.AccessoryFilter;
import android.hardware.usb.DeviceFilter;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Environment;
import android.os.Process;
import android.os.UserHandle;
import android.service.usb.UsbAccessoryPermissionProto;
import android.service.usb.UsbAccessoryPersistentPermissionProto;
import android.service.usb.UsbDevicePermissionProto;
import android.service.usb.UsbDevicePersistentPermissionProto;
import android.service.usb.UsbUidPermissionProto;
import android.service.usb.UsbUserPermissionsManagerProto;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.LocalServices;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

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

    @GuardedBy("mLock")
    /** Mapping of USB device name to list of UIDs with permissions for the device
     * Each entry lasts until device is disconnected*/
    private final ArrayMap mDevicePermissionMap =
            new ArrayMap<>();
    @GuardedBy("mLock")
    /** Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory
     * Each entry lasts until accessory is disconnected*/
    private final ArrayMap mAccessoryPermissionMap =
            new ArrayMap<>();

    @GuardedBy("mLock")
    /** Maps USB device to list of UIDs with persistent permissions for the device*/
    private final ArrayMap
            mDevicePersistentPermissionMap = new ArrayMap<>();
    @GuardedBy("mLock")
    /** Maps Usb Accessory to list of UIDs with persistent permissions for the accessory*/
    private final ArrayMap
            mAccessoryPersistentPermissionMap = new ArrayMap<>();

    private final Context mContext;
    private final UserHandle mUser;
    private final UsbUserSettingsManager mUsbUserSettingsManager;
    private final boolean mDisablePermissionDialogs;

    private final @NonNull AtomicFile mPermissionsFile;

    private final Object mLock = new Object();

    /**
     * If a async task to persist the mDevicePersistentPreferenceMap and
     * mAccessoryPersistentPreferenceMap is currently scheduled.
     */
    @GuardedBy("mLock")
    private boolean mIsCopyPermissionsScheduled;
    private final SensorPrivacyManagerInternal mSensorPrivacyMgrInternal;

    UsbUserPermissionManager(@NonNull Context context,
            @NonNull UsbUserSettingsManager usbUserSettingsManager) {
        mContext = context;
        mUser = context.getUser();
        mUsbUserSettingsManager = usbUserSettingsManager;
        mSensorPrivacyMgrInternal = LocalServices.getService(SensorPrivacyManagerInternal.class);
        mDisablePermissionDialogs = context.getResources().getBoolean(
                com.android.internal.R.bool.config_disableUsbPermissionDialogs);

        mPermissionsFile = new AtomicFile(new File(
                Environment.getUserSystemDirectory(mUser.getIdentifier()),
                "usb_permissions.xml"), "usb-permissions");
        synchronized (mLock) {
            readPermissionsLocked();
        }
    }

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

    /**
     * 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());
        }
    }

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

    /**
     * Grants permission for USB accessory without showing system dialog for package with uid.
     *
     * @param accessory to grant permission for
     * @param uid to grant permission for
     */
    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);
        }
    }

    /**
     * Returns true if package with uid has permission to access the device.
     *
     * @param device to check permission for
     * @param pid to check permission for
     * @param uid to check permission for
     * @return {@code true} if package with uid has permission
     */
    boolean hasPermission(@NonNull UsbDevice device, @NonNull String packageName, int pid,
            int uid) {
        if (device.getHasVideoCapture()) {
            boolean isCameraPrivacyEnabled = mSensorPrivacyMgrInternal.isSensorPrivacyEnabled(
                    UserHandle.getUserId(uid), Sensors.CAMERA);
            if (DEBUG) {
                Slog.d(TAG, "isCameraPrivacyEnabled: " + isCameraPrivacyEnabled);
            }
            if (isCameraPrivacyEnabled || !isCameraPermissionGranted(packageName, pid, uid)) {
                return false;
            }
        }
        // Only check for microphone privacy and not RECORD_AUDIO permission, because access to usb
        // camera device with audio recording capabilities may still be granted with a warning
        if (device.getHasAudioCapture() && mSensorPrivacyMgrInternal.isSensorPrivacyEnabled(
                UserHandle.getUserId(uid), Sensors.MICROPHONE)) {
            if (DEBUG) {
                Slog.d(TAG,
                        "Access to device with audio recording capabilities denied because "
                                + "microphone privacy is enabled.");
            }
            return false;
        }
        synchronized (mLock) {
            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
                return true;
            }
            DeviceFilter filter = new DeviceFilter(device);
            SparseBooleanArray permissionsForDevice = mDevicePersistentPermissionMap.get(filter);
            if (permissionsForDevice != null) {
                int idx = permissionsForDevice.indexOfKey(uid);
                if (idx >= 0) {
                    return permissionsForDevice.valueAt(idx);
                }
            }
            SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
            if (uidList == null) {
                return false;
            }
            return uidList.get(uid);
        }
    }

    /**
     * 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;
            }
            AccessoryFilter filter = new AccessoryFilter(accessory);
            SparseBooleanArray permissionsForAccessory =
                    mAccessoryPersistentPermissionMap.get(filter);
            if (permissionsForAccessory != null) {
                int idx = permissionsForAccessory.indexOfKey(uid);
                if (idx >= 0) {
                    return permissionsForAccessory.valueAt(idx);
                }
            }
            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
            if (uidList == null) {
                return false;
            }
            return uidList.get(uid);
        }
    }

    void setDevicePersistentPermission(@NonNull UsbDevice device, int uid, boolean isGranted) {

        boolean isChanged;
        DeviceFilter filter = new DeviceFilter(device);
        synchronized (mLock) {
            SparseBooleanArray permissionsForDevice = mDevicePersistentPermissionMap.get(filter);
            if (permissionsForDevice == null) {
                permissionsForDevice = new SparseBooleanArray();
                mDevicePersistentPermissionMap.put(filter, permissionsForDevice);
            }
            int idx = permissionsForDevice.indexOfKey(uid);
            if (idx >= 0) {
                isChanged = permissionsForDevice.valueAt(idx) != isGranted;
                permissionsForDevice.setValueAt(idx, isGranted);
            } else {
                isChanged = true;
                permissionsForDevice.put(uid, isGranted);
            }

            if (isChanged) {
                scheduleWritePermissionsLocked();
            }
        }
    }

    void setAccessoryPersistentPermission(@NonNull UsbAccessory accessory, int uid,
            boolean isGranted) {

        boolean isChanged;
        AccessoryFilter filter = new AccessoryFilter(accessory);
        synchronized (mLock) {
            SparseBooleanArray permissionsForAccessory =
                    mAccessoryPersistentPermissionMap.get(filter);
            if (permissionsForAccessory == null) {
                permissionsForAccessory = new SparseBooleanArray();
                mAccessoryPersistentPermissionMap.put(filter, permissionsForAccessory);
            }
            int idx = permissionsForAccessory.indexOfKey(uid);
            if (idx >= 0) {
                isChanged = permissionsForAccessory.valueAt(idx) != isGranted;
                permissionsForAccessory.setValueAt(idx, isGranted);
            } else {
                isChanged = true;
                permissionsForAccessory.put(uid, isGranted);
            }

            if (isChanged) {
                scheduleWritePermissionsLocked();
            }
        }
    }

    private void readPermission(@NonNull XmlPullParser parser) throws XmlPullParserException,
            IOException {
        int uid;
        boolean isGranted;

        try {
            uid = XmlUtils.readIntAttribute(parser, "uid");
        } catch (NumberFormatException e) {
            Slog.e(TAG, "error reading usb permission uid", e);
            XmlUtils.skipCurrentTag(parser);
            return;
        }

        // only use "true"/"false" as valid values
        String isGrantedString = parser.getAttributeValue(null, "granted");
        if (isGrantedString == null || !(isGrantedString.equals(Boolean.TRUE.toString())
                || isGrantedString.equals(Boolean.FALSE.toString()))) {
            Slog.e(TAG, "error reading usb permission granted state");
            XmlUtils.skipCurrentTag(parser);
            return;
        }
        isGranted = isGrantedString.equals(Boolean.TRUE.toString());
        XmlUtils.nextElement(parser);
        if ("usb-device".equals(parser.getName())) {
            DeviceFilter filter = DeviceFilter.read(parser);
            int idx = mDevicePersistentPermissionMap.indexOfKey(filter);
            if (idx >= 0) {
                SparseBooleanArray permissionsForDevice =
                        mDevicePersistentPermissionMap.valueAt(idx);
                permissionsForDevice.put(uid, isGranted);
            } else {
                SparseBooleanArray permissionsForDevice = new SparseBooleanArray();
                mDevicePersistentPermissionMap.put(filter, permissionsForDevice);
                permissionsForDevice.put(uid, isGranted);
            }
        } else if ("usb-accessory".equals(parser.getName())) {
            AccessoryFilter filter = AccessoryFilter.read(parser);
            int idx = mAccessoryPersistentPermissionMap.indexOfKey(filter);
            if (idx >= 0) {
                SparseBooleanArray permissionsForAccessory =
                        mAccessoryPersistentPermissionMap.valueAt(idx);
                permissionsForAccessory.put(uid, isGranted);
            } else {
                SparseBooleanArray permissionsForAccessory = new SparseBooleanArray();
                mAccessoryPersistentPermissionMap.put(filter, permissionsForAccessory);
                permissionsForAccessory.put(uid, isGranted);
            }
        }
    }

    @GuardedBy("mLock")
    private void readPermissionsLocked() {
        mDevicePersistentPermissionMap.clear();
        mAccessoryPersistentPermissionMap.clear();

        try (FileInputStream in = mPermissionsFile.openRead()) {
            TypedXmlPullParser parser = Xml.resolvePullParser(in);

            XmlUtils.nextElement(parser);
            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
                String tagName = parser.getName();
                if ("permission".equals(tagName)) {
                    readPermission(parser);
                } else {
                    XmlUtils.nextElement(parser);
                }
            }
        } catch (FileNotFoundException e) {
            if (DEBUG) Slog.d(TAG, "usb permissions file not found");
        } catch (Exception e) {
            Slog.e(TAG, "error reading usb permissions file, deleting to start fresh", e);
            mPermissionsFile.delete();
        }
    }

    @GuardedBy("mLock")
    private void scheduleWritePermissionsLocked() {
        if (mIsCopyPermissionsScheduled) {
            return;
        }
        mIsCopyPermissionsScheduled = true;

        AsyncTask.execute(() -> {
            int numDevices;
            DeviceFilter[] devices;
            int[][] uidsForDevices;
            boolean[][] grantedValuesForDevices;

            int numAccessories;
            AccessoryFilter[] accessories;
            int[][] uidsForAccessories;
            boolean[][] grantedValuesForAccessories;

            synchronized (mLock) {
                // Copy the permission state so we can write outside of lock
                numDevices = mDevicePersistentPermissionMap.size();
                devices = new DeviceFilter[numDevices];
                uidsForDevices = new int[numDevices][];
                grantedValuesForDevices = new boolean[numDevices][];
                for (int deviceIdx = 0; deviceIdx < numDevices; deviceIdx++) {
                    devices[deviceIdx] =
                            new DeviceFilter(mDevicePersistentPermissionMap.keyAt(deviceIdx));
                    SparseBooleanArray permissions =
                            mDevicePersistentPermissionMap.valueAt(deviceIdx);
                    int numPermissions = permissions.size();
                    uidsForDevices[deviceIdx] = new int[numPermissions];
                    grantedValuesForDevices[deviceIdx] = new boolean[numPermissions];
                    for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) {
                        uidsForDevices[deviceIdx][permissionIdx] = permissions.keyAt(permissionIdx);
                        grantedValuesForDevices[deviceIdx][permissionIdx] =
                                permissions.valueAt(permissionIdx);
                    }
                }

                numAccessories = mAccessoryPersistentPermissionMap.size();
                accessories = new AccessoryFilter[numAccessories];
                uidsForAccessories = new int[numAccessories][];
                grantedValuesForAccessories = new boolean[numAccessories][];
                for (int accessoryIdx = 0; accessoryIdx < numAccessories; accessoryIdx++) {
                    accessories[accessoryIdx] = new AccessoryFilter(
                                    mAccessoryPersistentPermissionMap.keyAt(accessoryIdx));
                    SparseBooleanArray permissions =
                            mAccessoryPersistentPermissionMap.valueAt(accessoryIdx);
                    int numPermissions = permissions.size();
                    uidsForAccessories[accessoryIdx] = new int[numPermissions];
                    grantedValuesForAccessories[accessoryIdx] = new boolean[numPermissions];
                    for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) {
                        uidsForAccessories[accessoryIdx][permissionIdx] =
                                permissions.keyAt(permissionIdx);
                        grantedValuesForAccessories[accessoryIdx][permissionIdx] =
                                permissions.valueAt(permissionIdx);
                    }
                }
                mIsCopyPermissionsScheduled = false;
            }

            synchronized (mPermissionsFile) {
                FileOutputStream out = null;
                try {
                    out = mPermissionsFile.startWrite();
                    TypedXmlSerializer serializer = Xml.resolveSerializer(out);
                    serializer.startDocument(null, true);
                    serializer.startTag(null, "permissions");

                    for (int i = 0; i < numDevices; i++) {
                        int numPermissions = uidsForDevices[i].length;
                        for (int j = 0; j < numPermissions; j++) {
                            serializer.startTag(null, "permission");
                            serializer.attribute(null, "uid",
                                    Integer.toString(uidsForDevices[i][j]));
                            serializer.attribute(null, "granted",
                                    Boolean.toString(grantedValuesForDevices[i][j]));
                            devices[i].write(serializer);
                            serializer.endTag(null, "permission");
                        }
                    }

                    for (int i = 0; i < numAccessories; i++) {
                        int numPermissions = uidsForAccessories[i].length;
                        for (int j = 0; j < numPermissions; j++) {
                            serializer.startTag(null, "permission");
                            serializer.attribute(null, "uid",
                                    Integer.toString(uidsForAccessories[i][j]));
                            serializer.attribute(null, "granted",
                                    Boolean.toString(grantedValuesForDevices[i][j]));
                            accessories[i].write(serializer);
                            serializer.endTag(null, "permission");
                        }
                    }

                    serializer.endTag(null, "permissions");
                    serializer.endDocument();

                    mPermissionsFile.finishWrite(out);
                } catch (IOException e) {
                    Slog.e(TAG, "Failed to write permissions", e);
                    if (out != null) {
                        mPermissionsFile.failWrite(out);
                    }
                }
            }
        });
    }

    /**
     * Creates UI dialog to request permission for the given package to access the device
     * or accessory.
     *
     * @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
     */
    void requestPermissionDialog(@Nullable UsbDevice device,
            @Nullable UsbAccessory accessory,
            boolean canBeDefault,
            @NonNull String packageName,
            int uid,
            @NonNull Context userContext,
            @NonNull PendingIntent pi) {
        final long identity = Binder.clearCallingIdentity();
        try {
            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.setComponent(
                    ComponentName.unflattenFromString(userContext.getResources().getString(
                            com.android.internal.R.string.config_usbPermissionActivity)));
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

            userContext.startActivityAsUser(intent, mUser);
        } catch (ActivityNotFoundException e) {
            Slog.e(TAG, "unable to start UsbPermissionActivity");
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
        long token = dump.start(idName, id);
        synchronized (mLock) {
            dump.write("user_id", UsbUserPermissionsManagerProto.USER_ID, mUser.getIdentifier());
            int numMappings = mDevicePermissionMap.size();
            for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
                String deviceName = mDevicePermissionMap.keyAt(mappingsIdx);
                long devicePermissionToken = dump.start("device_permissions",
                        UsbUserPermissionsManagerProto.DEVICE_PERMISSIONS);

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

                SparseBooleanArray uidList = mDevicePermissionMap.valueAt(mappingsIdx);
                int numUids = uidList.size();
                for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) {
                    dump.write("uids", UsbDevicePermissionProto.UIDS, uidList.keyAt(uidsIdx));
                }

                dump.end(devicePermissionToken);
            }

            numMappings = mAccessoryPermissionMap.size();
            for (int mappingsIdx = 0; mappingsIdx < numMappings; ++mappingsIdx) {
                UsbAccessory accessory = mAccessoryPermissionMap.keyAt(mappingsIdx);
                long accessoryPermissionToken = dump.start("accessory_permissions",
                        UsbUserPermissionsManagerProto.ACCESSORY_PERMISSIONS);

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

                SparseBooleanArray uidList = mAccessoryPermissionMap.valueAt(mappingsIdx);
                int numUids = uidList.size();
                for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) {
                    dump.write("uids", UsbAccessoryPermissionProto.UIDS, uidList.keyAt(uidsIdx));
                }

                dump.end(accessoryPermissionToken);
            }

            numMappings = mDevicePersistentPermissionMap.size();
            for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
                DeviceFilter filter = mDevicePersistentPermissionMap.keyAt(mappingsIdx);
                long devicePermissionToken = dump.start("device_persistent_permissions",
                        UsbUserPermissionsManagerProto.DEVICE_PERSISTENT_PERMISSIONS);
                filter.dump(dump, "device",
                        UsbDevicePersistentPermissionProto.DEVICE_FILTER);
                SparseBooleanArray permissions =
                        mDevicePersistentPermissionMap.valueAt(mappingsIdx);
                int numPermissions = permissions.size();
                for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) {
                    long uidPermissionToken = dump.start("uid_permission",
                            UsbDevicePersistentPermissionProto.PERMISSION_VALUES);
                    dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx));
                    dump.write("is_granted",
                            UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx));
                    dump.end(uidPermissionToken);
                }
                dump.end(devicePermissionToken);
            }

            numMappings = mAccessoryPersistentPermissionMap.size();
            for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
                AccessoryFilter filter = mAccessoryPersistentPermissionMap.keyAt(mappingsIdx);
                long accessoryPermissionToken = dump.start("accessory_persistent_permissions",
                        UsbUserPermissionsManagerProto.ACCESSORY_PERSISTENT_PERMISSIONS);
                filter.dump(dump, "accessory",
                        UsbAccessoryPersistentPermissionProto.ACCESSORY_FILTER);
                SparseBooleanArray permissions =
                        mAccessoryPersistentPermissionMap.valueAt(mappingsIdx);
                int numPermissions = permissions.size();
                for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) {
                    long uidPermissionToken = dump.start("uid_permission",
                            UsbAccessoryPersistentPermissionProto.PERMISSION_VALUES);
                    dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx));
                    dump.write("is_granted",
                            UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx));
                    dump.end(uidPermissionToken);
                }
                dump.end(accessoryPermissionToken);
            }
        }
        dump.end(token);
    }

    /**
     * Check for camera permission of the calling process.
     *
     * @param packageName Package name of the caller.
     * @param pid         Linux pid of the calling process.
     * @param uid         Linux uid of the calling process.
     * @return True in case camera permission is available, False otherwise.
     */
    private boolean isCameraPermissionGranted(String packageName, int pid, int uid) {
        int targetSdkVersion = android.os.Build.VERSION_CODES.P;
        try {
            ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
            // compare uid with packageName to foil apps pretending to be someone else
            if (aInfo.uid != uid) {
                Slog.i(TAG, "Package " + packageName + " does not match caller's uid " + uid);
                return false;
            }
            targetSdkVersion = aInfo.targetSdkVersion;
        } catch (PackageManager.NameNotFoundException e) {
            Slog.i(TAG, "Package not found, likely due to invalid package name!");
            return false;
        }

        if (targetSdkVersion >= android.os.Build.VERSION_CODES.P) {
            int allowed = mContext.checkPermission(android.Manifest.permission.CAMERA, pid, uid);
            if (android.content.pm.PackageManager.PERMISSION_DENIED == allowed) {
                Slog.i(TAG, "Camera permission required for USB video class devices");
                return false;
            }
        }

        return true;
    }

    public void checkPermission(UsbDevice device, String packageName, int pid, int uid) {
        if (!hasPermission(device, packageName, pid, uid)) {
            throw new SecurityException("User has not given " + uid + "/" + packageName
                    + " permission to access device " + device.getDeviceName());
        }
    }

    public void checkPermission(UsbAccessory accessory, int uid) {
        if (!hasPermission(accessory, uid)) {
            throw new SecurityException("User has not given " + uid + " permission to accessory "
                    + accessory);
        }
    }

    private void requestPermissionDialog(@Nullable UsbDevice device,
            @Nullable UsbAccessory accessory,
            boolean canBeDefault,
            String packageName,
            PendingIntent pi,
            int uid) {
        // compare uid with packageName to foil apps pretending to be someone else
        try {
            ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
            if (aInfo.uid != uid) {
                throw new IllegalArgumentException("package " + packageName
                        + " does not match caller's uid " + uid);
            }
        } catch (PackageManager.NameNotFoundException e) {
            throw new IllegalArgumentException("package " + packageName + " not found");
        }

        requestPermissionDialog(device, accessory, canBeDefault, packageName, uid, mContext, pi);
    }

    public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid,
            int uid) {
        Intent intent = new Intent();

        // respond immediately if permission has already been granted
        if (hasPermission(device, packageName, pid, uid)) {
            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
            try {
                pi.send(mContext, 0, intent);
            } catch (PendingIntent.CanceledException e) {
                if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
            }
            return;
        }
        // If the app doesn't have camera permission do not request permission to the USB device.
        // Note that if the USB camera also has a microphone, a warning will be shown to the user if
        // the app doesn't have RECORD_AUDIO permission.
        if (device.getHasVideoCapture()) {
            if (!isCameraPermissionGranted(packageName, pid, uid)) {
                intent.putExtra(UsbManager.EXTRA_DEVICE, device);
                intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
                try {
                    pi.send(mContext, 0, intent);
                } catch (PendingIntent.CanceledException e) {
                    if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
                }
                return;
            }
        }

        requestPermissionDialog(device, null,
                mUsbUserSettingsManager.canBeDefault(device, packageName), packageName, pi, uid);
    }

    public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi,
            int uid) {
        // respond immediately if permission has already been granted
        if (hasPermission(accessory, uid)) {
            Intent intent = new Intent();
            intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
            try {
                pi.send(mContext, 0, intent);
            } catch (PendingIntent.CanceledException e) {
                if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
            }
            return;
        }

        requestPermissionDialog(null, accessory,
                mUsbUserSettingsManager.canBeDefault(accessory, packageName), packageName, pi, uid);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy