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