• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 package android.car.usb.handler;
17 
18 import android.car.IUsbAoapSupportCheckService;
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.ServiceConnection;
23 import android.content.pm.ActivityInfo;
24 import android.content.pm.PackageManager;
25 import android.content.pm.PackageManager.NameNotFoundException;
26 import android.content.pm.ResolveInfo;
27 import android.content.res.XmlResourceParser;
28 import android.hardware.usb.UsbDevice;
29 import android.hardware.usb.UsbDeviceConnection;
30 import android.hardware.usb.UsbInterface;
31 import android.hardware.usb.UsbManager;
32 import android.os.Handler;
33 import android.os.HandlerThread;
34 import android.os.IBinder;
35 import android.os.Looper;
36 import android.os.Message;
37 import android.os.RemoteException;
38 import android.util.Log;
39 import android.util.Pair;
40 import com.android.internal.util.XmlUtils;
41 import java.io.IOException;
42 import java.util.ArrayList;
43 import java.util.LinkedList;
44 import java.util.List;
45 import java.util.Queue;
46 import org.xmlpull.v1.XmlPullParser;
47 
48 /** Resolves supported handlers for USB device. */
49 public final class UsbDeviceHandlerResolver {
50     private static final String TAG = UsbDeviceHandlerResolver.class.getSimpleName();
51     private static final boolean LOCAL_LOGD = true;
52 
53     /**
54      * Callbacks for device resolver.
55      */
56     public interface UsbDeviceHandlerResolverCallback {
57         /** Handlers are resolved */
onHandlersResolveCompleted( UsbDevice device, List<UsbDeviceSettings> availableSettings)58         void onHandlersResolveCompleted(
59                 UsbDevice device, List<UsbDeviceSettings> availableSettings);
60         /** Device was dispatched */
onDeviceDispatched()61         void onDeviceDispatched();
62     }
63 
64     private final UsbManager mUsbManager;
65     private final PackageManager mPackageManager;
66     private final UsbDeviceHandlerResolverCallback mDeviceCallback;
67     private final Context mContext;
68     private final HandlerThread mHandlerThread;
69     private final UsbDeviceResolverHandler mHandler;
70 
71     private class DeviceContext {
72         public final UsbDevice usbDevice;
73         public final UsbDeviceConnection connection;
74         public final UsbDeviceSettings settings;
75         public final List<UsbDeviceSettings> activeDeviceSettings;
76         public final Queue<Pair<ResolveInfo, DeviceFilter>> mActiveDeviceOptions =
77                 new LinkedList<>();
78 
79         private volatile IUsbAoapSupportCheckService mUsbAoapSupportCheckService;
80         private final ServiceConnection mServiceConnection = new ServiceConnection() {
81             @Override
82             public void onServiceConnected(ComponentName className, IBinder service) {
83                 Log.i(TAG, "onServiceConnected: " + className);
84                 mUsbAoapSupportCheckService = IUsbAoapSupportCheckService.Stub.asInterface(service);
85                 mHandler.requestOnServiceConnectionStateChanged(DeviceContext.this);
86             }
87 
88             @Override
89             public void onServiceDisconnected(ComponentName className) {
90                 Log.i(TAG, "onServiceDisconnected: " + className);
91                 mUsbAoapSupportCheckService = null;
92                 mHandler.requestOnServiceConnectionStateChanged(DeviceContext.this);
93             }
94         };
95 
DeviceContext(UsbDevice usbDevice, UsbDeviceSettings settings, List<UsbDeviceSettings> activeDeviceSettings)96         public DeviceContext(UsbDevice usbDevice, UsbDeviceSettings settings,
97                 List<UsbDeviceSettings> activeDeviceSettings) {
98             this.usbDevice = usbDevice;
99             this.settings = settings;
100             this.activeDeviceSettings = activeDeviceSettings;
101             connection = UsbUtil.openConnection(mUsbManager, usbDevice);
102         }
103     }
104 
105     // This class is used to describe a USB device.
106     // When used in HashMaps all values must be specified,
107     // but wildcards can be used for any of the fields in
108     // the package meta-data.
109     private static class DeviceFilter {
110         // USB Vendor ID (or -1 for unspecified)
111         public final int mVendorId;
112         // USB Product ID (or -1 for unspecified)
113         public final int mProductId;
114         // USB device or interface class (or -1 for unspecified)
115         public final int mClass;
116         // USB device subclass (or -1 for unspecified)
117         public final int mSubclass;
118         // USB device protocol (or -1 for unspecified)
119         public final int mProtocol;
120         // USB device manufacturer name string (or null for unspecified)
121         public final String mManufacturerName;
122         // USB device product name string (or null for unspecified)
123         public final String mProductName;
124         // USB device serial number string (or null for unspecified)
125         public final String mSerialNumber;
126 
127         // USB device in AOAP mode manufacturer
128         public final String mAoapManufacturer;
129         // USB device in AOAP mode model
130         public final String mAoapModel;
131         // USB device in AOAP mode description string
132         public final String mAoapDescription;
133         // USB device in AOAP mode version
134         public final String mAoapVersion;
135         // USB device in AOAP mode URI
136         public final String mAoapUri;
137         // USB device in AOAP mode serial
138         public final String mAoapSerial;
139         // USB device in AOAP mode verification service
140         public final String mAoapService;
141 
DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol, String manufacturer, String product, String serialnum, String aoapManufacturer, String aoapModel, String aoapDescription, String aoapVersion, String aoapUri, String aoapSerial, String aoapService)142         DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
143                             String manufacturer, String product, String serialnum,
144                             String aoapManufacturer, String aoapModel, String aoapDescription,
145                             String aoapVersion, String aoapUri, String aoapSerial,
146                             String aoapService) {
147             mVendorId = vid;
148             mProductId = pid;
149             mClass = clasz;
150             mSubclass = subclass;
151             mProtocol = protocol;
152             mManufacturerName = manufacturer;
153             mProductName = product;
154             mSerialNumber = serialnum;
155 
156             mAoapManufacturer = aoapManufacturer;
157             mAoapModel = aoapModel;
158             mAoapDescription = aoapDescription;
159             mAoapVersion = aoapVersion;
160             mAoapUri = aoapUri;
161             mAoapSerial = aoapSerial;
162             mAoapService = aoapService;
163         }
164 
DeviceFilter(UsbDevice device)165         DeviceFilter(UsbDevice device) {
166             mVendorId = device.getVendorId();
167             mProductId = device.getProductId();
168             mClass = device.getDeviceClass();
169             mSubclass = device.getDeviceSubclass();
170             mProtocol = device.getDeviceProtocol();
171             mManufacturerName = device.getManufacturerName();
172             mProductName = device.getProductName();
173             mSerialNumber = device.getSerialNumber();
174             mAoapManufacturer = null;
175             mAoapModel = null;
176             mAoapDescription = null;
177             mAoapVersion = null;
178             mAoapUri = null;
179             mAoapSerial = null;
180             mAoapService = null;
181         }
182 
read(XmlPullParser parser, boolean aoapData)183         public static DeviceFilter read(XmlPullParser parser, boolean aoapData) {
184             int vendorId = -1;
185             int productId = -1;
186             int deviceClass = -1;
187             int deviceSubclass = -1;
188             int deviceProtocol = -1;
189             String manufacturerName = null;
190             String productName = null;
191             String serialNumber = null;
192 
193             String aoapManufacturer = null;
194             String aoapModel = null;
195             String aoapDescription = null;
196             String aoapVersion = null;
197             String aoapUri = null;
198             String aoapSerial = null;
199             String aoapService = null;
200 
201             int count = parser.getAttributeCount();
202             for (int i = 0; i < count; i++) {
203                 String name = parser.getAttributeName(i);
204                 String value = parser.getAttributeValue(i);
205                 // Attribute values are ints or strings
206                 if (!aoapData && "manufacturer-name".equals(name)) {
207                     manufacturerName = value;
208                 } else if (!aoapData && "product-name".equals(name)) {
209                     productName = value;
210                 } else if (!aoapData && "serial-number".equals(name)) {
211                     serialNumber = value;
212                 } else if (aoapData && "manufacturer".equals(name)) {
213                     aoapManufacturer = value;
214                 } else if (aoapData && "model".equals(name)) {
215                     aoapModel = value;
216                 } else if (aoapData && "description".equals(name)) {
217                     aoapDescription = value;
218                 } else if (aoapData && "version".equals(name)) {
219                     aoapVersion = value;
220                 } else if (aoapData && "uri".equals(name)) {
221                     aoapUri = value;
222                 } else if (aoapData && "serial".equals(name)) {
223                     aoapSerial = value;
224                 } else if (aoapData && "service".equals(name)) {
225                     aoapService = value;
226                 } else if (!aoapData) {
227                     int intValue = -1;
228                     int radix = 10;
229                     if (value != null && value.length() > 2 && value.charAt(0) == '0'
230                             && (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
231                         // allow hex values starting with 0x or 0X
232                         radix = 16;
233                         value = value.substring(2);
234                     }
235                     try {
236                         intValue = Integer.parseInt(value, radix);
237                     } catch (NumberFormatException e) {
238                         Log.e(TAG, "invalid number for field " + name, e);
239                         continue;
240                     }
241                     if ("vendor-id".equals(name)) {
242                         vendorId = intValue;
243                     } else if ("product-id".equals(name)) {
244                         productId = intValue;
245                     } else if ("class".equals(name)) {
246                         deviceClass = intValue;
247                     } else if ("subclass".equals(name)) {
248                         deviceSubclass = intValue;
249                     } else if ("protocol".equals(name)) {
250                         deviceProtocol = intValue;
251                     }
252                 }
253             }
254             return new DeviceFilter(vendorId, productId,
255                                     deviceClass, deviceSubclass, deviceProtocol,
256                                     manufacturerName, productName, serialNumber, aoapManufacturer,
257                                     aoapModel, aoapDescription, aoapVersion, aoapUri, aoapSerial,
258                                     aoapService);
259         }
260 
matches(int clasz, int subclass, int protocol)261         private boolean matches(int clasz, int subclass, int protocol) {
262             return ((mClass == -1 || clasz == mClass)
263                     && (mSubclass == -1 || subclass == mSubclass)
264                     && (mProtocol == -1 || protocol == mProtocol));
265         }
266 
matches(UsbDevice device)267         public boolean matches(UsbDevice device) {
268             if (mVendorId != -1 && device.getVendorId() != mVendorId) {
269                 return false;
270             }
271             if (mProductId != -1 && device.getProductId() != mProductId) {
272                 return false;
273             }
274             if (mManufacturerName != null && device.getManufacturerName() == null) {
275                 return false;
276             }
277             if (mProductName != null && device.getProductName() == null) {
278                 return false;
279             }
280             if (mSerialNumber != null && device.getSerialNumber() == null) {
281                 return false;
282             }
283             if (mManufacturerName != null && device.getManufacturerName() != null
284                     && !mManufacturerName.equals(device.getManufacturerName())) {
285                 return false;
286             }
287             if (mProductName != null && device.getProductName() != null
288                     && !mProductName.equals(device.getProductName())) {
289                 return false;
290             }
291             if (mSerialNumber != null && device.getSerialNumber() != null
292                     && !mSerialNumber.equals(device.getSerialNumber())) {
293                 return false;
294             }
295 
296             // check device class/subclass/protocol
297             if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
298                         device.getDeviceProtocol())) {
299                 return true;
300             }
301 
302             // if device doesn't match, check the interfaces
303             int count = device.getInterfaceCount();
304             for (int i = 0; i < count; i++) {
305                 UsbInterface intf = device.getInterface(i);
306                 if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
307                             intf.getInterfaceProtocol())) {
308                     return true;
309                 }
310             }
311 
312             return false;
313         }
314 
315         @Override
equals(Object obj)316         public boolean equals(Object obj) {
317             // can't compare if we have wildcard strings
318             if (mVendorId == -1 || mProductId == -1
319                     || mClass == -1 || mSubclass == -1 || mProtocol == -1) {
320                 return false;
321             }
322             if (obj instanceof DeviceFilter) {
323                 DeviceFilter filter = (DeviceFilter) obj;
324 
325                 if (filter.mVendorId != mVendorId
326                         || filter.mProductId != mProductId
327                         || filter.mClass != mClass
328                         || filter.mSubclass != mSubclass
329                         || filter.mProtocol != mProtocol) {
330                     return false;
331                 }
332                 if ((filter.mManufacturerName != null && mManufacturerName == null)
333                         || (filter.mManufacturerName == null && mManufacturerName != null)
334                         || (filter.mProductName != null && mProductName == null)
335                         || (filter.mProductName == null && mProductName != null)
336                         || (filter.mSerialNumber != null && mSerialNumber == null)
337                         || (filter.mSerialNumber == null && mSerialNumber != null)) {
338                     return false;
339                 }
340                 if  ((filter.mManufacturerName != null && mManufacturerName != null
341                           && !mManufacturerName.equals(filter.mManufacturerName))
342                           || (filter.mProductName != null && mProductName != null
343                           && !mProductName.equals(filter.mProductName))
344                           || (filter.mSerialNumber != null && mSerialNumber != null
345                           && !mSerialNumber.equals(filter.mSerialNumber))) {
346                     return false;
347                 }
348                 return true;
349             }
350             if (obj instanceof UsbDevice) {
351                 UsbDevice device = (UsbDevice) obj;
352                 if (device.getVendorId() != mVendorId
353                         || device.getProductId() != mProductId
354                         || device.getDeviceClass() != mClass
355                         || device.getDeviceSubclass() != mSubclass
356                         || device.getDeviceProtocol() != mProtocol) {
357                     return false;
358                 }
359                 if ((mManufacturerName != null && device.getManufacturerName() == null)
360                         || (mManufacturerName == null && device.getManufacturerName() != null)
361                         || (mProductName != null && device.getProductName() == null)
362                         || (mProductName == null && device.getProductName() != null)
363                         || (mSerialNumber != null && device.getSerialNumber() == null)
364                         || (mSerialNumber == null && device.getSerialNumber() != null)) {
365                     return false;
366                 }
367                 if ((device.getManufacturerName() != null
368                         && !mManufacturerName.equals(device.getManufacturerName()))
369                         || (device.getProductName() != null
370                         && !mProductName.equals(device.getProductName()))
371                         || (device.getSerialNumber() != null
372                         && !mSerialNumber.equals(device.getSerialNumber()))) {
373                     return false;
374                 }
375                 return true;
376             }
377             return false;
378         }
379 
380         @Override
hashCode()381         public int hashCode() {
382             return (((mVendorId << 16) | mProductId)
383                     ^ ((mClass << 16) | (mSubclass << 8) | mProtocol));
384         }
385 
386         @Override
toString()387         public String toString() {
388             return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId
389                     + ",mClass=" + mClass + ",mSubclass=" + mSubclass
390                     + ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName
391                     + ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber + "]";
392         }
393     }
394 
UsbDeviceHandlerResolver(UsbManager manager, Context context, UsbDeviceHandlerResolverCallback deviceListener)395     public UsbDeviceHandlerResolver(UsbManager manager, Context context,
396             UsbDeviceHandlerResolverCallback deviceListener) {
397         mUsbManager = manager;
398         mContext = context;
399         mDeviceCallback = deviceListener;
400         mHandlerThread = new HandlerThread(TAG);
401         mHandlerThread.start();
402         mHandler = new UsbDeviceResolverHandler(mHandlerThread.getLooper());
403         mPackageManager = context.getPackageManager();
404     }
405 
406     /**
407      * Releases current object.
408      */
release()409     public void release() {
410         if (mHandlerThread != null) {
411             mHandlerThread.quitSafely();
412         }
413     }
414 
415     /**
416      * Resolves handlers for USB device.
417      */
resolve(UsbDevice device)418     public void resolve(UsbDevice device) {
419         mHandler.requestResolveHandlers(device);
420     }
421 
422     /**
423      * Dispatches device to component.
424      */
dispatch(UsbDevice device, ComponentName component, boolean inAoap)425     public boolean dispatch(UsbDevice device, ComponentName component, boolean inAoap) {
426         if (LOCAL_LOGD) {
427             Log.d(TAG, "dispatch: " + device + " component: " + component + " inAoap: " + inAoap);
428         }
429 
430         ActivityInfo activityInfo;
431         try {
432             activityInfo = mPackageManager.getActivityInfo(component, PackageManager.GET_META_DATA);
433         } catch (NameNotFoundException e) {
434             Log.e(TAG, "Activity not found: " + component);
435             return false;
436         }
437 
438         Intent intent = createDeviceAttachedIntent(device);
439         if (inAoap) {
440             if (AoapInterface.isDeviceInAoapMode(device)) {
441                 mDeviceCallback.onDeviceDispatched();
442             } else {
443                 DeviceFilter filter =
444                         packageMatches(activityInfo, intent.getAction(), device, true);
445                 if (filter != null) {
446                     requestAoapSwitch(device, filter);
447                     return true;
448                 }
449             }
450         }
451 
452         intent.setComponent(component);
453         mUsbManager.grantPermission(device, activityInfo.applicationInfo.uid);
454 
455         mContext.startActivity(intent);
456         mHandler.requestCompleteDeviceDispatch();
457         return true;
458     }
459 
createDeviceAttachedIntent(UsbDevice device)460     private static Intent createDeviceAttachedIntent(UsbDevice device) {
461         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
462         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
463         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
464         return intent;
465     }
466 
doHandleResolveHandlers(UsbDevice device)467     private void doHandleResolveHandlers(UsbDevice device) {
468         if (LOCAL_LOGD) {
469             Log.d(TAG, "doHandleResolveHandlers: " + device);
470         }
471 
472         Intent intent = createDeviceAttachedIntent(device);
473         List<Pair<ResolveInfo, DeviceFilter>> matches = getDeviceMatches(device, intent, false);
474         if (LOCAL_LOGD) {
475             Log.d(TAG, "matches size: " + matches.size());
476         }
477         List<UsbDeviceSettings> settings = new ArrayList<>(matches.size());
478         for (Pair<ResolveInfo, DeviceFilter> info : matches) {
479             UsbDeviceSettings setting = UsbDeviceSettings.constructSettings(device);
480             setting.setHandler(
481                     new ComponentName(
482                             info.first.activityInfo.packageName, info.first.activityInfo.name));
483             settings.add(setting);
484         }
485         DeviceContext deviceContext =
486                 new DeviceContext(device, UsbDeviceSettings.constructSettings(device), settings);
487         if (AoapInterface.isSupported(deviceContext.connection)) {
488             deviceContext.mActiveDeviceOptions.addAll(getDeviceMatches(device, intent, true));
489             queryNextAoapHandler(deviceContext);
490         } else {
491             deviceProbingComplete(deviceContext);
492         }
493     }
494 
queryNextAoapHandler(DeviceContext context)495     private void queryNextAoapHandler(DeviceContext context) {
496         Pair<ResolveInfo, DeviceFilter> option = context.mActiveDeviceOptions.peek();
497         if (option == null) {
498             Log.w(TAG, "No more options left.");
499             deviceProbingComplete(context);
500             return;
501         }
502         Intent serviceIntent = new Intent();
503         serviceIntent.setComponent(ComponentName.unflattenFromString(option.second.mAoapService));
504         boolean bound = mContext.bindService(serviceIntent, context.mServiceConnection,
505                 Context.BIND_AUTO_CREATE);
506         if (bound) {
507             mHandler.requestServiceConnectionTimeout();
508         } else {
509             if (LOCAL_LOGD) {
510                 Log.d(TAG, "Failed to bind to the service");
511             }
512             context.mActiveDeviceOptions.poll();
513             queryNextAoapHandler(context);
514         }
515     }
516 
requestAoapSwitch(UsbDevice device, DeviceFilter filter)517     private void requestAoapSwitch(UsbDevice device, DeviceFilter filter) {
518         UsbDeviceConnection connection = UsbUtil.openConnection(mUsbManager, device);
519         try {
520             UsbUtil.sendAoapAccessoryStart(
521                     connection,
522                     filter.mAoapManufacturer,
523                     filter.mAoapModel,
524                     filter.mAoapDescription,
525                     filter.mAoapVersion,
526                     filter.mAoapUri,
527                     filter.mAoapSerial);
528         } catch (IOException e) {
529             Log.w(TAG, "Failed to switch device into AOAP mode", e);
530         }
531         connection.close();
532     }
533 
deviceProbingComplete(DeviceContext context)534     private void deviceProbingComplete(DeviceContext context) {
535         if (LOCAL_LOGD) {
536             Log.d(TAG, "deviceProbingComplete");
537         }
538         mDeviceCallback.onHandlersResolveCompleted(context.usbDevice, context.activeDeviceSettings);
539     }
540 
doHandleServiceConnectionStateChanged(DeviceContext context)541     private void doHandleServiceConnectionStateChanged(DeviceContext context) {
542         if (LOCAL_LOGD) {
543             Log.d(TAG, "doHandleServiceConnectionStateChanged: "
544                     + context.mUsbAoapSupportCheckService);
545         }
546         if (context.mUsbAoapSupportCheckService != null) {
547             boolean deviceSupported = false;
548             try {
549                 deviceSupported =
550                         context.mUsbAoapSupportCheckService.isDeviceSupported(context.usbDevice);
551             } catch (RemoteException e) {
552                 Log.e(TAG, "Call to remote service failed", e);
553             }
554             if (deviceSupported) {
555                 Pair<ResolveInfo, DeviceFilter> option = context.mActiveDeviceOptions.peek();
556 
557                 UsbDeviceSettings setting = UsbDeviceSettings.constructSettings(context.settings);
558                 setting.setHandler(
559                         new ComponentName(
560                             option.first.activityInfo.packageName, option.first.activityInfo.name));
561                 setting.setAoap(true);
562                 context.activeDeviceSettings.add(setting);
563             }
564             mContext.unbindService(context.mServiceConnection);
565         }
566         context.mActiveDeviceOptions.poll();
567         queryNextAoapHandler(context);
568     }
569 
getDeviceMatches( UsbDevice device, Intent intent, boolean forAoap)570     private List<Pair<ResolveInfo, DeviceFilter>> getDeviceMatches(
571             UsbDevice device, Intent intent, boolean forAoap) {
572         List<Pair<ResolveInfo, DeviceFilter>> matches = new ArrayList<>();
573         List<ResolveInfo> resolveInfos =
574                 mPackageManager.queryIntentActivities(intent, PackageManager.GET_META_DATA);
575         for (ResolveInfo resolveInfo : resolveInfos) {
576             DeviceFilter filter = packageMatches(resolveInfo.activityInfo,
577                     intent.getAction(), device, forAoap);
578             if (filter != null) {
579                 matches.add(Pair.create(resolveInfo, filter));
580             }
581         }
582         return matches;
583     }
584 
packageMatches(ActivityInfo ai, String metaDataName, UsbDevice device, boolean forAoap)585     private DeviceFilter packageMatches(ActivityInfo ai, String metaDataName, UsbDevice device,
586             boolean forAoap) {
587         if (LOCAL_LOGD) {
588             Log.d(TAG, "packageMatches ai: " + ai + "metaDataName: " + metaDataName + " forAoap: "
589                     + forAoap);
590         }
591         String filterTagName = forAoap ? "usb-aoap-accessory" : "usb-device";
592         XmlResourceParser parser = null;
593         try {
594             parser = ai.loadXmlMetaData(mPackageManager, metaDataName);
595             if (parser == null) {
596                 Log.w(TAG, "no meta-data for " + ai);
597                 return null;
598             }
599 
600             XmlUtils.nextElement(parser);
601             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
602                 String tagName = parser.getName();
603                 if (device != null && filterTagName.equals(tagName)) {
604                     DeviceFilter filter = DeviceFilter.read(parser, forAoap);
605                     if (forAoap || filter.matches(device)) {
606                         return filter;
607                     }
608                 }
609                 XmlUtils.nextElement(parser);
610             }
611         } catch (Exception e) {
612             Log.w(TAG, "Unable to load component info " + ai.toString(), e);
613         } finally {
614             if (parser != null) parser.close();
615         }
616         return null;
617     }
618 
619     private class UsbDeviceResolverHandler extends Handler {
620         private static final int MSG_RESOLVE_HANDLERS = 0;
621         private static final int MSG_SERVICE_CONNECTION_STATE_CHANGE = 1;
622         private static final int MSG_SERVICE_CONNECTION_TIMEOUT = 2;
623         private static final int MSG_COMPLETE_DISPATCH = 3;
624 
625         private static final long CONNECT_TIMEOUT_MS = 5000;
626 
UsbDeviceResolverHandler(Looper looper)627         private UsbDeviceResolverHandler(Looper looper) {
628             super(looper);
629         }
630 
requestResolveHandlers(UsbDevice device)631         public void requestResolveHandlers(UsbDevice device) {
632             Message msg = obtainMessage(MSG_RESOLVE_HANDLERS, device);
633             sendMessage(msg);
634         }
635 
requestOnServiceConnectionStateChanged(DeviceContext deviceContext)636         public void requestOnServiceConnectionStateChanged(DeviceContext deviceContext) {
637             sendMessage(obtainMessage(MSG_SERVICE_CONNECTION_STATE_CHANGE, deviceContext));
638         }
639 
requestServiceConnectionTimeout()640         public void requestServiceConnectionTimeout() {
641             sendEmptyMessageDelayed(MSG_SERVICE_CONNECTION_TIMEOUT, CONNECT_TIMEOUT_MS);
642         }
643 
requestCompleteDeviceDispatch()644         public void requestCompleteDeviceDispatch() {
645             sendEmptyMessage(MSG_COMPLETE_DISPATCH);
646         }
647 
648         @Override
handleMessage(Message msg)649         public void handleMessage(Message msg) {
650             switch (msg.what) {
651                 case MSG_RESOLVE_HANDLERS:
652                     doHandleResolveHandlers((UsbDevice) msg.obj);
653                     break;
654                 case MSG_SERVICE_CONNECTION_STATE_CHANGE:
655                     removeMessages(MSG_SERVICE_CONNECTION_TIMEOUT);
656                     doHandleServiceConnectionStateChanged((DeviceContext) msg.obj);
657                     break;
658                 case MSG_SERVICE_CONNECTION_TIMEOUT:
659                     Log.i(TAG, "Service connection timeout");
660                     doHandleServiceConnectionStateChanged(null);
661                     break;
662                 case MSG_COMPLETE_DISPATCH:
663                     mDeviceCallback.onDeviceDispatched();
664                     break;
665                 default:
666                     Log.w(TAG, "Unsupported message: " + msg);
667             }
668         }
669     }
670 }
671