]> sigrok.org Git - sigrok-androidutils.git/blob - src/org/sigrok/androidutils/UsbSupplicant.java
f57dbdd7f9fd5676ed7c54bad7e9eb9181bda137
[sigrok-androidutils.git] / src / org / sigrok / androidutils / UsbSupplicant.java
1 /*
2  * This file is part of the sigrok 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) return false;
120             if (mProductId != -1 && device.getProductId() != mProductId) return false;
121
122             // check device class/subclass/protocol
123             if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
124                     device.getDeviceProtocol())) return true;
125
126             // if device doesn't match, check the interfaces
127             int count = device.getInterfaceCount();
128             for (int i = 0; i < count; i++) {
129                 UsbInterface intf = device.getInterface(i);
130                  if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
131                         intf.getInterfaceProtocol())) return true;
132             }
133
134             return false;
135         }
136
137         @Override
138         public String toString() {
139             return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
140                     ",mClass=" + mClass + ",mSubclass=" + mSubclass +
141                     ",mProtocol=" + mProtocol + "]";
142         }
143     }
144
145     public UsbSupplicant(Context ctx, int device_filter_resource)
146     {
147         context = ctx;
148         manager = (UsbManager) ctx.getSystemService(Context.USB_SERVICE);
149         permReceiver = new BroadcastReceiver() {
150                 @Override
151                 public void onReceive(Context context, Intent intent) {
152                     String action = intent.getAction();
153                     if (ACTION_USB_PERMISSION.equals(action)) {
154                         permissionCallback((UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE),
155                                            intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
156                     }
157                 }
158             };
159         hotplugReceiver = new BroadcastReceiver() {
160                 @Override
161                 public void onReceive(Context context, Intent intent) {
162                     if (intent != null &&
163                         UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
164                         attachCallback((UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE));
165                     } else if (intent != null &&
166                                UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
167                         detachCallback((UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE));
168                     }
169                 }
170             };
171         permFilter = new IntentFilter(ACTION_USB_PERMISSION);
172         hotplugFilter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED);
173         hotplugFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
174         deviceFilters = new Vector<DeviceFilter>();
175         addDeviceFilters(ctx.getResources(), device_filter_resource);
176     }
177
178     private void addDeviceFilters(Resources res, int res_id)
179     {
180         XmlResourceParser parser = res.getXml(res_id);
181         if (parser == null) {
182             Log.w("UsbSupplicant", "Unable to get device filter resource");
183             return;
184         }
185         deviceFilters.clear();
186         try {
187             while (parser.next() != XmlPullParser.END_DOCUMENT) {
188                 if (parser.getEventType() == XmlPullParser.START_TAG) {
189                     if ("usb-device".equals(parser.getName())) {
190                         deviceFilters.add(DeviceFilter.read(parser));
191                     }
192                 }
193             }
194         } catch(IOException e) {
195             Log.wtf("UsbSupplicant",
196                     "Failed to parse device filter resource", e);
197         } catch(XmlPullParserException e) {
198             Log.wtf("UsbSupplicant",
199                     "Failed to parse device filter resource", e);
200         }
201     }
202
203     protected boolean interresting(UsbDevice dev)
204     {
205         if (dev == null)
206             return false;
207
208         for (DeviceFilter f : deviceFilters)
209             if (f.matches(dev))
210                 return true;
211
212         return false;
213     }
214
215     protected void askFor(UsbDevice dev)
216     {
217         manager.requestPermission(dev, PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0));
218     }
219
220     public void start()
221     {
222         context.registerReceiver(permReceiver, permFilter);
223         context.registerReceiver(hotplugReceiver, hotplugFilter);
224         HashMap<String,UsbDevice> devlist = manager.getDeviceList();
225         for (UsbDevice dev : devlist.values()) {
226             if (interresting(dev) && !manager.hasPermission(dev)) {
227                 Log.d("UsbSupplicant", "found interresting device "+dev);
228                 askFor(dev);
229             }
230         }
231     }
232
233     public void stop()
234     {
235         context.unregisterReceiver(hotplugReceiver);
236         context.unregisterReceiver(permReceiver);
237     }
238
239     protected void permissionCallback(UsbDevice dev, boolean granted)
240     {
241         Log.d("UsbSupplicant", "permission " + (granted? "granted" : "denied") + " for device " + dev);
242     }
243
244     protected void attachCallback(UsbDevice dev)
245     {
246         if (interresting(dev) && !manager.hasPermission(dev)) {
247             askFor(dev);
248         }
249     }
250
251     protected void detachCallback(UsbDevice dev)
252     {
253     }
254 }