summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorMike Lockwood <lockwood@android.com>2011-02-27 09:10:37 -0800
committerMike Lockwood <lockwood@android.com>2011-02-28 17:00:45 -0800
commit02eb8746de2d60563ec2751a34d20923192e4293 (patch)
tree7bf0390f0cdfd07857581ed98911fca857979f13 /services
parent12511f64a05733d15d1a80a884b7b4711e7dd3b1 (diff)
downloadframeworks_base-02eb8746de2d60563ec2751a34d20923192e4293.zip
frameworks_base-02eb8746de2d60563ec2751a34d20923192e4293.tar.gz
frameworks_base-02eb8746de2d60563ec2751a34d20923192e4293.tar.bz2
UsbManager: Enhancements for managing USB devices and accessories
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>
Diffstat (limited to 'services')
-rw-r--r--services/java/com/android/server/SystemServer.java1
-rw-r--r--services/java/com/android/server/usb/UsbDeviceSettingsManager.java861
-rw-r--r--services/java/com/android/server/usb/UsbResolverActivity.java102
-rw-r--r--services/java/com/android/server/usb/UsbService.java (renamed from services/java/com/android/server/UsbService.java)160
-rw-r--r--services/jni/com_android_server_UsbService.cpp7
5 files changed, 1091 insertions, 40 deletions
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 52c47e1..d160963 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -17,6 +17,7 @@
package com.android.server;
import com.android.server.am.ActivityManagerService;
+import com.android.server.usb.UsbService;
import com.android.server.wm.WindowManagerService;
import com.android.internal.app.ShutdownThread;
import com.android.internal.os.BinderInternal;
diff --git a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
new file mode 100644
index 0000000..21eb7dc
--- /dev/null
+++ b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
@@ -0,0 +1,861 @@
+/*
+ * 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));
+ }
+ }
+}
diff --git a/services/java/com/android/server/usb/UsbResolverActivity.java b/services/java/com/android/server/usb/UsbResolverActivity.java
new file mode 100644
index 0000000..1bb3c21
--- /dev/null
+++ b/services/java/com/android/server/usb/UsbResolverActivity.java
@@ -0,0 +1,102 @@
+/*
+ * 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 com.android.internal.app.ResolverActivity;
+
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.hardware.IUsbManager;
+import android.hardware.UsbAccessory;
+import android.hardware.UsbDevice;
+import android.hardware.UsbManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/* Activity for choosing an application for a USB device or accessory */
+public class UsbResolverActivity extends ResolverActivity {
+ public static final String TAG = "UsbResolverActivity";
+ public static final String EXTRA_RESOLVE_INFOS = "rlist";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Intent intent = getIntent();
+ Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT);
+ if (!(targetParcelable instanceof Intent)) {
+ Log.w("UsbResolverActivity", "Target is not an intent: " + targetParcelable);
+ finish();
+ return;
+ }
+ Intent target = (Intent)targetParcelable;
+ ArrayList<ResolveInfo> rList = intent.getParcelableArrayListExtra(EXTRA_RESOLVE_INFOS);
+ Log.d(TAG, "rList.size() " + rList.size());
+ CharSequence title = getResources().getText(com.android.internal.R.string.chooseUsbActivity);
+ super.onCreate(savedInstanceState, target, title, null, rList,
+ true, /* Set alwaysUseOption to true to enable "always use this app" checkbox. */
+ true /* Set alwaysChoose to display activity when only one choice is available.
+ This is necessary because this activity is needed for the user to allow
+ the application permission to access the device */
+ );
+ }
+
+ protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
+ try {
+ IBinder b = ServiceManager.getService(USB_SERVICE);
+ IUsbManager service = IUsbManager.Stub.asInterface(b);
+ int uid = ri.activityInfo.applicationInfo.uid;
+ String action = intent.getAction();
+
+ if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
+ UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ // grant permission for the device
+ service.grantDevicePermission(device, uid);
+ // set or clear default setting
+ if (alwaysCheck) {
+ service.setDevicePackage(device, ri.activityInfo.packageName);
+ } else {
+ service.setDevicePackage(device, null);
+ }
+ } else if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action)) {
+ UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(
+ UsbManager.EXTRA_ACCESSORY);
+ // grant permission for the accessory
+ service.grantAccessoryPermission(accessory, uid);
+ // set or clear default setting
+ if (alwaysCheck) {
+ service.setAccessoryPackage(accessory, ri.activityInfo.packageName);
+ } else {
+ service.setAccessoryPackage(accessory, null);
+ }
+ }
+
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "startActivity failed", e);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "onIntentSelected failed", e);
+ }
+ }
+}
diff --git a/services/java/com/android/server/UsbService.java b/services/java/com/android/server/usb/UsbService.java
index 2f84713..de4ac03 100644
--- a/services/java/com/android/server/UsbService.java
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.usb;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.hardware.IUsbManager;
import android.hardware.UsbAccessory;
import android.hardware.UsbConstants;
@@ -27,6 +28,7 @@ import android.hardware.UsbEndpoint;
import android.hardware.UsbInterface;
import android.hardware.UsbManager;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -38,10 +40,13 @@ import android.util.Log;
import android.util.Slog;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileReader;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
/**
* UsbService monitors for changes to USB state.
@@ -50,7 +55,7 @@ import java.util.HashMap;
* Accessory mode is a special case of USB device mode, where the android device is
* connected to a USB host that supports the android accessory protocol.
*/
-class UsbService extends IUsbManager.Stub {
+public class UsbService extends IUsbManager.Stub {
private static final String TAG = UsbService.class.getSimpleName();
private static final boolean LOG = false;
@@ -102,6 +107,7 @@ class UsbService extends IUsbManager.Stub {
private final Context mContext;
private final Object mLock = new Object();
+ private final UsbDeviceSettingsManager mDeviceManager;
/*
* Handles USB function enable/disable events (device mode)
@@ -139,11 +145,9 @@ class UsbService extends IUsbManager.Stub {
if (enteringAccessoryMode) {
String[] strings = nativeGetAccessoryStrings();
if (strings != null) {
- Log.d(TAG, "entering USB accessory mode");
mCurrentAccessory = new UsbAccessory(strings);
- Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
- intent.putExtra(UsbManager.EXTRA_ACCESSORY, mCurrentAccessory);
- mContext.sendBroadcast(intent);
+ Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
+ mDeviceManager.accessoryAttached(mCurrentAccessory);
} else {
Log.e(TAG, "nativeGetAccessoryStrings failed");
}
@@ -170,7 +174,7 @@ class UsbService extends IUsbManager.Stub {
mConnected = intState;
// trigger an Intent broadcast
if (mSystemReady) {
- // debounce disconnects
+ // debounce disconnects to avoid problems bringing up USB tethering
update(mConnected == 0);
}
} else if ("usb_configuration".equals(name)) {
@@ -202,6 +206,8 @@ class UsbService extends IUsbManager.Stub {
public UsbService(Context context) {
mContext = context;
+ mDeviceManager = new UsbDeviceSettingsManager(context);
+
mHostBlacklist = context.getResources().getStringArray(
com.android.internal.R.array.config_usbHostBlacklist);
@@ -345,11 +351,7 @@ class UsbService extends IUsbManager.Stub {
UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
deviceClass, deviceSubclass, deviceProtocol, interfaces);
mDevices.put(deviceName, device);
-
- Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
- intent.putExtra(UsbManager.EXTRA_DEVICE, device);
- Log.d(TAG, "usbDeviceAdded, sending " + intent);
- mContext.sendBroadcast(intent);
+ mDeviceManager.deviceAttached(device);
}
}
@@ -358,10 +360,7 @@ class UsbService extends IUsbManager.Stub {
synchronized (mLock) {
UsbDevice device = mDevices.remove(deviceName);
if (device != null) {
- Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
- intent.putExtra(UsbManager.EXTRA_DEVICE, device);
- Log.d(TAG, "usbDeviceRemoved, sending " + intent);
- mContext.sendBroadcast(intent);
+ mDeviceManager.deviceDetached(device);
}
}
}
@@ -377,7 +376,7 @@ class UsbService extends IUsbManager.Stub {
new Thread(null, runnable, "UsbService host thread").start();
}
- void systemReady() {
+ public void systemReady() {
synchronized (mLock) {
if (mContext.getResources().getBoolean(
com.android.internal.R.bool.config_hasUsbHostSupport)) {
@@ -402,7 +401,6 @@ class UsbService extends IUsbManager.Stub {
/* Returns a list of all currently attached USB devices (host mdoe) */
public void getDeviceList(Bundle devices) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
synchronized (mLock) {
for (String name : mDevices.keySet()) {
devices.putParcelable(name, mDevices.get(name));
@@ -412,28 +410,85 @@ class UsbService extends IUsbManager.Stub {
/* Opens the specified USB device (host mode) */
public ParcelFileDescriptor openDevice(String deviceName) {
- if (isBlackListed(deviceName)) {
- throw new SecurityException("USB device is on a restricted bus");
- }
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
- if (mDevices.get(deviceName) == null) {
- // if it is not in mDevices, it either does not exist or is blacklisted
- throw new IllegalArgumentException(
- "device " + deviceName + " does not exist or is restricted");
+ synchronized (mLock) {
+ if (isBlackListed(deviceName)) {
+ throw new SecurityException("USB device is on a restricted bus");
+ }
+ UsbDevice device = mDevices.get(deviceName);
+ if (device == null) {
+ // if it is not in mDevices, it either does not exist or is blacklisted
+ throw new IllegalArgumentException(
+ "device " + deviceName + " does not exist or is restricted");
+ }
+ mDeviceManager.checkPermission(device);
+ return nativeOpenDevice(deviceName);
}
- return nativeOpenDevice(deviceName);
}
/* returns the currently attached USB accessory (device mode) */
public UsbAccessory getCurrentAccessory() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
- return mCurrentAccessory;
+ synchronized (mLock) {
+ mDeviceManager.checkPermission(mCurrentAccessory);
+ return mCurrentAccessory;
+ }
}
/* opens the currently attached USB accessory (device mode) */
- public ParcelFileDescriptor openAccessory() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
- return nativeOpenAccessory();
+ public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
+ synchronized (mLock) {
+ if (mCurrentAccessory == null) {
+ throw new IllegalArgumentException("no accessory attached");
+ }
+ if (!mCurrentAccessory.equals(accessory)) {
+ Log.e(TAG, accessory.toString() + " does not match current accessory "
+ + mCurrentAccessory);
+ throw new IllegalArgumentException("accessory not attached");
+ }
+ mDeviceManager.checkPermission(mCurrentAccessory);
+ return nativeOpenAccessory();
+ }
+ }
+
+ public void setDevicePackage(UsbDevice device, String packageName) {
+ synchronized (mLock) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ mDeviceManager.setDevicePackage(device, packageName);
+ }
+ }
+
+ public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
+ synchronized (mLock) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ mDeviceManager.setAccessoryPackage(accessory, packageName);
+ }
+ }
+
+ public void grantDevicePermission(UsbDevice device, int uid) {
+ synchronized (mLock) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ mDeviceManager.grantDevicePermission(device, uid);
+ }
+ }
+
+ public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
+ synchronized (mLock) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ mDeviceManager.grantAccessoryPermission(accessory, uid);
+ }
+ }
+
+ public boolean hasDefaults(String packageName, int uid) {
+ synchronized (mLock) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ return mDeviceManager.hasDefaults(packageName, uid);
+ }
+ }
+
+ public void clearDefaults(String packageName, int uid) {
+ synchronized (mLock) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ mDeviceManager.clearDefaults(packageName, uid);
+ }
}
/*
@@ -470,10 +525,7 @@ class UsbService extends IUsbManager.Stub {
}
mAccessoryRestoreFunctions.clear();
- Intent intent = new Intent(
- UsbManager.ACTION_USB_ACCESSORY_DETACHED);
- intent.putExtra(UsbManager.EXTRA_ACCESSORY, mCurrentAccessory);
- mContext.sendBroadcast(intent);
+ mDeviceManager.accessoryDetached(mCurrentAccessory);
mCurrentAccessory = null;
// this will cause an immediate reset of the USB bus,
@@ -513,6 +565,42 @@ class UsbService extends IUsbManager.Stub {
}
};
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump UsbManager from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mLock) {
+ pw.println("USB Manager State:");
+
+ pw.println(" USB Device State:");
+ pw.print(" Enabled Functions: ");
+ for (int i = 0; i < mEnabledFunctions.size(); i++) {
+ pw.print(mEnabledFunctions.get(i) + " ");
+ }
+ pw.println("");
+ pw.print(" Disabled Functions: ");
+ for (int i = 0; i < mDisabledFunctions.size(); i++) {
+ pw.print(mDisabledFunctions.get(i) + " ");
+ }
+ pw.println("");
+ pw.println(" mConnected: " + mConnected + ", mConfiguration: " + mConfiguration);
+
+ pw.println(" USB Host State:");
+ for (String name : mDevices.keySet()) {
+ pw.println(" " + name + ": " + mDevices.get(name));
+ }
+ pw.println(" mCurrentAccessory: " + mCurrentAccessory);
+
+ mDeviceManager.dump(fd, pw);
+ }
+ }
+
// host support
private native void monitorUsbHostBus();
private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
diff --git a/services/jni/com_android_server_UsbService.cpp b/services/jni/com_android_server_UsbService.cpp
index 192daaf..3c49e54 100644
--- a/services/jni/com_android_server_UsbService.cpp
+++ b/services/jni/com_android_server_UsbService.cpp
@@ -177,7 +177,6 @@ static void set_accessory_string(JNIEnv *env, int fd, int cmd, jobjectArray strA
buffer[0] = 0;
int length = ioctl(fd, cmd, buffer);
- LOGD("ioctl returned %d", length);
if (buffer[0]) {
jstring obj = env->NewStringUTF(buffer);
env->SetObjectArrayElement(strArray, index, obj);
@@ -236,9 +235,9 @@ static JNINativeMethod method_table[] = {
int register_android_server_UsbService(JNIEnv *env)
{
- jclass clazz = env->FindClass("com/android/server/UsbService");
+ jclass clazz = env->FindClass("com/android/server/usb/UsbService");
if (clazz == NULL) {
- LOGE("Can't find com/android/server/UsbService");
+ LOGE("Can't find com/android/server/usb/UsbService");
return -1;
}
method_usbDeviceAdded = env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;IIIII[I[I)V");
@@ -267,7 +266,7 @@ int register_android_server_UsbService(JNIEnv *env)
LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
"Unable to find constructor for android.os.ParcelFileDescriptor");
- return jniRegisterNativeMethods(env, "com/android/server/UsbService",
+ return jniRegisterNativeMethods(env, "com/android/server/usb/UsbService",
method_table, NELEM(method_table));
}