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.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.os.Handler; 24 import android.os.Looper; 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 import android.view.InputDevice; 33 34 import com.android.internal.R; 35 import com.android.server.input.InputManagerService; 36 import com.android.server.input.InputManagerService.WiredAccessoryCallbacks; 37 import static com.android.server.input.InputManagerService.SW_HEADPHONE_INSERT; 38 import static com.android.server.input.InputManagerService.SW_MICROPHONE_INSERT; 39 import static com.android.server.input.InputManagerService.SW_HEADPHONE_INSERT_BIT; 40 import static com.android.server.input.InputManagerService.SW_MICROPHONE_INSERT_BIT; 41 42 import java.io.File; 43 import java.io.FileReader; 44 import java.io.FileNotFoundException; 45 import java.util.ArrayList; 46 import java.util.List; 47 48 /** 49 * <p>WiredAccessoryManager monitors for a wired headset on the main board or dock using 50 * both the InputManagerService notifyWiredAccessoryChanged interface and the UEventObserver 51 * subsystem. 52 */ 53 final class WiredAccessoryManager implements WiredAccessoryCallbacks { 54 private static final String TAG = WiredAccessoryManager.class.getSimpleName(); 55 private static final boolean LOG = true; 56 57 private static final int BIT_HEADSET = (1 << 0); 58 private static final int BIT_HEADSET_NO_MIC = (1 << 1); 59 private static final int BIT_USB_HEADSET_ANLG = (1 << 2); 60 private static final int BIT_USB_HEADSET_DGTL = (1 << 3); 61 private static final int BIT_HDMI_AUDIO = (1 << 4); 62 private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC| 63 BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL| 64 BIT_HDMI_AUDIO); 65 66 private static final String NAME_H2W = "h2w"; 67 private static final String NAME_USB_AUDIO = "usb_audio"; 68 private static final String NAME_HDMI_AUDIO = "hdmi_audio"; 69 private static final String NAME_HDMI = "hdmi"; 70 71 private static final int MSG_NEW_DEVICE_STATE = 1; 72 73 private final Object mLock = new Object(); 74 75 private final WakeLock mWakeLock; // held while there is a pending route change 76 private final AudioManager mAudioManager; 77 78 private int mHeadsetState; 79 80 private int mSwitchValues; 81 82 private final WiredAccessoryObserver mObserver; 83 private final InputManagerService mInputManager; 84 85 private final boolean mUseDevInputEventForAudioJack; 86 87 public WiredAccessoryManager(Context context, InputManagerService inputManager) { 88 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 89 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryManager"); 90 mWakeLock.setReferenceCounted(false); 91 mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); 92 mInputManager = inputManager; 93 94 mUseDevInputEventForAudioJack = 95 context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); 96 97 mObserver = new WiredAccessoryObserver(); 98 99 context.registerReceiver(new BroadcastReceiver() { 100 @Override 101 public void onReceive(Context ctx, Intent intent) { 102 bootCompleted(); 103 } 104 }, 105 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); 106 } 107 108 private void bootCompleted() { 109 if (mUseDevInputEventForAudioJack) { 110 int switchValues = 0; 111 if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_HEADPHONE_INSERT) == 1) { 112 switchValues |= SW_HEADPHONE_INSERT_BIT; 113 } 114 if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_MICROPHONE_INSERT) == 1) { 115 switchValues |= SW_MICROPHONE_INSERT_BIT; 116 } 117 notifyWiredAccessoryChanged(0, switchValues, 118 SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT); 119 } 120 121 mObserver.init(); 122 } 123 124 @Override 125 public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask) { 126 if (LOG) Slog.v(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos 127 + " bits=" + switchCodeToString(switchValues, switchMask) 128 + " mask=" + Integer.toHexString(switchMask)); 129 130 synchronized (mLock) { 131 int headset; 132 mSwitchValues = (mSwitchValues & ~switchMask) | switchValues; 133 switch (mSwitchValues & (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT)) { 134 case 0: 135 headset = 0; 136 break; 137 138 case SW_HEADPHONE_INSERT_BIT: 139 headset = BIT_HEADSET_NO_MIC; 140 break; 141 142 case SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT: 143 headset = BIT_HEADSET; 144 break; 145 146 case SW_MICROPHONE_INSERT_BIT: 147 headset = BIT_HEADSET; 148 break; 149 150 default: 151 headset = 0; 152 break; 153 } 154 155 updateLocked(NAME_H2W, (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC)) | headset); 156 } 157 } 158 159 /** 160 * Compare the existing headset state with the new state and pass along accordingly. Note 161 * that this only supports a single headset at a time. Inserting both a usb and jacked headset 162 * results in support for the last one plugged in. Similarly, unplugging either is seen as 163 * unplugging all. 164 * 165 * @param newName One of the NAME_xxx variables defined above. 166 * @param newState 0 or one of the BIT_xxx variables defined above. 167 */ 168 private void updateLocked(String newName, int newState) { 169 // Retain only relevant bits 170 int headsetState = newState & SUPPORTED_HEADSETS; 171 int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG; 172 int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL; 173 int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC); 174 boolean h2wStateChange = true; 175 boolean usbStateChange = true; 176 if (LOG) Slog.v(TAG, "newName=" + newName 177 + " newState=" + newState 178 + " headsetState=" + headsetState 179 + " prev headsetState=" + mHeadsetState); 180 181 if (mHeadsetState == headsetState) { 182 Log.e(TAG, "No state change."); 183 return; 184 } 185 186 // reject all suspect transitions: only accept state changes from: 187 // - a: 0 headset to 1 headset 188 // - b: 1 headset to 0 headset 189 if (h2w_headset == (BIT_HEADSET | BIT_HEADSET_NO_MIC)) { 190 Log.e(TAG, "Invalid combination, unsetting h2w flag"); 191 h2wStateChange = false; 192 } 193 // - c: 0 usb headset to 1 usb headset 194 // - d: 1 usb headset to 0 usb headset 195 if (usb_headset_anlg == BIT_USB_HEADSET_ANLG && usb_headset_dgtl == BIT_USB_HEADSET_DGTL) { 196 Log.e(TAG, "Invalid combination, unsetting usb flag"); 197 usbStateChange = false; 198 } 199 if (!h2wStateChange && !usbStateChange) { 200 Log.e(TAG, "invalid transition, returning ..."); 201 return; 202 } 203 204 mWakeLock.acquire(); 205 206 Message msg = mHandler.obtainMessage(MSG_NEW_DEVICE_STATE, headsetState, 207 mHeadsetState, newName); 208 mHandler.sendMessage(msg); 209 210 mHeadsetState = headsetState; 211 } 212 213 private final Handler mHandler = new Handler(Looper.myLooper(), null, true) { 214 @Override 215 public void handleMessage(Message msg) { 216 switch (msg.what) { 217 case MSG_NEW_DEVICE_STATE: 218 setDevicesState(msg.arg1, msg.arg2, (String)msg.obj); 219 mWakeLock.release(); 220 } 221 } 222 }; 223 224 private void setDevicesState( 225 int headsetState, int prevHeadsetState, String headsetName) { 226 synchronized (mLock) { 227 int allHeadsets = SUPPORTED_HEADSETS; 228 for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) { 229 if ((curHeadset & allHeadsets) != 0) { 230 setDeviceStateLocked(curHeadset, headsetState, prevHeadsetState, headsetName); 231 allHeadsets &= ~curHeadset; 232 } 233 } 234 } 235 } 236 237 private void setDeviceStateLocked(int headset, 238 int headsetState, int prevHeadsetState, String headsetName) { 239 if ((headsetState & headset) != (prevHeadsetState & headset)) { 240 int device; 241 int state; 242 243 if ((headsetState & headset) != 0) { 244 state = 1; 245 } else { 246 state = 0; 247 } 248 249 if (headset == BIT_HEADSET) { 250 device = AudioManager.DEVICE_OUT_WIRED_HEADSET; 251 } else if (headset == BIT_HEADSET_NO_MIC){ 252 device = AudioManager.DEVICE_OUT_WIRED_HEADPHONE; 253 } else if (headset == BIT_USB_HEADSET_ANLG) { 254 device = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET; 255 } else if (headset == BIT_USB_HEADSET_DGTL) { 256 device = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET; 257 } else if (headset == BIT_HDMI_AUDIO) { 258 device = AudioManager.DEVICE_OUT_AUX_DIGITAL; 259 } else { 260 Slog.e(TAG, "setDeviceState() invalid headset type: "+headset); 261 return; 262 } 263 264 if (LOG) 265 Slog.v(TAG, "device "+headsetName+((state == 1) ? " connected" : " disconnected")); 266 267 mAudioManager.setWiredDeviceConnectionState(device, state, headsetName); 268 } 269 } 270 271 private String switchCodeToString(int switchValues, int switchMask) { 272 StringBuffer sb = new StringBuffer(); 273 if ((switchMask & SW_HEADPHONE_INSERT_BIT) != 0 && 274 (switchValues & SW_HEADPHONE_INSERT_BIT) != 0) { 275 sb.append("SW_HEADPHONE_INSERT "); 276 } 277 if ((switchMask & SW_MICROPHONE_INSERT_BIT) != 0 && 278 (switchValues & SW_MICROPHONE_INSERT_BIT) != 0) { 279 sb.append("SW_MICROPHONE_INSERT"); 280 } 281 return sb.toString(); 282 } 283 284 class WiredAccessoryObserver extends UEventObserver { 285 private final List<UEventInfo> mUEventInfo; 286 287 public WiredAccessoryObserver() { 288 mUEventInfo = makeObservedUEventList(); 289 } 290 291 void init() { 292 synchronized (mLock) { 293 if (LOG) Slog.v(TAG, "init()"); 294 char[] buffer = new char[1024]; 295 296 for (int i = 0; i < mUEventInfo.size(); ++i) { 297 UEventInfo uei = mUEventInfo.get(i); 298 try { 299 int curState; 300 FileReader file = new FileReader(uei.getSwitchStatePath()); 301 int len = file.read(buffer, 0, 1024); 302 file.close(); 303 curState = Integer.valueOf((new String(buffer, 0, len)).trim()); 304 305 if (curState > 0) { 306 updateStateLocked(uei.getDevPath(), uei.getDevName(), curState); 307 } 308 } catch (FileNotFoundException e) { 309 Slog.w(TAG, uei.getSwitchStatePath() + 310 " not found while attempting to determine initial switch state"); 311 } catch (Exception e) { 312 Slog.e(TAG, "" , e); 313 } 314 } 315 } 316 317 // At any given time accessories could be inserted 318 // one on the board, one on the dock and one on HDMI: 319 // observe three UEVENTs 320 for (int i = 0; i < mUEventInfo.size(); ++i) { 321 UEventInfo uei = mUEventInfo.get(i); 322 startObserving("DEVPATH="+uei.getDevPath()); 323 } 324 } 325 326 private List<UEventInfo> makeObservedUEventList() { 327 List<UEventInfo> retVal = new ArrayList<UEventInfo>(); 328 UEventInfo uei; 329 330 // Monitor h2w 331 if (!mUseDevInputEventForAudioJack) { 332 uei = new UEventInfo(NAME_H2W, BIT_HEADSET, BIT_HEADSET_NO_MIC); 333 if (uei.checkSwitchExists()) { 334 retVal.add(uei); 335 } else { 336 Slog.w(TAG, "This kernel does not have wired headset support"); 337 } 338 } 339 340 // Monitor USB 341 uei = new UEventInfo(NAME_USB_AUDIO, BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL); 342 if (uei.checkSwitchExists()) { 343 retVal.add(uei); 344 } else { 345 Slog.w(TAG, "This kernel does not have usb audio support"); 346 } 347 348 // Monitor HDMI 349 // 350 // If the kernel has support for the "hdmi_audio" switch, use that. It will be 351 // signalled only when the HDMI driver has a video mode configured, and the downstream 352 // sink indicates support for audio in its EDID. 353 // 354 // If the kernel does not have an "hdmi_audio" switch, just fall back on the older 355 // "hdmi" switch instead. 356 uei = new UEventInfo(NAME_HDMI_AUDIO, BIT_HDMI_AUDIO, 0); 357 if (uei.checkSwitchExists()) { 358 retVal.add(uei); 359 } else { 360 uei = new UEventInfo(NAME_HDMI, BIT_HDMI_AUDIO, 0); 361 if (uei.checkSwitchExists()) { 362 retVal.add(uei); 363 } else { 364 Slog.w(TAG, "This kernel does not have HDMI audio support"); 365 } 366 } 367 368 return retVal; 369 } 370 371 @Override 372 public void onUEvent(UEventObserver.UEvent event) { 373 if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString()); 374 375 try { 376 String devPath = event.get("DEVPATH"); 377 String name = event.get("SWITCH_NAME"); 378 int state = Integer.parseInt(event.get("SWITCH_STATE")); 379 synchronized (mLock) { 380 updateStateLocked(devPath, name, state); 381 } 382 } catch (NumberFormatException e) { 383 Slog.e(TAG, "Could not parse switch state from event " + event); 384 } 385 } 386 387 private void updateStateLocked(String devPath, String name, int state) { 388 for (int i = 0; i < mUEventInfo.size(); ++i) { 389 UEventInfo uei = mUEventInfo.get(i); 390 if (devPath.equals(uei.getDevPath())) { 391 updateLocked(name, uei.computeNewHeadsetState(mHeadsetState, state)); 392 return; 393 } 394 } 395 } 396 397 private final class UEventInfo { 398 private final String mDevName; 399 private final int mState1Bits; 400 private final int mState2Bits; 401 402 public UEventInfo(String devName, int state1Bits, int state2Bits) { 403 mDevName = devName; 404 mState1Bits = state1Bits; 405 mState2Bits = state2Bits; 406 } 407 408 public String getDevName() { return mDevName; } 409 410 public String getDevPath() { 411 return String.format("/devices/virtual/switch/%s", mDevName); 412 } 413 414 public String getSwitchStatePath() { 415 return String.format("/sys/class/switch/%s/state", mDevName); 416 } 417 418 public boolean checkSwitchExists() { 419 File f = new File(getSwitchStatePath()); 420 return f.exists(); 421 } 422 423 public int computeNewHeadsetState(int headsetState, int switchState) { 424 int preserveMask = ~(mState1Bits | mState2Bits); 425 int setBits = ((switchState == 1) ? mState1Bits : 426 ((switchState == 2) ? mState2Bits : 0)); 427 428 return ((headsetState & preserveMask) | setBits); 429 } 430 } 431 } 432 } 433