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