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