diff options
author | Craig Mautner <cmautner@google.com> | 2012-10-01 16:38:45 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-10-01 16:38:46 -0700 |
commit | b97d8764a0aa600d7bcc6242e7173453c87ee658 (patch) | |
tree | a4e26afccbcfc0ace858908ab6d4bf826d6a6d07 /services | |
parent | efe9b483547ac44dbc88bdfd6a54be5c95c2d889 (diff) | |
parent | 2f39e9f8333183312b7f7d423c2ea60a93a25e32 (diff) | |
download | frameworks_base-b97d8764a0aa600d7bcc6242e7173453c87ee658.zip frameworks_base-b97d8764a0aa600d7bcc6242e7173453c87ee658.tar.gz frameworks_base-b97d8764a0aa600d7bcc6242e7173453c87ee658.tar.bz2 |
Merge "Switch from uevent model to /dev/input/event." into jb-mr1-dev
Diffstat (limited to 'services')
4 files changed, 486 insertions, 330 deletions
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index b684c90..eaaf33f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -631,11 +631,12 @@ class ServerThread extends Thread { } try { - Slog.i(TAG, "Wired Accessory Observer"); + Slog.i(TAG, "Wired Accessory Manager"); // Listen for wired headset changes - new WiredAccessoryObserver(context); + inputManager.setWiredAccessoryCallbacks( + new WiredAccessoryManager(context, inputManager)); } catch (Throwable e) { - reportWtf("starting WiredAccessoryObserver", e); + reportWtf("starting WiredAccessoryManager", e); } try { diff --git a/services/java/com/android/server/WiredAccessoryManager.java b/services/java/com/android/server/WiredAccessoryManager.java new file mode 100644 index 0000000..63e8895 --- /dev/null +++ b/services/java/com/android/server/WiredAccessoryManager.java @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.os.UEventObserver; +import android.util.Slog; +import android.media.AudioManager; +import android.util.Log; +import android.view.InputDevice; + +import com.android.internal.R; +import com.android.server.input.InputManagerService; +import com.android.server.input.InputManagerService.WiredAccessoryCallbacks; +import static com.android.server.input.InputManagerService.SW_HEADPHONE_INSERT; +import static com.android.server.input.InputManagerService.SW_MICROPHONE_INSERT; +import static com.android.server.input.InputManagerService.SW_HEADPHONE_INSERT_BIT; +import static com.android.server.input.InputManagerService.SW_MICROPHONE_INSERT_BIT; + +import java.io.File; +import java.io.FileReader; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.List; + +/** + * <p>WiredAccessoryManager monitors for a wired headset on the main board or dock using + * both the InputManagerService notifyWiredAccessoryChanged interface and the UEventObserver + * subsystem. + */ +final class WiredAccessoryManager implements WiredAccessoryCallbacks { + private static final String TAG = WiredAccessoryManager.class.getSimpleName(); + private static final boolean LOG = true; + + private static final int BIT_HEADSET = (1 << 0); + private static final int BIT_HEADSET_NO_MIC = (1 << 1); + private static final int BIT_USB_HEADSET_ANLG = (1 << 2); + private static final int BIT_USB_HEADSET_DGTL = (1 << 3); + private static final int BIT_HDMI_AUDIO = (1 << 4); + private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC| + BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL| + BIT_HDMI_AUDIO); + + private static final String NAME_H2W = "h2w"; + private static final String NAME_USB_AUDIO = "usb_audio"; + private static final String NAME_HDMI_AUDIO = "hdmi_audio"; + private static final String NAME_HDMI = "hdmi"; + + private static final int MSG_NEW_DEVICE_STATE = 1; + + private final Object mLock = new Object(); + + private final WakeLock mWakeLock; // held while there is a pending route change + private final AudioManager mAudioManager; + + private int mHeadsetState; + + private int mSwitchValues; + + private final WiredAccessoryObserver mObserver; + private final InputManagerService mInputManager; + + private final boolean mUseDevInputEventForAudioJack; + + public WiredAccessoryManager(Context context, InputManagerService inputManager) { + PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryManager"); + mWakeLock.setReferenceCounted(false); + mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); + mInputManager = inputManager; + + mUseDevInputEventForAudioJack = + context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); + + mObserver = new WiredAccessoryObserver(); + + context.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context ctx, Intent intent) { + bootCompleted(); + } + }, + new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); + } + + private void bootCompleted() { + if (mUseDevInputEventForAudioJack) { + int switchValues = 0; + if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_HEADPHONE_INSERT) == 1) { + switchValues |= SW_HEADPHONE_INSERT_BIT; + } + if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_MICROPHONE_INSERT) == 1) { + switchValues |= SW_MICROPHONE_INSERT_BIT; + } + notifyWiredAccessoryChanged(0, switchValues, + SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT); + } + + mObserver.init(); + } + + @Override + public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask) { + if (LOG) Slog.v(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos + + " bits=" + switchCodeToString(switchValues, switchMask) + + " mask=" + Integer.toHexString(switchMask)); + + synchronized (mLock) { + int headset; + mSwitchValues = (mSwitchValues & ~switchMask) | switchValues; + switch (mSwitchValues & (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT)) { + case 0: + headset = 0; + break; + + case SW_HEADPHONE_INSERT_BIT: + headset = BIT_HEADSET_NO_MIC; + break; + + case SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT: + headset = BIT_HEADSET; + break; + + case SW_MICROPHONE_INSERT_BIT: + headset = BIT_HEADSET; + break; + + default: + headset = 0; + break; + } + + updateLocked(NAME_H2W, headset); + } + } + + /** + * Compare the existing headset state with the new state and pass along accordingly. Note + * that this only supports a single headset at a time. Inserting both a usb and jacked headset + * results in support for the last one plugged in. Similarly, unplugging either is seen as + * unplugging all. + * + * @param newName One of the NAME_xxx variables defined above. + * @param newState 0 or one of the BIT_xxx variables defined above. + */ + private void updateLocked(String newName, int newState) { + // Retain only relevant bits + int headsetState = newState & SUPPORTED_HEADSETS; + int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG; + int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL; + int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC); + boolean h2wStateChange = true; + boolean usbStateChange = true; + if (LOG) Slog.v(TAG, "newName=" + newName + + " newState=" + newState + + " headsetState=" + headsetState + + " prev headsetState=" + mHeadsetState); + + if (mHeadsetState == headsetState) { + Log.e(TAG, "No state change."); + return; + } + + // reject all suspect transitions: only accept state changes from: + // - a: 0 headset to 1 headset + // - b: 1 headset to 0 headset + if (h2w_headset == (BIT_HEADSET | BIT_HEADSET_NO_MIC)) { + Log.e(TAG, "Invalid combination, unsetting h2w flag"); + h2wStateChange = false; + } + // - c: 0 usb headset to 1 usb headset + // - d: 1 usb headset to 0 usb headset + if (usb_headset_anlg == BIT_USB_HEADSET_ANLG && usb_headset_dgtl == BIT_USB_HEADSET_DGTL) { + Log.e(TAG, "Invalid combination, unsetting usb flag"); + usbStateChange = false; + } + if (!h2wStateChange && !usbStateChange) { + Log.e(TAG, "invalid transition, returning ..."); + return; + } + + mWakeLock.acquire(); + + Message msg = mHandler.obtainMessage(MSG_NEW_DEVICE_STATE, headsetState, + mHeadsetState, newName); + mHandler.sendMessage(msg); + + mHeadsetState = headsetState; + } + + private final Handler mHandler = new Handler(Looper.myLooper(), null, true) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_NEW_DEVICE_STATE: + setDevicesState(msg.arg1, msg.arg2, (String)msg.obj); + mWakeLock.release(); + } + } + }; + + private void setDevicesState( + int headsetState, int prevHeadsetState, String headsetName) { + synchronized (mLock) { + int allHeadsets = SUPPORTED_HEADSETS; + for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) { + if ((curHeadset & allHeadsets) != 0) { + setDeviceStateLocked(curHeadset, headsetState, prevHeadsetState, headsetName); + allHeadsets &= ~curHeadset; + } + } + } + } + + private void setDeviceStateLocked(int headset, + int headsetState, int prevHeadsetState, String headsetName) { + if ((headsetState & headset) != (prevHeadsetState & headset)) { + int device; + int state; + + if ((headsetState & headset) != 0) { + state = 1; + } else { + state = 0; + } + + if (headset == BIT_HEADSET) { + device = AudioManager.DEVICE_OUT_WIRED_HEADSET; + } else if (headset == BIT_HEADSET_NO_MIC){ + device = AudioManager.DEVICE_OUT_WIRED_HEADPHONE; + } else if (headset == BIT_USB_HEADSET_ANLG) { + device = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET; + } else if (headset == BIT_USB_HEADSET_DGTL) { + device = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET; + } else if (headset == BIT_HDMI_AUDIO) { + device = AudioManager.DEVICE_OUT_AUX_DIGITAL; + } else { + Slog.e(TAG, "setDeviceState() invalid headset type: "+headset); + return; + } + + if (LOG) + Slog.v(TAG, "device "+headsetName+((state == 1) ? " connected" : " disconnected")); + + mAudioManager.setWiredDeviceConnectionState(device, state, headsetName); + } + } + + private String switchCodeToString(int switchValues, int switchMask) { + StringBuffer sb = new StringBuffer(); + if ((switchMask & SW_HEADPHONE_INSERT_BIT) != 0 && + (switchValues & SW_HEADPHONE_INSERT_BIT) != 0) { + sb.append("SW_HEADPHONE_INSERT "); + } + if ((switchMask & SW_MICROPHONE_INSERT_BIT) != 0 && + (switchValues & SW_MICROPHONE_INSERT_BIT) != 0) { + sb.append("SW_MICROPHONE_INSERT"); + } + return sb.toString(); + } + + class WiredAccessoryObserver extends UEventObserver { + private final List<UEventInfo> mUEventInfo; + + public WiredAccessoryObserver() { + mUEventInfo = makeObservedUEventList(); + } + + void init() { + synchronized (mLock) { + if (LOG) Slog.v(TAG, "init()"); + char[] buffer = new char[1024]; + + for (int i = 0; i < mUEventInfo.size(); ++i) { + UEventInfo uei = mUEventInfo.get(i); + try { + int curState; + FileReader file = new FileReader(uei.getSwitchStatePath()); + int len = file.read(buffer, 0, 1024); + file.close(); + curState = Integer.valueOf((new String(buffer, 0, len)).trim()); + + if (curState > 0) { + updateStateLocked(uei.getDevPath(), uei.getDevName(), curState); + } + } catch (FileNotFoundException e) { + Slog.w(TAG, uei.getSwitchStatePath() + + " not found while attempting to determine initial switch state"); + } catch (Exception e) { + Slog.e(TAG, "" , e); + } + } + } + + // At any given time accessories could be inserted + // one on the board, one on the dock and one on HDMI: + // observe three UEVENTs + for (int i = 0; i < mUEventInfo.size(); ++i) { + UEventInfo uei = mUEventInfo.get(i); + startObserving("DEVPATH="+uei.getDevPath()); + } + } + + private List<UEventInfo> makeObservedUEventList() { + List<UEventInfo> retVal = new ArrayList<UEventInfo>(); + UEventInfo uei; + + // Monitor h2w + if (!mUseDevInputEventForAudioJack) { + uei = new UEventInfo(NAME_H2W, BIT_HEADSET, BIT_HEADSET_NO_MIC); + if (uei.checkSwitchExists()) { + retVal.add(uei); + } else { + Slog.w(TAG, "This kernel does not have wired headset support"); + } + } + + // Monitor USB + uei = new UEventInfo(NAME_USB_AUDIO, BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL); + if (uei.checkSwitchExists()) { + retVal.add(uei); + } else { + Slog.w(TAG, "This kernel does not have usb audio support"); + } + + // Monitor HDMI + // + // If the kernel has support for the "hdmi_audio" switch, use that. It will be + // signalled only when the HDMI driver has a video mode configured, and the downstream + // sink indicates support for audio in its EDID. + // + // If the kernel does not have an "hdmi_audio" switch, just fall back on the older + // "hdmi" switch instead. + uei = new UEventInfo(NAME_HDMI_AUDIO, BIT_HDMI_AUDIO, 0); + if (uei.checkSwitchExists()) { + retVal.add(uei); + } else { + uei = new UEventInfo(NAME_HDMI, BIT_HDMI_AUDIO, 0); + if (uei.checkSwitchExists()) { + retVal.add(uei); + } else { + Slog.w(TAG, "This kernel does not have HDMI audio support"); + } + } + + return retVal; + } + + @Override + public void onUEvent(UEventObserver.UEvent event) { + if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString()); + + try { + String devPath = event.get("DEVPATH"); + String name = event.get("SWITCH_NAME"); + int state = Integer.parseInt(event.get("SWITCH_STATE")); + synchronized (mLock) { + updateStateLocked(devPath, name, state); + } + } catch (NumberFormatException e) { + Slog.e(TAG, "Could not parse switch state from event " + event); + } + } + + private void updateStateLocked(String devPath, String name, int state) { + for (int i = 0; i < mUEventInfo.size(); ++i) { + UEventInfo uei = mUEventInfo.get(i); + if (devPath.equals(uei.getDevPath())) { + updateLocked(name, uei.computeNewHeadsetState(mHeadsetState, state)); + return; + } + } + } + + private final class UEventInfo { + private final String mDevName; + private final int mState1Bits; + private final int mState2Bits; + + public UEventInfo(String devName, int state1Bits, int state2Bits) { + mDevName = devName; + mState1Bits = state1Bits; + mState2Bits = state2Bits; + } + + public String getDevName() { return mDevName; } + + public String getDevPath() { + return String.format("/devices/virtual/switch/%s", mDevName); + } + + public String getSwitchStatePath() { + return String.format("/sys/class/switch/%s/state", mDevName); + } + + public boolean checkSwitchExists() { + File f = new File(getSwitchStatePath()); + return f.exists(); + } + + public int computeNewHeadsetState(int headsetState, int switchState) { + int preserveMask = ~(mState1Bits | mState2Bits); + int setBits = ((switchState == 1) ? mState1Bits : + ((switchState == 2) ? mState2Bits : 0)); + + return ((headsetState & preserveMask) | setBits); + } + } + } +} diff --git a/services/java/com/android/server/WiredAccessoryObserver.java b/services/java/com/android/server/WiredAccessoryObserver.java deleted file mode 100644 index 56c0fdf..0000000 --- a/services/java/com/android/server/WiredAccessoryObserver.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.os.UEventObserver; -import android.util.Slog; -import android.media.AudioManager; -import android.util.Log; - -import java.io.File; -import java.io.FileReader; -import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.List; - -/** - * <p>WiredAccessoryObserver monitors for a wired headset on the main board or dock. - */ -final class WiredAccessoryObserver extends UEventObserver { - private static final String TAG = WiredAccessoryObserver.class.getSimpleName(); - private static final boolean LOG = true; - private static final int BIT_HEADSET = (1 << 0); - private static final int BIT_HEADSET_NO_MIC = (1 << 1); - private static final int BIT_USB_HEADSET_ANLG = (1 << 2); - private static final int BIT_USB_HEADSET_DGTL = (1 << 3); - private static final int BIT_HDMI_AUDIO = (1 << 4); - private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC| - BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL| - BIT_HDMI_AUDIO); - - private final Object mLock = new Object(); - - private final Context mContext; - private final WakeLock mWakeLock; // held while there is a pending route change - private final AudioManager mAudioManager; - private final List<UEventInfo> mUEventInfo; - - private int mHeadsetState; - private int mPrevHeadsetState; - private String mHeadsetName; - - public WiredAccessoryObserver(Context context) { - mContext = context; - - PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryObserver"); - mWakeLock.setReferenceCounted(false); - mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); - - mUEventInfo = makeObservedUEventList(); - - context.registerReceiver(new BootCompletedReceiver(), - new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); - } - - @Override - public void onUEvent(UEventObserver.UEvent event) { - if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString()); - - try { - String devPath = event.get("DEVPATH"); - String name = event.get("SWITCH_NAME"); - int state = Integer.parseInt(event.get("SWITCH_STATE")); - synchronized (mLock) { - updateStateLocked(devPath, name, state); - } - } catch (NumberFormatException e) { - Slog.e(TAG, "Could not parse switch state from event " + event); - } - } - - private void bootCompleted() { - synchronized (mLock) { - char[] buffer = new char[1024]; - mPrevHeadsetState = mHeadsetState; - - if (LOG) Slog.v(TAG, "init()"); - - for (int i = 0; i < mUEventInfo.size(); ++i) { - UEventInfo uei = mUEventInfo.get(i); - try { - int curState; - FileReader file = new FileReader(uei.getSwitchStatePath()); - int len = file.read(buffer, 0, 1024); - file.close(); - curState = Integer.valueOf((new String(buffer, 0, len)).trim()); - - if (curState > 0) { - updateStateLocked(uei.getDevPath(), uei.getDevName(), curState); - } - } catch (FileNotFoundException e) { - Slog.w(TAG, uei.getSwitchStatePath() + - " not found while attempting to determine initial switch state"); - } catch (Exception e) { - Slog.e(TAG, "" , e); - } - } - } - - // At any given time accessories could be inserted - // one on the board, one on the dock and one on HDMI: - // observe three UEVENTs - for (int i = 0; i < mUEventInfo.size(); ++i) { - UEventInfo uei = mUEventInfo.get(i); - startObserving("DEVPATH="+uei.getDevPath()); - } - } - - private void updateStateLocked(String devPath, String name, int state) { - for (int i = 0; i < mUEventInfo.size(); ++i) { - UEventInfo uei = mUEventInfo.get(i); - if (devPath.equals(uei.getDevPath())) { - updateLocked(name, uei.computeNewHeadsetState(mHeadsetState, state)); - return; - } - } - } - - private void updateLocked(String newName, int newState) { - // Retain only relevant bits - int headsetState = newState & SUPPORTED_HEADSETS; - int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG; - int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL; - int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC); - boolean h2wStateChange = true; - boolean usbStateChange = true; - // reject all suspect transitions: only accept state changes from: - // - a: 0 heaset to 1 headset - // - b: 1 headset to 0 headset - if (LOG) Slog.v(TAG, "newState = "+newState+", headsetState = "+headsetState+"," - + "mHeadsetState = "+mHeadsetState); - if (mHeadsetState == headsetState || ((h2w_headset & (h2w_headset - 1)) != 0)) { - Log.e(TAG, "unsetting h2w flag"); - h2wStateChange = false; - } - // - c: 0 usb headset to 1 usb headset - // - d: 1 usb headset to 0 usb headset - if ((usb_headset_anlg >> 2) == 1 && (usb_headset_dgtl >> 3) == 1) { - Log.e(TAG, "unsetting usb flag"); - usbStateChange = false; - } - if (!h2wStateChange && !usbStateChange) { - Log.e(TAG, "invalid transition, returning ..."); - return; - } - - mHeadsetName = newName; - mPrevHeadsetState = mHeadsetState; - mHeadsetState = headsetState; - - mWakeLock.acquire(); - - Message msg = mHandler.obtainMessage(0, mHeadsetState, mPrevHeadsetState, mHeadsetName); - mHandler.sendMessage(msg); - } - - private void setDevicesState( - int headsetState, int prevHeadsetState, String headsetName) { - synchronized (mLock) { - int allHeadsets = SUPPORTED_HEADSETS; - for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) { - if ((curHeadset & allHeadsets) != 0) { - setDeviceStateLocked(curHeadset, headsetState, prevHeadsetState, headsetName); - allHeadsets &= ~curHeadset; - } - } - } - } - - private void setDeviceStateLocked(int headset, - int headsetState, int prevHeadsetState, String headsetName) { - if ((headsetState & headset) != (prevHeadsetState & headset)) { - int device; - int state; - - if ((headsetState & headset) != 0) { - state = 1; - } else { - state = 0; - } - - if (headset == BIT_HEADSET) { - device = AudioManager.DEVICE_OUT_WIRED_HEADSET; - } else if (headset == BIT_HEADSET_NO_MIC){ - device = AudioManager.DEVICE_OUT_WIRED_HEADPHONE; - } else if (headset == BIT_USB_HEADSET_ANLG) { - device = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET; - } else if (headset == BIT_USB_HEADSET_DGTL) { - device = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET; - } else if (headset == BIT_HDMI_AUDIO) { - device = AudioManager.DEVICE_OUT_AUX_DIGITAL; - } else { - Slog.e(TAG, "setDeviceState() invalid headset type: "+headset); - return; - } - - if (LOG) - Slog.v(TAG, "device "+headsetName+((state == 1) ? " connected" : " disconnected")); - - mAudioManager.setWiredDeviceConnectionState(device, state, headsetName); - } - } - - private static List<UEventInfo> makeObservedUEventList() { - List<UEventInfo> retVal = new ArrayList<UEventInfo>(); - UEventInfo uei; - - // Monitor h2w - uei = new UEventInfo("h2w", BIT_HEADSET, BIT_HEADSET_NO_MIC); - if (uei.checkSwitchExists()) { - retVal.add(uei); - } else { - Slog.w(TAG, "This kernel does not have wired headset support"); - } - - // Monitor USB - uei = new UEventInfo("usb_audio", BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL); - if (uei.checkSwitchExists()) { - retVal.add(uei); - } else { - Slog.w(TAG, "This kernel does not have usb audio support"); - } - - // Monitor HDMI - // - // If the kernel has support for the "hdmi_audio" switch, use that. It will be signalled - // only when the HDMI driver has a video mode configured, and the downstream sink indicates - // support for audio in its EDID. - // - // If the kernel does not have an "hdmi_audio" switch, just fall back on the older "hdmi" - // switch instead. - uei = new UEventInfo("hdmi_audio", BIT_HDMI_AUDIO, 0); - if (uei.checkSwitchExists()) { - retVal.add(uei); - } else { - uei = new UEventInfo("hdmi", BIT_HDMI_AUDIO, 0); - if (uei.checkSwitchExists()) { - retVal.add(uei); - } else { - Slog.w(TAG, "This kernel does not have HDMI audio support"); - } - } - - return retVal; - } - - private final Handler mHandler = new Handler(Looper.myLooper(), null, true) { - @Override - public void handleMessage(Message msg) { - setDevicesState(msg.arg1, msg.arg2, (String)msg.obj); - mWakeLock.release(); - } - }; - - private static final class UEventInfo { - private final String mDevName; - private final int mState1Bits; - private final int mState2Bits; - - public UEventInfo(String devName, int state1Bits, int state2Bits) { - mDevName = devName; - mState1Bits = state1Bits; - mState2Bits = state2Bits; - } - - public String getDevName() { return mDevName; } - - public String getDevPath() { - return String.format("/devices/virtual/switch/%s", mDevName); - } - - public String getSwitchStatePath() { - return String.format("/sys/class/switch/%s/state", mDevName); - } - - public boolean checkSwitchExists() { - File f = new File(getSwitchStatePath()); - return ((null != f) && f.exists()); - } - - public int computeNewHeadsetState(int headsetState, int switchState) { - int preserveMask = ~(mState1Bits | mState2Bits); - int setBits = ((switchState == 1) ? mState1Bits : - ((switchState == 2) ? mState2Bits : 0)); - - return ((headsetState & preserveMask) | setBits); - } - } - - private final class BootCompletedReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - bootCompleted(); - } - } -} diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java index 7b0c452..5e4907e 100644 --- a/services/java/com/android/server/input/InputManagerService.java +++ b/services/java/com/android/server/input/InputManagerService.java @@ -113,6 +113,7 @@ public class InputManagerService extends IInputManager.Stub private final InputManagerHandler mHandler; private WindowManagerCallbacks mWindowManagerCallbacks; + private WiredAccessoryCallbacks mWiredAccessoryCallbacks; private boolean mSystemReady; private NotificationManager mNotificationManager; @@ -197,7 +198,7 @@ public class InputManagerService extends IInputManager.Stub // Key states (may be returned by queries about the current state of a // particular key code, scan code or switch). - + /** The key state is unknown or the requested key itself is not supported. */ public static final int KEY_STATE_UNKNOWN = -1; @@ -213,17 +214,41 @@ public class InputManagerService extends IInputManager.Stub /** Scan code: Mouse / trackball button. */ public static final int BTN_MOUSE = 0x110; + // Switch code values must match bionic/libc/kernel/common/linux/input.h /** Switch code: Lid switch. When set, lid is shut. */ public static final int SW_LID = 0x00; /** Switch code: Keypad slide. When set, keyboard is exposed. */ public static final int SW_KEYPAD_SLIDE = 0x0a; + /** Switch code: Headphone. When set, headphone is inserted. */ + public static final int SW_HEADPHONE_INSERT = 0x02; + + /** Switch code: Microphone. When set, microphone is inserted. */ + public static final int SW_MICROPHONE_INSERT = 0x04; + + /** Switch code: Headphone/Microphone Jack. When set, something is inserted. */ + public static final int SW_JACK_PHYSICAL_INSERT = 0x07; + + public static final int SW_LID_BIT = 1 << SW_LID; + public static final int SW_KEYPAD_SLIDE_BIT = 1 << SW_KEYPAD_SLIDE; + public static final int SW_HEADPHONE_INSERT_BIT = 1 << SW_HEADPHONE_INSERT; + public static final int SW_MICROPHONE_INSERT_BIT = 1 << SW_MICROPHONE_INSERT; + public static final int SW_JACK_PHYSICAL_INSERT_BIT = 1 << SW_JACK_PHYSICAL_INSERT; + public static final int SW_JACK_BITS = + SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_JACK_PHYSICAL_INSERT_BIT; + + /** Whether to use the dev/input/event or uevent subsystem for the audio jack. */ + final boolean mUseDevInputEventForAudioJack; + public InputManagerService(Context context, Handler handler) { this.mContext = context; this.mHandler = new InputManagerHandler(handler.getLooper()); - Slog.i(TAG, "Initializing input manager"); + mUseDevInputEventForAudioJack = + context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); + Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack=" + + mUseDevInputEventForAudioJack); mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue()); } @@ -231,6 +256,10 @@ public class InputManagerService extends IInputManager.Stub mWindowManagerCallbacks = callbacks; } + public void setWiredAccessoryCallbacks(WiredAccessoryCallbacks callbacks) { + mWiredAccessoryCallbacks = callbacks; + } + public void start() { Slog.i(TAG, "Starting input manager"); nativeStart(mPtr); @@ -335,7 +364,7 @@ public class InputManagerService extends IInputManager.Stub public int getKeyCodeState(int deviceId, int sourceMask, int keyCode) { return nativeGetKeyCodeState(mPtr, deviceId, sourceMask, keyCode); } - + /** * Gets the current state of a key or button by scan code. * @param deviceId The input device id, or -1 to consult all devices. @@ -513,7 +542,7 @@ public class InputManagerService extends IInputManager.Stub /** * Gets information about the input device with the specified id. - * @param id The device id. + * @param deviceId The device id. * @return The input device or null if not found. */ @Override // Binder call @@ -976,8 +1005,8 @@ public class InputManagerService extends IInputManager.Stub // Must be called on handler. private void handleSwitchKeyboardLayout(int deviceId, int direction) { final InputDevice device = getInputDevice(deviceId); - final String inputDeviceDescriptor = device.getDescriptor(); if (device != null) { + final String inputDeviceDescriptor = device.getDescriptor(); final boolean changed; final String keyboardLayoutDescriptor; synchronized (mDataStore) { @@ -1214,6 +1243,7 @@ public class InputManagerService extends IInputManager.Stub } // Called by the heartbeat to ensure locks are not held indefinitely (for deadlock detection). + @Override public void monitor() { synchronized (mInputFilterLock) { } nativeMonitor(mPtr); @@ -1244,10 +1274,15 @@ public class InputManagerService extends IInputManager.Stub + ", mask=" + Integer.toHexString(switchMask)); } - if ((switchMask & (1 << SW_LID)) != 0) { - final boolean lidOpen = ((switchValues & (1 << SW_LID)) == 0); + if ((switchMask & SW_LID_BIT) != 0) { + final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0); mWindowManagerCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen); } + + if (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) { + mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues, + switchMask); + } } // Native callback. @@ -1431,7 +1466,6 @@ public class InputManagerService extends IInputManager.Stub return null; } - /** * Callback interface implemented by the Window Manager. */ @@ -1459,6 +1493,13 @@ public class InputManagerService extends IInputManager.Stub } /** + * Callback interface implemented by WiredAccessoryObserver. + */ + public interface WiredAccessoryCallbacks { + public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask); + } + + /** * Private handler for the input manager. */ private final class InputManagerHandler extends Handler { @@ -1498,6 +1539,7 @@ public class InputManagerService extends IInputManager.Stub mDisconnected = true; } + @Override public void sendInputEvent(InputEvent event, int policyFlags) { if (event == null) { throw new IllegalArgumentException("event must not be null"); |