Added support for dock headset observer
Change-Id: I06b2e65e3bfa10735e6c7fd3349afa9ae7d45292
Signed-off-by: Praveen Bharathi <pbharathi@motorola.com>
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index bee8872..f993093 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -103,7 +103,6 @@
FileReader file = new FileReader(DOCK_STATE_PATH);
int len = file.read(buffer, 0, 1024);
mPreviousDockState = mDockState = Integer.valueOf((new String(buffer, 0, len)).trim());
-
} catch (FileNotFoundException e) {
Slog.w(TAG, "This kernel does not have dock station support");
} catch (Exception e) {
@@ -158,13 +157,17 @@
{
String whichSound = null;
if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
- if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) {
+ if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
+ (mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+ (mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
whichSound = Settings.System.DESK_UNDOCK_SOUND;
} else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) {
whichSound = Settings.System.CAR_UNDOCK_SOUND;
}
} else {
- if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
+ if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
+ (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+ (mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
whichSound = Settings.System.DESK_DOCK_SOUND;
} else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) {
whichSound = Settings.System.CAR_DOCK_SOUND;
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
deleted file mode 100644
index 6f0a91d..0000000
--- a/services/java/com/android/server/HeadsetObserver.java
+++ /dev/null
@@ -1,183 +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.app.ActivityManagerNative;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-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 java.io.FileReader;
-import java.io.FileNotFoundException;
-
-/**
- * <p>HeadsetObserver monitors for a wired headset.
- */
-class HeadsetObserver extends UEventObserver {
- private static final String TAG = HeadsetObserver.class.getSimpleName();
- private static final boolean LOG = true;
-
- private static final String HEADSET_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/h2w";
- private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state";
- private static final String HEADSET_NAME_PATH = "/sys/class/switch/h2w/name";
-
- private static final int BIT_HEADSET = (1 << 0);
- private static final int BIT_HEADSET_NO_MIC = (1 << 1);
- private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC);
- private static final int HEADSETS_WITH_MIC = BIT_HEADSET;
-
- private int mHeadsetState;
- private int mPrevHeadsetState;
- private String mHeadsetName;
-
- private final Context mContext;
- private final WakeLock mWakeLock; // held while there is a pending route change
-
- public HeadsetObserver(Context context) {
- mContext = context;
- PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetObserver");
- mWakeLock.setReferenceCounted(false);
-
- startObserving(HEADSET_UEVENT_MATCH);
-
- init(); // set initial status
- }
-
- @Override
- public void onUEvent(UEventObserver.UEvent event) {
- if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
-
- try {
- update(event.get("SWITCH_NAME"), Integer.parseInt(event.get("SWITCH_STATE")));
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Could not parse switch state from event " + event);
- }
- }
-
- private synchronized final void init() {
- char[] buffer = new char[1024];
-
- String newName = mHeadsetName;
- int newState = mHeadsetState;
- mPrevHeadsetState = mHeadsetState;
- try {
- FileReader file = new FileReader(HEADSET_STATE_PATH);
- int len = file.read(buffer, 0, 1024);
- newState = Integer.valueOf((new String(buffer, 0, len)).trim());
-
- file = new FileReader(HEADSET_NAME_PATH);
- len = file.read(buffer, 0, 1024);
- newName = new String(buffer, 0, len).trim();
-
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "This kernel does not have wired headset support");
- } catch (Exception e) {
- Slog.e(TAG, "" , e);
- }
-
- update(newName, newState);
- }
-
- private synchronized final void update(String newName, int newState) {
- // Retain only relevant bits
- int headsetState = newState & SUPPORTED_HEADSETS;
- int newOrOld = headsetState | mHeadsetState;
- int delay = 0;
- // reject all suspect transitions: only accept state changes from:
- // - a: 0 heaset to 1 headset
- // - b: 1 headset to 0 headset
- if (mHeadsetState == headsetState || ((newOrOld & (newOrOld - 1)) != 0)) {
- return;
- }
-
- mHeadsetName = newName;
- mPrevHeadsetState = mHeadsetState;
- mHeadsetState = headsetState;
-
- if (headsetState == 0) {
- Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
- mContext.sendBroadcast(intent);
- // It can take hundreds of ms flush the audio pipeline after
- // apps pause audio playback, but audio route changes are
- // immediate, so delay the route change by 1000ms.
- // This could be improved once the audio sub-system provides an
- // interface to clear the audio pipeline.
- delay = 1000;
- } else {
- // Insert the same delay for headset connection so that the connection event is not
- // broadcast before the disconnection event in case of fast removal/insertion
- if (mHandler.hasMessages(0)) {
- delay = 1000;
- }
- }
- mWakeLock.acquire();
- mHandler.sendMessageDelayed(mHandler.obtainMessage(0,
- mHeadsetState,
- mPrevHeadsetState,
- mHeadsetName),
- delay);
- }
-
- private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) {
- int allHeadsets = SUPPORTED_HEADSETS;
- for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
- if ((curHeadset & allHeadsets) != 0) {
- sendIntent(curHeadset, headsetState, prevHeadsetState, headsetName);
- allHeadsets &= ~curHeadset;
- }
- }
- }
-
- private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) {
- if ((headsetState & headset) != (prevHeadsetState & headset)) {
- // Pack up the values and broadcast them to everyone
- Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- int state = 0;
- int microphone = 0;
-
- if ((headset & HEADSETS_WITH_MIC) != 0) {
- microphone = 1;
- }
- if ((headsetState & headset) != 0) {
- state = 1;
- }
- intent.putExtra("state", state);
- intent.putExtra("name", headsetName);
- intent.putExtra("microphone", microphone);
-
- if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone);
- // TODO: Should we require a permission?
- ActivityManagerNative.broadcastStickyIntent(intent, null);
- }
- }
-
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- sendIntents(msg.arg1, msg.arg2, (String)msg.obj);
- mWakeLock.release();
- }
- };
-}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 237ab80..54f7441 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -121,7 +121,7 @@
WindowManagerService wm = null;
BluetoothService bluetooth = null;
BluetoothA2dpService bluetoothA2dp = null;
- HeadsetObserver headset = null;
+ WiredAccessoryObserver wiredAccessory = null;
DockObserver dock = null;
UsbObserver usb = null;
UiModeManagerService uiMode = null;
@@ -388,14 +388,6 @@
}
try {
- Slog.i(TAG, "Headset Observer");
- // Listen for wired headset changes
- headset = new HeadsetObserver(context);
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting HeadsetObserver", e);
- }
-
- try {
Slog.i(TAG, "Dock Observer");
// Listen for dock station changes
dock = new DockObserver(context, power);
@@ -404,6 +396,14 @@
}
try {
+ Slog.i(TAG, "Wired Accessory Observer");
+ // Listen for wired headset changes
+ wiredAccessory = new WiredAccessoryObserver(context);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting WiredAccessoryObserver", e);
+ }
+
+ try {
Slog.i(TAG, "USB Observer");
// Listen for USB changes
usb = new UsbObserver(context);
diff --git a/services/java/com/android/server/WiredAccessoryObserver.java b/services/java/com/android/server/WiredAccessoryObserver.java
new file mode 100644
index 0000000..ab92fdf
--- /dev/null
+++ b/services/java/com/android/server/WiredAccessoryObserver.java
@@ -0,0 +1,258 @@
+/*
+ * 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.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+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.FileReader;
+import java.io.FileNotFoundException;
+
+/**
+ * <p>WiredAccessoryObserver monitors for a wired headset on the main board or dock.
+ */
+class WiredAccessoryObserver extends UEventObserver {
+ private static final String TAG = WiredAccessoryObserver.class.getSimpleName();
+ private static final boolean LOG = true;
+ private static final int MAX_AUDIO_PORTS = 2; /* h2w & USB Audio */
+ private static final String uEventInfo[][] = { {"DEVPATH=/devices/virtual/switch/h2w",
+ "/sys/class/switch/h2w/state",
+ "/sys/class/switch/h2w/name"},
+ {"DEVPATH=/devices/virtual/switch/usb_audio",
+ "/sys/class/switch/usb_audio/state",
+ "/sys/class/switch/usb_audio/name"} };
+
+ 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 SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC|
+ BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL);
+ private static final int HEADSETS_WITH_MIC = BIT_HEADSET;
+
+ private int mHeadsetState;
+ private int mPrevHeadsetState;
+ private String mHeadsetName;
+ private int switchState;
+
+ private final Context mContext;
+ private final WakeLock mWakeLock; // held while there is a pending route change
+
+ 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);
+
+ // At any given time both headsets could be inserted
+ // one on the board and one on the dock
+ // observe two UEVENTs
+ for (int i = 0; i <= MAX_AUDIO_PORTS; i++) {
+ startObserving(uEventInfo[i][0]);
+ }
+ init(); // set initial status
+ }
+
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
+
+ try {
+ if ((event.get("SWITCH_NAME")).equals("usb_audio")) {
+ if (Integer.parseInt(event.get("SWITCH_STATE")) == 1) {
+ switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|
+ BIT_USB_HEADSET_DGTL)) |
+ (Integer.parseInt(event.get("SWITCH_STATE")) << 2));
+ } else if (Integer.parseInt(event.get("SWITCH_STATE")) == 2) {
+ switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|
+ BIT_USB_HEADSET_ANLG)) |
+ (Integer.parseInt(event.get("SWITCH_STATE")) << 3));
+ }
+ else switchState = (mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC));
+ }
+ else {
+ switchState = ((mHeadsetState & (BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL)) |
+ (Integer.parseInt(event.get("SWITCH_STATE"))));
+ }
+ update(event.get("SWITCH_NAME"), switchState);
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Could not parse switch state from event " + event);
+ }
+ }
+
+ private synchronized final void init() {
+ char[] buffer = new char[1024];
+
+ String newName = mHeadsetName;
+ int newState = mHeadsetState;
+ mPrevHeadsetState = mHeadsetState;
+
+ for (int i = 0; i <= MAX_AUDIO_PORTS; i++) {
+ try {
+ FileReader file = new FileReader(uEventInfo[i][1]);
+ int len = file.read(buffer, 0, 1024);
+ newState = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+ file = new FileReader(uEventInfo[i][2]);
+ len = file.read(buffer, 0, 1024);
+ newName = new String(buffer, 0, len).trim();
+
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "This kernel does not have wired headset support");
+ } catch (Exception e) {
+ Slog.e(TAG, "" , e);
+ }
+
+ update(newName, newState);
+ }
+ }
+
+ private synchronized final void update(String newName, int newState) {
+ // Retain only relevant bits
+ int headsetState = newState & SUPPORTED_HEADSETS;
+ int newOrOld = headsetState | mHeadsetState;
+ int delay = 0;
+ 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
+ Log.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;
+
+ if (headsetState == 0) {
+ Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+ mContext.sendBroadcast(intent);
+ // It can take hundreds of ms flush the audio pipeline after
+ // apps pause audio playback, but audio route changes are
+ // immediate, so delay the route change by 1000ms.
+ // This could be improved once the audio sub-system provides an
+ // interface to clear the audio pipeline.
+ delay = 1000;
+ } else {
+ // Insert the same delay for headset connection so that the connection event is not
+ // broadcast before the disconnection event in case of fast removal/insertion
+ if (mHandler.hasMessages(0)) {
+ delay = 1000;
+ }
+ }
+ mWakeLock.acquire();
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(0,
+ mHeadsetState,
+ mPrevHeadsetState,
+ mHeadsetName),
+ delay);
+ }
+
+ private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) {
+ int allHeadsets = SUPPORTED_HEADSETS;
+ for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
+ if ((curHeadset & allHeadsets) != 0) {
+ sendIntent(curHeadset, headsetState, prevHeadsetState, headsetName);
+ allHeadsets &= ~curHeadset;
+ }
+ }
+ }
+
+ private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) {
+ if ((headsetState & headset) != (prevHeadsetState & headset)) {
+
+ int state = 0;
+ if ((headsetState & headset) != 0) {
+ state = 1;
+ }
+ if((headset == BIT_USB_HEADSET_ANLG) || (headset == BIT_USB_HEADSET_DGTL)) {
+ Intent intent;
+
+ // Pack up the values and broadcast them to everyone
+ if (headset == BIT_USB_HEADSET_ANLG) {
+ intent = new Intent(Intent.ACTION_USB_ANLG_HEADSET_PLUG);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra("state", state);
+ intent.putExtra("name", headsetName);
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+ } else if (headset == BIT_USB_HEADSET_DGTL) {
+ intent = new Intent(Intent.ACTION_USB_DGTL_HEADSET_PLUG);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra("state", state);
+ intent.putExtra("name", headsetName);
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+ }
+
+ if (LOG) Slog.v(TAG, "Intent.ACTION_USB_HEADSET_PLUG: state: "+state+" name: "+headsetName);
+ // TODO: Should we require a permission?
+ }
+ if((headset == BIT_HEADSET) || (headset == BIT_HEADSET_NO_MIC)) {
+
+ // Pack up the values and broadcast them to everyone
+ Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ //int state = 0;
+ int microphone = 0;
+
+ if ((headset & HEADSETS_WITH_MIC) != 0) {
+ microphone = 1;
+ }
+
+ intent.putExtra("state", state);
+ intent.putExtra("name", headsetName);
+ intent.putExtra("microphone", microphone);
+
+ if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone);
+ // TODO: Should we require a permission?
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+ }
+ }
+ }
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ sendIntents(msg.arg1, msg.arg2, (String)msg.obj);
+ mWakeLock.release();
+ }
+ };
+}