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