2 * Copyright (C) 2011 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.server.usb;
19 import android.app.PendingIntent;
20 import android.content.ActivityNotFoundException;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.ActivityInfo;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.PackageInfo;
27 import android.content.pm.PackageManager;
28 import android.content.pm.PackageManager.NameNotFoundException;
29 import android.content.pm.ResolveInfo;
30 import android.content.res.XmlResourceParser;
31 import android.hardware.usb.UsbAccessory;
32 import android.hardware.usb.UsbDevice;
33 import android.hardware.usb.UsbInterface;
34 import android.hardware.usb.UsbManager;
35 import android.os.Binder;
36 import android.os.Environment;
37 import android.os.Process;
38 import android.os.UserHandle;
39 import android.util.AtomicFile;
40 import android.util.Log;
41 import android.util.Slog;
42 import android.util.SparseBooleanArray;
43 import android.util.Xml;
45 import com.android.internal.content.PackageMonitor;
46 import com.android.internal.util.FastXmlSerializer;
47 import com.android.internal.util.XmlUtils;
49 import org.xmlpull.v1.XmlPullParser;
50 import org.xmlpull.v1.XmlPullParserException;
51 import org.xmlpull.v1.XmlSerializer;
54 import java.io.FileDescriptor;
55 import java.io.FileInputStream;
56 import java.io.FileNotFoundException;
57 import java.io.FileOutputStream;
58 import java.io.IOException;
59 import java.io.PrintWriter;
60 import java.util.ArrayList;
61 import java.util.HashMap;
62 import java.util.List;
64 import libcore.io.IoUtils;
66 class UsbSettingsManager {
67 private static final String TAG = "UsbSettingsManager";
68 private static final boolean DEBUG = false;
70 /** Legacy settings file, before multi-user */
71 private static final File sSingleUserSettingsFile = new File(
72 "/data/system/usb_device_manager.xml");
74 private final UserHandle mUser;
75 private final AtomicFile mSettingsFile;
77 private final Context mContext;
78 private final Context mUserContext;
79 private final PackageManager mPackageManager;
81 // Temporary mapping USB device name to list of UIDs with permissions for the device
82 private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
83 new HashMap<String, SparseBooleanArray>();
84 // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory
85 private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
86 new HashMap<UsbAccessory, SparseBooleanArray>();
87 // Maps DeviceFilter to user preferred application package
88 private final HashMap<DeviceFilter, String> mDevicePreferenceMap =
89 new HashMap<DeviceFilter, String>();
90 // Maps AccessoryFilter to user preferred application package
91 private final HashMap<AccessoryFilter, String> mAccessoryPreferenceMap =
92 new HashMap<AccessoryFilter, String>();
94 private final Object mLock = new Object();
96 // This class is used to describe a USB device.
97 // When used in HashMaps all values must be specified,
98 // but wildcards can be used for any of the fields in
99 // the package meta-data.
100 private static class DeviceFilter {
101 // USB Vendor ID (or -1 for unspecified)
102 public final int mVendorId;
103 // USB Product ID (or -1 for unspecified)
104 public final int mProductId;
105 // USB device or interface class (or -1 for unspecified)
106 public final int mClass;
107 // USB device subclass (or -1 for unspecified)
108 public final int mSubclass;
109 // USB device protocol (or -1 for unspecified)
110 public final int mProtocol;
112 public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol) {
116 mSubclass = subclass;
117 mProtocol = protocol;
120 public DeviceFilter(UsbDevice device) {
121 mVendorId = device.getVendorId();
122 mProductId = device.getProductId();
123 mClass = device.getDeviceClass();
124 mSubclass = device.getDeviceSubclass();
125 mProtocol = device.getDeviceProtocol();
128 public static DeviceFilter read(XmlPullParser parser)
129 throws XmlPullParserException, IOException {
132 int deviceClass = -1;
133 int deviceSubclass = -1;
134 int deviceProtocol = -1;
136 int count = parser.getAttributeCount();
137 for (int i = 0; i < count; i++) {
138 String name = parser.getAttributeName(i);
139 // All attribute values are ints
140 int value = Integer.parseInt(parser.getAttributeValue(i));
142 if ("vendor-id".equals(name)) {
144 } else if ("product-id".equals(name)) {
146 } else if ("class".equals(name)) {
148 } else if ("subclass".equals(name)) {
149 deviceSubclass = value;
150 } else if ("protocol".equals(name)) {
151 deviceProtocol = value;
154 return new DeviceFilter(vendorId, productId,
155 deviceClass, deviceSubclass, deviceProtocol);
158 public void write(XmlSerializer serializer) throws IOException {
159 serializer.startTag(null, "usb-device");
160 if (mVendorId != -1) {
161 serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
163 if (mProductId != -1) {
164 serializer.attribute(null, "product-id", Integer.toString(mProductId));
167 serializer.attribute(null, "class", Integer.toString(mClass));
169 if (mSubclass != -1) {
170 serializer.attribute(null, "subclass", Integer.toString(mSubclass));
172 if (mProtocol != -1) {
173 serializer.attribute(null, "protocol", Integer.toString(mProtocol));
175 serializer.endTag(null, "usb-device");
178 private boolean matches(int clasz, int subclass, int protocol) {
179 return ((mClass == -1 || clasz == mClass) &&
180 (mSubclass == -1 || subclass == mSubclass) &&
181 (mProtocol == -1 || protocol == mProtocol));
184 public boolean matches(UsbDevice device) {
185 if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
186 if (mProductId != -1 && device.getProductId() != mProductId) return false;
188 // check device class/subclass/protocol
189 if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
190 device.getDeviceProtocol())) return true;
192 // if device doesn't match, check the interfaces
193 int count = device.getInterfaceCount();
194 for (int i = 0; i < count; i++) {
195 UsbInterface intf = device.getInterface(i);
196 if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
197 intf.getInterfaceProtocol())) return true;
203 public boolean matches(DeviceFilter f) {
204 if (mVendorId != -1 && f.mVendorId != mVendorId) return false;
205 if (mProductId != -1 && f.mProductId != mProductId) return false;
207 // check device class/subclass/protocol
208 return matches(f.mClass, f.mSubclass, f.mProtocol);
212 public boolean equals(Object obj) {
213 // can't compare if we have wildcard strings
214 if (mVendorId == -1 || mProductId == -1 ||
215 mClass == -1 || mSubclass == -1 || mProtocol == -1) {
218 if (obj instanceof DeviceFilter) {
219 DeviceFilter filter = (DeviceFilter)obj;
220 return (filter.mVendorId == mVendorId &&
221 filter.mProductId == mProductId &&
222 filter.mClass == mClass &&
223 filter.mSubclass == mSubclass &&
224 filter.mProtocol == mProtocol);
226 if (obj instanceof UsbDevice) {
227 UsbDevice device = (UsbDevice)obj;
228 return (device.getVendorId() == mVendorId &&
229 device.getProductId() == mProductId &&
230 device.getDeviceClass() == mClass &&
231 device.getDeviceSubclass() == mSubclass &&
232 device.getDeviceProtocol() == mProtocol);
238 public int hashCode() {
239 return (((mVendorId << 16) | mProductId) ^
240 ((mClass << 16) | (mSubclass << 8) | mProtocol));
244 public String toString() {
245 return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
246 ",mClass=" + mClass + ",mSubclass=" + mSubclass +
247 ",mProtocol=" + mProtocol + "]";
251 // This class is used to describe a USB accessory.
252 // When used in HashMaps all values must be specified,
253 // but wildcards can be used for any of the fields in
254 // the package meta-data.
255 private static class AccessoryFilter {
256 // USB accessory manufacturer (or null for unspecified)
257 public final String mManufacturer;
258 // USB accessory model (or null for unspecified)
259 public final String mModel;
260 // USB accessory version (or null for unspecified)
261 public final String mVersion;
263 public AccessoryFilter(String manufacturer, String model, String version) {
264 mManufacturer = manufacturer;
269 public AccessoryFilter(UsbAccessory accessory) {
270 mManufacturer = accessory.getManufacturer();
271 mModel = accessory.getModel();
272 mVersion = accessory.getVersion();
275 public static AccessoryFilter read(XmlPullParser parser)
276 throws XmlPullParserException, IOException {
277 String manufacturer = null;
279 String version = null;
281 int count = parser.getAttributeCount();
282 for (int i = 0; i < count; i++) {
283 String name = parser.getAttributeName(i);
284 String value = parser.getAttributeValue(i);
286 if ("manufacturer".equals(name)) {
287 manufacturer = value;
288 } else if ("model".equals(name)) {
290 } else if ("version".equals(name)) {
294 return new AccessoryFilter(manufacturer, model, version);
297 public void write(XmlSerializer serializer)throws IOException {
298 serializer.startTag(null, "usb-accessory");
299 if (mManufacturer != null) {
300 serializer.attribute(null, "manufacturer", mManufacturer);
302 if (mModel != null) {
303 serializer.attribute(null, "model", mModel);
305 if (mVersion != null) {
306 serializer.attribute(null, "version", mVersion);
308 serializer.endTag(null, "usb-accessory");
311 public boolean matches(UsbAccessory acc) {
312 if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
313 if (mModel != null && !acc.getModel().equals(mModel)) return false;
314 if (mVersion != null && !acc.getVersion().equals(mVersion)) return false;
318 public boolean matches(AccessoryFilter f) {
319 if (mManufacturer != null && !f.mManufacturer.equals(mManufacturer)) return false;
320 if (mModel != null && !f.mModel.equals(mModel)) return false;
321 if (mVersion != null && !f.mVersion.equals(mVersion)) return false;
326 public boolean equals(Object obj) {
327 // can't compare if we have wildcard strings
328 if (mManufacturer == null || mModel == null || mVersion == null) {
331 if (obj instanceof AccessoryFilter) {
332 AccessoryFilter filter = (AccessoryFilter)obj;
333 return (mManufacturer.equals(filter.mManufacturer) &&
334 mModel.equals(filter.mModel) &&
335 mVersion.equals(filter.mVersion));
337 if (obj instanceof UsbAccessory) {
338 UsbAccessory accessory = (UsbAccessory)obj;
339 return (mManufacturer.equals(accessory.getManufacturer()) &&
340 mModel.equals(accessory.getModel()) &&
341 mVersion.equals(accessory.getVersion()));
347 public int hashCode() {
348 return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
349 (mModel == null ? 0 : mModel.hashCode()) ^
350 (mVersion == null ? 0 : mVersion.hashCode()));
354 public String toString() {
355 return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
356 "\", mModel=\"" + mModel +
357 "\", mVersion=\"" + mVersion + "\"]";
361 private class MyPackageMonitor extends PackageMonitor {
363 public void onPackageAdded(String packageName, int uid) {
364 handlePackageUpdate(packageName);
368 public boolean onPackageChanged(String packageName, int uid, String[] components) {
369 handlePackageUpdate(packageName);
374 public void onPackageRemoved(String packageName, int uid) {
375 clearDefaults(packageName);
379 MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
381 public UsbSettingsManager(Context context, UserHandle user) {
382 if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
385 mUserContext = context.createPackageContextAsUser("android", 0, user);
386 } catch (NameNotFoundException e) {
387 throw new RuntimeException("Missing android package");
391 mPackageManager = mUserContext.getPackageManager();
394 mSettingsFile = new AtomicFile(new File(
395 Environment.getUserSystemDirectory(user.getIdentifier()),
396 "usb_device_manager.xml"));
398 synchronized (mLock) {
399 if (UserHandle.OWNER.equals(user)) {
400 upgradeSingleUserLocked();
402 readSettingsLocked();
405 mPackageMonitor.register(mUserContext, null, true);
408 private void readPreference(XmlPullParser parser)
409 throws XmlPullParserException, IOException {
410 String packageName = null;
411 int count = parser.getAttributeCount();
412 for (int i = 0; i < count; i++) {
413 if ("package".equals(parser.getAttributeName(i))) {
414 packageName = parser.getAttributeValue(i);
418 XmlUtils.nextElement(parser);
419 if ("usb-device".equals(parser.getName())) {
420 DeviceFilter filter = DeviceFilter.read(parser);
421 mDevicePreferenceMap.put(filter, packageName);
422 } else if ("usb-accessory".equals(parser.getName())) {
423 AccessoryFilter filter = AccessoryFilter.read(parser);
424 mAccessoryPreferenceMap.put(filter, packageName);
426 XmlUtils.nextElement(parser);
430 * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
431 * Should only by called by owner.
433 private void upgradeSingleUserLocked() {
434 if (sSingleUserSettingsFile.exists()) {
435 mDevicePreferenceMap.clear();
436 mAccessoryPreferenceMap.clear();
438 FileInputStream fis = null;
440 fis = new FileInputStream(sSingleUserSettingsFile);
441 XmlPullParser parser = Xml.newPullParser();
442 parser.setInput(fis, null);
444 XmlUtils.nextElement(parser);
445 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
446 final String tagName = parser.getName();
447 if ("preference".equals(tagName)) {
448 readPreference(parser);
450 XmlUtils.nextElement(parser);
453 } catch (IOException e) {
454 Log.wtf(TAG, "Failed to read single-user settings", e);
455 } catch (XmlPullParserException e) {
456 Log.wtf(TAG, "Failed to read single-user settings", e);
458 IoUtils.closeQuietly(fis);
461 writeSettingsLocked();
463 // Success or failure, we delete single-user file
464 sSingleUserSettingsFile.delete();
468 private void readSettingsLocked() {
469 if (DEBUG) Slog.v(TAG, "readSettingsLocked()");
471 mDevicePreferenceMap.clear();
472 mAccessoryPreferenceMap.clear();
474 FileInputStream stream = null;
476 stream = mSettingsFile.openRead();
477 XmlPullParser parser = Xml.newPullParser();
478 parser.setInput(stream, null);
480 XmlUtils.nextElement(parser);
481 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
482 String tagName = parser.getName();
483 if ("preference".equals(tagName)) {
484 readPreference(parser);
486 XmlUtils.nextElement(parser);
489 } catch (FileNotFoundException e) {
490 if (DEBUG) Slog.d(TAG, "settings file not found");
491 } catch (Exception e) {
492 Slog.e(TAG, "error reading settings file, deleting to start fresh", e);
493 mSettingsFile.delete();
495 IoUtils.closeQuietly(stream);
499 private void writeSettingsLocked() {
500 if (DEBUG) Slog.v(TAG, "writeSettingsLocked()");
502 FileOutputStream fos = null;
504 fos = mSettingsFile.startWrite();
506 FastXmlSerializer serializer = new FastXmlSerializer();
507 serializer.setOutput(fos, "utf-8");
508 serializer.startDocument(null, true);
509 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
510 serializer.startTag(null, "settings");
512 for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
513 serializer.startTag(null, "preference");
514 serializer.attribute(null, "package", mDevicePreferenceMap.get(filter));
515 filter.write(serializer);
516 serializer.endTag(null, "preference");
519 for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
520 serializer.startTag(null, "preference");
521 serializer.attribute(null, "package", mAccessoryPreferenceMap.get(filter));
522 filter.write(serializer);
523 serializer.endTag(null, "preference");
526 serializer.endTag(null, "settings");
527 serializer.endDocument();
529 mSettingsFile.finishWrite(fos);
530 } catch (IOException e) {
531 Slog.e(TAG, "Failed to write settings", e);
533 mSettingsFile.failWrite(fos);
538 // Checks to see if a package matches a device or accessory.
539 // Only one of device and accessory should be non-null.
540 private boolean packageMatchesLocked(ResolveInfo info, String metaDataName,
541 UsbDevice device, UsbAccessory accessory) {
542 ActivityInfo ai = info.activityInfo;
544 XmlResourceParser parser = null;
546 parser = ai.loadXmlMetaData(mPackageManager, metaDataName);
547 if (parser == null) {
548 Slog.w(TAG, "no meta-data for " + info);
552 XmlUtils.nextElement(parser);
553 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
554 String tagName = parser.getName();
555 if (device != null && "usb-device".equals(tagName)) {
556 DeviceFilter filter = DeviceFilter.read(parser);
557 if (filter.matches(device)) {
561 else if (accessory != null && "usb-accessory".equals(tagName)) {
562 AccessoryFilter filter = AccessoryFilter.read(parser);
563 if (filter.matches(accessory)) {
567 XmlUtils.nextElement(parser);
569 } catch (Exception e) {
570 Slog.w(TAG, "Unable to load component info " + info.toString(), e);
572 if (parser != null) parser.close();
577 private final ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) {
578 ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
579 List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent,
580 PackageManager.GET_META_DATA);
581 int count = resolveInfos.size();
582 for (int i = 0; i < count; i++) {
583 ResolveInfo resolveInfo = resolveInfos.get(i);
584 if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) {
585 matches.add(resolveInfo);
591 private final ArrayList<ResolveInfo> getAccessoryMatchesLocked(
592 UsbAccessory accessory, Intent intent) {
593 ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
594 List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent,
595 PackageManager.GET_META_DATA);
596 int count = resolveInfos.size();
597 for (int i = 0; i < count; i++) {
598 ResolveInfo resolveInfo = resolveInfos.get(i);
599 if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) {
600 matches.add(resolveInfo);
606 public void deviceAttached(UsbDevice device) {
607 Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
608 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
609 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
611 ArrayList<ResolveInfo> matches;
612 String defaultPackage;
613 synchronized (mLock) {
614 matches = getDeviceMatchesLocked(device, intent);
615 // Launch our default activity directly, if we have one.
616 // Otherwise we will start the UsbResolverActivity to allow the user to choose.
617 defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device));
620 // Send broadcast to running activity with registered intent
621 mUserContext.sendBroadcast(intent);
623 // Start activity with registered intent
624 resolveActivity(intent, matches, defaultPackage, device, null);
627 public void deviceDetached(UsbDevice device) {
628 // clear temporary permissions for the device
629 mDevicePermissionMap.remove(device.getDeviceName());
631 Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
632 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
633 if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent);
634 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
637 public void accessoryAttached(UsbAccessory accessory) {
638 Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
639 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
640 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
642 ArrayList<ResolveInfo> matches;
643 String defaultPackage;
644 synchronized (mLock) {
645 matches = getAccessoryMatchesLocked(accessory, intent);
646 // Launch our default activity directly, if we have one.
647 // Otherwise we will start the UsbResolverActivity to allow the user to choose.
648 defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory));
651 resolveActivity(intent, matches, defaultPackage, null, accessory);
654 public void accessoryDetached(UsbAccessory accessory) {
655 // clear temporary permissions for the accessory
656 mAccessoryPermissionMap.remove(accessory);
658 Intent intent = new Intent(
659 UsbManager.ACTION_USB_ACCESSORY_DETACHED);
660 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
661 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
664 private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches,
665 String defaultPackage, UsbDevice device, UsbAccessory accessory) {
666 int count = matches.size();
668 // don't show the resolver activity if there are no choices available
670 if (accessory != null) {
671 String uri = accessory.getUri();
672 if (uri != null && uri.length() > 0) {
673 // display URI to user
674 // start UsbResolverActivity so user can choose an activity
675 Intent dialogIntent = new Intent();
676 dialogIntent.setClassName("com.android.systemui",
677 "com.android.systemui.usb.UsbAccessoryUriActivity");
678 dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
679 dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
680 dialogIntent.putExtra("uri", uri);
682 mUserContext.startActivityAsUser(dialogIntent, mUser);
683 } catch (ActivityNotFoundException e) {
684 Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
693 ResolveInfo defaultRI = null;
694 if (count == 1 && defaultPackage == null) {
695 // Check to see if our single choice is on the system partition.
696 // If so, treat it as our default without calling UsbResolverActivity
697 ResolveInfo rInfo = matches.get(0);
698 if (rInfo.activityInfo != null &&
699 rInfo.activityInfo.applicationInfo != null &&
700 (rInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
705 if (defaultRI == null && defaultPackage != null) {
706 // look for default activity
707 for (int i = 0; i < count; i++) {
708 ResolveInfo rInfo = matches.get(i);
709 if (rInfo.activityInfo != null &&
710 defaultPackage.equals(rInfo.activityInfo.packageName)) {
717 if (defaultRI != null) {
718 // grant permission for default activity
719 if (device != null) {
720 grantDevicePermission(device, defaultRI.activityInfo.applicationInfo.uid);
721 } else if (accessory != null) {
722 grantAccessoryPermission(accessory, defaultRI.activityInfo.applicationInfo.uid);
725 // start default activity directly
728 new ComponentName(defaultRI.activityInfo.packageName,
729 defaultRI.activityInfo.name));
730 mUserContext.startActivityAsUser(intent, mUser);
731 } catch (ActivityNotFoundException e) {
732 Slog.e(TAG, "startActivity failed", e);
735 Intent resolverIntent = new Intent();
736 resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
739 // start UsbConfirmActivity if there is only one choice
740 resolverIntent.setClassName("com.android.systemui",
741 "com.android.systemui.usb.UsbConfirmActivity");
742 resolverIntent.putExtra("rinfo", matches.get(0));
744 if (device != null) {
745 resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
747 resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
750 // start UsbResolverActivity so user can choose an activity
751 resolverIntent.setClassName("com.android.systemui",
752 "com.android.systemui.usb.UsbResolverActivity");
753 resolverIntent.putParcelableArrayListExtra("rlist", matches);
754 resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
757 mUserContext.startActivityAsUser(resolverIntent, mUser);
758 } catch (ActivityNotFoundException e) {
759 Slog.e(TAG, "unable to start activity " + resolverIntent);
764 private boolean clearCompatibleMatchesLocked(String packageName, DeviceFilter filter) {
765 boolean changed = false;
766 for (DeviceFilter test : mDevicePreferenceMap.keySet()) {
767 if (filter.matches(test)) {
768 mDevicePreferenceMap.remove(test);
775 private boolean clearCompatibleMatchesLocked(String packageName, AccessoryFilter filter) {
776 boolean changed = false;
777 for (AccessoryFilter test : mAccessoryPreferenceMap.keySet()) {
778 if (filter.matches(test)) {
779 mAccessoryPreferenceMap.remove(test);
786 private boolean handlePackageUpdateLocked(String packageName, ActivityInfo aInfo,
787 String metaDataName) {
788 XmlResourceParser parser = null;
789 boolean changed = false;
792 parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName);
793 if (parser == null) return false;
795 XmlUtils.nextElement(parser);
796 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
797 String tagName = parser.getName();
798 if ("usb-device".equals(tagName)) {
799 DeviceFilter filter = DeviceFilter.read(parser);
800 if (clearCompatibleMatchesLocked(packageName, filter)) {
804 else if ("usb-accessory".equals(tagName)) {
805 AccessoryFilter filter = AccessoryFilter.read(parser);
806 if (clearCompatibleMatchesLocked(packageName, filter)) {
810 XmlUtils.nextElement(parser);
812 } catch (Exception e) {
813 Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e);
815 if (parser != null) parser.close();
820 // Check to see if the package supports any USB devices or accessories.
821 // If so, clear any non-matching preferences for matching devices/accessories.
822 private void handlePackageUpdate(String packageName) {
823 synchronized (mLock) {
825 boolean changed = false;
828 info = mPackageManager.getPackageInfo(packageName,
829 PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
830 } catch (NameNotFoundException e) {
831 Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e);
835 ActivityInfo[] activities = info.activities;
836 if (activities == null) return;
837 for (int i = 0; i < activities.length; i++) {
838 // check for meta-data, both for devices and accessories
839 if (handlePackageUpdateLocked(packageName, activities[i],
840 UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
843 if (handlePackageUpdateLocked(packageName, activities[i],
844 UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
850 writeSettingsLocked();
855 public boolean hasPermission(UsbDevice device) {
856 synchronized (mLock) {
857 int uid = Binder.getCallingUid();
858 if (uid == Process.SYSTEM_UID) {
861 SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
862 if (uidList == null) {
865 return uidList.get(uid);
869 public boolean hasPermission(UsbAccessory accessory) {
870 synchronized (mLock) {
871 int uid = Binder.getCallingUid();
872 if (uid == Process.SYSTEM_UID) {
875 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
876 if (uidList == null) {
879 return uidList.get(uid);
883 public void checkPermission(UsbDevice device) {
884 if (!hasPermission(device)) {
885 throw new SecurityException("User has not given permission to device " + device);
889 public void checkPermission(UsbAccessory accessory) {
890 if (!hasPermission(accessory)) {
891 throw new SecurityException("User has not given permission to accessory " + accessory);
895 private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) {
896 final int uid = Binder.getCallingUid();
898 // compare uid with packageName to foil apps pretending to be someone else
900 ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0);
901 if (aInfo.uid != uid) {
902 throw new IllegalArgumentException("package " + packageName +
903 " does not match caller's uid " + uid);
905 } catch (PackageManager.NameNotFoundException e) {
906 throw new IllegalArgumentException("package " + packageName + " not found");
909 long identity = Binder.clearCallingIdentity();
910 intent.setClassName("com.android.systemui",
911 "com.android.systemui.usb.UsbPermissionActivity");
912 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
913 intent.putExtra(Intent.EXTRA_INTENT, pi);
914 intent.putExtra("package", packageName);
915 intent.putExtra(Intent.EXTRA_UID, uid);
917 mUserContext.startActivityAsUser(intent, mUser);
918 } catch (ActivityNotFoundException e) {
919 Slog.e(TAG, "unable to start UsbPermissionActivity");
921 Binder.restoreCallingIdentity(identity);
925 public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) {
926 Intent intent = new Intent();
928 // respond immediately if permission has already been granted
929 if (hasPermission(device)) {
930 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
931 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
933 pi.send(mUserContext, 0, intent);
934 } catch (PendingIntent.CanceledException e) {
935 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
940 // start UsbPermissionActivity so user can choose an activity
941 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
942 requestPermissionDialog(intent, packageName, pi);
945 public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) {
946 Intent intent = new Intent();
948 // respond immediately if permission has already been granted
949 if (hasPermission(accessory)) {
950 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
951 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
953 pi.send(mUserContext, 0, intent);
954 } catch (PendingIntent.CanceledException e) {
955 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
960 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
961 requestPermissionDialog(intent, packageName, pi);
964 public void setDevicePackage(UsbDevice device, String packageName) {
965 DeviceFilter filter = new DeviceFilter(device);
966 boolean changed = false;
967 synchronized (mLock) {
968 if (packageName == null) {
969 changed = (mDevicePreferenceMap.remove(filter) != null);
971 changed = !packageName.equals(mDevicePreferenceMap.get(filter));
973 mDevicePreferenceMap.put(filter, packageName);
977 writeSettingsLocked();
982 public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
983 AccessoryFilter filter = new AccessoryFilter(accessory);
984 boolean changed = false;
985 synchronized (mLock) {
986 if (packageName == null) {
987 changed = (mAccessoryPreferenceMap.remove(filter) != null);
989 changed = !packageName.equals(mAccessoryPreferenceMap.get(filter));
991 mAccessoryPreferenceMap.put(filter, packageName);
995 writeSettingsLocked();
1000 public void grantDevicePermission(UsbDevice device, int uid) {
1001 synchronized (mLock) {
1002 String deviceName = device.getDeviceName();
1003 SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
1004 if (uidList == null) {
1005 uidList = new SparseBooleanArray(1);
1006 mDevicePermissionMap.put(deviceName, uidList);
1008 uidList.put(uid, true);
1012 public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
1013 synchronized (mLock) {
1014 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
1015 if (uidList == null) {
1016 uidList = new SparseBooleanArray(1);
1017 mAccessoryPermissionMap.put(accessory, uidList);
1019 uidList.put(uid, true);
1023 public boolean hasDefaults(String packageName) {
1024 synchronized (mLock) {
1025 if (mDevicePreferenceMap.values().contains(packageName)) return true;
1026 if (mAccessoryPreferenceMap.values().contains(packageName)) return true;
1031 public void clearDefaults(String packageName) {
1032 synchronized (mLock) {
1033 if (clearPackageDefaultsLocked(packageName)) {
1034 writeSettingsLocked();
1039 private boolean clearPackageDefaultsLocked(String packageName) {
1040 boolean cleared = false;
1041 synchronized (mLock) {
1042 if (mDevicePreferenceMap.containsValue(packageName)) {
1043 // make a copy of the key set to avoid ConcurrentModificationException
1044 Object[] keys = mDevicePreferenceMap.keySet().toArray();
1045 for (int i = 0; i < keys.length; i++) {
1046 Object key = keys[i];
1047 if (packageName.equals(mDevicePreferenceMap.get(key))) {
1048 mDevicePreferenceMap.remove(key);
1053 if (mAccessoryPreferenceMap.containsValue(packageName)) {
1054 // make a copy of the key set to avoid ConcurrentModificationException
1055 Object[] keys = mAccessoryPreferenceMap.keySet().toArray();
1056 for (int i = 0; i < keys.length; i++) {
1057 Object key = keys[i];
1058 if (packageName.equals(mAccessoryPreferenceMap.get(key))) {
1059 mAccessoryPreferenceMap.remove(key);
1068 public void dump(FileDescriptor fd, PrintWriter pw) {
1069 synchronized (mLock) {
1070 pw.println(" Device permissions:");
1071 for (String deviceName : mDevicePermissionMap.keySet()) {
1072 pw.print(" " + deviceName + ": ");
1073 SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
1074 int count = uidList.size();
1075 for (int i = 0; i < count; i++) {
1076 pw.print(Integer.toString(uidList.keyAt(i)) + " ");
1080 pw.println(" Accessory permissions:");
1081 for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
1082 pw.print(" " + accessory + ": ");
1083 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
1084 int count = uidList.size();
1085 for (int i = 0; i < count; i++) {
1086 pw.print(Integer.toString(uidList.keyAt(i)) + " ");
1090 pw.println(" Device preferences:");
1091 for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
1092 pw.println(" " + filter + ": " + mDevicePreferenceMap.get(filter));
1094 pw.println(" Accessory preferences:");
1095 for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
1096 pw.println(" " + filter + ": " + mAccessoryPreferenceMap.get(filter));