Home | History | Annotate | Download | only in server
      1 /*
      2  * Copyright (C) 2008 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;
     18 
     19 import android.app.ActivityManagerNative;
     20 import android.content.BroadcastReceiver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.IntentFilter;
     24 import android.os.Handler;
     25 import android.os.Message;
     26 import android.os.PowerManager;
     27 import android.os.PowerManager.WakeLock;
     28 import android.os.UEventObserver;
     29 import android.util.Slog;
     30 import android.media.AudioManager;
     31 import android.util.Log;
     32 
     33 import java.io.File;
     34 import java.io.FileReader;
     35 import java.io.FileNotFoundException;
     36 import java.util.ArrayList;
     37 import java.util.List;
     38 
     39 /**
     40  * <p>WiredAccessoryObserver monitors for a wired headset on the main board or dock.
     41  */
     42 class WiredAccessoryObserver extends UEventObserver {
     43     private static final String TAG = WiredAccessoryObserver.class.getSimpleName();
     44     private static final boolean LOG = true;
     45     private static final int BIT_HEADSET = (1 << 0);
     46     private static final int BIT_HEADSET_NO_MIC = (1 << 1);
     47     private static final int BIT_USB_HEADSET_ANLG = (1 << 2);
     48     private static final int BIT_USB_HEADSET_DGTL = (1 << 3);
     49     private static final int BIT_HDMI_AUDIO = (1 << 4);
     50     private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC|
     51                                                    BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL|
     52                                                    BIT_HDMI_AUDIO);
     53     private static final int HEADSETS_WITH_MIC = BIT_HEADSET;
     54 
     55     private static class UEventInfo {
     56         private final String mDevName;
     57         private final int mState1Bits;
     58         private final int mState2Bits;
     59 
     60         public UEventInfo(String devName, int state1Bits, int state2Bits) {
     61             mDevName = devName;
     62             mState1Bits = state1Bits;
     63             mState2Bits = state2Bits;
     64         }
     65 
     66         public String getDevName() { return mDevName; }
     67 
     68         public String getDevPath() {
     69             return String.format("/devices/virtual/switch/%s", mDevName);
     70         }
     71 
     72         public String getSwitchStatePath() {
     73             return String.format("/sys/class/switch/%s/state", mDevName);
     74         }
     75 
     76         public boolean checkSwitchExists() {
     77             File f = new File(getSwitchStatePath());
     78             return ((null != f) && f.exists());
     79         }
     80 
     81         public int computeNewHeadsetState(int headsetState, int switchState) {
     82             int preserveMask = ~(mState1Bits | mState2Bits);
     83             int setBits = ((switchState == 1) ? mState1Bits :
     84                           ((switchState == 2) ? mState2Bits : 0));
     85 
     86             return ((headsetState & preserveMask) | setBits);
     87         }
     88     }
     89 
     90     private static List<UEventInfo> makeObservedUEventList() {
     91         List<UEventInfo> retVal = new ArrayList<UEventInfo>();
     92         UEventInfo uei;
     93 
     94         // Monitor h2w
     95         uei = new UEventInfo("h2w", BIT_HEADSET, BIT_HEADSET_NO_MIC);
     96         if (uei.checkSwitchExists()) {
     97             retVal.add(uei);
     98         } else {
     99             Slog.w(TAG, "This kernel does not have wired headset support");
    100         }
    101 
    102         // Monitor USB
    103         uei = new UEventInfo("usb_audio", BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL);
    104         if (uei.checkSwitchExists()) {
    105             retVal.add(uei);
    106         } else {
    107             Slog.w(TAG, "This kernel does not have usb audio support");
    108         }
    109 
    110         // Monitor HDMI
    111         //
    112         // If the kernel has support for the "hdmi_audio" switch, use that.  It will be signalled
    113         // only when the HDMI driver has a video mode configured, and the downstream sink indicates
    114         // support for audio in its EDID.
    115         //
    116         // If the kernel does not have an "hdmi_audio" switch, just fall back on the older "hdmi"
    117         // switch instead.
    118         uei = new UEventInfo("hdmi_audio", BIT_HDMI_AUDIO, 0);
    119         if (uei.checkSwitchExists()) {
    120             retVal.add(uei);
    121         } else {
    122             uei = new UEventInfo("hdmi", BIT_HDMI_AUDIO, 0);
    123             if (uei.checkSwitchExists()) {
    124                 retVal.add(uei);
    125             } else {
    126                 Slog.w(TAG, "This kernel does not have HDMI audio support");
    127             }
    128         }
    129 
    130         return retVal;
    131     }
    132 
    133     private static List<UEventInfo> uEventInfo = makeObservedUEventList();
    134 
    135     private int mHeadsetState;
    136     private int mPrevHeadsetState;
    137     private String mHeadsetName;
    138 
    139     private final Context mContext;
    140     private final WakeLock mWakeLock;  // held while there is a pending route change
    141 
    142     private final AudioManager mAudioManager;
    143 
    144     public WiredAccessoryObserver(Context context) {
    145         mContext = context;
    146         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
    147         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryObserver");
    148         mWakeLock.setReferenceCounted(false);
    149         mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
    150 
    151         context.registerReceiver(new BootCompletedReceiver(),
    152             new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
    153     }
    154 
    155     private final class BootCompletedReceiver extends BroadcastReceiver {
    156       @Override
    157       public void onReceive(Context context, Intent intent) {
    158         // At any given time accessories could be inserted
    159         // one on the board, one on the dock and one on HDMI:
    160         // observe three UEVENTs
    161         init();  // set initial status
    162         for (int i = 0; i < uEventInfo.size(); ++i) {
    163             UEventInfo uei = uEventInfo.get(i);
    164             startObserving("DEVPATH="+uei.getDevPath());
    165         }
    166       }
    167     }
    168 
    169     @Override
    170     public void onUEvent(UEventObserver.UEvent event) {
    171         if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
    172 
    173         try {
    174             String devPath = event.get("DEVPATH");
    175             String name = event.get("SWITCH_NAME");
    176             int state = Integer.parseInt(event.get("SWITCH_STATE"));
    177             updateState(devPath, name, state);
    178         } catch (NumberFormatException e) {
    179             Slog.e(TAG, "Could not parse switch state from event " + event);
    180         }
    181     }
    182 
    183     private synchronized final void updateState(String devPath, String name, int state)
    184     {
    185         for (int i = 0; i < uEventInfo.size(); ++i) {
    186             UEventInfo uei = uEventInfo.get(i);
    187             if (devPath.equals(uei.getDevPath())) {
    188                 update(name, uei.computeNewHeadsetState(mHeadsetState, state));
    189                 return;
    190             }
    191         }
    192     }
    193 
    194     private synchronized final void init() {
    195         char[] buffer = new char[1024];
    196         mPrevHeadsetState = mHeadsetState;
    197 
    198         if (LOG) Slog.v(TAG, "init()");
    199 
    200         for (int i = 0; i < uEventInfo.size(); ++i) {
    201             UEventInfo uei = uEventInfo.get(i);
    202             try {
    203                 int curState;
    204                 FileReader file = new FileReader(uei.getSwitchStatePath());
    205                 int len = file.read(buffer, 0, 1024);
    206                 file.close();
    207                 curState = Integer.valueOf((new String(buffer, 0, len)).trim());
    208 
    209                 if (curState > 0) {
    210                     updateState(uei.getDevPath(), uei.getDevName(), curState);
    211                 }
    212 
    213             } catch (FileNotFoundException e) {
    214                 Slog.w(TAG, uei.getSwitchStatePath() +
    215                         " not found while attempting to determine initial switch state");
    216             } catch (Exception e) {
    217                 Slog.e(TAG, "" , e);
    218             }
    219         }
    220     }
    221 
    222     private synchronized final void update(String newName, int newState) {
    223         // Retain only relevant bits
    224         int headsetState = newState & SUPPORTED_HEADSETS;
    225         int newOrOld = headsetState | mHeadsetState;
    226         int delay = 0;
    227         int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG;
    228         int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL;
    229         int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC);
    230         boolean h2wStateChange = true;
    231         boolean usbStateChange = true;
    232         // reject all suspect transitions: only accept state changes from:
    233         // - a: 0 heaset to 1 headset
    234         // - b: 1 headset to 0 headset
    235         if (LOG) Slog.v(TAG, "newState = "+newState+", headsetState = "+headsetState+","
    236             + "mHeadsetState = "+mHeadsetState);
    237         if (mHeadsetState == headsetState || ((h2w_headset & (h2w_headset - 1)) != 0)) {
    238             Log.e(TAG, "unsetting h2w flag");
    239             h2wStateChange = false;
    240         }
    241         // - c: 0 usb headset to 1 usb headset
    242         // - d: 1 usb headset to 0 usb headset
    243         if ((usb_headset_anlg >> 2) == 1 && (usb_headset_dgtl >> 3) == 1) {
    244             Log.e(TAG, "unsetting usb flag");
    245             usbStateChange = false;
    246         }
    247         if (!h2wStateChange && !usbStateChange) {
    248             Log.e(TAG, "invalid transition, returning ...");
    249             return;
    250         }
    251 
    252         mHeadsetName = newName;
    253         mPrevHeadsetState = mHeadsetState;
    254         mHeadsetState = headsetState;
    255 
    256         mWakeLock.acquire();
    257         mHandler.sendMessage(mHandler.obtainMessage(0,
    258                                                     mHeadsetState,
    259                                                     mPrevHeadsetState,
    260                                                     mHeadsetName));
    261     }
    262 
    263     private synchronized final void setDevicesState(int headsetState,
    264                                                     int prevHeadsetState,
    265                                                     String headsetName) {
    266         int allHeadsets = SUPPORTED_HEADSETS;
    267         for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
    268             if ((curHeadset & allHeadsets) != 0) {
    269                 setDeviceState(curHeadset, headsetState, prevHeadsetState, headsetName);
    270                 allHeadsets &= ~curHeadset;
    271             }
    272         }
    273     }
    274 
    275     private final void setDeviceState(int headset,
    276                                       int headsetState,
    277                                       int prevHeadsetState,
    278                                       String headsetName) {
    279         if ((headsetState & headset) != (prevHeadsetState & headset)) {
    280             int device;
    281             int state;
    282 
    283             if ((headsetState & headset) != 0) {
    284                 state = 1;
    285             } else {
    286                 state = 0;
    287             }
    288 
    289             if (headset == BIT_HEADSET) {
    290                 device = AudioManager.DEVICE_OUT_WIRED_HEADSET;
    291             } else if (headset == BIT_HEADSET_NO_MIC){
    292                 device = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;
    293             } else if (headset == BIT_USB_HEADSET_ANLG) {
    294                 device = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;
    295             } else if (headset == BIT_USB_HEADSET_DGTL) {
    296                 device = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET;
    297             } else if (headset == BIT_HDMI_AUDIO) {
    298                 device = AudioManager.DEVICE_OUT_AUX_DIGITAL;
    299             } else {
    300                 Slog.e(TAG, "setDeviceState() invalid headset type: "+headset);
    301                 return;
    302             }
    303 
    304             if (LOG)
    305                 Slog.v(TAG, "device "+headsetName+((state == 1) ? " connected" : " disconnected"));
    306 
    307             mAudioManager.setWiredDeviceConnectionState(device, state, headsetName);
    308         }
    309     }
    310 
    311     private final Handler mHandler = new Handler() {
    312         @Override
    313         public void handleMessage(Message msg) {
    314             setDevicesState(msg.arg1, msg.arg2, (String)msg.obj);
    315             mWakeLock.release();
    316         }
    317     };
    318 }
    319