blob: 96ac493f680d54ea489193e302b485c889d07ba0 [file] [log] [blame]
Praveen Bharathi21e941b2010-10-06 15:23:14 -05001/*
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;
Eric Laurent3fc78a52010-11-18 15:50:22 -080020import android.content.BroadcastReceiver;
Praveen Bharathi21e941b2010-10-06 15:23:14 -050021import android.content.Context;
22import android.content.Intent;
Eric Laurent3fc78a52010-11-18 15:50:22 -080023import android.content.IntentFilter;
Praveen Bharathi21e941b2010-10-06 15:23:14 -050024import android.os.Handler;
25import android.os.Message;
26import android.os.PowerManager;
27import android.os.PowerManager.WakeLock;
28import android.os.UEventObserver;
29import android.util.Slog;
30import android.media.AudioManager;
31import android.util.Log;
32
John Grossman5e643212012-03-05 16:01:26 -080033import java.io.File;
Praveen Bharathi21e941b2010-10-06 15:23:14 -050034import java.io.FileReader;
35import java.io.FileNotFoundException;
John Grossman5e643212012-03-05 16:01:26 -080036import java.util.ArrayList;
37import java.util.List;
Praveen Bharathi21e941b2010-10-06 15:23:14 -050038
39/**
40 * <p>WiredAccessoryObserver monitors for a wired headset on the main board or dock.
41 */
42class WiredAccessoryObserver extends UEventObserver {
43 private static final String TAG = WiredAccessoryObserver.class.getSimpleName();
44 private static final boolean LOG = true;
Praveen Bharathi21e941b2010-10-06 15:23:14 -050045 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);
Praveen Bharathi26e37342010-11-02 19:23:30 -070049 private static final int BIT_HDMI_AUDIO = (1 << 4);
Praveen Bharathi21e941b2010-10-06 15:23:14 -050050 private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC|
Praveen Bharathi26e37342010-11-02 19:23:30 -070051 BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL|
52 BIT_HDMI_AUDIO);
Praveen Bharathi21e941b2010-10-06 15:23:14 -050053 private static final int HEADSETS_WITH_MIC = BIT_HEADSET;
54
John Grossman5e643212012-03-05 16:01:26 -080055 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() {
Eric Laurent857d6c22012-03-23 17:24:07 -070069 return String.format("/devices/virtual/switch/%s", mDevName);
John Grossman5e643212012-03-05 16:01:26 -080070 }
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
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500135 private int mHeadsetState;
136 private int mPrevHeadsetState;
137 private String mHeadsetName;
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500138
139 private final Context mContext;
140 private final WakeLock mWakeLock; // held while there is a pending route change
141
Eric Laurentb1fbaac2012-05-29 09:24:28 -0700142 private final AudioManager mAudioManager;
143
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500144 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);
Eric Laurentb1fbaac2012-05-29 09:24:28 -0700149 mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500150
Eric Laurent3fc78a52010-11-18 15:50:22 -0800151 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
John Grossman5e643212012-03-05 16:01:26 -0800162 for (int i = 0; i < uEventInfo.size(); ++i) {
163 UEventInfo uei = uEventInfo.get(i);
Eric Laurent857d6c22012-03-23 17:24:07 -0700164 startObserving("DEVPATH="+uei.getDevPath());
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500165 }
Eric Laurent3fc78a52010-11-18 15:50:22 -0800166 }
John Grossman5e643212012-03-05 16:01:26 -0800167 }
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500168
169 @Override
170 public void onUEvent(UEventObserver.UEvent event) {
171 if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
172
173 try {
Eric Laurent857d6c22012-03-23 17:24:07 -0700174 String devPath = event.get("DEVPATH");
Praveen Bharathi7526da42010-11-16 02:08:02 -0600175 String name = event.get("SWITCH_NAME");
176 int state = Integer.parseInt(event.get("SWITCH_STATE"));
Eric Laurent857d6c22012-03-23 17:24:07 -0700177 updateState(devPath, name, state);
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500178 } catch (NumberFormatException e) {
179 Slog.e(TAG, "Could not parse switch state from event " + event);
180 }
181 }
182
Eric Laurent857d6c22012-03-23 17:24:07 -0700183 private synchronized final void updateState(String devPath, String name, int state)
Praveen Bharathi7526da42010-11-16 02:08:02 -0600184 {
John Grossman5e643212012-03-05 16:01:26 -0800185 for (int i = 0; i < uEventInfo.size(); ++i) {
186 UEventInfo uei = uEventInfo.get(i);
Eric Laurent857d6c22012-03-23 17:24:07 -0700187 if (devPath.equals(uei.getDevPath())) {
John Grossman5e643212012-03-05 16:01:26 -0800188 update(name, uei.computeNewHeadsetState(mHeadsetState, state));
189 return;
190 }
Praveen Bharathi7526da42010-11-16 02:08:02 -0600191 }
Praveen Bharathi7526da42010-11-16 02:08:02 -0600192 }
193
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500194 private synchronized final void init() {
195 char[] buffer = new char[1024];
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500196 mPrevHeadsetState = mHeadsetState;
197
Eric Laurent3fc78a52010-11-18 15:50:22 -0800198 if (LOG) Slog.v(TAG, "init()");
199
John Grossman5e643212012-03-05 16:01:26 -0800200 for (int i = 0; i < uEventInfo.size(); ++i) {
201 UEventInfo uei = uEventInfo.get(i);
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500202 try {
John Grossman5e643212012-03-05 16:01:26 -0800203 int curState;
204 FileReader file = new FileReader(uei.getSwitchStatePath());
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500205 int len = file.read(buffer, 0, 1024);
Brian Carlstrom237171f2010-11-04 16:23:21 -0700206 file.close();
John Grossman5e643212012-03-05 16:01:26 -0800207 curState = Integer.valueOf((new String(buffer, 0, len)).trim());
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500208
John Grossman5e643212012-03-05 16:01:26 -0800209 if (curState > 0) {
Eric Laurent857d6c22012-03-23 17:24:07 -0700210 updateState(uei.getDevPath(), uei.getDevName(), curState);
Praveen Bharathi7526da42010-11-16 02:08:02 -0600211 }
212
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500213 } catch (FileNotFoundException e) {
John Grossman5e643212012-03-05 16:01:26 -0800214 Slog.w(TAG, uei.getSwitchStatePath() +
215 " not found while attempting to determine initial switch state");
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500216 } catch (Exception e) {
217 Slog.e(TAG, "" , e);
218 }
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500219 }
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
Eric Laurent3fc78a52010-11-18 15:50:22 -0800235 if (LOG) Slog.v(TAG, "newState = "+newState+", headsetState = "+headsetState+","
236 + "mHeadsetState = "+mHeadsetState);
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500237 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
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500256 mWakeLock.acquire();
Eric Laurentb1fbaac2012-05-29 09:24:28 -0700257 mHandler.sendMessage(mHandler.obtainMessage(0,
258 mHeadsetState,
259 mPrevHeadsetState,
260 mHeadsetName));
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500261 }
262
Eric Laurentb1fbaac2012-05-29 09:24:28 -0700263 private synchronized final void setDevicesState(int headsetState,
264 int prevHeadsetState,
265 String headsetName) {
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500266 int allHeadsets = SUPPORTED_HEADSETS;
267 for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
268 if ((curHeadset & allHeadsets) != 0) {
Eric Laurentb1fbaac2012-05-29 09:24:28 -0700269 setDeviceState(curHeadset, headsetState, prevHeadsetState, headsetName);
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500270 allHeadsets &= ~curHeadset;
271 }
272 }
273 }
274
Eric Laurentb1fbaac2012-05-29 09:24:28 -0700275 private final void setDeviceState(int headset,
276 int headsetState,
277 int prevHeadsetState,
278 String headsetName) {
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500279 if ((headsetState & headset) != (prevHeadsetState & headset)) {
Eric Laurentb1fbaac2012-05-29 09:24:28 -0700280 int device;
281 int state;
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500282
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500283 if ((headsetState & headset) != 0) {
284 state = 1;
Eric Laurentb1fbaac2012-05-29 09:24:28 -0700285 } else {
286 state = 0;
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500287 }
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500288
Eric Laurentb1fbaac2012-05-29 09:24:28 -0700289 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;
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500302 }
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500303
Eric Laurentb1fbaac2012-05-29 09:24:28 -0700304 if (LOG)
305 Slog.v(TAG, "device "+headsetName+((state == 1) ? " connected" : " disconnected"));
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500306
Eric Laurentb1fbaac2012-05-29 09:24:28 -0700307 mAudioManager.setWiredDeviceConnectionState(device, state, headsetName);
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500308 }
309 }
310
311 private final Handler mHandler = new Handler() {
312 @Override
313 public void handleMessage(Message msg) {
Eric Laurentb1fbaac2012-05-29 09:24:28 -0700314 setDevicesState(msg.arg1, msg.arg2, (String)msg.obj);
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500315 mWakeLock.release();
316 }
317 };
318}