OSDN Git Service

Allow system server to bind to USB devices.
[android-x86/frameworks-base.git] / services / java / com / android / server / usb / UsbSettingsManager.java
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.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;
44
45 import com.android.internal.content.PackageMonitor;
46 import com.android.internal.util.FastXmlSerializer;
47 import com.android.internal.util.XmlUtils;
48
49 import org.xmlpull.v1.XmlPullParser;
50 import org.xmlpull.v1.XmlPullParserException;
51 import org.xmlpull.v1.XmlSerializer;
52
53 import java.io.File;
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;
63
64 import libcore.io.IoUtils;
65
66 class UsbSettingsManager {
67     private static final String TAG = "UsbSettingsManager";
68     private static final boolean DEBUG = false;
69
70     /** Legacy settings file, before multi-user */
71     private static final File sSingleUserSettingsFile = new File(
72             "/data/system/usb_device_manager.xml");
73
74     private final UserHandle mUser;
75     private final AtomicFile mSettingsFile;
76
77     private final Context mContext;
78     private final Context mUserContext;
79     private final PackageManager mPackageManager;
80
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>();
93
94     private final Object mLock = new Object();
95
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;
111
112         public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol) {
113             mVendorId = vid;
114             mProductId = pid;
115             mClass = clasz;
116             mSubclass = subclass;
117             mProtocol = protocol;
118         }
119
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();
126         }
127
128         public static DeviceFilter read(XmlPullParser parser)
129                 throws XmlPullParserException, IOException {
130             int vendorId = -1;
131             int productId = -1;
132             int deviceClass = -1;
133             int deviceSubclass = -1;
134             int deviceProtocol = -1;
135
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));
141
142                 if ("vendor-id".equals(name)) {
143                     vendorId = value;
144                 } else if ("product-id".equals(name)) {
145                     productId = value;
146                 } else if ("class".equals(name)) {
147                     deviceClass = value;
148                 } else if ("subclass".equals(name)) {
149                     deviceSubclass = value;
150                 } else if ("protocol".equals(name)) {
151                     deviceProtocol = value;
152                 }
153             }
154             return new DeviceFilter(vendorId, productId,
155                     deviceClass, deviceSubclass, deviceProtocol);
156         }
157
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));
162             }
163             if (mProductId != -1) {
164                 serializer.attribute(null, "product-id", Integer.toString(mProductId));
165             }
166             if (mClass != -1) {
167                 serializer.attribute(null, "class", Integer.toString(mClass));
168             }
169             if (mSubclass != -1) {
170                 serializer.attribute(null, "subclass", Integer.toString(mSubclass));
171             }
172             if (mProtocol != -1) {
173                 serializer.attribute(null, "protocol", Integer.toString(mProtocol));
174             }
175             serializer.endTag(null, "usb-device");
176         }
177
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));
182         }
183
184         public boolean matches(UsbDevice device) {
185             if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
186             if (mProductId != -1 && device.getProductId() != mProductId) return false;
187
188             // check device class/subclass/protocol
189             if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
190                     device.getDeviceProtocol())) return true;
191
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;
198             }
199
200             return false;
201         }
202
203         public boolean matches(DeviceFilter f) {
204             if (mVendorId != -1 && f.mVendorId != mVendorId) return false;
205             if (mProductId != -1 && f.mProductId != mProductId) return false;
206
207             // check device class/subclass/protocol
208             return matches(f.mClass, f.mSubclass, f.mProtocol);
209         }
210
211         @Override
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) {
216                 return false;
217             }
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);
225             }
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);
233             }
234             return false;
235         }
236
237         @Override
238         public int hashCode() {
239             return (((mVendorId << 16) | mProductId) ^
240                     ((mClass << 16) | (mSubclass << 8) | mProtocol));
241         }
242
243         @Override
244         public String toString() {
245             return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
246                     ",mClass=" + mClass + ",mSubclass=" + mSubclass +
247                     ",mProtocol=" + mProtocol + "]";
248         }
249     }
250
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;
262
263         public AccessoryFilter(String manufacturer, String model, String version) {
264             mManufacturer = manufacturer;
265             mModel = model;
266             mVersion = version;
267         }
268
269         public AccessoryFilter(UsbAccessory accessory) {
270             mManufacturer = accessory.getManufacturer();
271             mModel = accessory.getModel();
272             mVersion = accessory.getVersion();
273         }
274
275         public static AccessoryFilter read(XmlPullParser parser)
276                 throws XmlPullParserException, IOException {
277             String manufacturer = null;
278             String model = null;
279             String version = null;
280
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);
285
286                 if ("manufacturer".equals(name)) {
287                     manufacturer = value;
288                 } else if ("model".equals(name)) {
289                     model = value;
290                 } else if ("version".equals(name)) {
291                     version = value;
292                 }
293              }
294              return new AccessoryFilter(manufacturer, model, version);
295         }
296
297         public void write(XmlSerializer serializer)throws IOException {
298             serializer.startTag(null, "usb-accessory");
299             if (mManufacturer != null) {
300                 serializer.attribute(null, "manufacturer", mManufacturer);
301             }
302             if (mModel != null) {
303                 serializer.attribute(null, "model", mModel);
304             }
305             if (mVersion != null) {
306                 serializer.attribute(null, "version", mVersion);
307             }
308             serializer.endTag(null, "usb-accessory");
309         }
310
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;
315             return true;
316         }
317
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;
322             return true;
323         }
324
325         @Override
326         public boolean equals(Object obj) {
327             // can't compare if we have wildcard strings
328             if (mManufacturer == null || mModel == null || mVersion == null) {
329                 return false;
330             }
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));
336             }
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()));
342             }
343             return false;
344         }
345
346         @Override
347         public int hashCode() {
348             return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
349                     (mModel == null ? 0 : mModel.hashCode()) ^
350                     (mVersion == null ? 0 : mVersion.hashCode()));
351         }
352
353         @Override
354         public String toString() {
355             return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
356                                 "\", mModel=\"" + mModel +
357                                 "\", mVersion=\"" + mVersion + "\"]";
358         }
359     }
360
361     private class MyPackageMonitor extends PackageMonitor {
362         @Override
363         public void onPackageAdded(String packageName, int uid) {
364             handlePackageUpdate(packageName);
365         }
366
367         @Override
368         public boolean onPackageChanged(String packageName, int uid, String[] components) {
369             handlePackageUpdate(packageName);
370             return false;
371         }
372
373         @Override
374         public void onPackageRemoved(String packageName, int uid) {
375             clearDefaults(packageName);
376         }
377     }
378
379     MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
380
381     public UsbSettingsManager(Context context, UserHandle user) {
382         if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
383
384         try {
385             mUserContext = context.createPackageContextAsUser("android", 0, user);
386         } catch (NameNotFoundException e) {
387             throw new RuntimeException("Missing android package");
388         }
389
390         mContext = context;
391         mPackageManager = mUserContext.getPackageManager();
392
393         mUser = user;
394         mSettingsFile = new AtomicFile(new File(
395                 Environment.getUserSystemDirectory(user.getIdentifier()),
396                 "usb_device_manager.xml"));
397
398         synchronized (mLock) {
399             if (UserHandle.OWNER.equals(user)) {
400                 upgradeSingleUserLocked();
401             }
402             readSettingsLocked();
403         }
404
405         mPackageMonitor.register(mUserContext, null, true);
406     }
407
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);
415                 break;
416             }
417         }
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);
425         }
426         XmlUtils.nextElement(parser);
427     }
428
429     /**
430      * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
431      * Should only by called by owner.
432      */
433     private void upgradeSingleUserLocked() {
434         if (sSingleUserSettingsFile.exists()) {
435             mDevicePreferenceMap.clear();
436             mAccessoryPreferenceMap.clear();
437
438             FileInputStream fis = null;
439             try {
440                 fis = new FileInputStream(sSingleUserSettingsFile);
441                 XmlPullParser parser = Xml.newPullParser();
442                 parser.setInput(fis, null);
443
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);
449                     } else {
450                         XmlUtils.nextElement(parser);
451                     }
452                 }
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);
457             } finally {
458                 IoUtils.closeQuietly(fis);
459             }
460
461             writeSettingsLocked();
462
463             // Success or failure, we delete single-user file
464             sSingleUserSettingsFile.delete();
465         }
466     }
467
468     private void readSettingsLocked() {
469         if (DEBUG) Slog.v(TAG, "readSettingsLocked()");
470
471         mDevicePreferenceMap.clear();
472         mAccessoryPreferenceMap.clear();
473
474         FileInputStream stream = null;
475         try {
476             stream = mSettingsFile.openRead();
477             XmlPullParser parser = Xml.newPullParser();
478             parser.setInput(stream, null);
479
480             XmlUtils.nextElement(parser);
481             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
482                 String tagName = parser.getName();
483                 if ("preference".equals(tagName)) {
484                     readPreference(parser);
485                 } else {
486                     XmlUtils.nextElement(parser);
487                 }
488             }
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();
494         } finally {
495             IoUtils.closeQuietly(stream);
496         }
497     }
498
499     private void writeSettingsLocked() {
500         if (DEBUG) Slog.v(TAG, "writeSettingsLocked()");
501
502         FileOutputStream fos = null;
503         try {
504             fos = mSettingsFile.startWrite();
505
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");
511
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");
517             }
518
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");
524             }
525
526             serializer.endTag(null, "settings");
527             serializer.endDocument();
528
529             mSettingsFile.finishWrite(fos);
530         } catch (IOException e) {
531             Slog.e(TAG, "Failed to write settings", e);
532             if (fos != null) {
533                 mSettingsFile.failWrite(fos);
534             }
535         }
536     }
537
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;
543
544         XmlResourceParser parser = null;
545         try {
546             parser = ai.loadXmlMetaData(mPackageManager, metaDataName);
547             if (parser == null) {
548                 Slog.w(TAG, "no meta-data for " + info);
549                 return false;
550             }
551
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)) {
558                         return true;
559                     }
560                 }
561                 else if (accessory != null && "usb-accessory".equals(tagName)) {
562                     AccessoryFilter filter = AccessoryFilter.read(parser);
563                     if (filter.matches(accessory)) {
564                         return true;
565                     }
566                 }
567                 XmlUtils.nextElement(parser);
568             }
569         } catch (Exception e) {
570             Slog.w(TAG, "Unable to load component info " + info.toString(), e);
571         } finally {
572             if (parser != null) parser.close();
573         }
574         return false;
575     }
576
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);
586             }
587         }
588         return matches;
589     }
590
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);
601             }
602         }
603         return matches;
604     }
605
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);
610
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));
618         }
619
620         // Send broadcast to running activity with registered intent
621         mUserContext.sendBroadcast(intent);
622
623         // Start activity with registered intent
624         resolveActivity(intent, matches, defaultPackage, device, null);
625     }
626
627     public void deviceDetached(UsbDevice device) {
628         // clear temporary permissions for the device
629         mDevicePermissionMap.remove(device.getDeviceName());
630
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);
635     }
636
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);
641
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));
649         }
650
651         resolveActivity(intent, matches, defaultPackage, null, accessory);
652     }
653
654     public void accessoryDetached(UsbAccessory accessory) {
655         // clear temporary permissions for the accessory
656         mAccessoryPermissionMap.remove(accessory);
657
658         Intent intent = new Intent(
659                 UsbManager.ACTION_USB_ACCESSORY_DETACHED);
660         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
661         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
662     }
663
664     private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches,
665             String defaultPackage, UsbDevice device, UsbAccessory accessory) {
666         int count = matches.size();
667
668         // don't show the resolver activity if there are no choices available
669         if (count == 0) {
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);
681                     try {
682                         mUserContext.startActivityAsUser(dialogIntent, mUser);
683                     } catch (ActivityNotFoundException e) {
684                         Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
685                     }
686                 }
687             }
688
689             // do nothing
690             return;
691         }
692
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) {
701                 defaultRI = rInfo;
702             }
703         }
704
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)) {
711                     defaultRI = rInfo;
712                     break;
713                 }
714             }
715         }
716
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);
723             }
724
725             // start default activity directly
726             try {
727                 intent.setComponent(
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);
733             }
734         } else {
735             Intent resolverIntent = new Intent();
736             resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
737
738             if (count == 1) {
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));
743
744                 if (device != null) {
745                     resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
746                 } else {
747                     resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
748                 }
749             } else {
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);
755             }
756             try {
757                 mUserContext.startActivityAsUser(resolverIntent, mUser);
758             } catch (ActivityNotFoundException e) {
759                 Slog.e(TAG, "unable to start activity " + resolverIntent);
760             }
761         }
762     }
763
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);
769                 changed = true;
770             }
771         }
772         return changed;
773     }
774
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);
780                 changed = true;
781             }
782         }
783         return changed;
784     }
785
786     private boolean handlePackageUpdateLocked(String packageName, ActivityInfo aInfo,
787             String metaDataName) {
788         XmlResourceParser parser = null;
789         boolean changed = false;
790
791         try {
792             parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName);
793             if (parser == null) return false;
794
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)) {
801                         changed = true;
802                     }
803                 }
804                 else if ("usb-accessory".equals(tagName)) {
805                     AccessoryFilter filter = AccessoryFilter.read(parser);
806                     if (clearCompatibleMatchesLocked(packageName, filter)) {
807                         changed = true;
808                     }
809                 }
810                 XmlUtils.nextElement(parser);
811             }
812         } catch (Exception e) {
813             Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e);
814         } finally {
815             if (parser != null) parser.close();
816         }
817         return changed;
818     }
819
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) {
824             PackageInfo info;
825             boolean changed = false;
826
827             try {
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);
832                 return;
833             }
834
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)) {
841                     changed = true;
842                 }
843                 if (handlePackageUpdateLocked(packageName, activities[i],
844                         UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
845                     changed = true;
846                 }
847             }
848
849             if (changed) {
850                 writeSettingsLocked();
851             }
852         }
853     }
854
855     public boolean hasPermission(UsbDevice device) {
856         synchronized (mLock) {
857             int uid = Binder.getCallingUid();
858             if (uid == Process.SYSTEM_UID) {
859                 return true;
860             }
861             SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
862             if (uidList == null) {
863                 return false;
864             }
865             return uidList.get(uid);
866         }
867     }
868
869     public boolean hasPermission(UsbAccessory accessory) {
870         synchronized (mLock) {
871             int uid = Binder.getCallingUid();
872             if (uid == Process.SYSTEM_UID) {
873                 return true;
874             }
875             SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
876             if (uidList == null) {
877                 return false;
878             }
879             return uidList.get(uid);
880         }
881     }
882
883     public void checkPermission(UsbDevice device) {
884         if (!hasPermission(device)) {
885             throw new SecurityException("User has not given permission to device " + device);
886         }
887     }
888
889     public void checkPermission(UsbAccessory accessory) {
890         if (!hasPermission(accessory)) {
891             throw new SecurityException("User has not given permission to accessory " + accessory);
892         }
893     }
894
895     private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) {
896         final int uid = Binder.getCallingUid();
897
898         // compare uid with packageName to foil apps pretending to be someone else
899         try {
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);
904             }
905         } catch (PackageManager.NameNotFoundException e) {
906             throw new IllegalArgumentException("package " + packageName + " not found");
907         }
908
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);
916         try {
917             mUserContext.startActivityAsUser(intent, mUser);
918         } catch (ActivityNotFoundException e) {
919             Slog.e(TAG, "unable to start UsbPermissionActivity");
920         } finally {
921             Binder.restoreCallingIdentity(identity);
922         }
923     }
924
925     public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) {
926       Intent intent = new Intent();
927
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);
932             try {
933                 pi.send(mUserContext, 0, intent);
934             } catch (PendingIntent.CanceledException e) {
935                 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
936             }
937             return;
938         }
939
940         // start UsbPermissionActivity so user can choose an activity
941         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
942         requestPermissionDialog(intent, packageName, pi);
943     }
944
945     public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) {
946         Intent intent = new Intent();
947
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);
952             try {
953                 pi.send(mUserContext, 0, intent);
954             } catch (PendingIntent.CanceledException e) {
955                 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
956             }
957             return;
958         }
959
960         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
961         requestPermissionDialog(intent, packageName, pi);
962     }
963
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);
970             } else {
971                 changed = !packageName.equals(mDevicePreferenceMap.get(filter));
972                 if (changed) {
973                     mDevicePreferenceMap.put(filter, packageName);
974                 }
975             }
976             if (changed) {
977                 writeSettingsLocked();
978             }
979         }
980     }
981
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);
988             } else {
989                 changed = !packageName.equals(mAccessoryPreferenceMap.get(filter));
990                 if (changed) {
991                     mAccessoryPreferenceMap.put(filter, packageName);
992                 }
993             }
994             if (changed) {
995                 writeSettingsLocked();
996             }
997         }
998     }
999
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);
1007             }
1008             uidList.put(uid, true);
1009         }
1010     }
1011
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);
1018             }
1019             uidList.put(uid, true);
1020         }
1021     }
1022
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;
1027             return false;
1028         }
1029     }
1030
1031     public void clearDefaults(String packageName) {
1032         synchronized (mLock) {
1033             if (clearPackageDefaultsLocked(packageName)) {
1034                 writeSettingsLocked();
1035             }
1036         }
1037     }
1038
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);
1049                         cleared = true;
1050                     }
1051                 }
1052             }
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);
1060                         cleared = true;
1061                     }
1062                 }
1063             }
1064             return cleared;
1065         }
1066     }
1067
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)) + " ");
1077                 }
1078                 pw.println("");
1079             }
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)) + " ");
1087                 }
1088                 pw.println("");
1089             }
1090             pw.println("  Device preferences:");
1091             for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
1092                 pw.println("    " + filter + ": " + mDevicePreferenceMap.get(filter));
1093             }
1094             pw.println("  Accessory preferences:");
1095             for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
1096                 pw.println("    " + filter + ": " + mAccessoryPreferenceMap.get(filter));
1097             }
1098         }
1099     }
1100 }