19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License. 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage com.android.server; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.app.ActivityManagerNative; 209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context; 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Intent; 229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Handler; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Message; 249ac932179c704822c7ae5c7accb02119b578254eNick Pellyimport android.os.PowerManager; 259ac932179c704822c7ae5c7accb02119b578254eNick Pellyimport android.os.PowerManager.WakeLock; 269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.UEventObserver; 279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log; 289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.media.AudioManager; 299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileReader; 319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileNotFoundException; 329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>HeadsetObserver monitors for a wired headset. 359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectclass HeadsetObserver extends UEventObserver { 379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String TAG = HeadsetObserver.class.getSimpleName(); 38e7096ebba1f188871d0c5f04055d21c6153b9907Eric Olsen private static final boolean LOG = true; 399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String HEADSET_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/h2w"; 419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state"; 429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String HEADSET_NAME_PATH = "/sys/class/switch/h2w/name"; 439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 44a553c25b33c99b345cf1c8688f8df0ed8df14e5aEric Laurent private static final int BIT_HEADSET = (1 << 0); 45a553c25b33c99b345cf1c8688f8df0ed8df14e5aEric Laurent private static final int BIT_HEADSET_NO_MIC = (1 << 1); 462083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC); 472083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent private static final int HEADSETS_WITH_MIC = BIT_HEADSET; 48a553c25b33c99b345cf1c8688f8df0ed8df14e5aEric Laurent 499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mHeadsetState; 50a553c25b33c99b345cf1c8688f8df0ed8df14e5aEric Laurent private int mPrevHeadsetState; 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private String mHeadsetName; 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 539ac932179c704822c7ae5c7accb02119b578254eNick Pelly private final Context mContext; 549ac932179c704822c7ae5c7accb02119b578254eNick Pelly private final WakeLock mWakeLock; // held while there is a pending route change 559ac932179c704822c7ae5c7accb02119b578254eNick Pelly 569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public HeadsetObserver(Context context) { 579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mContext = context; 589ac932179c704822c7ae5c7accb02119b578254eNick Pelly PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 599ac932179c704822c7ae5c7accb02119b578254eNick Pelly mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetObserver"); 609ac932179c704822c7ae5c7accb02119b578254eNick Pelly mWakeLock.setReferenceCounted(false); 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project startObserving(HEADSET_UEVENT_MATCH); 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project init(); // set initial status 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onUEvent(UEventObserver.UEvent event) { 699a5e3e115fc69181e143bdb9ec455e0f94fbc11fJoe Onorato if (LOG) Log.v(TAG, "Headset UEVENT: " + event.toString()); 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project update(event.get("SWITCH_NAME"), Integer.parseInt(event.get("SWITCH_STATE"))); 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (NumberFormatException e) { 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.e(TAG, "Could not parse switch state from event " + event); 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private synchronized final void init() { 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char[] buffer = new char[1024]; 809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String newName = mHeadsetName; 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int newState = mHeadsetState; 83a553c25b33c99b345cf1c8688f8df0ed8df14e5aEric Laurent mPrevHeadsetState = mHeadsetState; 849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project FileReader file = new FileReader(HEADSET_STATE_PATH); 869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int len = file.read(buffer, 0, 1024); 879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project newState = Integer.valueOf((new String(buffer, 0, len)).trim()); 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project file = new FileReader(HEADSET_NAME_PATH); 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project len = file.read(buffer, 0, 1024); 919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project newName = new String(buffer, 0, len).trim(); 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (FileNotFoundException e) { 949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.w(TAG, "This kernel does not have wired headset support"); 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (Exception e) { 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.e(TAG, "" , e); 979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project update(newName, newState); 1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private synchronized final void update(String newName, int newState) { 103923d7d721d37f6ba5148e7d79d61a4fa48e79df2Eric Laurent // Retain only relevant bits 1042083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent int headsetState = newState & SUPPORTED_HEADSETS; 1052083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent int newOrOld = headsetState | mHeadsetState; 106700aab67eb286b44663f885325f8db6b049cb638Eric Laurent int delay = 0; 1072083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent // reject all suspect transitions: only accept state changes from: 1082083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent // - a: 0 heaset to 1 headset 1092083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent // - b: 1 headset to 0 headset 1102083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent if (mHeadsetState == headsetState || ((newOrOld & (newOrOld - 1)) != 0)) { 1112083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent return; 1122083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent } 113923d7d721d37f6ba5148e7d79d61a4fa48e79df2Eric Laurent 1142083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent mHeadsetName = newName; 1152083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent mPrevHeadsetState = mHeadsetState; 1162083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent mHeadsetState = headsetState; 1172083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent 1182083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent if (headsetState == 0) { 1192083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); 1202083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent mContext.sendBroadcast(intent); 1212083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent // It can take hundreds of ms flush the audio pipeline after 1222083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent // apps pause audio playback, but audio route changes are 1232083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent // immediate, so delay the route change by 1000ms. 1242083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent // This could be improved once the audio sub-system provides an 1252083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent // interface to clear the audio pipeline. 126700aab67eb286b44663f885325f8db6b049cb638Eric Laurent delay = 1000; 1272083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent } else { 128700aab67eb286b44663f885325f8db6b049cb638Eric Laurent // Insert the same delay for headset connection so that the connection event is not 129700aab67eb286b44663f885325f8db6b049cb638Eric Laurent // broadcast before the disconnection event in case of fast removal/insertion 130700aab67eb286b44663f885325f8db6b049cb638Eric Laurent if (mHandler.hasMessages(0)) { 131700aab67eb286b44663f885325f8db6b049cb638Eric Laurent delay = 1000; 132700aab67eb286b44663f885325f8db6b049cb638Eric Laurent } 1332083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent } 134700aab67eb286b44663f885325f8db6b049cb638Eric Laurent mWakeLock.acquire(); 135700aab67eb286b44663f885325f8db6b049cb638Eric Laurent mHandler.sendMessageDelayed(mHandler.obtainMessage(0, 136700aab67eb286b44663f885325f8db6b049cb638Eric Laurent mHeadsetState, 137700aab67eb286b44663f885325f8db6b049cb638Eric Laurent mPrevHeadsetState, 138700aab67eb286b44663f885325f8db6b049cb638Eric Laurent mHeadsetName), 139700aab67eb286b44663f885325f8db6b049cb638Eric Laurent delay); 1402083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent } 1412083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent 142da4cc34308d65730c404b669926a92e37b378555Eric Laurent private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) { 1432083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent int allHeadsets = SUPPORTED_HEADSETS; 1442083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) { 1452083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent if ((curHeadset & allHeadsets) != 0) { 146da4cc34308d65730c404b669926a92e37b378555Eric Laurent sendIntent(curHeadset, headsetState, prevHeadsetState, headsetName); 1472083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent allHeadsets &= ~curHeadset; 1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 152da4cc34308d65730c404b669926a92e37b378555Eric Laurent private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) { 153da4cc34308d65730c404b669926a92e37b378555Eric Laurent if ((headsetState & headset) != (prevHeadsetState & headset)) { 1542083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent // Pack up the values and broadcast them to everyone 1552083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG); 1562083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 1572083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent int state = 0; 1582083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent int microphone = 0; 159923d7d721d37f6ba5148e7d79d61a4fa48e79df2Eric Laurent 1602083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent if ((headset & HEADSETS_WITH_MIC) != 0) { 1612083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent microphone = 1; 162923d7d721d37f6ba5148e7d79d61a4fa48e79df2Eric Laurent } 163da4cc34308d65730c404b669926a92e37b378555Eric Laurent if ((headsetState & headset) != 0) { 164923d7d721d37f6ba5148e7d79d61a4fa48e79df2Eric Laurent state = 1; 165923d7d721d37f6ba5148e7d79d61a4fa48e79df2Eric Laurent } 1662083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent intent.putExtra("state", state); 167da4cc34308d65730c404b669926a92e37b378555Eric Laurent intent.putExtra("name", headsetName); 1682083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent intent.putExtra("microphone", microphone); 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 170da4cc34308d65730c404b669926a92e37b378555Eric Laurent if (LOG) Log.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone); 1712083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent // TODO: Should we require a permission? 1722083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent ActivityManagerNative.broadcastStickyIntent(intent, null); 1732083b297e401957dfae07ca11c29e8c95f440a97Eric Laurent } 1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private final Handler mHandler = new Handler() { 1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void handleMessage(Message msg) { 179da4cc34308d65730c404b669926a92e37b378555Eric Laurent sendIntents(msg.arg1, msg.arg2, (String)msg.obj); 1809ac932179c704822c7ae5c7accb02119b578254eNick Pelly mWakeLock.release(); 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1829ac932179c704822c7ae5c7accb02119b578254eNick Pelly }; 1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 184