blob: dd2f29ba74ae09460e3687436cb119a354fc5ead [file] [log] [blame]
/*
* 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.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 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();
@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<>();
private final UserHandle mUser;
private final boolean mDisablePermissionDialogs;
private final Object mLock = new Object();
UsbPermissionManager(@NonNull Context context, @NonNull UserHandle user) {
mUser = user;
mDisablePermissionDialogs = context.getResources().getBoolean(
com.android.internal.R.bool.config_disableUsbPermissionDialogs);
}
/**
* 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 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);
}
}
/**
* 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);
}
}
/**
* 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) {
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 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));
}
dump.end(accessoryPermissionToken);
}
}
}
}