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 191f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeauimport android.app.Activity; 20733fdf34b6a8a8e3983ff7663828a928d3c19944Mike Lockwoodimport android.app.KeyguardManager; 213fbf7b62bb48b10316353087d09cc3720af00642Jaikumar Ganeshimport android.bluetooth.BluetoothAdapter; 223fbf7b62bb48b10316353087d09cc3720af00642Jaikumar Ganeshimport android.bluetooth.BluetoothDevice; 239092ab4d458ad60b459d5fad52368bde5d25f665Mike Lockwoodimport android.content.ActivityNotFoundException; 241f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeauimport android.content.BroadcastReceiver; 25c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphyimport android.content.Context; 26c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphyimport android.content.Intent; 27c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphyimport android.os.Handler; 28c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphyimport android.os.Message; 29f02c0740d3039977149773604a229c9f76c8121dKen Schultzimport android.os.SystemClock; 30c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphyimport android.os.UEventObserver; 314949334d2064b32118b04c231dd622804a9df1cbDianne Hackbornimport android.provider.Settings; 323fbf7b62bb48b10316353087d09cc3720af00642Jaikumar Ganeshimport android.server.BluetoothService; 33c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphyimport android.util.Log; 34c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 35733fdf34b6a8a8e3983ff7663828a928d3c19944Mike Lockwoodimport com.android.internal.widget.LockPatternUtils; 36733fdf34b6a8a8e3983ff7663828a928d3c19944Mike Lockwood 37c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphyimport java.io.FileNotFoundException; 383fbf7b62bb48b10316353087d09cc3720af00642Jaikumar Ganeshimport java.io.FileReader; 39c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 40c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy/** 41c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy * <p>DockObserver monitors for a docking station. 42c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy */ 43c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphyclass DockObserver extends UEventObserver { 44c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy private static final String TAG = DockObserver.class.getSimpleName(); 45c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy private static final boolean LOG = false; 46c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 47c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock"; 48c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state"; 49c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 50d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 51d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood private boolean mSystemReady; 52c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 53c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy private final Context mContext; 54c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 55f02c0740d3039977149773604a229c9f76c8121dKen Schultz private PowerManagerService mPowerManager; 56733fdf34b6a8a8e3983ff7663828a928d3c19944Mike Lockwood 57733fdf34b6a8a8e3983ff7663828a928d3c19944Mike Lockwood private KeyguardManager.KeyguardLock mKeyguardLock; 58733fdf34b6a8a8e3983ff7663828a928d3c19944Mike Lockwood private boolean mKeyguardDisabled; 59733fdf34b6a8a8e3983ff7663828a928d3c19944Mike Lockwood private LockPatternUtils mLockPatternUtils; 60733fdf34b6a8a8e3983ff7663828a928d3c19944Mike Lockwood 611f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau // The broadcast receiver which receives the result of the ordered broadcast sent when 621f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau // the dock state changes. The original ordered broadcast is sent with an initial result 631f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g., 641f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau // to RESULT_CANCELED, then the intent to start a dock app will not be sent. 651f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() { 661f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau @Override 671f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau public void onReceive(Context context, Intent intent) { 681f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau if (getResultCode() != Activity.RESULT_OK) { 691f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau return; 701f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau } 713fbf7b62bb48b10316353087d09cc3720af00642Jaikumar Ganesh 721f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau // Launch a dock activity 731f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau String category; 741f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau switch (mDockState) { 751f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau case Intent.EXTRA_DOCK_STATE_CAR: 761f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau category = Intent.CATEGORY_CAR_DOCK; 771f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau break; 781f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau case Intent.EXTRA_DOCK_STATE_DESK: 791f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau category = Intent.CATEGORY_DESK_DOCK; 801f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau break; 811f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau default: 821f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau category = null; 831f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau break; 841f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau } 851f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau if (category != null) { 861f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau intent = new Intent(Intent.ACTION_MAIN); 871f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau intent.addCategory(category); 889bfb707597898f54722460b48588007b682f3e2aDianne Hackborn intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 899bfb707597898f54722460b48588007b682f3e2aDianne Hackborn | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 901f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau try { 911f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau mContext.startActivity(intent); 921f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau } catch (ActivityNotFoundException e) { 931f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau Log.w(TAG, e.getCause()); 941f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau } 951f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau } 961f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau } 971f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau }; 98f02c0740d3039977149773604a229c9f76c8121dKen Schultz 99f02c0740d3039977149773604a229c9f76c8121dKen Schultz public DockObserver(Context context, PowerManagerService pm) { 100c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy mContext = context; 101f02c0740d3039977149773604a229c9f76c8121dKen Schultz mPowerManager = pm; 102733fdf34b6a8a8e3983ff7663828a928d3c19944Mike Lockwood mLockPatternUtils = new LockPatternUtils(context.getContentResolver()); 103c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy init(); // set initial status 104d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood startObserving(DOCK_UEVENT_MATCH); 105c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 106c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 107c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy @Override 108c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy public void onUEvent(UEventObserver.UEvent event) { 109c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy if (Log.isLoggable(TAG, Log.VERBOSE)) { 110c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy Log.v(TAG, "Dock UEVENT: " + event.toString()); 111c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 112c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 113d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood synchronized (this) { 114d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood try { 115d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood int newState = Integer.parseInt(event.get("SWITCH_STATE")); 116d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood if (newState != mDockState) { 1171d069925363c684eda639f076d19232b7b6d47caMike Lockwood int oldState = mDockState; 118d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood mDockState = newState; 119d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood if (mSystemReady) { 1201d069925363c684eda639f076d19232b7b6d47caMike Lockwood // Don't force screen on when undocking from the desk dock. 1211d069925363c684eda639f076d19232b7b6d47caMike Lockwood // The change in power state will do this anyway. 1221d069925363c684eda639f076d19232b7b6d47caMike Lockwood // FIXME - we should be configurable. 1231d069925363c684eda639f076d19232b7b6d47caMike Lockwood if (oldState != Intent.EXTRA_DOCK_STATE_DESK || 1241d069925363c684eda639f076d19232b7b6d47caMike Lockwood newState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { 1251d069925363c684eda639f076d19232b7b6d47caMike Lockwood mPowerManager.userActivityWithForce(SystemClock.uptimeMillis(), 1261d069925363c684eda639f076d19232b7b6d47caMike Lockwood false, true); 1271d069925363c684eda639f076d19232b7b6d47caMike Lockwood } 128d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood update(); 129d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood } 130d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood } 131d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood } catch (NumberFormatException e) { 132d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood Log.e(TAG, "Could not parse switch state from event " + event); 133d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood } 134c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 135c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 136c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 137d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood private final void init() { 138c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy char[] buffer = new char[1024]; 139c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 140c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy try { 141c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy FileReader file = new FileReader(DOCK_STATE_PATH); 142c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy int len = file.read(buffer, 0, 1024); 143d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood mDockState = Integer.valueOf((new String(buffer, 0, len)).trim()); 144c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 145c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } catch (FileNotFoundException e) { 146c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy Log.w(TAG, "This kernel does not have dock station support"); 147c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } catch (Exception e) { 148c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy Log.e(TAG, "" , e); 149c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 150c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 151c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 152d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood void systemReady() { 153d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood synchronized (this) { 154733fdf34b6a8a8e3983ff7663828a928d3c19944Mike Lockwood KeyguardManager keyguardManager = 155733fdf34b6a8a8e3983ff7663828a928d3c19944Mike Lockwood (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE); 156733fdf34b6a8a8e3983ff7663828a928d3c19944Mike Lockwood mKeyguardLock = keyguardManager.newKeyguardLock(TAG); 157733fdf34b6a8a8e3983ff7663828a928d3c19944Mike Lockwood 158d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood // don't bother broadcasting undocked here 159d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { 160d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood update(); 161d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood } 162d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood mSystemReady = true; 163c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 164c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 165c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 166d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood private final void update() { 167d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood mHandler.sendEmptyMessage(0); 168c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 169c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy 170c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy private final Handler mHandler = new Handler() { 171c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy @Override 172c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy public void handleMessage(Message msg) { 173d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood synchronized (this) { 1744949334d2064b32118b04c231dd622804a9df1cbDianne Hackborn Log.i(TAG, "Dock state changed: " + mDockState); 1754949334d2064b32118b04c231dd622804a9df1cbDianne Hackborn if (Settings.Secure.getInt(mContext.getContentResolver(), 1764949334d2064b32118b04c231dd622804a9df1cbDianne Hackborn Settings.Secure.DEVICE_PROVISIONED, 0) == 0) { 1774949334d2064b32118b04c231dd622804a9df1cbDianne Hackborn Log.i(TAG, "Device not provisioned, skipping dock broadcast"); 1784949334d2064b32118b04c231dd622804a9df1cbDianne Hackborn return; 1794949334d2064b32118b04c231dd622804a9df1cbDianne Hackborn } 180d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood // Pack up the values and broadcast them to everyone 181d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood Intent intent = new Intent(Intent.ACTION_DOCK_EVENT); 182d0e82cec71442485d9cc952e5e16165d9d98b16eMike Lockwood intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState); 1833fbf7b62bb48b10316353087d09cc3720af00642Jaikumar Ganesh 1843fbf7b62bb48b10316353087d09cc3720af00642Jaikumar Ganesh // Check if this is Bluetooth Dock 1853fbf7b62bb48b10316353087d09cc3720af00642Jaikumar Ganesh String address = BluetoothService.readDockBluetoothAddress(); 1863fbf7b62bb48b10316353087d09cc3720af00642Jaikumar Ganesh if (address != null) 1873fbf7b62bb48b10316353087d09cc3720af00642Jaikumar Ganesh intent.putExtra(BluetoothDevice.EXTRA_DEVICE, 1883fbf7b62bb48b10316353087d09cc3720af00642Jaikumar Ganesh BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address)); 1893fbf7b62bb48b10316353087d09cc3720af00642Jaikumar Ganesh 1901f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau // Send the ordered broadcast; the result receiver will receive after all 1911f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau // broadcasts have been sent. If any broadcast receiver changes the result 1921f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau // code from the initial value of RESULT_OK, then the result receiver will 1931f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau // not launch the corresponding dock application. This gives apps a chance 1941f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau // to override the behavior and stay in their app even when the device is 1951f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau // placed into a dock. 1961f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau mContext.sendStickyOrderedBroadcast( 1971f6c7e6a2b99f99fdfbe438118241e60b4ded745Mike LeBeau intent, mResultReceiver, null, Activity.RESULT_OK, null, null); 198c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 199c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy } 200c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy }; 201c9f4eaf4388087f23aa97e55349850f0902a19d3Dan Murphy} 202