1c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy/* 2c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy * Copyright (C) 2008 The Android Open Source Project 3c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy * 4c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy * Licensed under the Apache License, Version 2.0 (the "License"); 5c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy * you may not use this file except in compliance with the License. 6c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy * You may obtain a copy of the License at 7c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy * 8c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy * http://www.apache.org/licenses/LICENSE-2.0 9c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy * 10c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy * Unless required by applicable law or agreed to in writing, software 11c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy * distributed under the License is distributed on an "AS IS" BASIS, 12c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy * See the License for the specific language governing permissions and 14c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy * limitations under the License. 15c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy */ 16c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 17c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphypackage com.android.server; 18c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 190e9d2af2d60b381ba52d0c25e583b3d2a9906051Daniel Sandlerimport android.content.ContentResolver; 20c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphyimport android.content.Context; 21c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphyimport android.content.Intent; 22ec2c88d835dac156be0d14bd2b73cf3825e2e164Daniel Sandlerimport android.media.AudioManager; 230e9d2af2d60b381ba52d0c25e583b3d2a9906051Daniel Sandlerimport android.media.Ringtone; 240e9d2af2d60b381ba52d0c25e583b3d2a9906051Daniel Sandlerimport android.media.RingtoneManager; 250e9d2af2d60b381ba52d0c25e583b3d2a9906051Daniel Sandlerimport android.net.Uri; 26c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphyimport android.os.Handler; 27c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphyimport android.os.Message; 289630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brownimport android.os.PowerManager; 29f02c0740d3039977149773604a229c9f76c8121dKen Schultzimport android.os.SystemClock; 30c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphyimport android.os.UEventObserver; 315ac72a29593ab9a20337a2225df52bdf4754be02Dianne Hackbornimport android.os.UserHandle; 324949334d2064b32118b04c231dd622804a9df1cbDianne Hackbornimport android.provider.Settings; 33c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphyimport android.util.Log; 348a9b22056b13477f59df934928c00c58b5871c95Joe Onoratoimport android.util.Slog; 35c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 36c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphyimport java.io.FileNotFoundException; 373fbf7b62bb48b10316353087d09cc3720af00642Jaikumar Ganeshimport java.io.FileReader; 38c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 39c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy/** 40c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy * <p>DockObserver monitors for a docking station. 41c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy */ 42008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brownfinal class DockObserver extends UEventObserver { 43c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy private static final String TAG = DockObserver.class.getSimpleName(); 44c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 45c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock"; 46c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state"; 47c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 48008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown private static final int MSG_DOCK_STATE_CHANGED = 0; 49008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown 50008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown private final Object mLock = new Object(); 51bfca3a0056a42bde672c8ade83f13f139149f756Bernd Holzhey 52d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 530e9d2af2d60b381ba52d0c25e583b3d2a9906051Daniel Sandler private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 540e9d2af2d60b381ba52d0c25e583b3d2a9906051Daniel Sandler 55d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood private boolean mSystemReady; 56c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 57c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy private final Context mContext; 5862c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown private final PowerManager mPowerManager; 5962c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown private final PowerManager.WakeLock mWakeLock; 60c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 619630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown public DockObserver(Context context) { 62c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy mContext = context; 6327b28b3f62bd3b54fa13acd5d035940b9be464f3Tobias Haamel 6462c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown mPowerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); 6562c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); 6662c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown 6762c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown init(); // set initial status 68d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood startObserving(DOCK_UEVENT_MATCH); 69c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 70c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 71c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy @Override 72c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy public void onUEvent(UEventObserver.UEvent event) { 73c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy if (Log.isLoggable(TAG, Log.VERBOSE)) { 748a9b22056b13477f59df934928c00c58b5871c95Joe Onorato Slog.v(TAG, "Dock UEVENT: " + event.toString()); 75c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 76c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 77008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown synchronized (mLock) { 78d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood try { 79d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood int newState = Integer.parseInt(event.get("SWITCH_STATE")); 80d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood if (newState != mDockState) { 810e9d2af2d60b381ba52d0c25e583b3d2a9906051Daniel Sandler mPreviousDockState = mDockState; 82d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood mDockState = newState; 83d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood if (mSystemReady) { 8462c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown // Wake up immediately when docked or undocked. 8562c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown mPowerManager.wakeUp(SystemClock.uptimeMillis()); 8662c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown 87008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown updateLocked(); 88d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood } 89d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood } 90d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood } catch (NumberFormatException e) { 918a9b22056b13477f59df934928c00c58b5871c95Joe Onorato Slog.e(TAG, "Could not parse switch state from event " + event); 92d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood } 93c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 94c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 95c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 96008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown private void init() { 97008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown synchronized (mLock) { 98008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown try { 99008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown char[] buffer = new char[1024]; 100008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown FileReader file = new FileReader(DOCK_STATE_PATH); 101008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown try { 102008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown int len = file.read(buffer, 0, 1024); 103008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown mDockState = Integer.valueOf((new String(buffer, 0, len)).trim()); 104008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown mPreviousDockState = mDockState; 105008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } finally { 106008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown file.close(); 107008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } 108008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } catch (FileNotFoundException e) { 109008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown Slog.w(TAG, "This kernel does not have dock station support"); 110008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } catch (Exception e) { 111008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown Slog.e(TAG, "" , e); 112008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } 113c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 114c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 115c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 116d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood void systemReady() { 117008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown synchronized (mLock) { 118d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood // don't bother broadcasting undocked here 119d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { 120008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown updateLocked(); 121d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood } 122d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood mSystemReady = true; 123c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 124c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 125c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 126008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown private void updateLocked() { 12762c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown mWakeLock.acquire(); 128008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown mHandler.sendEmptyMessage(MSG_DOCK_STATE_CHANGED); 129c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 130c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 131008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown private void handleDockStateChange() { 132008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown synchronized (mLock) { 133008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown Slog.i(TAG, "Dock state changed: " + mDockState); 134786546eaccfc7a284b90b077f505cf42206c6f5fJohn Spurlock 13562c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown // Skip the dock intent if not yet provisioned. 136008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown final ContentResolver cr = mContext.getContentResolver(); 137bf6f6f9de72c9fd15e6bda9f228c05a9b37d6324Jeff Brown if (Settings.Global.getInt(cr, 138bf6f6f9de72c9fd15e6bda9f228c05a9b37d6324Jeff Brown Settings.Global.DEVICE_PROVISIONED, 0) == 0) { 139008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown Slog.i(TAG, "Device not provisioned, skipping dock broadcast"); 140008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown return; 141008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } 1420e9d2af2d60b381ba52d0c25e583b3d2a9906051Daniel Sandler 143008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown // Pack up the values and broadcast them to everyone 144008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown Intent intent = new Intent(Intent.ACTION_DOCK_EVENT); 145008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 146008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState); 147008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown 14862c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown // Play a sound to provide feedback to confirm dock connection. 14962c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown // Particularly useful for flaky contact pins... 150bf6f6f9de72c9fd15e6bda9f228c05a9b37d6324Jeff Brown if (Settings.Global.getInt(cr, 151bf6f6f9de72c9fd15e6bda9f228c05a9b37d6324Jeff Brown Settings.Global.DOCK_SOUNDS_ENABLED, 1) == 1) { 152008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown String whichSound = null; 153008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) { 154008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) || 155008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown (mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) || 156008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown (mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) { 157bf6f6f9de72c9fd15e6bda9f228c05a9b37d6324Jeff Brown whichSound = Settings.Global.DESK_UNDOCK_SOUND; 158008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) { 159bf6f6f9de72c9fd15e6bda9f228c05a9b37d6324Jeff Brown whichSound = Settings.Global.CAR_UNDOCK_SOUND; 160008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } 161008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } else { 162008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) || 163008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) || 164008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown (mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) { 165bf6f6f9de72c9fd15e6bda9f228c05a9b37d6324Jeff Brown whichSound = Settings.Global.DESK_DOCK_SOUND; 166008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) { 167bf6f6f9de72c9fd15e6bda9f228c05a9b37d6324Jeff Brown whichSound = Settings.Global.CAR_DOCK_SOUND; 168008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } 169008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } 1700e9d2af2d60b381ba52d0c25e583b3d2a9906051Daniel Sandler 171008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown if (whichSound != null) { 172bf6f6f9de72c9fd15e6bda9f228c05a9b37d6324Jeff Brown final String soundPath = Settings.Global.getString(cr, whichSound); 173008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown if (soundPath != null) { 174008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown final Uri soundUri = Uri.parse("file://" + soundPath); 175008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown if (soundUri != null) { 176008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); 177008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown if (sfx != null) { 178008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown sfx.setStreamType(AudioManager.STREAM_SYSTEM); 179008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown sfx.play(); 1800e9d2af2d60b381ba52d0c25e583b3d2a9906051Daniel Sandler } 1810e9d2af2d60b381ba52d0c25e583b3d2a9906051Daniel Sandler } 182008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } 183008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } 184008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } 185bfca3a0056a42bde672c8ade83f13f139149f756Bernd Holzhey 18662c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown // Send the dock event intent. 18762c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown // There are many components in the system watching for this so as to 18862c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown // adjust audio routing, screen orientation, etc. 18962c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 190008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown 19162c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown // Release the wake lock that was acquired when the message was posted. 19262c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown mWakeLock.release(); 19362c82e4d92cc0b856059f905d81885f7808a0e7dJeff Brown } 194008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown } 195008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown 196a2910d0abbbe18ba1710dfd4a31af45769632255Jeff Brown private final Handler mHandler = new Handler(true /*async*/) { 197008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown @Override 198008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown public void handleMessage(Message msg) { 199008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown switch (msg.what) { 200008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown case MSG_DOCK_STATE_CHANGED: 201008b1762a8d5c908281a832ff90817ade6c7f9f6Jeff Brown handleDockStateChange(); 202bfca3a0056a42bde672c8ade83f13f139149f756Bernd Holzhey break; 203bfca3a0056a42bde672c8ade83f13f139149f756Bernd Holzhey } 204bfca3a0056a42bde672c8ade83f13f139149f756Bernd Holzhey } 20527b28b3f62bd3b54fa13acd5d035940b9be464f3Tobias Haamel }; 206c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy} 207