UsbMidiDeviceFactoryAndroid.java revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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
5package org.chromium.media;
6
7import android.app.PendingIntent;
8import android.content.BroadcastReceiver;
9import android.content.Context;
10import android.content.Intent;
11import android.content.IntentFilter;
12import android.hardware.usb.UsbConstants;
13import android.hardware.usb.UsbDevice;
14import android.hardware.usb.UsbInterface;
15import android.hardware.usb.UsbManager;
16
17import org.chromium.base.CalledByNative;
18import org.chromium.base.JNINamespace;
19
20import java.util.ArrayList;
21import java.util.HashSet;
22import java.util.List;
23import java.util.Map;
24import java.util.Set;
25
26/**
27 * Owned by its native counterpart declared in
28 * usb_midi_device_factory_android.h. Refer to that class for general comments.
29 */
30@JNINamespace("media")
31class UsbMidiDeviceFactoryAndroid {
32    /**
33     * The UsbManager of this system.
34     */
35    private UsbManager mUsbManager;
36
37    /**
38     * A BroadcastReceiver for USB device permission requests.
39     */
40    private BroadcastReceiver mReceiver;
41
42    /**
43     * Accessible USB-MIDI devices got so far.
44     */
45    private final List<UsbMidiDeviceAndroid> mDevices = new ArrayList<UsbMidiDeviceAndroid>();
46
47    /**
48     * Devices whose access permission requested but not resolved so far.
49     */
50    private Set<UsbDevice> mRequestedDevices;
51
52    /**
53     * The identifier of this factory.
54     */
55    private long mNativePointer;
56
57    private static final String ACTION_USB_PERMISSION =
58        "org.chromium.media.USB_PERMISSION";
59
60    /**
61     * Constructs a UsbMidiDeviceAndroid.
62     * @param natviePointer The native pointer to which the created factory is associated.
63     */
64    UsbMidiDeviceFactoryAndroid(long nativePointer) {
65        mNativePointer = nativePointer;
66    }
67
68    /**
69     * Constructs a UsbMidiDeviceAndroid.
70     * @param nativePointer The native pointer to which the created factory is associated.
71     */
72    @CalledByNative
73    static UsbMidiDeviceFactoryAndroid create(long nativePointer) {
74        return new UsbMidiDeviceFactoryAndroid(nativePointer);
75    }
76
77    /**
78     * Enumerates USB-MIDI devices.
79     * If there are devices having USB-MIDI interfaces, this function requests permission for
80     * accessing the device to the user.
81     * When the permission request is accepted or rejected onRequestDone will be called.
82     *
83     * If there are no USB-MIDI interfaces, this function returns false.
84     * @return true if some permission requests are in progress.
85     */
86    @CalledByNative
87    boolean enumerateDevices(Context context) {
88        mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
89        Map<String, UsbDevice> devices = mUsbManager.getDeviceList();
90        PendingIntent pendingIntent = PendingIntent.getBroadcast(
91                context, 0, new Intent(ACTION_USB_PERMISSION), 0);
92        mRequestedDevices = new HashSet<UsbDevice>();
93        for (UsbDevice device : devices.values()) {
94            boolean found = false;
95            for (int i = 0; i < device.getInterfaceCount() && !found; ++i) {
96                UsbInterface iface = device.getInterface(i);
97                if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_AUDIO &&
98                    iface.getInterfaceSubclass() == UsbMidiDeviceAndroid.MIDI_SUBCLASS) {
99                    found = true;
100                }
101            }
102            if (found) {
103                mUsbManager.requestPermission(device, pendingIntent);
104                mRequestedDevices.add(device);
105            }
106        }
107        if (mRequestedDevices.isEmpty()) {
108            // No USB-MIDI devices are found.
109            return false;
110        }
111
112        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
113        mReceiver = new BroadcastReceiver() {
114            public void onReceive(Context context, Intent intent) {
115                if (ACTION_USB_PERMISSION.equals(intent.getAction())) {
116                    onRequestDone(context, intent);
117                }
118            }
119        };
120        context.registerReceiver(mReceiver, filter);
121        return true;
122    }
123
124    /**
125     * Called when the user accepts or rejects the permission request requested by
126     * EnumerateDevices.
127     * If all permission requests are responded, this function calls
128     * nativeOnUsbMidiDeviceRequestDone with the accessible USB-MIDI devices.
129     */
130    private void onRequestDone(Context context, Intent intent) {
131        UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
132        if (!mRequestedDevices.contains(device)) {
133            // We are not interested in the device.
134            return;
135        }
136        mRequestedDevices.remove(device);
137        if (!intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
138            // The request was rejected.
139            device = null;
140        }
141        if (device != null) {
142            // Now we can add the device.
143            mDevices.add(new UsbMidiDeviceAndroid(mUsbManager, device));
144        }
145        if (mRequestedDevices.isEmpty()) {
146            // All requests are done.
147            context.unregisterReceiver(mReceiver);
148            if (mNativePointer != 0) {
149                nativeOnUsbMidiDeviceRequestDone(mNativePointer, mDevices.toArray());
150            }
151        }
152    }
153
154    /**
155     * Disconnects the native object.
156     */
157    @CalledByNative
158    void close() {
159        mNativePointer = 0;
160    }
161
162    private static native void nativeOnUsbMidiDeviceRequestDone(
163            long nativeUsbMidiDeviceFactoryAndroid, Object[] devices);
164}
165