146d0adf8256a42416584765625852b6e48497c90Mike Lockwood/*
246d0adf8256a42416584765625852b6e48497c90Mike Lockwood * Copyright (C) 2011 The Android Open Source Project
346d0adf8256a42416584765625852b6e48497c90Mike Lockwood *
446d0adf8256a42416584765625852b6e48497c90Mike Lockwood * Licensed under the Apache License, Version 2.0 (the "License");
546d0adf8256a42416584765625852b6e48497c90Mike Lockwood * you may not use this file except in compliance with the License.
646d0adf8256a42416584765625852b6e48497c90Mike Lockwood * You may obtain a copy of the License at
746d0adf8256a42416584765625852b6e48497c90Mike Lockwood *
846d0adf8256a42416584765625852b6e48497c90Mike Lockwood *      http://www.apache.org/licenses/LICENSE-2.0
946d0adf8256a42416584765625852b6e48497c90Mike Lockwood *
1046d0adf8256a42416584765625852b6e48497c90Mike Lockwood * Unless required by applicable law or agreed to in writing, software
1146d0adf8256a42416584765625852b6e48497c90Mike Lockwood * distributed under the License is distributed on an "AS IS" BASIS,
1246d0adf8256a42416584765625852b6e48497c90Mike Lockwood * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1346d0adf8256a42416584765625852b6e48497c90Mike Lockwood * See the License for the specific language governing permissions an
1446d0adf8256a42416584765625852b6e48497c90Mike Lockwood * limitations under the License.
1546d0adf8256a42416584765625852b6e48497c90Mike Lockwood */
1646d0adf8256a42416584765625852b6e48497c90Mike Lockwood
1746d0adf8256a42416584765625852b6e48497c90Mike Lockwoodpackage com.android.server.usb;
1846d0adf8256a42416584765625852b6e48497c90Mike Lockwood
1965a30ab519580deed21d206e66f456fc2c706fbePaul McLeanimport android.alsa.AlsaCardsParser;
2065a30ab519580deed21d206e66f456fc2c706fbePaul McLeanimport android.alsa.AlsaDevicesParser;
2146d0adf8256a42416584765625852b6e48497c90Mike Lockwoodimport android.content.Context;
22c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLeanimport android.content.Intent;
237531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwoodimport android.hardware.usb.UsbConfiguration;
2446d0adf8256a42416584765625852b6e48497c90Mike Lockwoodimport android.hardware.usb.UsbConstants;
2546d0adf8256a42416584765625852b6e48497c90Mike Lockwoodimport android.hardware.usb.UsbDevice;
2646d0adf8256a42416584765625852b6e48497c90Mike Lockwoodimport android.hardware.usb.UsbEndpoint;
2746d0adf8256a42416584765625852b6e48497c90Mike Lockwoodimport android.hardware.usb.UsbInterface;
28c5258433dd353769ccfa2e5e769a7379378a3a0bJean-Michel Triviimport android.media.AudioManager;
2946d0adf8256a42416584765625852b6e48497c90Mike Lockwoodimport android.os.Bundle;
3046d0adf8256a42416584765625852b6e48497c90Mike Lockwoodimport android.os.ParcelFileDescriptor;
31fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkeyimport android.os.Parcelable;
32c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLeanimport android.os.UserHandle;
3346d0adf8256a42416584765625852b6e48497c90Mike Lockwoodimport android.util.Slog;
3446d0adf8256a42416584765625852b6e48497c90Mike Lockwood
358b2c3a14603d163d7564e6f60286995079687690Jeff Sharkeyimport com.android.internal.annotations.GuardedBy;
368b2c3a14603d163d7564e6f60286995079687690Jeff Sharkey
37c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLeanimport java.io.File;
3846d0adf8256a42416584765625852b6e48497c90Mike Lockwoodimport java.io.FileDescriptor;
39c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLeanimport java.io.FileNotFoundException;
4046d0adf8256a42416584765625852b6e48497c90Mike Lockwoodimport java.io.PrintWriter;
417531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwoodimport java.util.ArrayList;
4246d0adf8256a42416584765625852b6e48497c90Mike Lockwoodimport java.util.HashMap;
4346d0adf8256a42416584765625852b6e48497c90Mike Lockwood
4446d0adf8256a42416584765625852b6e48497c90Mike Lockwood/**
4546d0adf8256a42416584765625852b6e48497c90Mike Lockwood * UsbHostManager manages USB state in host mode.
4646d0adf8256a42416584765625852b6e48497c90Mike Lockwood */
4746d0adf8256a42416584765625852b6e48497c90Mike Lockwoodpublic class UsbHostManager {
4846d0adf8256a42416584765625852b6e48497c90Mike Lockwood    private static final String TAG = UsbHostManager.class.getSimpleName();
49c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean    private static final boolean DEBUG_AUDIO = false;
5046d0adf8256a42416584765625852b6e48497c90Mike Lockwood
5146d0adf8256a42416584765625852b6e48497c90Mike Lockwood    // contains all connected USB devices
52fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey    private final HashMap<String, UsbDevice> mDevices = new HashMap<String, UsbDevice>();
5346d0adf8256a42416584765625852b6e48497c90Mike Lockwood
5446d0adf8256a42416584765625852b6e48497c90Mike Lockwood    // USB busses to exclude from USB host support
5546d0adf8256a42416584765625852b6e48497c90Mike Lockwood    private final String[] mHostBlacklist;
5646d0adf8256a42416584765625852b6e48497c90Mike Lockwood
5746d0adf8256a42416584765625852b6e48497c90Mike Lockwood    private final Context mContext;
5846d0adf8256a42416584765625852b6e48497c90Mike Lockwood    private final Object mLock = new Object();
5946d0adf8256a42416584765625852b6e48497c90Mike Lockwood
607531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    private UsbDevice mNewDevice;
617531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    private UsbConfiguration mNewConfiguration;
627531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    private UsbInterface mNewInterface;
637531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    private ArrayList<UsbConfiguration> mNewConfigurations;
647531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    private ArrayList<UsbInterface> mNewInterfaces;
657531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    private ArrayList<UsbEndpoint> mNewEndpoints;
667531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood
67df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean    // Attributes of any connected USB audio device.
68df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean    //TODO(pmclean) When we extend to multiple, USB Audio devices, we will need to get
69df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean    // more clever about this.
70df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean    private int mConnectedUsbCard = -1;
71df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean    private int mConnectedUsbDeviceNum = -1;
72df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean    private boolean mConnectedHasPlayback = false;
73df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean    private boolean mConnectedHasCapture = false;
74df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean    private boolean mConnectedHasMIDI = false;
75df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean
768b2c3a14603d163d7564e6f60286995079687690Jeff Sharkey    @GuardedBy("mLock")
77fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey    private UsbSettingsManager mCurrentSettings;
78fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey
79fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey    public UsbHostManager(Context context) {
8046d0adf8256a42416584765625852b6e48497c90Mike Lockwood        mContext = context;
8146d0adf8256a42416584765625852b6e48497c90Mike Lockwood        mHostBlacklist = context.getResources().getStringArray(
8246d0adf8256a42416584765625852b6e48497c90Mike Lockwood                com.android.internal.R.array.config_usbHostBlacklist);
8346d0adf8256a42416584765625852b6e48497c90Mike Lockwood    }
8446d0adf8256a42416584765625852b6e48497c90Mike Lockwood
85fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey    public void setCurrentSettings(UsbSettingsManager settings) {
86fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey        synchronized (mLock) {
87fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey            mCurrentSettings = settings;
88fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey        }
89fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey    }
90fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey
91fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey    private UsbSettingsManager getCurrentSettings() {
92fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey        synchronized (mLock) {
93fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey            return mCurrentSettings;
94fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey        }
95fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey    }
96fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey
9746d0adf8256a42416584765625852b6e48497c90Mike Lockwood    private boolean isBlackListed(String deviceName) {
9846d0adf8256a42416584765625852b6e48497c90Mike Lockwood        int count = mHostBlacklist.length;
9946d0adf8256a42416584765625852b6e48497c90Mike Lockwood        for (int i = 0; i < count; i++) {
10046d0adf8256a42416584765625852b6e48497c90Mike Lockwood            if (deviceName.startsWith(mHostBlacklist[i])) {
10146d0adf8256a42416584765625852b6e48497c90Mike Lockwood                return true;
10246d0adf8256a42416584765625852b6e48497c90Mike Lockwood            }
10346d0adf8256a42416584765625852b6e48497c90Mike Lockwood        }
10446d0adf8256a42416584765625852b6e48497c90Mike Lockwood        return false;
10546d0adf8256a42416584765625852b6e48497c90Mike Lockwood    }
10646d0adf8256a42416584765625852b6e48497c90Mike Lockwood
10746d0adf8256a42416584765625852b6e48497c90Mike Lockwood    /* returns true if the USB device should not be accessible by applications */
10846d0adf8256a42416584765625852b6e48497c90Mike Lockwood    private boolean isBlackListed(int clazz, int subClass, int protocol) {
10946d0adf8256a42416584765625852b6e48497c90Mike Lockwood        // blacklist hubs
11046d0adf8256a42416584765625852b6e48497c90Mike Lockwood        if (clazz == UsbConstants.USB_CLASS_HUB) return true;
11146d0adf8256a42416584765625852b6e48497c90Mike Lockwood
11246d0adf8256a42416584765625852b6e48497c90Mike Lockwood        // blacklist HID boot devices (mouse and keyboard)
11346d0adf8256a42416584765625852b6e48497c90Mike Lockwood        if (clazz == UsbConstants.USB_CLASS_HID &&
11446d0adf8256a42416584765625852b6e48497c90Mike Lockwood                subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT) {
11546d0adf8256a42416584765625852b6e48497c90Mike Lockwood            return true;
11646d0adf8256a42416584765625852b6e48497c90Mike Lockwood        }
11746d0adf8256a42416584765625852b6e48497c90Mike Lockwood
11846d0adf8256a42416584765625852b6e48497c90Mike Lockwood        return false;
11946d0adf8256a42416584765625852b6e48497c90Mike Lockwood    }
12046d0adf8256a42416584765625852b6e48497c90Mike Lockwood
121c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean    // Broadcasts the arrival/departure of a USB audio interface
122c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean    // card - the ALSA card number of the physical interface
123c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean    // device - the ALSA device number of the physical interface
124c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean    // enabled - if true, we're connecting a device (it's arrived), else disconnecting
125c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean    private void sendDeviceNotification(int card, int device, boolean enabled,
126df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            boolean hasPlayback, boolean hasCapture, boolean hasMIDI) {
127df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        // send a sticky broadcast containing current USB state
128c5258433dd353769ccfa2e5e769a7379378a3a0bJean-Michel Trivi        Intent intent = new Intent(AudioManager.ACTION_USB_AUDIO_DEVICE_PLUG);
129df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
130df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
131df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        intent.putExtra("state", enabled ? 1 : 0);
132df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        intent.putExtra("card", card);
133df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        intent.putExtra("device", device);
134df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        intent.putExtra("hasPlayback", hasPlayback);
135df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        intent.putExtra("hasCapture", hasCapture);
136df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        intent.putExtra("hasMIDI", hasMIDI);
137df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
138c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean    }
139c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean
140df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean    private boolean waitForAlsaFile(int card, int device, boolean capture) {
141df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        // These values were empirically determined.
142df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        final int kNumRetries = 5;
143df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        final int kSleepTime = 500; // ms
144df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        String alsaDevPath = "/dev/snd/pcmC" + card + "D" + device + (capture ? "c" : "p");
145df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        File alsaDevFile = new File(alsaDevPath);
146df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        boolean exists = false;
147df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        for (int retry = 0; !exists && retry < kNumRetries; retry++) {
148df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            exists = alsaDevFile.exists();
149df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            if (!exists) {
150df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                try {
151df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                    Thread.sleep(kSleepTime);
152df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                } catch (IllegalThreadStateException ex) {
153df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                    Slog.d(TAG, "usb: IllegalThreadStateException while waiting for ALSA file.");
154df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                } catch (java.lang.InterruptedException ex) {
155df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                    Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file.");
156df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                }
157df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            }
158df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        }
159df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean
160df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        return exists;
161c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean    }
162c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean
1637531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    /* Called from JNI in monitorUsbHostBus() to report new USB devices
1647531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood       Returns true if successful, in which case the JNI code will continue adding configurations,
1657531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood       interfaces and endpoints, and finally call endUsbDeviceAdded after all descriptors
1667531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood       have been processed
1677531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood     */
1687531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    private boolean beginUsbDeviceAdded(String deviceName, int vendorID, int productID,
16946d0adf8256a42416584765625852b6e48497c90Mike Lockwood            int deviceClass, int deviceSubclass, int deviceProtocol,
1707531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            String manufacturerName, String productName, String serialNumber) {
17146d0adf8256a42416584765625852b6e48497c90Mike Lockwood
172df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        if (DEBUG_AUDIO) {
173df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            Slog.d(TAG, "usb:UsbHostManager.beginUsbDeviceAdded(" + deviceName + ")");
174df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            // Audio Class Codes:
175df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            // Audio: 0x01
176df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            // Audio Subclass Codes:
177df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            // undefined: 0x00
178df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            // audio control: 0x01
179df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            // audio streaming: 0x02
180df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            // midi streaming: 0x03
181df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean
182df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            // some useful debugging info
183df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            Slog.d(TAG, "usb: nm:" + deviceName + " vnd:" + vendorID + " prd:" + productID + " cls:"
184df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                    + deviceClass + " sub:" + deviceSubclass + " proto:" + deviceProtocol);
185c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean        }
186c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean
187df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        // OK this is non-obvious, but true. One can't tell if the device being attached is even
188df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        // potentially an audio device without parsing the interface descriptors, so punt on any
189df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        // such test until endUsbDeviceAdded() when we have that info.
190df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean
19146d0adf8256a42416584765625852b6e48497c90Mike Lockwood        if (isBlackListed(deviceName) ||
19246d0adf8256a42416584765625852b6e48497c90Mike Lockwood                isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) {
1937531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            return false;
19446d0adf8256a42416584765625852b6e48497c90Mike Lockwood        }
19546d0adf8256a42416584765625852b6e48497c90Mike Lockwood
19646d0adf8256a42416584765625852b6e48497c90Mike Lockwood        synchronized (mLock) {
19746d0adf8256a42416584765625852b6e48497c90Mike Lockwood            if (mDevices.get(deviceName) != null) {
198fdc0c2984d05e32954608f46514c4cbe3a5a9424Mike Lockwood                Slog.w(TAG, "device already on mDevices list: " + deviceName);
1997531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood                return false;
20046d0adf8256a42416584765625852b6e48497c90Mike Lockwood            }
20146d0adf8256a42416584765625852b6e48497c90Mike Lockwood
2027531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            if (mNewDevice != null) {
2037531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood                Slog.e(TAG, "mNewDevice is not null in endUsbDeviceAdded");
2047531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood                return false;
20546d0adf8256a42416584765625852b6e48497c90Mike Lockwood            }
20646d0adf8256a42416584765625852b6e48497c90Mike Lockwood
2077531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            mNewDevice = new UsbDevice(deviceName, vendorID, productID,
208575ca85c147f1521480ea98aca13aa3b1ec38884Robin Cutshaw                    deviceClass, deviceSubclass, deviceProtocol,
2097531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood                    manufacturerName, productName, serialNumber);
2107531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood
2117531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            mNewConfigurations = new ArrayList<UsbConfiguration>();
2127531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            mNewInterfaces = new ArrayList<UsbInterface>();
2137531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            mNewEndpoints = new ArrayList<UsbEndpoint>();
2147531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        }
215df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean
2167531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        return true;
2177531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    }
2187531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood
2197531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    /* Called from JNI in monitorUsbHostBus() to report new USB configuration for the device
2207531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood       currently being added.  Returns true if successful, false in case of error.
2217531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood     */
2227531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    private void addUsbConfiguration(int id, String name, int attributes, int maxPower) {
2237531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        if (mNewConfiguration != null) {
2247531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            mNewConfiguration.setInterfaces(
2257531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood                    mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
2267531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            mNewInterfaces.clear();
2277531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        }
2287531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood
2297531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        mNewConfiguration = new UsbConfiguration(id, name, attributes, maxPower);
2307531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        mNewConfigurations.add(mNewConfiguration);
2317531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    }
2327531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood
2337531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    /* Called from JNI in monitorUsbHostBus() to report new USB interface for the device
2347531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood       currently being added.  Returns true if successful, false in case of error.
2357531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood     */
2367531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    private void addUsbInterface(int id, String name, int altSetting,
2377531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            int Class, int subClass, int protocol) {
2387531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        if (mNewInterface != null) {
2397531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            mNewInterface.setEndpoints(
2407531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood                    mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
2417531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            mNewEndpoints.clear();
2427531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        }
2437531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood
2447531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        mNewInterface = new UsbInterface(id, altSetting, name, Class, subClass, protocol);
2457531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        mNewInterfaces.add(mNewInterface);
2467531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    }
2477531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood
2487531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    /* Called from JNI in monitorUsbHostBus() to report new USB endpoint for the device
2497531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood       currently being added.  Returns true if successful, false in case of error.
2507531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood     */
2517531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    private void addUsbEndpoint(int address, int attributes, int maxPacketSize, int interval) {
2527531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        mNewEndpoints.add(new UsbEndpoint(address, attributes, maxPacketSize, interval));
2537531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    }
2547531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood
2557531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    /* Called from JNI in monitorUsbHostBus() to finish adding a new device */
2567531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood    private void endUsbDeviceAdded() {
257c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean        if (DEBUG_AUDIO) {
258c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean            Slog.d(TAG, "usb:UsbHostManager.endUsbDeviceAdded()");
259c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean        }
2607531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        if (mNewInterface != null) {
2617531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            mNewInterface.setEndpoints(
2627531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood                    mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
2637531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        }
2647531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        if (mNewConfiguration != null) {
2657531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            mNewConfiguration.setInterfaces(
2667531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood                    mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
2677531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        }
2687531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood
269df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        // Is there an audio interface in there?
270df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        final int kUsbClassId_Audio = 0x01;
271df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        boolean isAudioDevice = false;
272df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        for (int ntrfaceIndex = 0; !isAudioDevice && ntrfaceIndex < mNewInterfaces.size();
273df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                ntrfaceIndex++) {
274df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            UsbInterface ntrface = mNewInterfaces.get(ntrfaceIndex);
275df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            if (ntrface.getInterfaceClass() == kUsbClassId_Audio) {
276df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                isAudioDevice = true;
277df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            }
278df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        }
279df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean
2807531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood        synchronized (mLock) {
2817531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            if (mNewDevice != null) {
2827531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood                mNewDevice.setConfigurations(
2837531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood                        mNewConfigurations.toArray(new UsbConfiguration[mNewConfigurations.size()]));
2847531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood                mDevices.put(mNewDevice.getDeviceName(), mNewDevice);
2857531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood                Slog.d(TAG, "Added device " + mNewDevice);
2867531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood                getCurrentSettings().deviceAttached(mNewDevice);
2877531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            } else {
2887531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood                Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded");
2897531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            }
2907531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            mNewDevice = null;
2917531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            mNewConfigurations = null;
2927531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            mNewInterfaces = null;
2937531aa22355cf03f51def61ba67f1636bf85f408Mike Lockwood            mNewEndpoints = null;
29446d0adf8256a42416584765625852b6e48497c90Mike Lockwood        }
295df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean
296df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        if (!isAudioDevice) {
297df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            return; // bail
298df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        }
299df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean
300df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        //TODO(pmclean) The "Parser" objects inspect files in "/proc/asound" which we presume is
301df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        // present, unlike the waitForAlsaFile() which waits on a file in /dev/snd. It is not
302df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        // clear why this works, or that it can be relied on going forward.  Needs further
303df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        // research.
304df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        AlsaCardsParser cardsParser = new AlsaCardsParser();
305df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        cardsParser.scan();
306df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        // cardsParser.Log();
307df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean
308df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        // But we need to parse the device to determine its capabilities.
309df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        AlsaDevicesParser devicesParser = new AlsaDevicesParser();
310df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        devicesParser.scan();
311df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        // devicesParser.Log();
312df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean
313df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        // The protocol for now will be to select the last-connected (highest-numbered)
314df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        // Alsa Card.
315df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        mConnectedUsbCard = cardsParser.getNumCardRecords() - 1;
316df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        mConnectedUsbDeviceNum = 0;
317df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean
318df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        mConnectedHasPlayback = devicesParser.hasPlaybackDevices(mConnectedUsbCard);
319df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        mConnectedHasCapture = devicesParser.hasCaptureDevices(mConnectedUsbCard);
320df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        mConnectedHasMIDI = devicesParser.hasMIDIDevices(mConnectedUsbCard);
321df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean
322c7f084f61c3ad24acb35cde1854808285216d3dePaul McLean        // Playback device file needed/present?
323c7f084f61c3ad24acb35cde1854808285216d3dePaul McLean        if (mConnectedHasPlayback &&
324c7f084f61c3ad24acb35cde1854808285216d3dePaul McLean            !waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, false)) {
325c7f084f61c3ad24acb35cde1854808285216d3dePaul McLean            return;
326c7f084f61c3ad24acb35cde1854808285216d3dePaul McLean        }
327c7f084f61c3ad24acb35cde1854808285216d3dePaul McLean
328c7f084f61c3ad24acb35cde1854808285216d3dePaul McLean        // Capture device file needed/present?
329c7f084f61c3ad24acb35cde1854808285216d3dePaul McLean        if (mConnectedHasCapture &&
330c7f084f61c3ad24acb35cde1854808285216d3dePaul McLean            !waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, true)) {
331c7f084f61c3ad24acb35cde1854808285216d3dePaul McLean            return;
332c7f084f61c3ad24acb35cde1854808285216d3dePaul McLean        }
333c7f084f61c3ad24acb35cde1854808285216d3dePaul McLean
334df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        if (DEBUG_AUDIO) {
335df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            Slog.d(TAG,
336df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                    "usb: hasPlayback:" + mConnectedHasPlayback + " hasCapture:" + mConnectedHasCapture);
337df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        }
338df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean
339df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        sendDeviceNotification(mConnectedUsbCard,
340df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                mConnectedUsbDeviceNum,
341df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                true,
342df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                mConnectedHasPlayback,
343df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                mConnectedHasCapture,
344df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                mConnectedHasMIDI);
34546d0adf8256a42416584765625852b6e48497c90Mike Lockwood    }
34646d0adf8256a42416584765625852b6e48497c90Mike Lockwood
34746d0adf8256a42416584765625852b6e48497c90Mike Lockwood    /* Called from JNI in monitorUsbHostBus to report USB device removal */
34846d0adf8256a42416584765625852b6e48497c90Mike Lockwood    private void usbDeviceRemoved(String deviceName) {
349c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean        if (DEBUG_AUDIO) {
350c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean          Slog.d(TAG, "usb:UsbHostManager.usbDeviceRemoved() nm:" + deviceName);
351c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean        }
352c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean
353df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        if (mConnectedUsbCard != -1 && mConnectedUsbDeviceNum != -1) {
354df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            sendDeviceNotification(mConnectedUsbCard,
355df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                    mConnectedUsbDeviceNum,
356df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                    false,
357df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                    mConnectedHasPlayback,
358df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                    mConnectedHasCapture,
359df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean                    mConnectedHasMIDI);
360df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            mConnectedUsbCard = -1;
361df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            mConnectedUsbDeviceNum = -1;
362df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            mConnectedHasPlayback = false;
363df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            mConnectedHasCapture = false;
364df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean            mConnectedHasMIDI = false;
365df3614693dd4fe52a116dcd28bd74eae80818a4fPaul McLean        }
366c837a451946b64d70ed7c642fbde03c182c28b6fPaul McLean
36746d0adf8256a42416584765625852b6e48497c90Mike Lockwood        synchronized (mLock) {
36846d0adf8256a42416584765625852b6e48497c90Mike Lockwood            UsbDevice device = mDevices.remove(deviceName);
36946d0adf8256a42416584765625852b6e48497c90Mike Lockwood            if (device != null) {
370fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey                getCurrentSettings().deviceDetached(device);
37146d0adf8256a42416584765625852b6e48497c90Mike Lockwood            }
37246d0adf8256a42416584765625852b6e48497c90Mike Lockwood        }
37346d0adf8256a42416584765625852b6e48497c90Mike Lockwood    }
37446d0adf8256a42416584765625852b6e48497c90Mike Lockwood
37546d0adf8256a42416584765625852b6e48497c90Mike Lockwood    public void systemReady() {
37646d0adf8256a42416584765625852b6e48497c90Mike Lockwood        synchronized (mLock) {
37746d0adf8256a42416584765625852b6e48497c90Mike Lockwood            // Create a thread to call into native code to wait for USB host events.
37846d0adf8256a42416584765625852b6e48497c90Mike Lockwood            // This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
37946d0adf8256a42416584765625852b6e48497c90Mike Lockwood            Runnable runnable = new Runnable() {
38046d0adf8256a42416584765625852b6e48497c90Mike Lockwood                public void run() {
38146d0adf8256a42416584765625852b6e48497c90Mike Lockwood                    monitorUsbHostBus();
38246d0adf8256a42416584765625852b6e48497c90Mike Lockwood                }
38346d0adf8256a42416584765625852b6e48497c90Mike Lockwood            };
38446d0adf8256a42416584765625852b6e48497c90Mike Lockwood            new Thread(null, runnable, "UsbService host thread").start();
38546d0adf8256a42416584765625852b6e48497c90Mike Lockwood        }
38646d0adf8256a42416584765625852b6e48497c90Mike Lockwood    }
38746d0adf8256a42416584765625852b6e48497c90Mike Lockwood
38846d0adf8256a42416584765625852b6e48497c90Mike Lockwood    /* Returns a list of all currently attached USB devices */
38946d0adf8256a42416584765625852b6e48497c90Mike Lockwood    public void getDeviceList(Bundle devices) {
39046d0adf8256a42416584765625852b6e48497c90Mike Lockwood        synchronized (mLock) {
39146d0adf8256a42416584765625852b6e48497c90Mike Lockwood            for (String name : mDevices.keySet()) {
39246d0adf8256a42416584765625852b6e48497c90Mike Lockwood                devices.putParcelable(name, mDevices.get(name));
39346d0adf8256a42416584765625852b6e48497c90Mike Lockwood            }
39446d0adf8256a42416584765625852b6e48497c90Mike Lockwood        }
39546d0adf8256a42416584765625852b6e48497c90Mike Lockwood    }
39646d0adf8256a42416584765625852b6e48497c90Mike Lockwood
39746d0adf8256a42416584765625852b6e48497c90Mike Lockwood    /* Opens the specified USB device */
39846d0adf8256a42416584765625852b6e48497c90Mike Lockwood    public ParcelFileDescriptor openDevice(String deviceName) {
39946d0adf8256a42416584765625852b6e48497c90Mike Lockwood        synchronized (mLock) {
40046d0adf8256a42416584765625852b6e48497c90Mike Lockwood            if (isBlackListed(deviceName)) {
40146d0adf8256a42416584765625852b6e48497c90Mike Lockwood                throw new SecurityException("USB device is on a restricted bus");
40246d0adf8256a42416584765625852b6e48497c90Mike Lockwood            }
40346d0adf8256a42416584765625852b6e48497c90Mike Lockwood            UsbDevice device = mDevices.get(deviceName);
40446d0adf8256a42416584765625852b6e48497c90Mike Lockwood            if (device == null) {
40546d0adf8256a42416584765625852b6e48497c90Mike Lockwood                // if it is not in mDevices, it either does not exist or is blacklisted
40646d0adf8256a42416584765625852b6e48497c90Mike Lockwood                throw new IllegalArgumentException(
40746d0adf8256a42416584765625852b6e48497c90Mike Lockwood                        "device " + deviceName + " does not exist or is restricted");
40846d0adf8256a42416584765625852b6e48497c90Mike Lockwood            }
409fc3f24b4b60c10e0d3f41f70df37e11ea311cc2cJeff Sharkey            getCurrentSettings().checkPermission(device);
41046d0adf8256a42416584765625852b6e48497c90Mike Lockwood            return nativeOpenDevice(deviceName);
41146d0adf8256a42416584765625852b6e48497c90Mike Lockwood        }
41246d0adf8256a42416584765625852b6e48497c90Mike Lockwood    }
41346d0adf8256a42416584765625852b6e48497c90Mike Lockwood
41446d0adf8256a42416584765625852b6e48497c90Mike Lockwood    public void dump(FileDescriptor fd, PrintWriter pw) {
41546d0adf8256a42416584765625852b6e48497c90Mike Lockwood        synchronized (mLock) {
41646d0adf8256a42416584765625852b6e48497c90Mike Lockwood            pw.println("  USB Host State:");
41746d0adf8256a42416584765625852b6e48497c90Mike Lockwood            for (String name : mDevices.keySet()) {
41846d0adf8256a42416584765625852b6e48497c90Mike Lockwood                pw.println("    " + name + ": " + mDevices.get(name));
41946d0adf8256a42416584765625852b6e48497c90Mike Lockwood            }
42046d0adf8256a42416584765625852b6e48497c90Mike Lockwood        }
42146d0adf8256a42416584765625852b6e48497c90Mike Lockwood    }
42246d0adf8256a42416584765625852b6e48497c90Mike Lockwood
42346d0adf8256a42416584765625852b6e48497c90Mike Lockwood    private native void monitorUsbHostBus();
42446d0adf8256a42416584765625852b6e48497c90Mike Lockwood    private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
42546d0adf8256a42416584765625852b6e48497c90Mike Lockwood}
426