blob: 9b0a2d465dbe804ef0ef9de2be1e7060e056a659 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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.app.ActivityManagerNative;
20import android.content.Context;
21import android.content.Intent;
22import android.os.Handler;
23import android.os.Message;
Nick Pelly9ac93212009-04-08 15:09:15 -070024import android.os.PowerManager;
25import android.os.PowerManager.WakeLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.os.UEventObserver;
27import android.util.Log;
28import android.media.AudioManager;
29
30import java.io.FileReader;
31import java.io.FileNotFoundException;
32
33/**
34 * <p>HeadsetObserver monitors for a wired headset.
35 */
36class HeadsetObserver extends UEventObserver {
37 private static final String TAG = HeadsetObserver.class.getSimpleName();
Joe Onorato9a5e3e12009-07-01 21:04:03 -040038 private static final boolean LOG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039
40 private static final String HEADSET_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/h2w";
41 private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state";
42 private static final String HEADSET_NAME_PATH = "/sys/class/switch/h2w/name";
43
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 private int mHeadsetState;
45 private String mHeadsetName;
46 private boolean mAudioRouteNeedsUpdate;
47 private AudioManager mAudioManager;
48
Nick Pelly9ac93212009-04-08 15:09:15 -070049 private final Context mContext;
50 private final WakeLock mWakeLock; // held while there is a pending route change
51
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 public HeadsetObserver(Context context) {
53 mContext = context;
Nick Pelly9ac93212009-04-08 15:09:15 -070054 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
55 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetObserver");
56 mWakeLock.setReferenceCounted(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057
58 startObserving(HEADSET_UEVENT_MATCH);
59
60 init(); // set initial status
61 }
62
63 @Override
64 public void onUEvent(UEventObserver.UEvent event) {
Joe Onorato9a5e3e12009-07-01 21:04:03 -040065 if (LOG) Log.v(TAG, "Headset UEVENT: " + event.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066
67 try {
68 update(event.get("SWITCH_NAME"), Integer.parseInt(event.get("SWITCH_STATE")));
69 } catch (NumberFormatException e) {
70 Log.e(TAG, "Could not parse switch state from event " + event);
71 }
72 }
73
74 private synchronized final void init() {
75 char[] buffer = new char[1024];
76
77 String newName = mHeadsetName;
78 int newState = mHeadsetState;
79 try {
80 FileReader file = new FileReader(HEADSET_STATE_PATH);
81 int len = file.read(buffer, 0, 1024);
82 newState = Integer.valueOf((new String(buffer, 0, len)).trim());
83
84 file = new FileReader(HEADSET_NAME_PATH);
85 len = file.read(buffer, 0, 1024);
86 newName = new String(buffer, 0, len).trim();
87
88 } catch (FileNotFoundException e) {
89 Log.w(TAG, "This kernel does not have wired headset support");
90 } catch (Exception e) {
91 Log.e(TAG, "" , e);
92 }
93
94 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
95 update(newName, newState);
96 }
97
98 private synchronized final void update(String newName, int newState) {
99 if (newName != mHeadsetName || newState != mHeadsetState) {
100 boolean isUnplug = (newState == 0 && mHeadsetState == 1);
101 mHeadsetName = newName;
102 mHeadsetState = newState;
103 mAudioRouteNeedsUpdate = true;
104
105 sendIntent(isUnplug);
106
107 if (isUnplug) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700108 // It can take hundreds of ms flush the audio pipeline after
109 // apps pause audio playback, but audio route changes are
110 // immediate, so delay the route change by 1000ms.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 // This could be improved once the audio sub-system provides an
112 // interface to clear the audio pipeline.
Nick Pelly9ac93212009-04-08 15:09:15 -0700113 mWakeLock.acquire();
The Android Open Source Project10592532009-03-18 17:39:46 -0700114 mHandler.sendEmptyMessageDelayed(0, 1000);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 } else {
116 updateAudioRoute();
117 }
118 }
119 }
120
121 private synchronized final void sendIntent(boolean isUnplug) {
122 // Pack up the values and broadcast them to everyone
123 Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
124 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
125
126 intent.putExtra("state", mHeadsetState);
127 intent.putExtra("name", mHeadsetName);
128
129 // TODO: Should we require a permission?
130 ActivityManagerNative.broadcastStickyIntent(intent, null);
131
132 if (isUnplug) {
133 intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
134 mContext.sendBroadcast(intent);
135 }
136 }
137
138 private synchronized final void updateAudioRoute() {
139 if (mAudioRouteNeedsUpdate) {
140 mAudioManager.setWiredHeadsetOn(mHeadsetState == 1);
141 mAudioRouteNeedsUpdate = false;
142 }
143 }
144
145 private final Handler mHandler = new Handler() {
146 @Override
147 public void handleMessage(Message msg) {
148 updateAudioRoute();
Nick Pelly9ac93212009-04-08 15:09:15 -0700149 mWakeLock.release();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 }
Nick Pelly9ac93212009-04-08 15:09:15 -0700151 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152
153}