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