• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 package com.android.server.usb;
18 
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.FileUtils;
37 import android.os.Process;
38 import android.util.Slog;
39 import android.util.SparseBooleanArray;
40 import android.util.Xml;
41 
42 import com.android.internal.content.PackageMonitor;
43 import com.android.internal.util.FastXmlSerializer;
44 import com.android.internal.util.XmlUtils;
45 
46 import org.xmlpull.v1.XmlPullParser;
47 import org.xmlpull.v1.XmlPullParserException;
48 import org.xmlpull.v1.XmlSerializer;
49 
50 import java.io.BufferedOutputStream;
51 import java.io.File;
52 import java.io.FileDescriptor;
53 import java.io.FileInputStream;
54 import java.io.FileNotFoundException;
55 import java.io.FileOutputStream;
56 import java.io.IOException;
57 import java.io.PrintWriter;
58 import java.util.ArrayList;
59 import java.util.HashMap;
60 import java.util.List;
61 
62 class UsbSettingsManager {
63 
64     private static final String TAG = "UsbSettingsManager";
65     private static final boolean DEBUG = false;
66     private static final File sSettingsFile = new File("/data/system/usb_device_manager.xml");
67 
68     private final Context mContext;
69     private final PackageManager mPackageManager;
70 
71     // Temporary mapping USB device name to list of UIDs with permissions for the device
72     private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
73             new HashMap<String, SparseBooleanArray>();
74     // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory
75     private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
76             new HashMap<UsbAccessory, SparseBooleanArray>();
77     // Maps DeviceFilter to user preferred application package
78     private final HashMap<DeviceFilter, String> mDevicePreferenceMap =
79             new HashMap<DeviceFilter, String>();
80     // Maps AccessoryFilter to user preferred application package
81     private final HashMap<AccessoryFilter, String> mAccessoryPreferenceMap =
82             new HashMap<AccessoryFilter, String>();
83 
84     private final Object mLock = new Object();
85 
86     // This class is used to describe a USB device.
87     // When used in HashMaps all values must be specified,
88     // but wildcards can be used for any of the fields in
89     // the package meta-data.
90     private static class DeviceFilter {
91         // USB Vendor ID (or -1 for unspecified)
92         public final int mVendorId;
93         // USB Product ID (or -1 for unspecified)
94         public final int mProductId;
95         // USB device or interface class (or -1 for unspecified)
96         public final int mClass;
97         // USB device subclass (or -1 for unspecified)
98         public final int mSubclass;
99         // USB device protocol (or -1 for unspecified)
100         public final int mProtocol;
101 
DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol)102         public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol) {
103             mVendorId = vid;
104             mProductId = pid;
105             mClass = clasz;
106             mSubclass = subclass;
107             mProtocol = protocol;
108         }
109 
DeviceFilter(UsbDevice device)110         public DeviceFilter(UsbDevice device) {
111             mVendorId = device.getVendorId();
112             mProductId = device.getProductId();
113             mClass = device.getDeviceClass();
114             mSubclass = device.getDeviceSubclass();
115             mProtocol = device.getDeviceProtocol();
116         }
117 
read(XmlPullParser parser)118         public static DeviceFilter read(XmlPullParser parser)
119                 throws XmlPullParserException, IOException {
120             int vendorId = -1;
121             int productId = -1;
122             int deviceClass = -1;
123             int deviceSubclass = -1;
124             int deviceProtocol = -1;
125 
126             int count = parser.getAttributeCount();
127             for (int i = 0; i < count; i++) {
128                 String name = parser.getAttributeName(i);
129                 // All attribute values are ints
130                 int value = Integer.parseInt(parser.getAttributeValue(i));
131 
132                 if ("vendor-id".equals(name)) {
133                     vendorId = value;
134                 } else if ("product-id".equals(name)) {
135                     productId = value;
136                 } else if ("class".equals(name)) {
137                     deviceClass = value;
138                 } else if ("subclass".equals(name)) {
139                     deviceSubclass = value;
140                 } else if ("protocol".equals(name)) {
141                     deviceProtocol = value;
142                 }
143             }
144             return new DeviceFilter(vendorId, productId,
145                     deviceClass, deviceSubclass, deviceProtocol);
146         }
147 
write(XmlSerializer serializer)148         public void write(XmlSerializer serializer) throws IOException {
149             serializer.startTag(null, "usb-device");
150             if (mVendorId != -1) {
151                 serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
152             }
153             if (mProductId != -1) {
154                 serializer.attribute(null, "product-id", Integer.toString(mProductId));
155             }
156             if (mClass != -1) {
157                 serializer.attribute(null, "class", Integer.toString(mClass));
158             }
159             if (mSubclass != -1) {
160                 serializer.attribute(null, "subclass", Integer.toString(mSubclass));
161             }
162             if (mProtocol != -1) {
163                 serializer.attribute(null, "protocol", Integer.toString(mProtocol));
164             }
165             serializer.endTag(null, "usb-device");
166         }
167 
matches(int clasz, int subclass, int protocol)168         private boolean matches(int clasz, int subclass, int protocol) {
169             return ((mClass == -1 || clasz == mClass) &&
170                     (mSubclass == -1 || subclass == mSubclass) &&
171                     (mProtocol == -1 || protocol == mProtocol));
172         }
173 
matches(UsbDevice device)174         public boolean matches(UsbDevice device) {
175             if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
176             if (mProductId != -1 && device.getProductId() != mProductId) return false;
177 
178             // check device class/subclass/protocol
179             if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
180                     device.getDeviceProtocol())) return true;
181 
182             // if device doesn't match, check the interfaces
183             int count = device.getInterfaceCount();
184             for (int i = 0; i < count; i++) {
185                 UsbInterface intf = device.getInterface(i);
186                  if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
187                         intf.getInterfaceProtocol())) return true;
188             }
189 
190             return false;
191         }
192 
matches(DeviceFilter f)193         public boolean matches(DeviceFilter f) {
194             if (mVendorId != -1 && f.mVendorId != mVendorId) return false;
195             if (mProductId != -1 && f.mProductId != mProductId) return false;
196 
197             // check device class/subclass/protocol
198             return matches(f.mClass, f.mSubclass, f.mProtocol);
199         }
200 
201         @Override
equals(Object obj)202         public boolean equals(Object obj) {
203             // can't compare if we have wildcard strings
204             if (mVendorId == -1 || mProductId == -1 ||
205                     mClass == -1 || mSubclass == -1 || mProtocol == -1) {
206                 return false;
207             }
208             if (obj instanceof DeviceFilter) {
209                 DeviceFilter filter = (DeviceFilter)obj;
210                 return (filter.mVendorId == mVendorId &&
211                         filter.mProductId == mProductId &&
212                         filter.mClass == mClass &&
213                         filter.mSubclass == mSubclass &&
214                         filter.mProtocol == mProtocol);
215             }
216             if (obj instanceof UsbDevice) {
217                 UsbDevice device = (UsbDevice)obj;
218                 return (device.getVendorId() == mVendorId &&
219                         device.getProductId() == mProductId &&
220                         device.getDeviceClass() == mClass &&
221                         device.getDeviceSubclass() == mSubclass &&
222                         device.getDeviceProtocol() == mProtocol);
223             }
224             return false;
225         }
226 
227         @Override
hashCode()228         public int hashCode() {
229             return (((mVendorId << 16) | mProductId) ^
230                     ((mClass << 16) | (mSubclass << 8) | mProtocol));
231         }
232 
233         @Override
toString()234         public String toString() {
235             return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
236                     ",mClass=" + mClass + ",mSubclass=" + mSubclass +
237                     ",mProtocol=" + mProtocol + "]";
238         }
239     }
240 
241     // This class is used to describe a USB accessory.
242     // When used in HashMaps all values must be specified,
243     // but wildcards can be used for any of the fields in
244     // the package meta-data.
245     private static class AccessoryFilter {
246         // USB accessory manufacturer (or null for unspecified)
247         public final String mManufacturer;
248         // USB accessory model (or null for unspecified)
249         public final String mModel;
250         // USB accessory version (or null for unspecified)
251         public final String mVersion;
252 
AccessoryFilter(String manufacturer, String model, String version)253         public AccessoryFilter(String manufacturer, String model, String version) {
254             mManufacturer = manufacturer;
255             mModel = model;
256             mVersion = version;
257         }
258 
AccessoryFilter(UsbAccessory accessory)259         public AccessoryFilter(UsbAccessory accessory) {
260             mManufacturer = accessory.getManufacturer();
261             mModel = accessory.getModel();
262             mVersion = accessory.getVersion();
263         }
264 
read(XmlPullParser parser)265         public static AccessoryFilter read(XmlPullParser parser)
266                 throws XmlPullParserException, IOException {
267             String manufacturer = null;
268             String model = null;
269             String version = null;
270 
271             int count = parser.getAttributeCount();
272             for (int i = 0; i < count; i++) {
273                 String name = parser.getAttributeName(i);
274                 String value = parser.getAttributeValue(i);
275 
276                 if ("manufacturer".equals(name)) {
277                     manufacturer = value;
278                 } else if ("model".equals(name)) {
279                     model = value;
280                 } else if ("version".equals(name)) {
281                     version = value;
282                 }
283              }
284              return new AccessoryFilter(manufacturer, model, version);
285         }
286 
write(XmlSerializer serializer)287         public void write(XmlSerializer serializer)throws IOException {
288             serializer.startTag(null, "usb-accessory");
289             if (mManufacturer != null) {
290                 serializer.attribute(null, "manufacturer", mManufacturer);
291             }
292             if (mModel != null) {
293                 serializer.attribute(null, "model", mModel);
294             }
295             if (mVersion != null) {
296                 serializer.attribute(null, "version", mVersion);
297             }
298             serializer.endTag(null, "usb-accessory");
299         }
300 
matches(UsbAccessory acc)301         public boolean matches(UsbAccessory acc) {
302             if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
303             if (mModel != null && !acc.getModel().equals(mModel)) return false;
304             if (mVersion != null && !acc.getVersion().equals(mVersion)) return false;
305             return true;
306         }
307 
matches(AccessoryFilter f)308         public boolean matches(AccessoryFilter f) {
309             if (mManufacturer != null && !f.mManufacturer.equals(mManufacturer)) return false;
310             if (mModel != null && !f.mModel.equals(mModel)) return false;
311             if (mVersion != null && !f.mVersion.equals(mVersion)) return false;
312             return true;
313         }
314 
315         @Override
equals(Object obj)316         public boolean equals(Object obj) {
317             // can't compare if we have wildcard strings
318             if (mManufacturer == null || mModel == null || mVersion == null) {
319                 return false;
320             }
321             if (obj instanceof AccessoryFilter) {
322                 AccessoryFilter filter = (AccessoryFilter)obj;
323                 return (mManufacturer.equals(filter.mManufacturer) &&
324                         mModel.equals(filter.mModel) &&
325                         mVersion.equals(filter.mVersion));
326             }
327             if (obj instanceof UsbAccessory) {
328                 UsbAccessory accessory = (UsbAccessory)obj;
329                 return (mManufacturer.equals(accessory.getManufacturer()) &&
330                         mModel.equals(accessory.getModel()) &&
331                         mVersion.equals(accessory.getVersion()));
332             }
333             return false;
334         }
335 
336         @Override
hashCode()337         public int hashCode() {
338             return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
339                     (mModel == null ? 0 : mModel.hashCode()) ^
340                     (mVersion == null ? 0 : mVersion.hashCode()));
341         }
342 
343         @Override
toString()344         public String toString() {
345             return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
346                                 "\", mModel=\"" + mModel +
347                                 "\", mVersion=\"" + mVersion + "\"]";
348         }
349     }
350 
351     private class MyPackageMonitor extends PackageMonitor {
352 
onPackageAdded(String packageName, int uid)353         public void onPackageAdded(String packageName, int uid) {
354             handlePackageUpdate(packageName);
355         }
356 
onPackageChanged(String packageName, int uid, String[] components)357         public void onPackageChanged(String packageName, int uid, String[] components) {
358             handlePackageUpdate(packageName);
359         }
360 
onPackageRemoved(String packageName, int uid)361         public void onPackageRemoved(String packageName, int uid) {
362             clearDefaults(packageName);
363         }
364     }
365     MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
366 
UsbSettingsManager(Context context)367     public UsbSettingsManager(Context context) {
368         mContext = context;
369         mPackageManager = context.getPackageManager();
370         synchronized (mLock) {
371             readSettingsLocked();
372         }
373         mPackageMonitor.register(context, null, true);
374     }
375 
readPreference(XmlPullParser parser)376     private void readPreference(XmlPullParser parser)
377             throws XmlPullParserException, IOException {
378         String packageName = null;
379         int count = parser.getAttributeCount();
380         for (int i = 0; i < count; i++) {
381             if ("package".equals(parser.getAttributeName(i))) {
382                 packageName = parser.getAttributeValue(i);
383                 break;
384             }
385         }
386         XmlUtils.nextElement(parser);
387         if ("usb-device".equals(parser.getName())) {
388             DeviceFilter filter = DeviceFilter.read(parser);
389             mDevicePreferenceMap.put(filter, packageName);
390         } else if ("usb-accessory".equals(parser.getName())) {
391             AccessoryFilter filter = AccessoryFilter.read(parser);
392             mAccessoryPreferenceMap.put(filter, packageName);
393         }
394         XmlUtils.nextElement(parser);
395     }
396 
readSettingsLocked()397     private void readSettingsLocked() {
398         FileInputStream stream = null;
399         try {
400             stream = new FileInputStream(sSettingsFile);
401             XmlPullParser parser = Xml.newPullParser();
402             parser.setInput(stream, null);
403 
404             XmlUtils.nextElement(parser);
405             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
406                 String tagName = parser.getName();
407                 if ("preference".equals(tagName)) {
408                     readPreference(parser);
409                  } else {
410                     XmlUtils.nextElement(parser);
411                 }
412             }
413         } catch (FileNotFoundException e) {
414             if (DEBUG) Slog.d(TAG, "settings file not found");
415         } catch (Exception e) {
416             Slog.e(TAG, "error reading settings file, deleting to start fresh", e);
417             sSettingsFile.delete();
418         } finally {
419             if (stream != null) {
420                 try {
421                     stream.close();
422                 } catch (IOException e) {
423                 }
424             }
425         }
426     }
427 
writeSettingsLocked()428     private void writeSettingsLocked() {
429         FileOutputStream fos = null;
430         try {
431             FileOutputStream fstr = new FileOutputStream(sSettingsFile);
432             if (DEBUG) Slog.d(TAG, "writing settings to " + fstr);
433             BufferedOutputStream str = new BufferedOutputStream(fstr);
434             FastXmlSerializer serializer = new FastXmlSerializer();
435             serializer.setOutput(str, "utf-8");
436             serializer.startDocument(null, true);
437             serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
438             serializer.startTag(null, "settings");
439 
440             for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
441                 serializer.startTag(null, "preference");
442                 serializer.attribute(null, "package", mDevicePreferenceMap.get(filter));
443                 filter.write(serializer);
444                 serializer.endTag(null, "preference");
445             }
446 
447             for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
448                 serializer.startTag(null, "preference");
449                 serializer.attribute(null, "package", mAccessoryPreferenceMap.get(filter));
450                 filter.write(serializer);
451                 serializer.endTag(null, "preference");
452             }
453 
454             serializer.endTag(null, "settings");
455             serializer.endDocument();
456 
457             str.flush();
458             FileUtils.sync(fstr);
459             str.close();
460         } catch (Exception e) {
461             Slog.e(TAG, "error writing settings file, deleting to start fresh", e);
462             sSettingsFile.delete();
463         }
464     }
465 
466     // Checks to see if a package matches a device or accessory.
467     // Only one of device and accessory should be non-null.
packageMatchesLocked(ResolveInfo info, String metaDataName, UsbDevice device, UsbAccessory accessory)468     private boolean packageMatchesLocked(ResolveInfo info, String metaDataName,
469             UsbDevice device, UsbAccessory accessory) {
470         ActivityInfo ai = info.activityInfo;
471 
472         XmlResourceParser parser = null;
473         try {
474             parser = ai.loadXmlMetaData(mPackageManager, metaDataName);
475             if (parser == null) {
476                 Slog.w(TAG, "no meta-data for " + info);
477                 return false;
478             }
479 
480             XmlUtils.nextElement(parser);
481             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
482                 String tagName = parser.getName();
483                 if (device != null && "usb-device".equals(tagName)) {
484                     DeviceFilter filter = DeviceFilter.read(parser);
485                     if (filter.matches(device)) {
486                         return true;
487                     }
488                 }
489                 else if (accessory != null && "usb-accessory".equals(tagName)) {
490                     AccessoryFilter filter = AccessoryFilter.read(parser);
491                     if (filter.matches(accessory)) {
492                         return true;
493                     }
494                 }
495                 XmlUtils.nextElement(parser);
496             }
497         } catch (Exception e) {
498             Slog.w(TAG, "Unable to load component info " + info.toString(), e);
499         } finally {
500             if (parser != null) parser.close();
501         }
502         return false;
503     }
504 
getDeviceMatchesLocked(UsbDevice device, Intent intent)505     private final ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) {
506         ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
507         List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent,
508                 PackageManager.GET_META_DATA);
509         int count = resolveInfos.size();
510         for (int i = 0; i < count; i++) {
511             ResolveInfo resolveInfo = resolveInfos.get(i);
512             if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) {
513                 matches.add(resolveInfo);
514             }
515         }
516         return matches;
517     }
518 
getAccessoryMatchesLocked( UsbAccessory accessory, Intent intent)519     private final ArrayList<ResolveInfo> getAccessoryMatchesLocked(
520             UsbAccessory accessory, Intent intent) {
521         ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
522         List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent,
523                 PackageManager.GET_META_DATA);
524         int count = resolveInfos.size();
525         for (int i = 0; i < count; i++) {
526             ResolveInfo resolveInfo = resolveInfos.get(i);
527             if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) {
528                 matches.add(resolveInfo);
529             }
530         }
531         return matches;
532     }
533 
deviceAttached(UsbDevice device)534     public void deviceAttached(UsbDevice device) {
535         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
536         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
537         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
538 
539         ArrayList<ResolveInfo> matches;
540         String defaultPackage;
541         synchronized (mLock) {
542             matches = getDeviceMatchesLocked(device, intent);
543             // Launch our default activity directly, if we have one.
544             // Otherwise we will start the UsbResolverActivity to allow the user to choose.
545             defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device));
546         }
547 
548         resolveActivity(intent, matches, defaultPackage, device, null);
549     }
550 
deviceDetached(UsbDevice device)551     public void deviceDetached(UsbDevice device) {
552         // clear temporary permissions for the device
553         mDevicePermissionMap.remove(device.getDeviceName());
554 
555         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
556         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
557         if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent);
558         mContext.sendBroadcast(intent);
559     }
560 
accessoryAttached(UsbAccessory accessory)561     public void accessoryAttached(UsbAccessory accessory) {
562         Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
563         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
564         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
565 
566         ArrayList<ResolveInfo> matches;
567         String defaultPackage;
568         synchronized (mLock) {
569             matches = getAccessoryMatchesLocked(accessory, intent);
570             // Launch our default activity directly, if we have one.
571             // Otherwise we will start the UsbResolverActivity to allow the user to choose.
572             defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory));
573         }
574 
575         resolveActivity(intent, matches, defaultPackage, null, accessory);
576     }
577 
accessoryDetached(UsbAccessory accessory)578     public void accessoryDetached(UsbAccessory accessory) {
579         // clear temporary permissions for the accessory
580         mAccessoryPermissionMap.remove(accessory);
581 
582         Intent intent = new Intent(
583                 UsbManager.ACTION_USB_ACCESSORY_DETACHED);
584         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
585         mContext.sendBroadcast(intent);
586     }
587 
resolveActivity(Intent intent, ArrayList<ResolveInfo> matches, String defaultPackage, UsbDevice device, UsbAccessory accessory)588     private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches,
589             String defaultPackage, UsbDevice device, UsbAccessory accessory) {
590         int count = matches.size();
591 
592         // don't show the resolver activity if there are no choices available
593         if (count == 0) {
594             if (accessory != null) {
595                 String uri = accessory.getUri();
596                 if (uri != null && uri.length() > 0) {
597                     // display URI to user
598                     // start UsbResolverActivity so user can choose an activity
599                     Intent dialogIntent = new Intent();
600                     dialogIntent.setClassName("com.android.systemui",
601                             "com.android.systemui.usb.UsbAccessoryUriActivity");
602                     dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
603                     dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
604                     dialogIntent.putExtra("uri", uri);
605                     try {
606                         mContext.startActivity(dialogIntent);
607                     } catch (ActivityNotFoundException e) {
608                         Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
609                     }
610                 }
611             }
612 
613             // do nothing
614             return;
615         }
616 
617         ResolveInfo defaultRI = null;
618         if (count == 1 && defaultPackage == null) {
619             // Check to see if our single choice is on the system partition.
620             // If so, treat it as our default without calling UsbResolverActivity
621             ResolveInfo rInfo = matches.get(0);
622             if (rInfo.activityInfo != null &&
623                     rInfo.activityInfo.applicationInfo != null &&
624                     (rInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
625                 defaultRI = rInfo;
626             }
627         }
628 
629         if (defaultRI == null && defaultPackage != null) {
630             // look for default activity
631             for (int i = 0; i < count; i++) {
632                 ResolveInfo rInfo = matches.get(i);
633                 if (rInfo.activityInfo != null &&
634                         defaultPackage.equals(rInfo.activityInfo.packageName)) {
635                     defaultRI = rInfo;
636                     break;
637                 }
638             }
639         }
640 
641         if (defaultRI != null) {
642             // grant permission for default activity
643             if (device != null) {
644                 grantDevicePermission(device, defaultRI.activityInfo.applicationInfo.uid);
645             } else if (accessory != null) {
646                 grantAccessoryPermission(accessory, defaultRI.activityInfo.applicationInfo.uid);
647             }
648 
649             // start default activity directly
650             try {
651                 intent.setComponent(
652                         new ComponentName(defaultRI.activityInfo.packageName,
653                                 defaultRI.activityInfo.name));
654                 mContext.startActivity(intent);
655             } catch (ActivityNotFoundException e) {
656                 Slog.e(TAG, "startActivity failed", e);
657             }
658         } else {
659             Intent resolverIntent = new Intent();
660             resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
661 
662             if (count == 1) {
663                 // start UsbConfirmActivity if there is only one choice
664                 resolverIntent.setClassName("com.android.systemui",
665                         "com.android.systemui.usb.UsbConfirmActivity");
666                 resolverIntent.putExtra("rinfo", matches.get(0));
667 
668                 if (device != null) {
669                     resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
670                 } else {
671                     resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
672                 }
673             } else {
674                 // start UsbResolverActivity so user can choose an activity
675                 resolverIntent.setClassName("com.android.systemui",
676                         "com.android.systemui.usb.UsbResolverActivity");
677                 resolverIntent.putParcelableArrayListExtra("rlist", matches);
678                 resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
679             }
680             try {
681                 mContext.startActivity(resolverIntent);
682             } catch (ActivityNotFoundException e) {
683                 Slog.e(TAG, "unable to start activity " + resolverIntent);
684             }
685         }
686     }
687 
clearCompatibleMatchesLocked(String packageName, DeviceFilter filter)688     private boolean clearCompatibleMatchesLocked(String packageName, DeviceFilter filter) {
689         boolean changed = false;
690         for (DeviceFilter test : mDevicePreferenceMap.keySet()) {
691             if (filter.matches(test)) {
692                 mDevicePreferenceMap.remove(test);
693                 changed = true;
694             }
695         }
696         return changed;
697     }
698 
clearCompatibleMatchesLocked(String packageName, AccessoryFilter filter)699     private boolean clearCompatibleMatchesLocked(String packageName, AccessoryFilter filter) {
700         boolean changed = false;
701         for (AccessoryFilter test : mAccessoryPreferenceMap.keySet()) {
702             if (filter.matches(test)) {
703                 mAccessoryPreferenceMap.remove(test);
704                 changed = true;
705             }
706         }
707         return changed;
708     }
709 
handlePackageUpdateLocked(String packageName, ActivityInfo aInfo, String metaDataName)710     private boolean handlePackageUpdateLocked(String packageName, ActivityInfo aInfo,
711             String metaDataName) {
712         XmlResourceParser parser = null;
713         boolean changed = false;
714 
715         try {
716             parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName);
717             if (parser == null) return false;
718 
719             XmlUtils.nextElement(parser);
720             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
721                 String tagName = parser.getName();
722                 if ("usb-device".equals(tagName)) {
723                     DeviceFilter filter = DeviceFilter.read(parser);
724                     if (clearCompatibleMatchesLocked(packageName, filter)) {
725                         changed = true;
726                     }
727                 }
728                 else if ("usb-accessory".equals(tagName)) {
729                     AccessoryFilter filter = AccessoryFilter.read(parser);
730                     if (clearCompatibleMatchesLocked(packageName, filter)) {
731                         changed = true;
732                     }
733                 }
734                 XmlUtils.nextElement(parser);
735             }
736         } catch (Exception e) {
737             Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e);
738         } finally {
739             if (parser != null) parser.close();
740         }
741         return changed;
742     }
743 
744     // Check to see if the package supports any USB devices or accessories.
745     // If so, clear any non-matching preferences for matching devices/accessories.
handlePackageUpdate(String packageName)746     private void handlePackageUpdate(String packageName) {
747         synchronized (mLock) {
748             PackageInfo info;
749             boolean changed = false;
750 
751             try {
752                 info = mPackageManager.getPackageInfo(packageName,
753                         PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
754             } catch (NameNotFoundException e) {
755                 Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e);
756                 return;
757             }
758 
759             ActivityInfo[] activities = info.activities;
760             if (activities == null) return;
761             for (int i = 0; i < activities.length; i++) {
762                 // check for meta-data, both for devices and accessories
763                 if (handlePackageUpdateLocked(packageName, activities[i],
764                         UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
765                     changed = true;
766                 }
767                 if (handlePackageUpdateLocked(packageName, activities[i],
768                         UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
769                     changed = true;
770                 }
771             }
772 
773             if (changed) {
774                 writeSettingsLocked();
775             }
776         }
777     }
778 
hasPermission(UsbDevice device)779     public boolean hasPermission(UsbDevice device) {
780         synchronized (mLock) {
781             SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
782             if (uidList == null) {
783                 return false;
784             }
785             return uidList.get(Binder.getCallingUid());
786         }
787     }
788 
hasPermission(UsbAccessory accessory)789     public boolean hasPermission(UsbAccessory accessory) {
790         synchronized (mLock) {
791             SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
792             if (uidList == null) {
793                 return false;
794             }
795             return uidList.get(Binder.getCallingUid());
796         }
797     }
798 
checkPermission(UsbDevice device)799     public void checkPermission(UsbDevice device) {
800         if (!hasPermission(device)) {
801             throw new SecurityException("User has not given permission to device " + device);
802         }
803     }
804 
checkPermission(UsbAccessory accessory)805     public void checkPermission(UsbAccessory accessory) {
806         if (!hasPermission(accessory)) {
807             throw new SecurityException("User has not given permission to accessory " + accessory);
808         }
809     }
810 
requestPermissionDialog(Intent intent, String packageName, PendingIntent pi)811     private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) {
812         int uid = Binder.getCallingUid();
813 
814         // compare uid with packageName to foil apps pretending to be someone else
815         try {
816             ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0);
817             if (aInfo.uid != uid) {
818                 throw new IllegalArgumentException("package " + packageName +
819                         " does not match caller's uid " + uid);
820             }
821         } catch (PackageManager.NameNotFoundException e) {
822             throw new IllegalArgumentException("package " + packageName + " not found");
823         }
824 
825         long identity = Binder.clearCallingIdentity();
826         intent.setClassName("com.android.systemui",
827                 "com.android.systemui.usb.UsbPermissionActivity");
828         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
829         intent.putExtra(Intent.EXTRA_INTENT, pi);
830         intent.putExtra("package", packageName);
831         intent.putExtra("uid", uid);
832         try {
833             mContext.startActivity(intent);
834         } catch (ActivityNotFoundException e) {
835             Slog.e(TAG, "unable to start UsbPermissionActivity");
836         } finally {
837             Binder.restoreCallingIdentity(identity);
838         }
839     }
840 
requestPermission(UsbDevice device, String packageName, PendingIntent pi)841     public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) {
842       Intent intent = new Intent();
843 
844         // respond immediately if permission has already been granted
845       if (hasPermission(device)) {
846             intent.putExtra(UsbManager.EXTRA_DEVICE, device);
847             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
848             try {
849                 pi.send(mContext, 0, intent);
850             } catch (PendingIntent.CanceledException e) {
851                 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
852             }
853             return;
854         }
855 
856         // start UsbPermissionActivity so user can choose an activity
857         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
858         requestPermissionDialog(intent, packageName, pi);
859     }
860 
requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi)861     public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) {
862       Intent intent = new Intent();
863 
864         // respond immediately if permission has already been granted
865         if (hasPermission(accessory)) {
866             intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
867             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
868            try {
869                 pi.send(mContext, 0, intent);
870             } catch (PendingIntent.CanceledException e) {
871                 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
872             }
873             return;
874         }
875 
876         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
877         requestPermissionDialog(intent, packageName, pi);
878     }
879 
setDevicePackage(UsbDevice device, String packageName)880     public void setDevicePackage(UsbDevice device, String packageName) {
881         DeviceFilter filter = new DeviceFilter(device);
882         boolean changed = false;
883         synchronized (mLock) {
884             if (packageName == null) {
885                 changed = (mDevicePreferenceMap.remove(filter) != null);
886             } else {
887                 changed = !packageName.equals(mDevicePreferenceMap.get(filter));
888                 if (changed) {
889                     mDevicePreferenceMap.put(filter, packageName);
890                 }
891             }
892             if (changed) {
893                 writeSettingsLocked();
894             }
895         }
896     }
897 
setAccessoryPackage(UsbAccessory accessory, String packageName)898     public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
899         AccessoryFilter filter = new AccessoryFilter(accessory);
900         boolean changed = false;
901         synchronized (mLock) {
902             if (packageName == null) {
903                 changed = (mAccessoryPreferenceMap.remove(filter) != null);
904             } else {
905                 changed = !packageName.equals(mAccessoryPreferenceMap.get(filter));
906                 if (changed) {
907                     mAccessoryPreferenceMap.put(filter, packageName);
908                 }
909             }
910             if (changed) {
911                 writeSettingsLocked();
912             }
913         }
914     }
915 
grantDevicePermission(UsbDevice device, int uid)916     public void grantDevicePermission(UsbDevice device, int uid) {
917         synchronized (mLock) {
918             String deviceName = device.getDeviceName();
919             SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
920             if (uidList == null) {
921                 uidList = new SparseBooleanArray(1);
922                 mDevicePermissionMap.put(deviceName, uidList);
923             }
924             uidList.put(uid, true);
925         }
926     }
927 
grantAccessoryPermission(UsbAccessory accessory, int uid)928     public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
929         synchronized (mLock) {
930             SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
931             if (uidList == null) {
932                 uidList = new SparseBooleanArray(1);
933                 mAccessoryPermissionMap.put(accessory, uidList);
934             }
935             uidList.put(uid, true);
936         }
937     }
938 
hasDefaults(String packageName)939     public boolean hasDefaults(String packageName) {
940         synchronized (mLock) {
941             if (mDevicePreferenceMap.values().contains(packageName)) return true;
942             if (mAccessoryPreferenceMap.values().contains(packageName)) return true;
943             return false;
944         }
945     }
946 
clearDefaults(String packageName)947     public void clearDefaults(String packageName) {
948         synchronized (mLock) {
949             if (clearPackageDefaultsLocked(packageName)) {
950                 writeSettingsLocked();
951             }
952         }
953     }
954 
clearPackageDefaultsLocked(String packageName)955     private boolean clearPackageDefaultsLocked(String packageName) {
956         boolean cleared = false;
957         synchronized (mLock) {
958             if (mDevicePreferenceMap.containsValue(packageName)) {
959                 // make a copy of the key set to avoid ConcurrentModificationException
960                 Object[] keys = mDevicePreferenceMap.keySet().toArray();
961                 for (int i = 0; i < keys.length; i++) {
962                     Object key = keys[i];
963                     if (packageName.equals(mDevicePreferenceMap.get(key))) {
964                         mDevicePreferenceMap.remove(key);
965                         cleared = true;
966                     }
967                 }
968             }
969             if (mAccessoryPreferenceMap.containsValue(packageName)) {
970                 // make a copy of the key set to avoid ConcurrentModificationException
971                 Object[] keys = mAccessoryPreferenceMap.keySet().toArray();
972                 for (int i = 0; i < keys.length; i++) {
973                     Object key = keys[i];
974                     if (packageName.equals(mAccessoryPreferenceMap.get(key))) {
975                         mAccessoryPreferenceMap.remove(key);
976                         cleared = true;
977                     }
978                 }
979             }
980             return cleared;
981         }
982     }
983 
dump(FileDescriptor fd, PrintWriter pw)984     public void dump(FileDescriptor fd, PrintWriter pw) {
985         synchronized (mLock) {
986             pw.println("  Device permissions:");
987             for (String deviceName : mDevicePermissionMap.keySet()) {
988                 pw.print("    " + deviceName + ": ");
989                 SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
990                 int count = uidList.size();
991                 for (int i = 0; i < count; i++) {
992                     pw.print(Integer.toString(uidList.keyAt(i)) + " ");
993                 }
994                 pw.println("");
995             }
996             pw.println("  Accessory permissions:");
997             for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
998                 pw.print("    " + accessory + ": ");
999                 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
1000                 int count = uidList.size();
1001                 for (int i = 0; i < count; i++) {
1002                     pw.print(Integer.toString(uidList.keyAt(i)) + " ");
1003                 }
1004                 pw.println("");
1005             }
1006             pw.println("  Device preferences:");
1007             for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
1008                 pw.println("    " + filter + ": " + mDevicePreferenceMap.get(filter));
1009             }
1010             pw.println("  Accessory preferences:");
1011             for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
1012                 pw.println("    " + filter + ": " + mAccessoryPreferenceMap.get(filter));
1013             }
1014         }
1015     }
1016 }
1017