summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server/HeadsetObserver.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server/HeadsetObserver.java')
-rw-r--r--services/java/com/android/server/HeadsetObserver.java144
1 files changed, 144 insertions, 0 deletions
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
new file mode 100644
index 0000000..855734d
--- /dev/null
+++ b/services/java/com/android/server/HeadsetObserver.java
@@ -0,0 +1,144 @@
+/*
+ * 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.UEventObserver;
+import android.util.Log;
+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 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 Context mContext;
+
+ private int mHeadsetState;
+ private String mHeadsetName;
+ private boolean mAudioRouteNeedsUpdate;
+ private AudioManager mAudioManager;
+
+ public HeadsetObserver(Context context) {
+ mContext = context;
+
+ startObserving(HEADSET_UEVENT_MATCH);
+
+ init(); // set initial status
+ }
+
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ Log.v(TAG, "Headset UEVENT: " + event.toString());
+
+ try {
+ update(event.get("SWITCH_NAME"), Integer.parseInt(event.get("SWITCH_STATE")));
+ } catch (NumberFormatException e) {
+ Log.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;
+ 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) {
+ Log.w(TAG, "This kernel does not have wired headset support");
+ } catch (Exception e) {
+ Log.e(TAG, "" , e);
+ }
+
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ update(newName, newState);
+ }
+
+ private synchronized final void update(String newName, int newState) {
+ if (newName != mHeadsetName || newState != mHeadsetState) {
+ boolean isUnplug = (newState == 0 && mHeadsetState == 1);
+ mHeadsetName = newName;
+ mHeadsetState = newState;
+ mAudioRouteNeedsUpdate = true;
+
+ sendIntent(isUnplug);
+
+ if (isUnplug) {
+ // It often takes >200ms to flush the audio pipeline after apps
+ // pause audio playback, but audio route changes are immediate,
+ // so delay the route change by 400ms.
+ // This could be improved once the audio sub-system provides an
+ // interface to clear the audio pipeline.
+ mHandler.sendEmptyMessageDelayed(0, 400);
+ } else {
+ updateAudioRoute();
+ }
+ }
+ }
+
+ private synchronized final void sendIntent(boolean isUnplug) {
+ // Pack up the values and broadcast them to everyone
+ Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+
+ intent.putExtra("state", mHeadsetState);
+ intent.putExtra("name", mHeadsetName);
+
+ // TODO: Should we require a permission?
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+
+ if (isUnplug) {
+ intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+ mContext.sendBroadcast(intent);
+ }
+ }
+
+ private synchronized final void updateAudioRoute() {
+ if (mAudioRouteNeedsUpdate) {
+ mAudioManager.setWiredHeadsetOn(mHeadsetState == 1);
+ mAudioRouteNeedsUpdate = false;
+ }
+ }
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ updateAudioRoute();
+ }
+ };
+
+}