15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// found in the LICENSE file.
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)package org.chromium.media;
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.app.PendingIntent;
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.content.BroadcastReceiver;
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.content.Context;
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.content.Intent;
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.content.IntentFilter;
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.hardware.usb.UsbConstants;
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.hardware.usb.UsbDevice;
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.hardware.usb.UsbInterface;
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.hardware.usb.UsbManager;
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import org.chromium.base.CalledByNative;
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import org.chromium.base.JNINamespace;
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import java.util.ArrayList;
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import java.util.HashSet;
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import java.util.List;
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import java.util.Map;
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import java.util.Set;
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/**
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Owned by its native counterpart declared in
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * usb_midi_device_factory_android.h. Refer to that class for general comments.
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)@JNINamespace("media")
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class UsbMidiDeviceFactoryAndroid {
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * The UsbManager of this system.
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    private UsbManager mUsbManager;
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * A BroadcastReceiver for USB device permission requests.
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    private BroadcastReceiver mReceiver;
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * Accessible USB-MIDI devices got so far.
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    private final List<UsbMidiDeviceAndroid> mDevices = new ArrayList<UsbMidiDeviceAndroid>();
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * Devices whose access permission requested but not resolved so far.
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    private Set<UsbDevice> mRequestedDevices;
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * The identifier of this factory.
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    private long mNativePointer;
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    private static final String ACTION_USB_PERMISSION =
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        "org.chromium.media.USB_PERMISSION";
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * Constructs a UsbMidiDeviceAndroid.
621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     * @param nativePointer The native pointer to which the created factory is associated.
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    UsbMidiDeviceFactoryAndroid(long nativePointer) {
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        mNativePointer = nativePointer;
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * Constructs a UsbMidiDeviceAndroid.
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @param nativePointer The native pointer to which the created factory is associated.
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @CalledByNative
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    static UsbMidiDeviceFactoryAndroid create(long nativePointer) {
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return new UsbMidiDeviceFactoryAndroid(nativePointer);
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * Enumerates USB-MIDI devices.
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * If there are devices having USB-MIDI interfaces, this function requests permission for
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * accessing the device to the user.
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * When the permission request is accepted or rejected onRequestDone will be called.
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     *
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * If there are no USB-MIDI interfaces, this function returns false.
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @return true if some permission requests are in progress.
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @CalledByNative
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    boolean enumerateDevices(Context context) {
881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        Map<String, UsbDevice> devices = mUsbManager.getDeviceList();
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        PendingIntent pendingIntent = PendingIntent.getBroadcast(
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                context, 0, new Intent(ACTION_USB_PERMISSION), 0);
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        mRequestedDevices = new HashSet<UsbDevice>();
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        for (UsbDevice device : devices.values()) {
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            boolean found = false;
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            for (int i = 0; i < device.getInterfaceCount() && !found; ++i) {
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                UsbInterface iface = device.getInterface(i);
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_AUDIO &&
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    iface.getInterfaceSubclass() == UsbMidiDeviceAndroid.MIDI_SUBCLASS) {
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    found = true;
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                }
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            }
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if (found) {
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                mUsbManager.requestPermission(device, pendingIntent);
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                mRequestedDevices.add(device);
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            }
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (mRequestedDevices.isEmpty()) {
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            // No USB-MIDI devices are found.
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            return false;
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        mReceiver = new BroadcastReceiver() {
1141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            @Override
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            public void onReceive(Context context, Intent intent) {
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                if (ACTION_USB_PERMISSION.equals(intent.getAction())) {
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    onRequestDone(context, intent);
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                }
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            }
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        };
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        context.registerReceiver(mReceiver, filter);
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return true;
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * Called when the user accepts or rejects the permission request requested by
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * EnumerateDevices.
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * If all permission requests are responded, this function calls
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * nativeOnUsbMidiDeviceRequestDone with the accessible USB-MIDI devices.
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    private void onRequestDone(Context context, Intent intent) {
1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (!mRequestedDevices.contains(device)) {
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            // We are not interested in the device.
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            return;
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        mRequestedDevices.remove(device);
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (!intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            // The request was rejected.
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            device = null;
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (device != null) {
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            // Now we can add the device.
1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            mDevices.add(new UsbMidiDeviceAndroid(mUsbManager, device));
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (mRequestedDevices.isEmpty()) {
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            // All requests are done.
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            context.unregisterReceiver(mReceiver);
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if (mNativePointer != 0) {
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                nativeOnUsbMidiDeviceRequestDone(mNativePointer, mDevices.toArray());
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            }
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * Disconnects the native object.
1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @CalledByNative
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    void close() {
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        mNativePointer = 0;
1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    private static native void nativeOnUsbMidiDeviceRequestDone(
1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            long nativeUsbMidiDeviceFactoryAndroid, Object[] devices);
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
166