Home | History | Annotate | Download | only in usb
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.usb;
     18 
     19 import android.annotation.NonNull;
     20 import android.media.AudioSystem;
     21 import android.media.IAudioService;
     22 import android.os.RemoteException;
     23 import android.service.usb.UsbAlsaDeviceProto;
     24 import android.util.Slog;
     25 
     26 import com.android.internal.util.dump.DualDumpOutputStream;
     27 import com.android.server.audio.AudioService;
     28 
     29 /**
     30  * Represents the ALSA specification, and attributes of an ALSA device.
     31  */
     32 public final class UsbAlsaDevice {
     33     private static final String TAG = "UsbAlsaDevice";
     34     protected static final boolean DEBUG = false;
     35 
     36     private final int mCardNum;
     37     private final int mDeviceNum;
     38     private final String mDeviceAddress;
     39     private final boolean mHasOutput;
     40     private final boolean mHasInput;
     41 
     42     private final boolean mIsInputHeadset;
     43     private final boolean mIsOutputHeadset;
     44 
     45     private boolean mSelected = false;
     46     private int mOutputState;
     47     private int mInputState;
     48     private UsbAlsaJackDetector mJackDetector;
     49     private IAudioService mAudioService;
     50 
     51     private String mDeviceName = "";
     52     private String mDeviceDescription = "";
     53 
     54     public UsbAlsaDevice(IAudioService audioService, int card, int device, String deviceAddress,
     55             boolean hasOutput, boolean hasInput,
     56             boolean isInputHeadset, boolean isOutputHeadset) {
     57         mAudioService = audioService;
     58         mCardNum = card;
     59         mDeviceNum = device;
     60         mDeviceAddress = deviceAddress;
     61         mHasOutput = hasOutput;
     62         mHasInput = hasInput;
     63         mIsInputHeadset = isInputHeadset;
     64         mIsOutputHeadset = isOutputHeadset;
     65     }
     66 
     67     /**
     68      * @returns the ALSA card number associated with this peripheral.
     69      */
     70     public int getCardNum() {
     71         return mCardNum;
     72     }
     73 
     74     /**
     75      * @returns the ALSA device number associated with this peripheral.
     76      */
     77     public int getDeviceNum() {
     78         return mDeviceNum;
     79     }
     80 
     81     /**
     82      * @returns the USB device device address associated with this peripheral.
     83      */
     84     public String getDeviceAddress() {
     85         return mDeviceAddress;
     86     }
     87 
     88     /**
     89      * @returns the ALSA card/device address string.
     90      */
     91     public String getAlsaCardDeviceString() {
     92         if (mCardNum < 0 || mDeviceNum < 0) {
     93             Slog.e(TAG, "Invalid alsa card or device alsaCard: " + mCardNum
     94                         + " alsaDevice: " + mDeviceNum);
     95             return null;
     96         }
     97         return AudioService.makeAlsaAddressString(mCardNum, mDeviceNum);
     98     }
     99 
    100     /**
    101      * @returns true if the device supports output.
    102      */
    103     public boolean hasOutput() {
    104         return mHasOutput;
    105     }
    106 
    107     /**
    108      * @returns true if the device supports input (recording).
    109      */
    110     public boolean hasInput() {
    111         return mHasInput;
    112     }
    113 
    114     /**
    115      * @returns true if the device is a headset for purposes of input.
    116      */
    117     public boolean isInputHeadset() {
    118         return mIsInputHeadset;
    119     }
    120 
    121     /**
    122      * @returns true if the device is a headset for purposes of output.
    123      */
    124     public boolean isOutputHeadset() {
    125         return mIsOutputHeadset;
    126     }
    127 
    128     /**
    129      * @returns true if input jack is detected or jack detection is not supported.
    130      */
    131     private synchronized boolean isInputJackConnected() {
    132         if (mJackDetector == null) {
    133             return true;  // If jack detect isn't supported, say it's connected.
    134         }
    135         return mJackDetector.isInputJackConnected();
    136     }
    137 
    138     /**
    139      * @returns true if input jack is detected or jack detection is not supported.
    140      */
    141     private synchronized boolean isOutputJackConnected() {
    142         if (mJackDetector == null) {
    143             return true;  // if jack detect isn't supported, say it's connected.
    144         }
    145         return mJackDetector.isOutputJackConnected();
    146     }
    147 
    148     /** Begins a jack-detection thread. */
    149     private synchronized void startJackDetect() {
    150         // If no jack detect capabilities exist, mJackDetector will be null.
    151         mJackDetector = UsbAlsaJackDetector.startJackDetect(this);
    152     }
    153 
    154     /** Stops a jack-detection thread. */
    155     private synchronized void stopJackDetect() {
    156         if (mJackDetector != null) {
    157             mJackDetector.pleaseStop();
    158         }
    159         mJackDetector = null;
    160     }
    161 
    162     /** Start using this device as the selected USB Audio Device. */
    163     public synchronized void start() {
    164         mSelected = true;
    165         mInputState = 0;
    166         mOutputState = 0;
    167         startJackDetect();
    168         updateWiredDeviceConnectionState(true);
    169     }
    170 
    171     /** Stop using this device as the selected USB Audio Device. */
    172     public synchronized void stop() {
    173         stopJackDetect();
    174         updateWiredDeviceConnectionState(false);
    175         mSelected = false;
    176     }
    177 
    178     /** Updates AudioService with the connection state of the alsaDevice.
    179      *  Checks ALSA Jack state for inputs and outputs before reporting.
    180      */
    181     public synchronized void updateWiredDeviceConnectionState(boolean enable) {
    182         if (!mSelected) {
    183             Slog.e(TAG, "updateWiredDeviceConnectionState on unselected AlsaDevice!");
    184             return;
    185         }
    186         String alsaCardDeviceString = getAlsaCardDeviceString();
    187         if (alsaCardDeviceString == null) {
    188             return;
    189         }
    190         try {
    191             // Output Device
    192             if (mHasOutput) {
    193                 int device = mIsOutputHeadset
    194                         ? AudioSystem.DEVICE_OUT_USB_HEADSET
    195                         : AudioSystem.DEVICE_OUT_USB_DEVICE;
    196                 if (DEBUG) {
    197                     Slog.d(TAG, "pre-call device:0x" + Integer.toHexString(device)
    198                             + " addr:" + alsaCardDeviceString
    199                             + " name:" + mDeviceName);
    200                 }
    201                 boolean connected = isOutputJackConnected();
    202                 Slog.i(TAG, "OUTPUT JACK connected: " + connected);
    203                 int outputState = (enable && connected) ? 1 : 0;
    204                 if (outputState != mOutputState) {
    205                     mOutputState = outputState;
    206                     mAudioService.setWiredDeviceConnectionState(device, outputState,
    207                                                                 alsaCardDeviceString,
    208                                                                 mDeviceName, TAG);
    209                 }
    210             }
    211 
    212             // Input Device
    213             if (mHasInput) {
    214                 int device = mIsInputHeadset ? AudioSystem.DEVICE_IN_USB_HEADSET
    215                         : AudioSystem.DEVICE_IN_USB_DEVICE;
    216                 boolean connected = isInputJackConnected();
    217                 Slog.i(TAG, "INPUT JACK connected: " + connected);
    218                 int inputState = (enable && connected) ? 1 : 0;
    219                 if (inputState != mInputState) {
    220                     mInputState = inputState;
    221                     mAudioService.setWiredDeviceConnectionState(
    222                             device, inputState, alsaCardDeviceString,
    223                             mDeviceName, TAG);
    224                 }
    225             }
    226         } catch (RemoteException e) {
    227             Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState");
    228         }
    229     }
    230 
    231 
    232     /**
    233      * @Override
    234      * @returns a string representation of the object.
    235      */
    236     public synchronized String toString() {
    237         return "UsbAlsaDevice: [card: " + mCardNum
    238             + ", device: " + mDeviceNum
    239             + ", name: " + mDeviceName
    240             + ", hasOutput: " + mHasOutput
    241             + ", hasInput: " + mHasInput + "]";
    242     }
    243 
    244     /**
    245      * Write a description of the device to a dump stream.
    246      */
    247     public synchronized void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
    248         long token = dump.start(idName, id);
    249 
    250         dump.write("card", UsbAlsaDeviceProto.CARD, mCardNum);
    251         dump.write("device", UsbAlsaDeviceProto.DEVICE, mDeviceNum);
    252         dump.write("name", UsbAlsaDeviceProto.NAME, mDeviceName);
    253         dump.write("has_output", UsbAlsaDeviceProto.HAS_PLAYBACK, mHasOutput);
    254         dump.write("has_input", UsbAlsaDeviceProto.HAS_CAPTURE, mHasInput);
    255         dump.write("address", UsbAlsaDeviceProto.ADDRESS, mDeviceAddress);
    256 
    257         dump.end(token);
    258     }
    259 
    260     // called by logDevices
    261     synchronized String toShortString() {
    262         return "[card:" + mCardNum + " device:" + mDeviceNum + " " + mDeviceName + "]";
    263     }
    264 
    265     synchronized String getDeviceName() {
    266         return mDeviceName;
    267     }
    268 
    269     synchronized void setDeviceNameAndDescription(String deviceName, String deviceDescription) {
    270         mDeviceName = deviceName;
    271         mDeviceDescription = deviceDescription;
    272     }
    273 
    274     /**
    275      * @Override
    276      * @returns true if the objects are equivalent.
    277      */
    278     public boolean equals(Object obj) {
    279         if (!(obj instanceof UsbAlsaDevice)) {
    280             return false;
    281         }
    282         UsbAlsaDevice other = (UsbAlsaDevice) obj;
    283         return (mCardNum == other.mCardNum
    284                 && mDeviceNum == other.mDeviceNum
    285                 && mHasOutput == other.mHasOutput
    286                 && mHasInput == other.mHasInput
    287                 && mIsInputHeadset == other.mIsInputHeadset
    288                 && mIsOutputHeadset == other.mIsOutputHeadset);
    289     }
    290 
    291     /**
    292      * @Override
    293      * @returns a hash code generated from the object contents.
    294      */
    295     public int hashCode() {
    296         final int prime = 31;
    297         int result = 1;
    298         result = prime * result + mCardNum;
    299         result = prime * result + mDeviceNum;
    300         result = prime * result + (mHasOutput ? 0 : 1);
    301         result = prime * result + (mHasInput ? 0 : 1);
    302         result = prime * result + (mIsInputHeadset ? 0 : 1);
    303         result = prime * result + (mIsOutputHeadset ? 0 : 1);
    304 
    305         return result;
    306     }
    307 }
    308 
    309