]> sigrok.org Git - sigrok-androidutils.git/blob - src/org/sigrok/androidutils/UsbSupplicant.java
eaec3d69c1b2d3ca673a0b6d337a44ba01e8aff2
[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  * Copyright (C) 2011 The Android Open Source Project
21  *
22  * Licensed under the Apache License, Version 2.0 (the "License");
23  * you may not use this file except in compliance with the License.
24  * You may obtain a copy of the License at
25  *
26  * http://www.apache.org/licenses/LICENSE-2.0
27  *
28  * Unless required by applicable law or agreed to in writing, software
29  * distributed under the License is distributed on an "AS IS" BASIS,
30  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31  * See the License for the specific language governing permissions and
32  * limitations under the License.
33  */
34
35 package org.sigrok.androidutils;
36
37 import android.app.PendingIntent;
38 import android.content.BroadcastReceiver;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.IntentFilter;
42 import android.content.res.Resources;
43 import android.content.res.XmlResourceParser;
44 import android.hardware.usb.UsbDevice;
45 import android.hardware.usb.UsbInterface;
46 import android.hardware.usb.UsbManager;
47 import android.util.Log;
48 import java.io.IOException;
49 import java.util.HashMap;
50 import java.util.Vector;
51 import org.xmlpull.v1.XmlPullParser;
52 import org.xmlpull.v1.XmlPullParserException;
53
54 public class UsbSupplicant
55 {
56         static final String ACTION_USB_PERMISSION =
57         "org.sigrok.androidutils.USB_PERMISSION";
58
59         protected final Context context;
60         protected final UsbManager manager;
61         private final BroadcastReceiver permReceiver;
62         private final BroadcastReceiver hotplugReceiver;
63         private final IntentFilter permFilter;
64         private final IntentFilter hotplugFilter;
65         private final Vector<DeviceFilter> deviceFilters;
66
67         // The code in the following inner class is taken from AOSP,
68         // which is licensed under the Apache License, Version 2.0.
69         private static class DeviceFilter {
70                 // USB Vendor ID (or -1 for unspecified)
71                 public final int mVendorId;
72                 // USB Product ID (or -1 for unspecified)
73                 public final int mProductId;
74                 // USB device or interface class (or -1 for unspecified)
75                 public final int mClass;
76                 // USB device subclass (or -1 for unspecified)
77                 public final int mSubclass;
78                 // USB device protocol (or -1 for unspecified)
79                 public final int mProtocol;
80
81                 public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol) {
82                         mVendorId = vid;
83                         mProductId = pid;
84                         mClass = clasz;
85                         mSubclass = subclass;
86                         mProtocol = protocol;
87                 }
88
89                 public DeviceFilter(UsbDevice device) {
90                         mVendorId = device.getVendorId();
91                         mProductId = device.getProductId();
92                         mClass = device.getDeviceClass();
93                         mSubclass = device.getDeviceSubclass();
94                         mProtocol = device.getDeviceProtocol();
95                 }
96
97                 public static DeviceFilter read(XmlPullParser parser)
98                                 throws XmlPullParserException, IOException {
99                         int vendorId = -1;
100                         int productId = -1;
101                         int deviceClass = -1;
102                         int deviceSubclass = -1;
103                         int deviceProtocol = -1;
104
105                         int count = parser.getAttributeCount();
106                         for (int i = 0; i < count; i++) {
107                                 String name = parser.getAttributeName(i);
108                                 // All attribute values are ints
109                                 int value = parseAttribute(name, parser.getAttributeValue(i));
110
111                                 if ("vendor-id".equals(name)) {
112                                         vendorId = value;
113                                 } else if ("product-id".equals(name)) {
114                                         productId = value;
115                                 } else if ("class".equals(name)) {
116                                         deviceClass = value;
117                                 } else if ("subclass".equals(name)) {
118                                         deviceSubclass = value;
119                                 } else if ("protocol".equals(name)) {
120                                         deviceProtocol = value;
121                                 }
122                         }
123                         return new DeviceFilter(vendorId, productId,
124                                         deviceClass, deviceSubclass, deviceProtocol);
125                 }
126
127                 private static int parseAttribute(String name, String value) {
128                         // vendor-id and product-id are in hexadecimal format.
129                         if ("vendor-id".equals(name) || "product-id".equals(name))
130                                 return Integer.parseInt(value, 16);
131                         else
132                                 return Integer.parseInt(value);
133                 }
134
135                 private boolean matches(int clasz, int subclass, int protocol) {
136                         return ((mClass == -1 || clasz == mClass) &&
137                                         (mSubclass == -1 || subclass == mSubclass) &&
138                                         (mProtocol == -1 || protocol == mProtocol));
139                 }
140
141                 public boolean matches(UsbDevice device) {
142                         if (mVendorId != -1 && device.getVendorId() != mVendorId)
143                                 return false;
144                         if (mProductId != -1 && device.getProductId() != mProductId)
145                                 return false;
146
147                         // Check device class/subclass/protocol.
148                         if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
149                                         device.getDeviceProtocol()))
150                                 return true;
151
152                         // If device doesn't match, check the interfaces.
153                         int count = device.getInterfaceCount();
154                         for (int i = 0; i < count; i++) {
155                                 UsbInterface intf = device.getInterface(i);
156                                 if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
157                                                 intf.getInterfaceProtocol()))
158                                         return true;
159                         }
160
161                         return false;
162                 }
163
164                 @Override
165                 public String toString() {
166                         return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
167                                         ",mClass=" + mClass + ",mSubclass=" + mSubclass +
168                                         ",mProtocol=" + mProtocol + "]";
169                 }
170         }
171
172         public UsbSupplicant(Context ctx, int device_filter_resource)
173         {
174                 context = ctx;
175                 manager = (UsbManager) ctx.getSystemService(Context.USB_SERVICE);
176                 permReceiver = new BroadcastReceiver() {
177                         @Override
178                         public void onReceive(Context context, Intent intent) {
179                                 String action = intent.getAction();
180                                 if (ACTION_USB_PERMISSION.equals(action)) {
181                                         permissionCallback((UsbDevice)intent.getParcelableExtra(
182                                                 UsbManager.EXTRA_DEVICE), intent.getBooleanExtra(
183                                                 UsbManager.EXTRA_PERMISSION_GRANTED, false));
184                                 }
185                         }
186                 };
187                 hotplugReceiver = new BroadcastReceiver() {
188                         @Override
189                         public void onReceive(Context context, Intent intent) {
190                                 if (intent != null && UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
191                                         attachCallback((UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE));
192                                 } else if (intent != null && UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
193                                         detachCallback((UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE));
194                                 }
195                         }
196                 };
197                 permFilter = new IntentFilter(ACTION_USB_PERMISSION);
198                 hotplugFilter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED);
199                 hotplugFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
200                 deviceFilters = new Vector<DeviceFilter>();
201                 addDeviceFilters(ctx.getResources(), device_filter_resource);
202         }
203
204         private void addDeviceFilters(Resources res, int res_id)
205         {
206                 XmlResourceParser parser = res.getXml(res_id);
207                 if (parser == null) {
208                         Log.w("UsbSupplicant", "Unable to get device filter resource");
209                         return;
210                 }
211                 deviceFilters.clear();
212                 try {
213                         while (parser.next() != XmlPullParser.END_DOCUMENT) {
214                                 if (parser.getEventType() == XmlPullParser.START_TAG) {
215                                         if ("usb-device".equals(parser.getName()))
216                                                 deviceFilters.add(DeviceFilter.read(parser));
217                                 }
218                         }
219                 } catch (IOException e) {
220                         Log.wtf("UsbSupplicant",
221                                 "Failed to parse device filter resource", e);
222                 } catch (XmlPullParserException e) {
223                         Log.wtf("UsbSupplicant",
224                                 "Failed to parse device filter resource", e);
225                 }
226         }
227
228         protected boolean interesting(UsbDevice dev)
229         {
230                 if (dev == null)
231                         return false;
232
233                 for (DeviceFilter f : deviceFilters)
234                         if (f.matches(dev))
235                                 return true;
236
237                 return false;
238         }
239
240         protected void askFor(UsbDevice dev)
241         {
242                 manager.requestPermission(dev, PendingIntent.getBroadcast(context, 0,
243                         new Intent(ACTION_USB_PERMISSION), 0));
244         }
245
246         public void start()
247         {
248                 context.registerReceiver(permReceiver, permFilter);
249                 context.registerReceiver(hotplugReceiver, hotplugFilter);
250                 HashMap<String,UsbDevice> devlist = manager.getDeviceList();
251                 for (UsbDevice dev : devlist.values()) {
252                         if (interesting(dev) && !manager.hasPermission(dev)) {
253                                 Log.d("UsbSupplicant", "found interesting device " + dev);
254                                 askFor(dev);
255                         }
256                 }
257         }
258
259         public void stop()
260         {
261                 context.unregisterReceiver(hotplugReceiver);
262                 context.unregisterReceiver(permReceiver);
263         }
264
265         protected void permissionCallback(UsbDevice dev, boolean granted)
266         {
267                 Log.d("UsbSupplicant", "permission " +
268                                 (granted ? "granted" : "denied") + " for device " + dev);
269         }
270
271         protected void attachCallback(UsbDevice dev)
272         {
273                 if (interesting(dev) && !manager.hasPermission(dev))
274                         askFor(dev);
275         }
276
277         protected void detachCallback(UsbDevice dev)
278         {
279         }
280 }