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