| 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 |