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