| /* |
| * 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.app.PendingIntent; |
| 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.ApplicationInfo; |
| import android.content.pm.PackageInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.PackageManager.NameNotFoundException; |
| import android.content.pm.ResolveInfo; |
| import android.content.res.XmlResourceParser; |
| import android.hardware.usb.UsbAccessory; |
| import android.hardware.usb.UsbDevice; |
| import android.hardware.usb.UsbInterface; |
| import android.hardware.usb.UsbManager; |
| import android.os.Binder; |
| import android.os.FileUtils; |
| import android.os.Process; |
| import android.util.Slog; |
| import android.util.SparseBooleanArray; |
| 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 UsbSettingsManager { |
| |
| private static final String TAG = "UsbSettingsManager"; |
| private static final boolean DEBUG = false; |
| private static final File sSettingsFile = new File("/data/system/usb_device_manager.xml"); |
| |
| private final Context mContext; |
| private final PackageManager mPackageManager; |
| |
| // Temporary mapping USB device name to list of UIDs with permissions for the device |
| private final HashMap<String, SparseBooleanArray> mDevicePermissionMap = |
| new HashMap<String, SparseBooleanArray>(); |
| // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory |
| private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap = |
| new HashMap<UsbAccessory, SparseBooleanArray>(); |
| // Maps DeviceFilter to user preferred application package |
| private final HashMap<DeviceFilter, String> mDevicePreferenceMap = |
| new HashMap<DeviceFilter, String>(); |
| // Maps AccessoryFilter to user preferred application package |
| private final HashMap<AccessoryFilter, String> mAccessoryPreferenceMap = |
| new HashMap<AccessoryFilter, String>(); |
| |
| private final Object mLock = new Object(); |
| |
| // 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; |
| } |
| |
| public boolean matches(DeviceFilter f) { |
| if (mVendorId != -1 && f.mVendorId != mVendorId) return false; |
| if (mProductId != -1 && f.mProductId != mProductId) return false; |
| |
| // check device class/subclass/protocol |
| return matches(f.mClass, f.mSubclass, f.mProtocol); |
| } |
| |
| @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 version (or null for unspecified) |
| public final String mVersion; |
| |
| public AccessoryFilter(String manufacturer, String model, String version) { |
| mManufacturer = manufacturer; |
| mModel = model; |
| mVersion = version; |
| } |
| |
| public AccessoryFilter(UsbAccessory accessory) { |
| mManufacturer = accessory.getManufacturer(); |
| mModel = accessory.getModel(); |
| mVersion = accessory.getVersion(); |
| } |
| |
| public static AccessoryFilter read(XmlPullParser parser) |
| throws XmlPullParserException, IOException { |
| String manufacturer = null; |
| String model = 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 ("version".equals(name)) { |
| version = value; |
| } |
| } |
| return new AccessoryFilter(manufacturer, model, 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 (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 (mVersion != null && !acc.getVersion().equals(mVersion)) return false; |
| return true; |
| } |
| |
| public boolean matches(AccessoryFilter f) { |
| if (mManufacturer != null && !f.mManufacturer.equals(mManufacturer)) return false; |
| if (mModel != null && !f.mModel.equals(mModel)) return false; |
| if (mVersion != null && !f.mVersion.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 || mVersion == null) { |
| return false; |
| } |
| if (obj instanceof AccessoryFilter) { |
| AccessoryFilter filter = (AccessoryFilter)obj; |
| return (mManufacturer.equals(filter.mManufacturer) && |
| mModel.equals(filter.mModel) && |
| mVersion.equals(filter.mVersion)); |
| } |
| if (obj instanceof UsbAccessory) { |
| UsbAccessory accessory = (UsbAccessory)obj; |
| return (mManufacturer.equals(accessory.getManufacturer()) && |
| mModel.equals(accessory.getModel()) && |
| mVersion.equals(accessory.getVersion())); |
| } |
| return false; |
| } |
| |
| @Override |
| public int hashCode() { |
| return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^ |
| (mModel == null ? 0 : mModel.hashCode()) ^ |
| (mVersion == null ? 0 : mVersion.hashCode())); |
| } |
| |
| @Override |
| public String toString() { |
| return "AccessoryFilter[mManufacturer=\"" + mManufacturer + |
| "\", mModel=\"" + mModel + |
| "\", mVersion=\"" + mVersion + "\"]"; |
| } |
| } |
| |
| private class MyPackageMonitor extends PackageMonitor { |
| |
| public void onPackageAdded(String packageName, int uid) { |
| handlePackageUpdate(packageName); |
| } |
| |
| public void onPackageChanged(String packageName, int uid, String[] components) { |
| handlePackageUpdate(packageName); |
| } |
| |
| public void onPackageRemoved(String packageName, int uid) { |
| clearDefaults(packageName); |
| } |
| } |
| MyPackageMonitor mPackageMonitor = new MyPackageMonitor(); |
| |
| public UsbSettingsManager(Context context) { |
| mContext = context; |
| mPackageManager = context.getPackageManager(); |
| synchronized (mLock) { |
| readSettingsLocked(); |
| } |
| mPackageMonitor.register(context, true); |
| } |
| |
| 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 readSettingsLocked() { |
| 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 ("preference".equals(tagName)) { |
| readPreference(parser); |
| } else { |
| XmlUtils.nextElement(parser); |
| } |
| } |
| } catch (FileNotFoundException e) { |
| if (DEBUG) Slog.d(TAG, "settings file not found"); |
| } catch (Exception e) { |
| Slog.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 writeSettingsLocked() { |
| FileOutputStream fos = null; |
| try { |
| FileOutputStream fstr = new FileOutputStream(sSettingsFile); |
| if (DEBUG) Slog.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"); |
| |
| 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) { |
| Slog.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 packageMatchesLocked(ResolveInfo info, String metaDataName, |
| UsbDevice device, UsbAccessory accessory) { |
| ActivityInfo ai = info.activityInfo; |
| |
| XmlResourceParser parser = null; |
| try { |
| parser = ai.loadXmlMetaData(mPackageManager, metaDataName); |
| if (parser == null) { |
| Slog.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) { |
| Slog.w(TAG, "Unable to load component info " + info.toString(), e); |
| } finally { |
| if (parser != null) parser.close(); |
| } |
| return false; |
| } |
| |
| private final ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) { |
| ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>(); |
| List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent, |
| PackageManager.GET_META_DATA); |
| int count = resolveInfos.size(); |
| for (int i = 0; i < count; i++) { |
| ResolveInfo resolveInfo = resolveInfos.get(i); |
| if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) { |
| matches.add(resolveInfo); |
| } |
| } |
| return matches; |
| } |
| |
| private final ArrayList<ResolveInfo> getAccessoryMatchesLocked( |
| UsbAccessory accessory, Intent intent) { |
| ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>(); |
| List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent, |
| PackageManager.GET_META_DATA); |
| int count = resolveInfos.size(); |
| for (int i = 0; i < count; i++) { |
| ResolveInfo resolveInfo = resolveInfos.get(i); |
| if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) { |
| matches.add(resolveInfo); |
| } |
| } |
| return matches; |
| } |
| |
| public void deviceAttached(UsbDevice device) { |
| Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED); |
| intent.putExtra(UsbManager.EXTRA_DEVICE, device); |
| intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| |
| ArrayList<ResolveInfo> matches; |
| String defaultPackage; |
| synchronized (mLock) { |
| matches = getDeviceMatchesLocked(device, intent); |
| // Launch our default activity directly, if we have one. |
| // Otherwise we will start the UsbResolverActivity to allow the user to choose. |
| defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device)); |
| } |
| |
| resolveActivity(intent, matches, defaultPackage, device, null); |
| } |
| |
| public void deviceDetached(UsbDevice device) { |
| // clear temporary permissions for the device |
| mDevicePermissionMap.remove(device.getDeviceName()); |
| |
| Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED); |
| intent.putExtra(UsbManager.EXTRA_DEVICE, device); |
| if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent); |
| mContext.sendBroadcast(intent); |
| } |
| |
| public void accessoryAttached(UsbAccessory accessory) { |
| Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED); |
| intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); |
| intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| |
| ArrayList<ResolveInfo> matches; |
| String defaultPackage; |
| synchronized (mLock) { |
| matches = getAccessoryMatchesLocked(accessory, intent); |
| // Launch our default activity directly, if we have one. |
| // Otherwise we will start the UsbResolverActivity to allow the user to choose. |
| defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory)); |
| } |
| |
| resolveActivity(intent, matches, defaultPackage, null, accessory); |
| } |
| |
| public void accessoryDetached(UsbAccessory accessory) { |
| // clear temporary permissions for the accessory |
| mAccessoryPermissionMap.remove(accessory); |
| |
| Intent intent = new Intent( |
| UsbManager.ACTION_USB_ACCESSORY_DETACHED); |
| intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); |
| mContext.sendBroadcast(intent); |
| } |
| |
| private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches, |
| String defaultPackage, UsbDevice device, UsbAccessory accessory) { |
| int count = matches.size(); |
| |
| // don't show the resolver activity if there are no choices available |
| if (count == 0) { |
| if (accessory != null) { |
| String uri = accessory.getUri(); |
| if (uri != null && uri.length() > 0) { |
| // display URI to user |
| // start UsbResolverActivity so user can choose an activity |
| Intent dialogIntent = new Intent(); |
| dialogIntent.setClassName("com.android.systemui", |
| "com.android.systemui.usb.UsbAccessoryUriActivity"); |
| dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); |
| dialogIntent.putExtra("uri", uri); |
| try { |
| mContext.startActivity(dialogIntent); |
| } catch (ActivityNotFoundException e) { |
| Slog.e(TAG, "unable to start UsbAccessoryUriActivity"); |
| } |
| } |
| } |
| |
| // do nothing |
| return; |
| } |
| |
| ResolveInfo defaultRI = null; |
| if (count == 1 && defaultPackage == null) { |
| // Check to see if our single choice is on the system partition. |
| // If so, treat it as our default without calling UsbResolverActivity |
| ResolveInfo rInfo = matches.get(0); |
| if (rInfo.activityInfo != null && |
| rInfo.activityInfo.applicationInfo != null && |
| (rInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { |
| defaultRI = rInfo; |
| } |
| } |
| |
| if (defaultRI == null && defaultPackage != null) { |
| // look for default activity |
| for (int i = 0; i < count; i++) { |
| ResolveInfo rInfo = matches.get(i); |
| if (rInfo.activityInfo != null && |
| defaultPackage.equals(rInfo.activityInfo.packageName)) { |
| defaultRI = rInfo; |
| break; |
| } |
| } |
| } |
| |
| if (defaultRI != null) { |
| // grant permission for default activity |
| if (device != null) { |
| grantDevicePermission(device, defaultRI.activityInfo.applicationInfo.uid); |
| } else if (accessory != null) { |
| grantAccessoryPermission(accessory, defaultRI.activityInfo.applicationInfo.uid); |
| } |
| |
| // start default activity directly |
| try { |
| intent.setComponent( |
| new ComponentName(defaultRI.activityInfo.packageName, |
| defaultRI.activityInfo.name)); |
| mContext.startActivity(intent); |
| } catch (ActivityNotFoundException e) { |
| Slog.e(TAG, "startActivity failed", e); |
| } |
| } else { |
| Intent resolverIntent = new Intent(); |
| resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| |
| if (count == 1) { |
| // start UsbConfirmActivity if there is only one choice |
| resolverIntent.setClassName("com.android.systemui", |
| "com.android.systemui.usb.UsbConfirmActivity"); |
| resolverIntent.putExtra("rinfo", matches.get(0)); |
| |
| if (device != null) { |
| resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device); |
| } else { |
| resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); |
| } |
| } else { |
| // start UsbResolverActivity so user can choose an activity |
| resolverIntent.setClassName("com.android.systemui", |
| "com.android.systemui.usb.UsbResolverActivity"); |
| resolverIntent.putParcelableArrayListExtra("rlist", matches); |
| resolverIntent.putExtra(Intent.EXTRA_INTENT, intent); |
| } |
| try { |
| mContext.startActivity(resolverIntent); |
| } catch (ActivityNotFoundException e) { |
| Slog.e(TAG, "unable to start activity " + resolverIntent); |
| } |
| } |
| } |
| |
| private boolean clearCompatibleMatchesLocked(String packageName, DeviceFilter filter) { |
| boolean changed = false; |
| for (DeviceFilter test : mDevicePreferenceMap.keySet()) { |
| if (filter.matches(test)) { |
| mDevicePreferenceMap.remove(test); |
| changed = true; |
| } |
| } |
| return changed; |
| } |
| |
| private boolean clearCompatibleMatchesLocked(String packageName, AccessoryFilter filter) { |
| boolean changed = false; |
| for (AccessoryFilter test : mAccessoryPreferenceMap.keySet()) { |
| if (filter.matches(test)) { |
| mAccessoryPreferenceMap.remove(test); |
| changed = true; |
| } |
| } |
| return changed; |
| } |
| |
| private boolean handlePackageUpdateLocked(String packageName, ActivityInfo aInfo, |
| String metaDataName) { |
| XmlResourceParser parser = null; |
| boolean changed = false; |
| |
| try { |
| parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName); |
| if (parser == null) return false; |
| |
| XmlUtils.nextElement(parser); |
| while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { |
| String tagName = parser.getName(); |
| if ("usb-device".equals(tagName)) { |
| DeviceFilter filter = DeviceFilter.read(parser); |
| if (clearCompatibleMatchesLocked(packageName, filter)) { |
| changed = true; |
| } |
| } |
| else if ("usb-accessory".equals(tagName)) { |
| AccessoryFilter filter = AccessoryFilter.read(parser); |
| if (clearCompatibleMatchesLocked(packageName, filter)) { |
| changed = true; |
| } |
| } |
| XmlUtils.nextElement(parser); |
| } |
| } catch (Exception e) { |
| Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e); |
| } finally { |
| if (parser != null) parser.close(); |
| } |
| return changed; |
| } |
| |
| // Check to see if the package supports any USB devices or accessories. |
| // If so, clear any non-matching preferences for matching devices/accessories. |
| private void handlePackageUpdate(String packageName) { |
| synchronized (mLock) { |
| PackageInfo info; |
| boolean changed = false; |
| |
| try { |
| info = mPackageManager.getPackageInfo(packageName, |
| PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA); |
| } catch (NameNotFoundException e) { |
| Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e); |
| return; |
| } |
| |
| ActivityInfo[] activities = info.activities; |
| if (activities == null) return; |
| for (int i = 0; i < activities.length; i++) { |
| // check for meta-data, both for devices and accessories |
| if (handlePackageUpdateLocked(packageName, activities[i], |
| UsbManager.ACTION_USB_DEVICE_ATTACHED)) { |
| changed = true; |
| } |
| if (handlePackageUpdateLocked(packageName, activities[i], |
| UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) { |
| changed = true; |
| } |
| } |
| |
| if (changed) { |
| writeSettingsLocked(); |
| } |
| } |
| } |
| |
| public boolean hasPermission(UsbDevice device) { |
| synchronized (mLock) { |
| SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName()); |
| if (uidList == null) { |
| return false; |
| } |
| return uidList.get(Binder.getCallingUid()); |
| } |
| } |
| |
| public boolean hasPermission(UsbAccessory accessory) { |
| synchronized (mLock) { |
| SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); |
| if (uidList == null) { |
| return false; |
| } |
| return uidList.get(Binder.getCallingUid()); |
| } |
| } |
| |
| public void checkPermission(UsbDevice device) { |
| if (!hasPermission(device)) { |
| throw new SecurityException("User has not given permission to device " + device); |
| } |
| } |
| |
| public void checkPermission(UsbAccessory accessory) { |
| if (!hasPermission(accessory)) { |
| throw new SecurityException("User has not given permission to accessory " + accessory); |
| } |
| } |
| |
| private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) { |
| int uid = Binder.getCallingUid(); |
| |
| // compare uid with packageName to foil apps pretending to be someone else |
| try { |
| ApplicationInfo aInfo = mPackageManager.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"); |
| } |
| |
| long identity = Binder.clearCallingIdentity(); |
| intent.setClassName("com.android.systemui", |
| "com.android.systemui.usb.UsbPermissionActivity"); |
| intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| intent.putExtra(Intent.EXTRA_INTENT, pi); |
| intent.putExtra("package", packageName); |
| intent.putExtra("uid", uid); |
| try { |
| mContext.startActivity(intent); |
| } catch (ActivityNotFoundException e) { |
| Slog.e(TAG, "unable to start UsbPermissionActivity"); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) { |
| Intent intent = new Intent(); |
| |
| // respond immediately if permission has already been granted |
| if (hasPermission(device)) { |
| 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; |
| } |
| |
| // start UsbPermissionActivity so user can choose an activity |
| intent.putExtra(UsbManager.EXTRA_DEVICE, device); |
| requestPermissionDialog(intent, packageName, pi); |
| } |
| |
| public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) { |
| Intent intent = new Intent(); |
| |
| // respond immediately if permission has already been granted |
| if (hasPermission(accessory)) { |
| 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; |
| } |
| |
| intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); |
| requestPermissionDialog(intent, packageName, pi); |
| } |
| |
| public void setDevicePackage(UsbDevice device, String packageName) { |
| DeviceFilter filter = new DeviceFilter(device); |
| boolean changed = false; |
| synchronized (mLock) { |
| if (packageName == null) { |
| changed = (mDevicePreferenceMap.remove(filter) != null); |
| } else { |
| changed = !packageName.equals(mDevicePreferenceMap.get(filter)); |
| if (changed) { |
| mDevicePreferenceMap.put(filter, packageName); |
| } |
| } |
| if (changed) { |
| writeSettingsLocked(); |
| } |
| } |
| } |
| |
| public void setAccessoryPackage(UsbAccessory accessory, String packageName) { |
| AccessoryFilter filter = new AccessoryFilter(accessory); |
| boolean changed = false; |
| synchronized (mLock) { |
| if (packageName == null) { |
| changed = (mAccessoryPreferenceMap.remove(filter) != null); |
| } else { |
| changed = !packageName.equals(mAccessoryPreferenceMap.get(filter)); |
| if (changed) { |
| mAccessoryPreferenceMap.put(filter, packageName); |
| } |
| } |
| if (changed) { |
| writeSettingsLocked(); |
| } |
| } |
| } |
| |
| public void grantDevicePermission(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); |
| } |
| } |
| |
| public void grantAccessoryPermission(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); |
| } |
| } |
| |
| public boolean hasDefaults(String packageName) { |
| synchronized (mLock) { |
| if (mDevicePreferenceMap.values().contains(packageName)) return true; |
| if (mAccessoryPreferenceMap.values().contains(packageName)) return true; |
| return false; |
| } |
| } |
| |
| public void clearDefaults(String packageName) { |
| synchronized (mLock) { |
| if (clearPackageDefaultsLocked(packageName)) { |
| writeSettingsLocked(); |
| } |
| } |
| } |
| |
| private boolean clearPackageDefaultsLocked(String packageName) { |
| boolean cleared = false; |
| synchronized (mLock) { |
| 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) { |
| synchronized (mLock) { |
| pw.println(" Device permissions:"); |
| for (String deviceName : mDevicePermissionMap.keySet()) { |
| pw.print(" " + deviceName + ": "); |
| SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName); |
| int count = uidList.size(); |
| for (int i = 0; i < count; i++) { |
| pw.print(Integer.toString(uidList.keyAt(i)) + " "); |
| } |
| pw.println(""); |
| } |
| pw.println(" Accessory permissions:"); |
| for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) { |
| pw.print(" " + accessory + ": "); |
| SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); |
| int count = uidList.size(); |
| for (int i = 0; i < count; i++) { |
| pw.print(Integer.toString(uidList.keyAt(i)) + " "); |
| } |
| pw.println(""); |
| } |
| 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)); |
| } |
| } |
| } |
| } |