OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.media; | |
6 | |
7 import android.app.PendingIntent; | |
8 import android.content.BroadcastReceiver; | |
9 import android.content.Context; | |
10 import android.content.Intent; | |
11 import android.content.IntentFilter; | |
12 import android.hardware.usb.UsbConstants; | |
13 import android.hardware.usb.UsbDevice; | |
14 import android.hardware.usb.UsbInterface; | |
15 import android.hardware.usb.UsbManager; | |
16 import android.os.Parcelable; | |
17 | |
18 import org.chromium.base.CalledByNative; | |
19 import org.chromium.base.JNINamespace; | |
20 | |
21 import java.util.ArrayList; | |
22 import java.util.HashSet; | |
23 import java.util.List; | |
24 import java.util.Map; | |
25 import java.util.Set; | |
26 | |
27 /** | |
28 * Owned by its native counterpart declared in | |
29 * usb_midi_device_factory_android.h. Refer to that class for general comments. | |
30 */ | |
31 @JNINamespace("media") | |
32 class UsbMidiDeviceFactoryAndroid { | |
33 /** | |
34 * The UsbManager of this system. | |
35 */ | |
36 private UsbManager mUsbManager; | |
37 | |
38 /** | |
39 * A BroadcastReceiver for USB device events. | |
40 */ | |
41 private BroadcastReceiver mReceiver; | |
42 | |
43 /** | |
44 * Accessible USB-MIDI devices got so far. | |
45 */ | |
46 private final List<UsbMidiDeviceAndroid> mDevices = new ArrayList<UsbMidiDev
iceAndroid>(); | |
47 | |
48 /** | |
49 * Devices whose access permission requested but not resolved so far. | |
50 */ | |
51 private Set<UsbDevice> mRequestedDevices; | |
52 | |
53 /** | |
54 * True when the enumeration is in progress. | |
55 */ | |
56 private boolean mIsEnumeratingDevices; | |
57 | |
58 /** | |
59 * The identifier of this factory. | |
60 */ | |
61 private long mNativePointer; | |
62 | |
63 private static final String ACTION_USB_PERMISSION = | |
64 "org.chromium.media.USB_PERMISSION"; | |
65 | |
66 /** | |
67 * Constructs a UsbMidiDeviceAndroid. | |
68 * @param context | |
69 * @param nativePointer The native pointer to which the created factory is a
ssociated. | |
70 */ | |
71 UsbMidiDeviceFactoryAndroid(Context context, long nativePointer) { | |
72 mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE)
; | |
73 mNativePointer = nativePointer; | |
74 mReceiver = new BroadcastReceiver() { | |
75 @Override | |
76 public void onReceive(Context context, Intent intent) { | |
77 Parcelable extra = intent.getParcelableExtra(UsbManager.EXTRA_DE
VICE); | |
78 if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getActio
n())) { | |
79 requestDevicePermissionIfNecessary(context, (UsbDevice) extr
a); | |
80 } | |
81 if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getActio
n())) { | |
82 onUsbDeviceDetached((UsbDevice) extra); | |
83 } | |
84 if (ACTION_USB_PERMISSION.equals(intent.getAction())) { | |
85 onUsbDevicePermissionRequestDone(context, intent); | |
86 } | |
87 } | |
88 }; | |
89 IntentFilter filter = new IntentFilter(); | |
90 filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); | |
91 filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); | |
92 filter.addAction(ACTION_USB_PERMISSION); | |
93 context.registerReceiver(mReceiver, filter); | |
94 mRequestedDevices = new HashSet<UsbDevice>(); | |
95 } | |
96 | |
97 /** | |
98 * Constructs a UsbMidiDeviceAndroid. | |
99 * @param context | |
100 * @param nativePointer The native pointer to which the created factory is a
ssociated. | |
101 */ | |
102 @CalledByNative | |
103 static UsbMidiDeviceFactoryAndroid create(Context context, long nativePointe
r) { | |
104 return new UsbMidiDeviceFactoryAndroid(context, nativePointer); | |
105 } | |
106 | |
107 /** | |
108 * Enumerates USB-MIDI devices. | |
109 * If there are devices having USB-MIDI interfaces, this function requests p
ermission for | |
110 * accessing the device to the user. | |
111 * When the permission request is accepted or rejected, nativeOnUsbMidiDevi
ceRequestDone | |
112 * will be called. | |
113 * | |
114 * If there are no USB-MIDI interfaces, this function returns false. | |
115 * @param context | |
116 * @return true if some permission requests are in progress. | |
117 */ | |
118 @CalledByNative | |
119 boolean enumerateDevices(Context context) { | |
120 assert !mIsEnumeratingDevices; | |
121 mIsEnumeratingDevices = true; | |
122 Map<String, UsbDevice> devices = mUsbManager.getDeviceList(); | |
123 if (devices.isEmpty()) { | |
124 // No USB-MIDI devices are found. | |
125 mIsEnumeratingDevices = false; | |
126 return false; | |
127 } | |
128 for (UsbDevice device: devices.values()) { | |
129 requestDevicePermissionIfNecessary(context, device); | |
130 } | |
131 return true; | |
132 } | |
133 | |
134 /** | |
135 * Request a device access permission if there is a MIDI interface in the de
vice. | |
136 * | |
137 * @param context | |
138 * @param device a USB device | |
139 */ | |
140 private void requestDevicePermissionIfNecessary(Context context, UsbDevice d
evice) { | |
141 for (UsbDevice d: mRequestedDevices) { | |
142 if (d.getDeviceId() == device.getDeviceId()) { | |
143 // It is already requested. | |
144 return; | |
145 } | |
146 } | |
147 | |
148 for (int i = 0; i < device.getInterfaceCount(); ++i) { | |
149 UsbInterface iface = device.getInterface(i); | |
150 if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_AUDIO | |
151 && iface.getInterfaceSubclass() == UsbMidiDeviceAndroid.MIDI
_SUBCLASS) { | |
152 // There is at least one interface supporting MIDI. | |
153 mUsbManager.requestPermission(device, PendingIntent.getBroadcast
( | |
154 context, 0, new Intent(ACTION_USB_PERMISSION), 0)); | |
155 mRequestedDevices.add(device); | |
156 break; | |
157 } | |
158 } | |
159 } | |
160 | |
161 /** | |
162 * Called when a USB device is detached. | |
163 * | |
164 * @param device a USB device | |
165 */ | |
166 private void onUsbDeviceDetached(UsbDevice device) { | |
167 for (UsbDevice usbDevice: mRequestedDevices) { | |
168 if (usbDevice.getDeviceId() == device.getDeviceId()) { | |
169 mRequestedDevices.remove(usbDevice); | |
170 break; | |
171 } | |
172 } | |
173 for (int i = 0; i < mDevices.size(); ++i) { | |
174 UsbMidiDeviceAndroid midiDevice = mDevices.get(i); | |
175 if (midiDevice.isClosed()) { | |
176 // Once a device is disconnected, the system may reassign its de
vice ID to | |
177 // another device. So we should ignore disconnected ones. | |
178 continue; | |
179 } | |
180 if (midiDevice.getUsbDevice().getDeviceId() == device.getDeviceId())
{ | |
181 midiDevice.close(); | |
182 if (mIsEnumeratingDevices) { | |
183 // In this case, we don't have to keep mDevices sync with th
e devices list | |
184 // in MidiManagerUsb. | |
185 mDevices.remove(i); | |
186 return; | |
187 } | |
188 if (mNativePointer != 0) { | |
189 nativeOnUsbMidiDeviceDetached(mNativePointer, i); | |
190 } | |
191 return; | |
192 } | |
193 } | |
194 } | |
195 | |
196 /** | |
197 * Called when the user accepts or rejects the permission request requested
by | |
198 * EnumerateDevices. | |
199 * | |
200 * @param context | |
201 * @param intent | |
202 */ | |
203 private void onUsbDevicePermissionRequestDone(Context context, Intent intent
) { | |
204 UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTR
A_DEVICE); | |
205 UsbMidiDeviceAndroid midiDevice = null; | |
206 if (mRequestedDevices.contains(device)) { | |
207 mRequestedDevices.remove(device); | |
208 if (!intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, fal
se)) { | |
209 // The request was rejected. | |
210 device = null; | |
211 } | |
212 } else { | |
213 device = null; | |
214 } | |
215 | |
216 if (device != null) { | |
217 for (UsbMidiDeviceAndroid registered: mDevices) { | |
218 if (!registered.isClosed() | |
219 && registered.getUsbDevice().getDeviceId() == device.get
DeviceId()) { | |
220 // The device is already registered. | |
221 device = null; | |
222 break; | |
223 } | |
224 } | |
225 } | |
226 | |
227 if (device != null) { | |
228 // Now we can add the device. | |
229 midiDevice = new UsbMidiDeviceAndroid(mUsbManager, device); | |
230 mDevices.add(midiDevice); | |
231 } | |
232 | |
233 if (!mRequestedDevices.isEmpty()) { | |
234 return; | |
235 } | |
236 if (mNativePointer == 0) { | |
237 return; | |
238 } | |
239 | |
240 if (mIsEnumeratingDevices) { | |
241 nativeOnUsbMidiDeviceRequestDone(mNativePointer, mDevices.toArray())
; | |
242 mIsEnumeratingDevices = false; | |
243 } else if (midiDevice != null) { | |
244 nativeOnUsbMidiDeviceAttached(mNativePointer, midiDevice); | |
245 } | |
246 } | |
247 | |
248 /** | |
249 * Disconnects the native object. | |
250 * @param context | |
251 */ | |
252 @CalledByNative | |
253 void close(Context context) { | |
254 mNativePointer = 0; | |
255 context.unregisterReceiver(mReceiver); | |
256 } | |
257 | |
258 private static native void nativeOnUsbMidiDeviceRequestDone( | |
259 long nativeUsbMidiDeviceFactoryAndroid, Object[] devices); | |
260 private static native void nativeOnUsbMidiDeviceAttached( | |
261 long nativeUsbMidiDeviceFactoryAndroid, Object device); | |
262 private static native void nativeOnUsbMidiDeviceDetached( | |
263 long nativeUsbMidiDeviceFactoryAndroid, int index); | |
264 } | |
OLD | NEW |