diff options
Diffstat (limited to 'services/java/com/android/server/HeadsetObserver.java')
-rw-r--r-- | services/java/com/android/server/HeadsetObserver.java | 144 |
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(); + } + }; + +} |