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