]> sigrok.org Git - sigrok-androidutils.git/blob - src/org/sigrok/androidutils/UsbSupplicant.java
928cdef04f46bec6f596992dd0d6838dad4f30f2
[sigrok-androidutils.git] / src / org / sigrok / androidutils / UsbSupplicant.java
1 /*
2  * This file is part of the sigrok-androidutils project.
3  *
4  * Copyright (C) 2014 Marcus Comstedt <marcus@mc.pp.se>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package org.sigrok.androidutils;
21
22 import android.app.PendingIntent;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.res.Resources;
28 import android.content.res.XmlResourceParser;
29 import android.hardware.usb.UsbDevice;
30 import android.hardware.usb.UsbInterface;
31 import android.hardware.usb.UsbManager;
32 import android.util.Log;
33 import java.io.IOException;
34 import java.util.HashMap;
35 import java.util.Vector;
36 import org.xmlpull.v1.XmlPullParser;
37 import org.xmlpull.v1.XmlPullParserException;
38
39 public class UsbSupplicant
40 {
41         private static final String ACTION_USB_PERMISSION =
42         "org.sigrok.androidutils.USB_PERMISSION";
43
44         protected final Context context;
45         protected final UsbManager manager;
46         private final BroadcastReceiver permReceiver;
47         private final BroadcastReceiver hotplugReceiver;
48         private final IntentFilter permFilter;
49         private final IntentFilter hotplugFilter;
50         private final Vector<DeviceFilter> deviceFilters;
51
52         // The code in the following inner class is taken from AOSP,
53         // which is licensed under the Apache License, Version 2.0.
54         private static class DeviceFilter {
55                 // USB Vendor ID (or -1 for unspecified)
56                 public final int mVendorId;
57                 // USB Product ID (or -1 for unspecified)
58                 public final int mProductId;
59                 // USB device or interface class (or -1 for unspecified)
60                 public final int mClass;
61                 // USB device subclass (or -1 for unspecified)
62                 public final int mSubclass;
63                 // USB device protocol (or -1 for unspecified)
64                 public final int mProtocol;
65
66                 public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol) {
67                         mVendorId = vid;
68                         mProductId = pid;
69                         mClass = clasz;
70                         mSubclass = subclass;
71                         mProtocol = protocol;
72                 }
73
74                 public DeviceFilter(UsbDevice device) {
75                         mVendorId = device.getVendorId();
76                         mProductId = device.getProductId();
77                         mClass = device.getDeviceClass();
78                         mSubclass = device.getDeviceSubclass();
79                         mProtocol = device.getDeviceProtocol();
80                 }
81
82                 public static DeviceFilter read(XmlPullParser parser)
83                                 throws XmlPullParserException, IOException {
84                         int vendorId = -1;
85                         int productId = -1;
86                         int deviceClass = -1;
87                         int deviceSubclass = -1;
88                         int deviceProtocol = -1;
89
90                         int count = parser.getAttributeCount();
91                         for (int i = 0; i < count; i++) {
92                                 String name = parser.getAttributeName(i);
93                                 // All attribute values are ints
94                                 int value = Integer.parseInt(parser.getAttributeValue(i));
95
96                                 if ("vendor-id".equals(name)) {
97                                         vendorId = value;
98                                 } else if ("product-id".equals(name)) {
99                                         productId = value;
100                                 } else if ("class".equals(name)) {
101                                         deviceClass = value;
102                                 } else if ("subclass".equals(name)) {
103                                         deviceSubclass = value;
104                                 } else if ("protocol".equals(name)) {
105                                         deviceProtocol = value;
106                                 }
107                         }
108                         return new DeviceFilter(vendorId, productId,
109                                         deviceClass, deviceSubclass, deviceProtocol);
110                 }
111
112                 private boolean matches(int clasz, int subclass, int protocol) {
113                         return ((mClass == -1 || clasz == mClass) &&
114                                         (mSubclass == -1 || subclass == mSubclass) &&
115                                         (mProtocol == -1 || protocol == mProtocol));
116                 }
117
118                 public boolean matches(UsbDevice device) {
119                         if (mVendorId != -1 && device.getVendorId() != mVendorId)
120                                 return false;
121                         if (mProductId != -1 && device.getProductId() != mProductId)
122                                 return false;
123
124                         // Check device class/subclass/protocol.
125                         if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
126                                         device.getDeviceProtocol()))
127                                 return true;
128
129                         // If device doesn't match, check the interfaces.
130                         int count = device.getInterfaceCount();
131                         for (int i = 0; i < count; i++) {
132                                 UsbInterface intf = device.getInterface(i);
133                                 if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
134                                                 intf.getInterfaceProtocol()))
135                                         return true;
136                         }
137
138                         return false;
139                 }
140
141                 @Override
142                 public String toString() {
143                         return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
144                                         ",mClass=" + mClass + ",mSubclass=" + mSubclass +
145                                         ",mProtocol=" + mProtocol + "]";
146                 }
147         }
148
149         public UsbSupplicant(Context ctx, int device_filter_resource)
150         {
151                 context = ctx;
152                 manager = (UsbManager) ctx.getSystemService(Context.USB_SERVICE);
153                 permReceiver = new BroadcastReceiver() {
154                         @Override
155                         public void onReceive(Context context, Intent intent) {
156                                 String action = intent.getAction();
157                                 if (ACTION_USB_PERMISSION.equals(action)) {
158                                         permissionCallback((UsbDevice)intent.getParcelableExtra(
159                                                 UsbManager.EXTRA_DEVICE), intent.getBooleanExtra(
160                                                 UsbManager.EXTRA_PERMISSION_GRANTED, false));
161                                 }
162                         }
163                 };
164                 hotplugReceiver = new BroadcastReceiver() {
165                         @Override
166                         public void onReceive(Context context, Intent intent) {
167                                 if (intent != null && UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
168                                         attachCallback((UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE));
169                                 } else if (intent != null && UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
170                                         detachCallback((UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE));
171                                 }
172                         }
173                 };
174                 permFilter = new IntentFilter(ACTION_USB_PERMISSION);
175                 hotplugFilter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED);
176                 hotplugFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
177                 deviceFilters = new Vector<DeviceFilter>();
178                 addDeviceFilters(ctx.getResources(), device_filter_resource);
179         }
180
181         private void addDeviceFilters(Resources res, int res_id)
182         {
183                 XmlResourceParser parser = res.getXml(res_id);
184                 if (parser == null) {
185                         Log.w("UsbSupplicant", "Unable to get device filter resource");
186                         return;
187                 }
188                 deviceFilters.clear();
189                 try {
190                         while (parser.next() != XmlPullParser.END_DOCUMENT) {
191                                 if (parser.getEventType() == XmlPullParser.START_TAG) {
192                                         if ("usb-device".equals(parser.getName()))
193                                                 deviceFilters.add(DeviceFilter.read(parser));
194                                 }
195                         }
196                 } catch (IOException e) {
197                         Log.wtf("UsbSupplicant",
198                                 "Failed to parse device filter resource", e);
199                 } catch (XmlPullParserException e) {
200                         Log.wtf("UsbSupplicant",
201                                 "Failed to parse device filter resource", e);
202                 }
203         }
204
205         protected boolean interresting(UsbDevice dev)
206         {
207                 if (dev == null)
208                         return false;
209
210                 for (DeviceFilter f : deviceFilters)
211                         if (f.matches(dev))
212                                 return true;
213
214                 return false;
215         }
216
217         protected void askFor(UsbDevice dev)
218         {
219                 manager.requestPermission(dev, PendingIntent.getBroadcast(context, 0,
220                         new Intent(ACTION_USB_PERMISSION), 0));
221         }
222
223         public void start()
224         {
225                 context.registerReceiver(permReceiver, permFilter);
226                 context.registerReceiver(hotplugReceiver, hotplugFilter);
227                 HashMap<String,UsbDevice> devlist = manager.getDeviceList();
228                 for (UsbDevice dev : devlist.values()) {
229                         if (interresting(dev) && !manager.hasPermission(dev)) {
230                                 Log.d("UsbSupplicant", "found interresting device " + dev);
231                                 askFor(dev);
232                         }
233                 }
234         }
235
236         public void stop()
237         {
238                 context.unregisterReceiver(hotplugReceiver);
239                 context.unregisterReceiver(permReceiver);
240         }
241
242         protected void permissionCallback(UsbDevice dev, boolean granted)
243         {
244                 Log.d("UsbSupplicant", "permission " +
245                                 (granted ? "granted" : "denied") + " for device " + dev);
246         }
247
248         protected void attachCallback(UsbDevice dev)
249         {
250                 if (interresting(dev) && !manager.hasPermission(dev))
251                         askFor(dev);
252         }
253
254         protected void detachCallback(UsbDevice dev)
255         {
256         }
257 }