WiredAccessoryObserver.java revision b55dcc244dbf22704c79b5439a9d3fc9f9815308
1/* 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; 24import android.os.PowerManager; 25import android.os.PowerManager.WakeLock; 26import android.os.UEventObserver; 27import android.util.Slog; 28import android.media.AudioManager; 29import android.util.Log; 30 31import java.io.FileReader; 32import java.io.FileNotFoundException; 33 34/** 35 * <p>WiredAccessoryObserver monitors for a wired headset on the main board or dock. 36 */ 37class WiredAccessoryObserver extends UEventObserver { 38 private static final String TAG = WiredAccessoryObserver.class.getSimpleName(); 39 private static final boolean LOG = true; 40 private static final int MAX_AUDIO_PORTS = 3; /* h2w, USB Audio & hdmi */ 41 private static final String uEventInfo[][] = { {"DEVPATH=/devices/virtual/switch/h2w", 42 "/sys/class/switch/h2w/state", 43 "/sys/class/switch/h2w/name"}, 44 {"DEVPATH=/devices/virtual/switch/usb_audio", 45 "/sys/class/switch/usb_audio/state", 46 "/sys/class/switch/usb_audio/name"}, 47 {"DEVPATH=/devices/virtual/switch/hdmi", 48 "/sys/class/switch/hdmi/state", 49 "/sys/class/switch/hdmi/name"} }; 50 51 private static final int BIT_HEADSET = (1 << 0); 52 private static final int BIT_HEADSET_NO_MIC = (1 << 1); 53 private static final int BIT_USB_HEADSET_ANLG = (1 << 2); 54 private static final int BIT_USB_HEADSET_DGTL = (1 << 3); 55 private static final int BIT_HDMI_AUDIO = (1 << 4); 56 private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC| 57 BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL| 58 BIT_HDMI_AUDIO); 59 private static final int HEADSETS_WITH_MIC = BIT_HEADSET; 60 61 private int mHeadsetState; 62 private int mPrevHeadsetState; 63 private String mHeadsetName; 64 private int switchState; 65 66 private final Context mContext; 67 private final WakeLock mWakeLock; // held while there is a pending route change 68 69 public WiredAccessoryObserver(Context context) { 70 mContext = context; 71 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 72 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryObserver"); 73 mWakeLock.setReferenceCounted(false); 74 75 // At any given time both headsets could be inserted 76 // one on the board and one on the dock 77 // observe two UEVENTs 78 for (int i = 0; i < MAX_AUDIO_PORTS; i++) { 79 startObserving(uEventInfo[i][0]); 80 } 81 init(); // set initial status 82 } 83 84 @Override 85 public void onUEvent(UEventObserver.UEvent event) { 86 if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString()); 87 88 try { 89 if ((event.get("SWITCH_NAME")).equals("usb_audio")) { 90 if (Integer.parseInt(event.get("SWITCH_STATE")) == 1) { 91 switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC| 92 BIT_USB_HEADSET_DGTL)) | 93 (Integer.parseInt(event.get("SWITCH_STATE")) << 2)); 94 } else if (Integer.parseInt(event.get("SWITCH_STATE")) == 2) { 95 switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC| 96 BIT_USB_HEADSET_ANLG)) | 97 (Integer.parseInt(event.get("SWITCH_STATE")) << 3)); 98 } 99 else switchState = (mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC)); 100 } 101 else if ((event.get("SWITCH_NAME")).equals("hdmi")) { 102 switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC| 103 BIT_USB_HEADSET_DGTL|BIT_USB_HEADSET_ANLG)) | 104 (Integer.parseInt(event.get("SWITCH_STATE")) << 4)); 105 } 106 else { 107 switchState = ((mHeadsetState & (BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL)) | 108 (Integer.parseInt(event.get("SWITCH_STATE")))); 109 } 110 update(event.get("SWITCH_NAME"), switchState); 111 } catch (NumberFormatException e) { 112 Slog.e(TAG, "Could not parse switch state from event " + event); 113 } 114 } 115 116 private synchronized final void init() { 117 char[] buffer = new char[1024]; 118 119 String newName = mHeadsetName; 120 int newState = mHeadsetState; 121 mPrevHeadsetState = mHeadsetState; 122 123 for (int i = 0; i < MAX_AUDIO_PORTS; i++) { 124 try { 125 FileReader file = new FileReader(uEventInfo[i][1]); 126 int len = file.read(buffer, 0, 1024); 127 newState = Integer.valueOf((new String(buffer, 0, len)).trim()); 128 129 file = new FileReader(uEventInfo[i][2]); 130 len = file.read(buffer, 0, 1024); 131 newName = new String(buffer, 0, len).trim(); 132 133 } catch (FileNotFoundException e) { 134 Slog.w(TAG, "This kernel does not have wired headset support"); 135 } catch (Exception e) { 136 Slog.e(TAG, "" , e); 137 } 138 139 update(newName, newState); 140 } 141 } 142 143 private synchronized final void update(String newName, int newState) { 144 // Retain only relevant bits 145 int headsetState = newState & SUPPORTED_HEADSETS; 146 int newOrOld = headsetState | mHeadsetState; 147 int delay = 0; 148 int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG; 149 int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL; 150 int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC); 151 boolean h2wStateChange = true; 152 boolean usbStateChange = true; 153 // reject all suspect transitions: only accept state changes from: 154 // - a: 0 heaset to 1 headset 155 // - b: 1 headset to 0 headset 156 Log.v(TAG, "newState = "+newState+", headsetState = "+headsetState+", mHeadsetState = "+mHeadsetState); 157 if (mHeadsetState == headsetState || ((h2w_headset & (h2w_headset - 1)) != 0)) { 158 Log.e(TAG, "unsetting h2w flag"); 159 h2wStateChange = false; 160 } 161 // - c: 0 usb headset to 1 usb headset 162 // - d: 1 usb headset to 0 usb headset 163 if ((usb_headset_anlg >> 2) == 1 && (usb_headset_dgtl >> 3) == 1) { 164 Log.e(TAG, "unsetting usb flag"); 165 usbStateChange = false; 166 } 167 if (!h2wStateChange && !usbStateChange) { 168 Log.e(TAG, "invalid transition, returning ..."); 169 return; 170 } 171 172 mHeadsetName = newName; 173 mPrevHeadsetState = mHeadsetState; 174 mHeadsetState = headsetState; 175 176 if (headsetState == 0) { 177 Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); 178 mContext.sendBroadcast(intent); 179 // It can take hundreds of ms flush the audio pipeline after 180 // apps pause audio playback, but audio route changes are 181 // immediate, so delay the route change by 1000ms. 182 // This could be improved once the audio sub-system provides an 183 // interface to clear the audio pipeline. 184 delay = 1000; 185 } else { 186 // Insert the same delay for headset connection so that the connection event is not 187 // broadcast before the disconnection event in case of fast removal/insertion 188 if (mHandler.hasMessages(0)) { 189 delay = 1000; 190 } 191 } 192 mWakeLock.acquire(); 193 mHandler.sendMessageDelayed(mHandler.obtainMessage(0, 194 mHeadsetState, 195 mPrevHeadsetState, 196 mHeadsetName), 197 delay); 198 } 199 200 private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) { 201 int allHeadsets = SUPPORTED_HEADSETS; 202 for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) { 203 if ((curHeadset & allHeadsets) != 0) { 204 sendIntent(curHeadset, headsetState, prevHeadsetState, headsetName); 205 allHeadsets &= ~curHeadset; 206 } 207 } 208 } 209 210 private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) { 211 if ((headsetState & headset) != (prevHeadsetState & headset)) { 212 213 int state = 0; 214 if ((headsetState & headset) != 0) { 215 state = 1; 216 } 217 if((headset == BIT_USB_HEADSET_ANLG) || (headset == BIT_USB_HEADSET_DGTL) || 218 (headset == BIT_HDMI_AUDIO)) { 219 Intent intent; 220 221 // Pack up the values and broadcast them to everyone 222 if (headset == BIT_USB_HEADSET_ANLG) { 223 intent = new Intent(Intent.ACTION_USB_ANLG_HEADSET_PLUG); 224 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 225 intent.putExtra("state", state); 226 intent.putExtra("name", headsetName); 227 ActivityManagerNative.broadcastStickyIntent(intent, null); 228 } else if (headset == BIT_USB_HEADSET_DGTL) { 229 intent = new Intent(Intent.ACTION_USB_DGTL_HEADSET_PLUG); 230 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 231 intent.putExtra("state", state); 232 intent.putExtra("name", headsetName); 233 ActivityManagerNative.broadcastStickyIntent(intent, null); 234 } else if (headset == BIT_HDMI_AUDIO) { 235 intent = new Intent(Intent.ACTION_HDMI_AUDIO_PLUG); 236 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 237 intent.putExtra("state", state); 238 intent.putExtra("name", headsetName); 239 ActivityManagerNative.broadcastStickyIntent(intent, null); 240 } 241 242 if (LOG) Slog.v(TAG, "Intent.ACTION_USB_HEADSET_PLUG: state: "+state+" name: "+headsetName); 243 // TODO: Should we require a permission? 244 } 245 if((headset == BIT_HEADSET) || (headset == BIT_HEADSET_NO_MIC)) { 246 247 // Pack up the values and broadcast them to everyone 248 Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG); 249 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 250 //int state = 0; 251 int microphone = 0; 252 253 if ((headset & HEADSETS_WITH_MIC) != 0) { 254 microphone = 1; 255 } 256 257 intent.putExtra("state", state); 258 intent.putExtra("name", headsetName); 259 intent.putExtra("microphone", microphone); 260 261 if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone); 262 // TODO: Should we require a permission? 263 ActivityManagerNative.broadcastStickyIntent(intent, null); 264 } 265 } 266 } 267 268 private final Handler mHandler = new Handler() { 269 @Override 270 public void handleMessage(Message msg) { 271 sendIntents(msg.arg1, msg.arg2, (String)msg.obj); 272 mWakeLock.release(); 273 } 274 }; 275} 276