When a USB device or accessory is connected, the UsbService now asks the user which application to associate with the device or accessory. Applications interested in devices or accessories must specify the devices they work with via meta-data attached to their manifest. Permission to communicate with the device is assigned when the user chooses the activity to use for the device. The user has the option of clicking the "always use this application" checkbox to make the assignment automatic in the future. The user may later clear this preference and revoke permission for an application to have permission to communicate with the device by clicking the "Clear defaults" button for the activity in the Manage Applications panel in Settings. Added class UsbResolveActivity (a subclass or ResolveActivity for choosing an activity for a USB device or accessory) Added UsbDeviceManager, which manages the mapping between USB devices/accessories and applications, including default applications for devices and accessories, and manages application permissions. Add interface to allow Settings to clear device and accessory preferences and permissions for an application. Remove obsolete ACCESS_USB permission. Add new signatureOrSystem MANAGE_USB permission to allow administrating preferences and permissions. Moved UsbService.java to a "usb" subdirectory, along with new classes UsbResolveActivity and UsbDeviceManager. Change-Id: I92554381e9779e68ce380daaee4e1401fb875703 Signed-off-by: Mike Lockwood <lockwood@android.com>
862 lines
34 KiB
Java
862 lines
34 KiB
Java
/*
|
|
* Copyright (C) 2011 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.content.ActivityNotFoundException;
|
|
import android.content.ComponentName;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.pm.ActivityInfo;
|
|
import android.content.pm.PackageManager;
|
|
import android.content.pm.PackageManager.NameNotFoundException;
|
|
import android.content.pm.ResolveInfo;
|
|
import android.content.res.XmlResourceParser;
|
|
import android.hardware.UsbAccessory;
|
|
import android.hardware.UsbDevice;
|
|
import android.hardware.UsbInterface;
|
|
import android.hardware.UsbManager;
|
|
import android.os.Binder;
|
|
import android.os.FileUtils;
|
|
import android.os.Process;
|
|
import android.util.Log;
|
|
import android.util.SparseArray;
|
|
import android.util.Xml;
|
|
|
|
import com.android.internal.content.PackageMonitor;
|
|
import com.android.internal.util.FastXmlSerializer;
|
|
import com.android.internal.util.XmlUtils;
|
|
|
|
import org.xmlpull.v1.XmlPullParser;
|
|
import org.xmlpull.v1.XmlPullParserException;
|
|
import org.xmlpull.v1.XmlSerializer;
|
|
|
|
import java.io.BufferedOutputStream;
|
|
import java.io.File;
|
|
import java.io.FileDescriptor;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.PrintWriter;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
|
|
class UsbDeviceSettingsManager {
|
|
|
|
private static final String TAG = "UsbDeviceSettingsManager";
|
|
private static final File sSettingsFile = new File("/data/system/usb_device_manager.xml");
|
|
|
|
private final Context mContext;
|
|
|
|
// maps UID to user approved USB devices
|
|
final SparseArray<ArrayList<DeviceFilter>> mDevicePermissionMap =
|
|
new SparseArray<ArrayList<DeviceFilter>>();
|
|
// maps UID to user approved USB accessories
|
|
final SparseArray<ArrayList<AccessoryFilter>> mAccessoryPermissionMap =
|
|
new SparseArray<ArrayList<AccessoryFilter>>();
|
|
// Maps DeviceFilter to user preferred application package
|
|
final HashMap<DeviceFilter, String> mDevicePreferenceMap =
|
|
new HashMap<DeviceFilter, String>();
|
|
// Maps DeviceFilter to user preferred application package
|
|
final HashMap<AccessoryFilter, String> mAccessoryPreferenceMap =
|
|
new HashMap<AccessoryFilter, String>();
|
|
|
|
// This class is used to describe a USB device.
|
|
// When used in HashMaps all values must be specified,
|
|
// but wildcards can be used for any of the fields in
|
|
// the package meta-data.
|
|
private static class DeviceFilter {
|
|
// USB Vendor ID (or -1 for unspecified)
|
|
public final int mVendorId;
|
|
// USB Product ID (or -1 for unspecified)
|
|
public final int mProductId;
|
|
// USB device or interface class (or -1 for unspecified)
|
|
public final int mClass;
|
|
// USB device subclass (or -1 for unspecified)
|
|
public final int mSubclass;
|
|
// USB device protocol (or -1 for unspecified)
|
|
public final int mProtocol;
|
|
|
|
public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol) {
|
|
mVendorId = vid;
|
|
mProductId = pid;
|
|
mClass = clasz;
|
|
mSubclass = subclass;
|
|
mProtocol = protocol;
|
|
}
|
|
|
|
public DeviceFilter(UsbDevice device) {
|
|
mVendorId = device.getVendorId();
|
|
mProductId = device.getProductId();
|
|
mClass = device.getDeviceClass();
|
|
mSubclass = device.getDeviceSubclass();
|
|
mProtocol = device.getDeviceProtocol();
|
|
}
|
|
|
|
public static DeviceFilter read(XmlPullParser parser)
|
|
throws XmlPullParserException, IOException {
|
|
int vendorId = -1;
|
|
int productId = -1;
|
|
int deviceClass = -1;
|
|
int deviceSubclass = -1;
|
|
int deviceProtocol = -1;
|
|
|
|
int count = parser.getAttributeCount();
|
|
for (int i = 0; i < count; i++) {
|
|
String name = parser.getAttributeName(i);
|
|
// All attribute values are ints
|
|
int value = Integer.parseInt(parser.getAttributeValue(i));
|
|
|
|
if ("vendor-id".equals(name)) {
|
|
vendorId = value;
|
|
} else if ("product-id".equals(name)) {
|
|
productId = value;
|
|
} else if ("class".equals(name)) {
|
|
deviceClass = value;
|
|
} else if ("subclass".equals(name)) {
|
|
deviceSubclass = value;
|
|
} else if ("protocol".equals(name)) {
|
|
deviceProtocol = value;
|
|
}
|
|
}
|
|
return new DeviceFilter(vendorId, productId,
|
|
deviceClass, deviceSubclass, deviceProtocol);
|
|
}
|
|
|
|
public void write(XmlSerializer serializer) throws IOException {
|
|
serializer.startTag(null, "usb-device");
|
|
if (mVendorId != -1) {
|
|
serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
|
|
}
|
|
if (mProductId != -1) {
|
|
serializer.attribute(null, "product-id", Integer.toString(mProductId));
|
|
}
|
|
if (mClass != -1) {
|
|
serializer.attribute(null, "class", Integer.toString(mClass));
|
|
}
|
|
if (mSubclass != -1) {
|
|
serializer.attribute(null, "subclass", Integer.toString(mSubclass));
|
|
}
|
|
if (mProtocol != -1) {
|
|
serializer.attribute(null, "protocol", Integer.toString(mProtocol));
|
|
}
|
|
serializer.endTag(null, "usb-device");
|
|
}
|
|
|
|
private boolean matches(int clasz, int subclass, int protocol) {
|
|
return ((mClass == -1 || clasz == mClass) &&
|
|
(mSubclass == -1 || subclass == mSubclass) &&
|
|
(mProtocol == -1 || protocol == mProtocol));
|
|
}
|
|
|
|
public boolean matches(UsbDevice device) {
|
|
if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
|
|
if (mProductId != -1 && device.getProductId() != mProductId) return false;
|
|
|
|
// check device class/subclass/protocol
|
|
if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
|
|
device.getDeviceProtocol())) return true;
|
|
|
|
// if device doesn't match, check the interfaces
|
|
int count = device.getInterfaceCount();
|
|
for (int i = 0; i < count; i++) {
|
|
UsbInterface intf = device.getInterface(i);
|
|
if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
|
|
intf.getInterfaceProtocol())) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
// can't compare if we have wildcard strings
|
|
if (mVendorId == -1 || mProductId == -1 ||
|
|
mClass == -1 || mSubclass == -1 || mProtocol == -1) {
|
|
return false;
|
|
}
|
|
if (obj instanceof DeviceFilter) {
|
|
DeviceFilter filter = (DeviceFilter)obj;
|
|
return (filter.mVendorId == mVendorId &&
|
|
filter.mProductId == mProductId &&
|
|
filter.mClass == mClass &&
|
|
filter.mSubclass == mSubclass &&
|
|
filter.mProtocol == mProtocol);
|
|
}
|
|
if (obj instanceof UsbDevice) {
|
|
UsbDevice device = (UsbDevice)obj;
|
|
return (device.getVendorId() == mVendorId &&
|
|
device.getProductId() == mProductId &&
|
|
device.getDeviceClass() == mClass &&
|
|
device.getDeviceSubclass() == mSubclass &&
|
|
device.getDeviceProtocol() == mProtocol);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return (((mVendorId << 16) | mProductId) ^
|
|
((mClass << 16) | (mSubclass << 8) | mProtocol));
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
|
|
",mClass=" + mClass + ",mSubclass=" + mSubclass +
|
|
",mProtocol=" + mProtocol + "]";
|
|
}
|
|
}
|
|
|
|
// This class is used to describe a USB accessory.
|
|
// When used in HashMaps all values must be specified,
|
|
// but wildcards can be used for any of the fields in
|
|
// the package meta-data.
|
|
private static class AccessoryFilter {
|
|
// USB accessory manufacturer (or null for unspecified)
|
|
public final String mManufacturer;
|
|
// USB accessory model (or null for unspecified)
|
|
public final String mModel;
|
|
// USB accessory type (or null for unspecified)
|
|
public final String mType;
|
|
// USB accessory version (or null for unspecified)
|
|
public final String mVersion;
|
|
|
|
public AccessoryFilter(String manufacturer, String model, String type, String version) {
|
|
mManufacturer = manufacturer;
|
|
mModel = model;
|
|
mType = type;
|
|
mVersion = version;
|
|
}
|
|
|
|
public AccessoryFilter(UsbAccessory accessory) {
|
|
mManufacturer = accessory.getManufacturer();
|
|
mModel = accessory.getModel();
|
|
mType = accessory.getType();
|
|
mVersion = accessory.getVersion();
|
|
}
|
|
|
|
public static AccessoryFilter read(XmlPullParser parser)
|
|
throws XmlPullParserException, IOException {
|
|
String manufacturer = null;
|
|
String model = null;
|
|
String type = null;
|
|
String version = null;
|
|
|
|
int count = parser.getAttributeCount();
|
|
for (int i = 0; i < count; i++) {
|
|
String name = parser.getAttributeName(i);
|
|
String value = parser.getAttributeValue(i);
|
|
|
|
if ("manufacturer".equals(name)) {
|
|
manufacturer = value;
|
|
} else if ("model".equals(name)) {
|
|
model = value;
|
|
} else if ("type".equals(name)) {
|
|
type = value;
|
|
} else if ("version".equals(name)) {
|
|
version = value;
|
|
}
|
|
}
|
|
return new AccessoryFilter(manufacturer, model, type, version);
|
|
}
|
|
|
|
public void write(XmlSerializer serializer)throws IOException {
|
|
serializer.startTag(null, "usb-accessory");
|
|
if (mManufacturer != null) {
|
|
serializer.attribute(null, "manufacturer", mManufacturer);
|
|
}
|
|
if (mModel != null) {
|
|
serializer.attribute(null, "model", mModel);
|
|
}
|
|
if (mType != null) {
|
|
serializer.attribute(null, "type", mType);
|
|
}
|
|
if (mVersion != null) {
|
|
serializer.attribute(null, "version", mVersion);
|
|
}
|
|
serializer.endTag(null, "usb-accessory");
|
|
}
|
|
|
|
public boolean matches(UsbAccessory acc) {
|
|
if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
|
|
if (mModel != null && !acc.getModel().equals(mModel)) return false;
|
|
if (mType != null && !acc.getType().equals(mType)) return false;
|
|
if (mVersion != null && !acc.getVersion().equals(mVersion)) return false;
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
// can't compare if we have wildcard strings
|
|
if (mManufacturer == null || mModel == null || mType == null || mVersion == null) {
|
|
return false;
|
|
}
|
|
if (obj instanceof AccessoryFilter) {
|
|
AccessoryFilter filter = (AccessoryFilter)obj;
|
|
return (mManufacturer.equals(filter.mManufacturer) &&
|
|
mModel.equals(filter.mModel) &&
|
|
mType.equals(filter.mType) &&
|
|
mVersion.equals(filter.mVersion));
|
|
}
|
|
if (obj instanceof UsbAccessory) {
|
|
UsbAccessory accessory = (UsbAccessory)obj;
|
|
return (mManufacturer.equals(accessory.getManufacturer()) &&
|
|
mModel.equals(accessory.getModel()) &&
|
|
mType.equals(accessory.getType()) &&
|
|
mVersion.equals(accessory.getVersion()));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
|
|
(mModel == null ? 0 : mModel.hashCode()) ^
|
|
(mType == null ? 0 : mType.hashCode()) ^
|
|
(mVersion == null ? 0 : mVersion.hashCode()));
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
|
|
"\", mModel=\"" + mModel +
|
|
"\", mType=\"" + mType +
|
|
"\", mVersion=\"" + mVersion + "\"]";
|
|
}
|
|
}
|
|
|
|
private class MyPackageMonitor extends PackageMonitor {
|
|
public void onPackageRemoved(String packageName, int uid) {
|
|
// clear all activity preferences for the package
|
|
if (clearPackageDefaults(packageName)) {
|
|
writeSettings();
|
|
}
|
|
}
|
|
|
|
public void onUidRemoved(int uid) {
|
|
// clear all permissions for the UID
|
|
if (clearUidDefaults(uid)) {
|
|
writeSettings();
|
|
}
|
|
}
|
|
}
|
|
MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
|
|
|
|
public UsbDeviceSettingsManager(Context context) {
|
|
mContext = context;
|
|
readSettings();
|
|
mPackageMonitor.register(context, true);
|
|
}
|
|
|
|
private void readDevicePermission(XmlPullParser parser)
|
|
throws XmlPullParserException, IOException {
|
|
int uid = -1;
|
|
ArrayList<DeviceFilter> filters = new ArrayList<DeviceFilter>();
|
|
int count = parser.getAttributeCount();
|
|
for (int i = 0; i < count; i++) {
|
|
if ("uid".equals(parser.getAttributeName(i))) {
|
|
uid = Integer.parseInt(parser.getAttributeValue(i));
|
|
break;
|
|
}
|
|
}
|
|
XmlUtils.nextElement(parser);
|
|
while ("usb-device".equals(parser.getName())) {
|
|
filters.add(DeviceFilter.read(parser));
|
|
XmlUtils.nextElement(parser);
|
|
}
|
|
mDevicePermissionMap.put(uid, filters);
|
|
}
|
|
|
|
private void readAccessoryPermission(XmlPullParser parser)
|
|
throws XmlPullParserException, IOException {
|
|
int uid = -1;
|
|
ArrayList<AccessoryFilter> filters = new ArrayList<AccessoryFilter>();
|
|
int count = parser.getAttributeCount();
|
|
for (int i = 0; i < count; i++) {
|
|
if ("uid".equals(parser.getAttributeName(i))) {
|
|
uid = Integer.parseInt(parser.getAttributeValue(i));
|
|
break;
|
|
}
|
|
}
|
|
XmlUtils.nextElement(parser);
|
|
while ("usb-accessory".equals(parser.getName())) {
|
|
filters.add(AccessoryFilter.read(parser));
|
|
XmlUtils.nextElement(parser);
|
|
}
|
|
mAccessoryPermissionMap.put(uid, filters);
|
|
}
|
|
|
|
private void readPreference(XmlPullParser parser)
|
|
throws XmlPullParserException, IOException {
|
|
String packageName = null;
|
|
int count = parser.getAttributeCount();
|
|
for (int i = 0; i < count; i++) {
|
|
if ("package".equals(parser.getAttributeName(i))) {
|
|
packageName = parser.getAttributeValue(i);
|
|
break;
|
|
}
|
|
}
|
|
XmlUtils.nextElement(parser);
|
|
if ("usb-device".equals(parser.getName())) {
|
|
DeviceFilter filter = DeviceFilter.read(parser);
|
|
mDevicePreferenceMap.put(filter, packageName);
|
|
} else if ("usb-accessory".equals(parser.getName())) {
|
|
AccessoryFilter filter = AccessoryFilter.read(parser);
|
|
mAccessoryPreferenceMap.put(filter, packageName);
|
|
}
|
|
XmlUtils.nextElement(parser);
|
|
}
|
|
|
|
private void readSettings() {
|
|
FileInputStream stream = null;
|
|
try {
|
|
stream = new FileInputStream(sSettingsFile);
|
|
XmlPullParser parser = Xml.newPullParser();
|
|
parser.setInput(stream, null);
|
|
|
|
XmlUtils.nextElement(parser);
|
|
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
|
|
String tagName = parser.getName();
|
|
if ("device-permission".equals(tagName)) {
|
|
readDevicePermission(parser);
|
|
} else if ("accessory-permission".equals(tagName)) {
|
|
readAccessoryPermission(parser);
|
|
} else if ("preference".equals(tagName)) {
|
|
readPreference(parser);
|
|
} else {
|
|
XmlUtils.nextElement(parser);
|
|
}
|
|
}
|
|
} catch (FileNotFoundException e) {
|
|
Log.w(TAG, "settings file not found");
|
|
} catch (Exception e) {
|
|
Log.e(TAG, "error reading settings file, deleting to start fresh", e);
|
|
sSettingsFile.delete();
|
|
} finally {
|
|
if (stream != null) {
|
|
try {
|
|
stream.close();
|
|
} catch (IOException e) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void writeSettings() {
|
|
FileOutputStream fos = null;
|
|
try {
|
|
FileOutputStream fstr = new FileOutputStream(sSettingsFile);
|
|
Log.d(TAG, "writing settings to " + fstr);
|
|
BufferedOutputStream str = new BufferedOutputStream(fstr);
|
|
FastXmlSerializer serializer = new FastXmlSerializer();
|
|
serializer.setOutput(str, "utf-8");
|
|
serializer.startDocument(null, true);
|
|
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
|
|
serializer.startTag(null, "settings");
|
|
|
|
int count = mDevicePermissionMap.size();
|
|
for (int i = 0; i < count; i++) {
|
|
int uid = mDevicePermissionMap.keyAt(i);
|
|
ArrayList<DeviceFilter> filters = mDevicePermissionMap.valueAt(i);
|
|
serializer.startTag(null, "device-permission");
|
|
serializer.attribute(null, "uid", Integer.toString(uid));
|
|
int filterCount = filters.size();
|
|
for (int j = 0; j < filterCount; j++) {
|
|
filters.get(j).write(serializer);
|
|
}
|
|
serializer.endTag(null, "device-permission");
|
|
}
|
|
|
|
count = mAccessoryPermissionMap.size();
|
|
for (int i = 0; i < count; i++) {
|
|
int uid = mAccessoryPermissionMap.keyAt(i);
|
|
ArrayList<AccessoryFilter> filters = mAccessoryPermissionMap.valueAt(i);
|
|
serializer.startTag(null, "accessory-permission");
|
|
serializer.attribute(null, "uid", Integer.toString(uid));
|
|
int filterCount = filters.size();
|
|
for (int j = 0; j < filterCount; j++) {
|
|
filters.get(j).write(serializer);
|
|
}
|
|
serializer.endTag(null, "accessory-permission");
|
|
}
|
|
|
|
for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
|
|
serializer.startTag(null, "preference");
|
|
serializer.attribute(null, "package", mDevicePreferenceMap.get(filter));
|
|
filter.write(serializer);
|
|
serializer.endTag(null, "preference");
|
|
}
|
|
|
|
for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
|
|
serializer.startTag(null, "preference");
|
|
serializer.attribute(null, "package", mAccessoryPreferenceMap.get(filter));
|
|
filter.write(serializer);
|
|
serializer.endTag(null, "preference");
|
|
}
|
|
|
|
serializer.endTag(null, "settings");
|
|
serializer.endDocument();
|
|
|
|
str.flush();
|
|
FileUtils.sync(fstr);
|
|
str.close();
|
|
} catch (Exception e) {
|
|
Log.e(TAG, "error writing settings file, deleting to start fresh", e);
|
|
sSettingsFile.delete();
|
|
}
|
|
}
|
|
|
|
// Checks to see if a package matches a device or accessory.
|
|
// Only one of device and accessory should be non-null.
|
|
private boolean packageMatches(ResolveInfo info, String metaDataName,
|
|
UsbDevice device, UsbAccessory accessory) {
|
|
ActivityInfo ai = info.activityInfo;
|
|
PackageManager pm = mContext.getPackageManager();
|
|
|
|
XmlResourceParser parser = null;
|
|
try {
|
|
parser = ai.loadXmlMetaData(pm, metaDataName);
|
|
if (parser == null) {
|
|
Log.w(TAG, "no meta-data for " + info);
|
|
return false;
|
|
}
|
|
|
|
XmlUtils.nextElement(parser);
|
|
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
|
|
String tagName = parser.getName();
|
|
if (device != null && "usb-device".equals(tagName)) {
|
|
DeviceFilter filter = DeviceFilter.read(parser);
|
|
if (filter.matches(device)) {
|
|
return true;
|
|
}
|
|
}
|
|
else if (accessory != null && "usb-accessory".equals(tagName)) {
|
|
AccessoryFilter filter = AccessoryFilter.read(parser);
|
|
if (filter.matches(accessory)) {
|
|
return true;
|
|
}
|
|
}
|
|
XmlUtils.nextElement(parser);
|
|
}
|
|
} catch (Exception e) {
|
|
Log.w(TAG, "Unable to load component info " + info.toString(), e);
|
|
} finally {
|
|
if (parser != null) parser.close();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private final ArrayList<ResolveInfo> getDeviceMatches(UsbDevice device, Intent intent) {
|
|
ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
|
|
PackageManager pm = mContext.getPackageManager();
|
|
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent,
|
|
PackageManager.GET_META_DATA);
|
|
int count = resolveInfos.size();
|
|
for (int i = 0; i < count; i++) {
|
|
ResolveInfo resolveInfo = resolveInfos.get(i);
|
|
if (packageMatches(resolveInfo, intent.getAction(), device, null)) {
|
|
matches.add(resolveInfo);
|
|
}
|
|
}
|
|
return matches;
|
|
}
|
|
|
|
private final ArrayList<ResolveInfo> getAccessoryMatches(UsbAccessory accessory, Intent intent) {
|
|
ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
|
|
PackageManager pm = mContext.getPackageManager();
|
|
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent,
|
|
PackageManager.GET_META_DATA);
|
|
int count = resolveInfos.size();
|
|
for (int i = 0; i < count; i++) {
|
|
ResolveInfo resolveInfo = resolveInfos.get(i);
|
|
if (packageMatches(resolveInfo, intent.getAction(), null, accessory)) {
|
|
matches.add(resolveInfo);
|
|
}
|
|
}
|
|
return matches;
|
|
}
|
|
|
|
public void deviceAttached(UsbDevice device) {
|
|
Intent deviceIntent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
|
|
deviceIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
|
|
deviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
ArrayList<ResolveInfo> matches = getDeviceMatches(device, deviceIntent);
|
|
// Launch our default activity directly, if we have one.
|
|
// Otherwise we will start the UsbResolverActivity to allow the user to choose.
|
|
String defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device));
|
|
if (defaultPackage != null) {
|
|
int count = matches.size();
|
|
for (int i = 0; i < count; i++) {
|
|
ResolveInfo rInfo = matches.get(i);
|
|
if (rInfo.activityInfo != null &&
|
|
defaultPackage.equals(rInfo.activityInfo.packageName)) {
|
|
try {
|
|
deviceIntent.setComponent(new ComponentName(
|
|
defaultPackage, rInfo.activityInfo.name));
|
|
mContext.startActivity(deviceIntent);
|
|
} catch (ActivityNotFoundException e) {
|
|
Log.e(TAG, "startActivity failed", e);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
Intent intent = new Intent(mContext, UsbResolverActivity.class);
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
intent.putExtra(Intent.EXTRA_INTENT, deviceIntent);
|
|
intent.putParcelableArrayListExtra(UsbResolverActivity.EXTRA_RESOLVE_INFOS,
|
|
matches);
|
|
try {
|
|
mContext.startActivity(intent);
|
|
} catch (ActivityNotFoundException e) {
|
|
Log.w(TAG, "unable to start UsbResolverActivity");
|
|
}
|
|
}
|
|
|
|
public void deviceDetached(UsbDevice device) {
|
|
Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
|
|
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
|
|
Log.d(TAG, "usbDeviceRemoved, sending " + intent);
|
|
mContext.sendBroadcast(intent);
|
|
}
|
|
|
|
public void accessoryAttached(UsbAccessory accessory) {
|
|
Intent accessoryIntent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
|
|
accessoryIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
|
|
accessoryIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
ArrayList<ResolveInfo> matches = getAccessoryMatches(accessory, accessoryIntent);
|
|
// Launch our default activity directly, if we have one.
|
|
// Otherwise we will start the UsbResolverActivity to allow the user to choose.
|
|
String defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory));
|
|
if (defaultPackage != null) {
|
|
int count = matches.size();
|
|
for (int i = 0; i < count; i++) {
|
|
ResolveInfo rInfo = matches.get(i);
|
|
if (rInfo.activityInfo != null &&
|
|
defaultPackage.equals(rInfo.activityInfo.packageName)) {
|
|
try {
|
|
accessoryIntent.setComponent(new ComponentName(
|
|
defaultPackage, rInfo.activityInfo.name));
|
|
mContext.startActivity(accessoryIntent);
|
|
} catch (ActivityNotFoundException e) {
|
|
Log.e(TAG, "startActivity failed", e);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
Intent intent = new Intent(mContext, UsbResolverActivity.class);
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
intent.putExtra(Intent.EXTRA_INTENT, accessoryIntent);
|
|
intent.putParcelableArrayListExtra(UsbResolverActivity.EXTRA_RESOLVE_INFOS,
|
|
matches);
|
|
try {
|
|
mContext.startActivity(intent);
|
|
} catch (ActivityNotFoundException e) {
|
|
Log.w(TAG, "unable to start UsbResolverActivity");
|
|
}
|
|
}
|
|
|
|
public void accessoryDetached(UsbAccessory accessory) {
|
|
Intent intent = new Intent(
|
|
UsbManager.ACTION_USB_ACCESSORY_DETACHED);
|
|
intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
|
|
mContext.sendBroadcast(intent);
|
|
}
|
|
|
|
public void checkPermission(UsbDevice device) {
|
|
if (device == null) return;
|
|
ArrayList<DeviceFilter> filterList = mDevicePermissionMap.get(Binder.getCallingUid());
|
|
if (filterList != null) {
|
|
int count = filterList.size();
|
|
for (int i = 0; i < count; i++) {
|
|
DeviceFilter filter = filterList.get(i);
|
|
if (filter.equals(device)) {
|
|
// permission allowed
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
throw new SecurityException("User has not given permission to device " + device);
|
|
}
|
|
|
|
public void checkPermission(UsbAccessory accessory) {
|
|
if (accessory == null) return;
|
|
ArrayList<AccessoryFilter> filterList = mAccessoryPermissionMap.get(Binder.getCallingUid());
|
|
if (filterList != null) {
|
|
int count = filterList.size();
|
|
for (int i = 0; i < count; i++) {
|
|
AccessoryFilter filter = filterList.get(i);
|
|
if (filter.equals(accessory)) {
|
|
// permission allowed
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
throw new SecurityException("User has not given permission to accessory " + accessory);
|
|
}
|
|
|
|
public void setDevicePackage(UsbDevice device, String packageName) {
|
|
DeviceFilter filter = new DeviceFilter(device);
|
|
if (packageName == null) {
|
|
mDevicePreferenceMap.remove(filter);
|
|
} else {
|
|
mDevicePreferenceMap.put(filter, packageName);
|
|
}
|
|
// FIXME - only if changed
|
|
writeSettings();
|
|
}
|
|
|
|
public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
|
|
AccessoryFilter filter = new AccessoryFilter(accessory);
|
|
if (packageName == null) {
|
|
mAccessoryPreferenceMap.remove(filter);
|
|
} else {
|
|
mAccessoryPreferenceMap.put(filter, packageName);
|
|
}
|
|
// FIXME - only if changed
|
|
writeSettings();
|
|
}
|
|
|
|
public void grantDevicePermission(UsbDevice device, int uid) {
|
|
ArrayList<DeviceFilter> filterList = mDevicePermissionMap.get(uid);
|
|
if (filterList == null) {
|
|
filterList = new ArrayList<DeviceFilter>();
|
|
mDevicePermissionMap.put(uid, filterList);
|
|
} else {
|
|
int count = filterList.size();
|
|
for (int i = 0; i < count; i++) {
|
|
if (filterList.get(i).equals(device)) return;
|
|
}
|
|
}
|
|
filterList.add(new DeviceFilter(device));
|
|
writeSettings();
|
|
}
|
|
|
|
public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
|
|
ArrayList<AccessoryFilter> filterList = mAccessoryPermissionMap.get(uid);
|
|
if (filterList == null) {
|
|
filterList = new ArrayList<AccessoryFilter>();
|
|
mAccessoryPermissionMap.put(uid, filterList);
|
|
} else {
|
|
int count = filterList.size();
|
|
for (int i = 0; i < count; i++) {
|
|
if (filterList.get(i).equals(accessory)) return;
|
|
}
|
|
}
|
|
filterList.add(new AccessoryFilter(accessory));
|
|
writeSettings();
|
|
}
|
|
|
|
public boolean hasDefaults(String packageName, int uid) {
|
|
if (mDevicePermissionMap.get(uid) != null) return true;
|
|
if (mAccessoryPermissionMap.get(uid) != null) return true;
|
|
if (mDevicePreferenceMap.values().contains(packageName)) return true;
|
|
if (mAccessoryPreferenceMap.values().contains(packageName)) return true;
|
|
return false;
|
|
}
|
|
|
|
public void clearDefaults(String packageName, int uid) {
|
|
boolean packageCleared = clearPackageDefaults(packageName);
|
|
boolean uidCleared = clearUidDefaults(uid);
|
|
if (packageCleared || uidCleared) {
|
|
writeSettings();
|
|
}
|
|
}
|
|
|
|
private boolean clearUidDefaults(int uid) {
|
|
boolean cleared = false;
|
|
int index = mDevicePermissionMap.indexOfKey(uid);
|
|
if (index >= 0) {
|
|
mDevicePermissionMap.removeAt(index);
|
|
cleared = true;
|
|
}
|
|
index = mAccessoryPermissionMap.indexOfKey(uid);
|
|
if (index >= 0) {
|
|
mAccessoryPermissionMap.removeAt(index);
|
|
cleared = true;
|
|
}
|
|
return cleared;
|
|
}
|
|
|
|
private boolean clearPackageDefaults(String packageName) {
|
|
boolean cleared = false;
|
|
if (mDevicePreferenceMap.containsValue(packageName)) {
|
|
// make a copy of the key set to avoid ConcurrentModificationException
|
|
Object[] keys = mDevicePreferenceMap.keySet().toArray();
|
|
for (int i = 0; i < keys.length; i++) {
|
|
Object key = keys[i];
|
|
if (packageName.equals(mDevicePreferenceMap.get(key))) {
|
|
mDevicePreferenceMap.remove(key);
|
|
cleared = true;
|
|
}
|
|
}
|
|
}
|
|
if (mAccessoryPreferenceMap.containsValue(packageName)) {
|
|
// make a copy of the key set to avoid ConcurrentModificationException
|
|
Object[] keys = mAccessoryPreferenceMap.keySet().toArray();
|
|
for (int i = 0; i < keys.length; i++) {
|
|
Object key = keys[i];
|
|
if (packageName.equals(mAccessoryPreferenceMap.get(key))) {
|
|
mAccessoryPreferenceMap.remove(key);
|
|
cleared = true;
|
|
}
|
|
}
|
|
}
|
|
return cleared;
|
|
}
|
|
|
|
public void dump(FileDescriptor fd, PrintWriter pw) {
|
|
pw.println(" Device permissions:");
|
|
int count = mDevicePermissionMap.size();
|
|
for (int i = 0; i < count; i++) {
|
|
int uid = mDevicePermissionMap.keyAt(i);
|
|
pw.println(" " + "uid " + uid + ":");
|
|
ArrayList<DeviceFilter> filters = mDevicePermissionMap.valueAt(i);
|
|
for (DeviceFilter filter : filters) {
|
|
pw.println(" " + filter);
|
|
}
|
|
}
|
|
pw.println(" Accessory permissions:");
|
|
count = mAccessoryPermissionMap.size();
|
|
for (int i = 0; i < count; i++) {
|
|
int uid = mAccessoryPermissionMap.keyAt(i);
|
|
pw.println(" " + "uid " + uid + ":");
|
|
ArrayList<AccessoryFilter> filters = mAccessoryPermissionMap.valueAt(i);
|
|
for (AccessoryFilter filter : filters) {
|
|
pw.println(" " + filter);
|
|
}
|
|
}
|
|
pw.println(" Device preferences:");
|
|
for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
|
|
pw.println(" " + filter + ": " + mDevicePreferenceMap.get(filter));
|
|
}
|
|
pw.println(" Accessory preferences:");
|
|
for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
|
|
pw.println(" " + filter + ": " + mAccessoryPreferenceMap.get(filter));
|
|
}
|
|
}
|
|
}
|