WiredAccessoryObserver.java revision 237171f8ab476f1d3f9f54777dba62a349d2009a
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 17package com.android.server; 18 19import android.app.ActivityManagerNative; 20import android.content.Context; 21import android.content.Intent; 22import android.os.Handler; 23import android.os.Message; 24import android.os.PowerManager; 25import android.os.PowerManager.WakeLock; 26import android.os.UEventObserver; 27import android.util.Slog; 28import android.media.AudioManager; 29import android.util.Log; 30 31import java.io.FileReader; 32import java.io.FileNotFoundException; 33 34/** 35 * <p>WiredAccessoryObserver monitors for a wired headset on the main board or dock. 36 */ 37class WiredAccessoryObserver extends UEventObserver { 38 private static final String TAG = WiredAccessoryObserver.class.getSimpleName(); 39 private static final boolean LOG = true; 40 private static final int MAX_AUDIO_PORTS = 3; /* h2w, USB Audio & hdmi */ 41 private static final String uEventInfo[][] = { {"DEVPATH=/devices/virtual/switch/h2w", 42 "/sys/class/switch/h2w/state", 43 "/sys/class/switch/h2w/name"}, 44 {"DEVPATH=/devices/virtual/switch/usb_audio", 45 "/sys/class/switch/usb_audio/state", 46 "/sys/class/switch/usb_audio/name"}, 47 {"DEVPATH=/devices/virtual/switch/hdmi", 48 "/sys/class/switch/hdmi/state", 49 "/sys/class/switch/hdmi/name"} }; 50 51 private static final int BIT_HEADSET = (1 << 0); 52 private static final int BIT_HEADSET_NO_MIC = (1 << 1); 53 private static final int BIT_USB_HEADSET_ANLG = (1 << 2); 54 private static final int BIT_USB_HEADSET_DGTL = (1 << 3); 55 private static final int BIT_HDMI_AUDIO = (1 << 4); 56 private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC| 57 BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL| 58 BIT_HDMI_AUDIO); 59 private static final int HEADSETS_WITH_MIC = BIT_HEADSET; 60 61 private int mHeadsetState; 62 private int mPrevHeadsetState; 63 private String mHeadsetName; 64 private int switchState; 65 66 private final Context mContext; 67 private final WakeLock mWakeLock; // held while there is a pending route change 68 69 public WiredAccessoryObserver(Context context) { 70 mContext = context; 71 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 72 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryObserver"); 73 mWakeLock.setReferenceCounted(false); 74 75 // At any given time both headsets could be inserted 76 // one on the board and one on the dock 77 // observe two UEVENTs 78 for (int i = 0; i < MAX_AUDIO_PORTS; i++) { 79 startObserving(uEventInfo[i][0]); 80 } 81 init(); // set initial status 82 } 83 84 @Override 85 public void onUEvent(UEventObserver.UEvent event) { 86 if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString()); 87 88 try { 89 if ((event.get("SWITCH_NAME")).equals("usb_audio")) { 90 if (Integer.parseInt(event.get("SWITCH_STATE")) == 1) { 91 switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC| 92 BIT_USB_HEADSET_DGTL)) | 93 (Integer.parseInt(event.get("SWITCH_STATE")) << 2)); 94 } else if (Integer.parseInt(event.get("SWITCH_STATE")) == 2) { 95 switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC| 96 BIT_USB_HEADSET_ANLG)) | 97 (Integer.parseInt(event.get("SWITCH_STATE")) << 3)); 98 } 99 else switchState = (mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC)); 100 } 101 else if ((event.get("SWITCH_NAME")).equals("hdmi")) { 102 switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC| 103 BIT_USB_HEADSET_DGTL|BIT_USB_HEADSET_ANLG)) | 104 (Integer.parseInt(event.get("SWITCH_STATE")) << 4)); 105 } 106 else { 107 switchState = ((mHeadsetState & (BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL)) | 108 (Integer.parseInt(event.get("SWITCH_STATE")))); 109 } 110 update(event.get("SWITCH_NAME"), switchState); 111 } catch (NumberFormatException e) { 112 Slog.e(TAG, "Could not parse switch state from event " + event); 113 } 114 } 115 116 private synchronized final void init() { 117 char[] buffer = new char[1024]; 118 119 String newName = mHeadsetName; 120 int newState = mHeadsetState; 121 mPrevHeadsetState = mHeadsetState; 122 123 for (int i = 0; i < MAX_AUDIO_PORTS; i++) { 124 try { 125 FileReader file = new FileReader(uEventInfo[i][1]); 126 int len = file.read(buffer, 0, 1024); 127 file.close(); 128 newState = Integer.valueOf((new String(buffer, 0, len)).trim()); 129 130 file = new FileReader(uEventInfo[i][2]); 131 len = file.read(buffer, 0, 1024); 132 file.close(); 133 newName = new String(buffer, 0, len).trim(); 134 135 } catch (FileNotFoundException e) { 136 Slog.w(TAG, "This kernel does not have wired headset support"); 137 } catch (Exception e) { 138 Slog.e(TAG, "" , e); 139 } 140 141 update(newName, newState); 142 } 143 } 144 145 private synchronized final void update(String newName, int newState) { 146 // Retain only relevant bits 147 int headsetState = newState & SUPPORTED_HEADSETS; 148 int newOrOld = headsetState | mHeadsetState; 149 int delay = 0; 150 int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG; 151 int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL; 152 int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC); 153 boolean h2wStateChange = true; 154 boolean usbStateChange = true; 155 // reject all suspect transitions: only accept state changes from: 156 // - a: 0 heaset to 1 headset 157 // - b: 1 headset to 0 headset 158 Log.v(TAG, "newState = "+newState+", headsetState = "+headsetState+", mHeadsetState = "+mHeadsetState); 159 if (mHeadsetState == headsetState || ((h2w_headset & (h2w_headset - 1)) != 0)) { 160 Log.e(TAG, "unsetting h2w flag"); 161 h2wStateChange = false; 162 } 163 // - c: 0 usb headset to 1 usb headset 164 // - d: 1 usb headset to 0 usb headset 165 if ((usb_headset_anlg >> 2) == 1 && (usb_headset_dgtl >> 3) == 1) { 166 Log.e(TAG, "unsetting usb flag"); 167 usbStateChange = false; 168 } 169 if (!h2wStateChange && !usbStateChange) { 170 Log.e(TAG, "invalid transition, returning ..."); 171 return; 172 } 173 174 mHeadsetName = newName; 175 mPrevHeadsetState = mHeadsetState; 176 mHeadsetState = headsetState; 177 178 if (headsetState == 0) { 179 Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); 180 mContext.sendBroadcast(intent); 181 // It can take hundreds of ms flush the audio pipeline after 182 // apps pause audio playback, but audio route changes are 183 // immediate, so delay the route change by 1000ms. 184 // This could be improved once the audio sub-system provides an 185 // interface to clear the audio pipeline. 186 delay = 1000; 187 } else { 188 // Insert the same delay for headset connection so that the connection event is not 189 // broadcast before the disconnection event in case of fast removal/insertion 190 if (mHandler.hasMessages(0)) { 191 delay = 1000; 192 } 193 } 194 mWakeLock.acquire(); 195 mHandler.sendMessageDelayed(mHandler.obtainMessage(0, 196 mHeadsetState, 197 mPrevHeadsetState, 198 mHeadsetName), 199 delay); 200 } 201 202 private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) { 203 int allHeadsets = SUPPORTED_HEADSETS; 204 for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) { 205 if ((curHeadset & allHeadsets) != 0) { 206 sendIntent(curHeadset, headsetState, prevHeadsetState, headsetName); 207 allHeadsets &= ~curHeadset; 208 } 209 } 210 } 211 212 private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) { 213 if ((headsetState & headset) != (prevHeadsetState & headset)) { 214 215 int state = 0; 216 if ((headsetState & headset) != 0) { 217 state = 1; 218 } 219 if((headset == BIT_USB_HEADSET_ANLG) || (headset == BIT_USB_HEADSET_DGTL) || 220 (headset == BIT_HDMI_AUDIO)) { 221 Intent intent; 222 223 // Pack up the values and broadcast them to everyone 224 if (headset == BIT_USB_HEADSET_ANLG) { 225 intent = new Intent(Intent.ACTION_USB_ANLG_HEADSET_PLUG); 226 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 227 intent.putExtra("state", state); 228 intent.putExtra("name", headsetName); 229 ActivityManagerNative.broadcastStickyIntent(intent, null); 230 } else if (headset == BIT_USB_HEADSET_DGTL) { 231 intent = new Intent(Intent.ACTION_USB_DGTL_HEADSET_PLUG); 232 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 233 intent.putExtra("state", state); 234 intent.putExtra("name", headsetName); 235 ActivityManagerNative.broadcastStickyIntent(intent, null); 236 } else if (headset == BIT_HDMI_AUDIO) { 237 intent = new Intent(Intent.ACTION_HDMI_AUDIO_PLUG); 238 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 239 intent.putExtra("state", state); 240 intent.putExtra("name", headsetName); 241 ActivityManagerNative.broadcastStickyIntent(intent, null); 242 } 243 244 if (LOG) Slog.v(TAG, "Intent.ACTION_USB_HEADSET_PLUG: state: "+state+" name: "+headsetName); 245 // TODO: Should we require a permission? 246 } 247 if((headset == BIT_HEADSET) || (headset == BIT_HEADSET_NO_MIC)) { 248 249 // Pack up the values and broadcast them to everyone 250 Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG); 251 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 252 //int state = 0; 253 int microphone = 0; 254 255 if ((headset & HEADSETS_WITH_MIC) != 0) { 256 microphone = 1; 257 } 258 259 intent.putExtra("state", state); 260 intent.putExtra("name", headsetName); 261 intent.putExtra("microphone", microphone); 262 263 if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone); 264 // TODO: Should we require a permission? 265 ActivityManagerNative.broadcastStickyIntent(intent, null); 266 } 267 } 268 } 269 270 private final Handler mHandler = new Handler() { 271 @Override 272 public void handleMessage(Message msg) { 273 sendIntents(msg.arg1, msg.arg2, (String)msg.obj); 274 mWakeLock.release(); 275 } 276 }; 277} 278