blob: 8bdd7be8e196a9edf4ff0eb59b81e3b7804e8beb [file] [log] [blame]
Dan Murphyc9f4eaf2009-08-12 15:15:43 -05001/*
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
John Spurlock786546e2012-08-08 11:40:20 -040019import static android.provider.Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK;
20
Jeff Brown4f8ecd82012-06-18 18:29:13 -070021import com.android.server.power.PowerManagerService;
22
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080023import android.bluetooth.BluetoothAdapter;
24import android.bluetooth.BluetoothDevice;
Daniel Sandler0e9d2af2010-01-25 11:33:03 -050025import android.content.ContentResolver;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050026import android.content.Context;
27import android.content.Intent;
Daniel Sandlerec2c88d2010-02-20 01:04:57 -050028import android.media.AudioManager;
Daniel Sandler0e9d2af2010-01-25 11:33:03 -050029import android.media.Ringtone;
30import android.media.RingtoneManager;
31import android.net.Uri;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050032import android.os.Handler;
33import android.os.Message;
John Spurlockbc632a22012-07-31 08:28:12 -040034import android.os.RemoteException;
35import android.os.ServiceManager;
Jeff Brown96307042012-07-27 15:51:34 -070036import android.os.PowerManager;
Ken Schultzf02c0742009-09-10 18:37:37 -050037import android.os.SystemClock;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050038import android.os.UEventObserver;
Dianne Hackborn49493342009-10-02 10:44:41 -070039import android.provider.Settings;
John Spurlockbc632a22012-07-31 08:28:12 -040040import android.service.dreams.IDreamManager;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050041import android.util.Log;
Joe Onorato8a9b2202010-02-26 18:56:32 -080042import android.util.Slog;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050043
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050044import java.io.FileNotFoundException;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080045import java.io.FileReader;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050046
47/**
48 * <p>DockObserver monitors for a docking station.
49 */
50class DockObserver extends UEventObserver {
51 private static final String TAG = DockObserver.class.getSimpleName();
52 private static final boolean LOG = false;
53
54 private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
55 private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
56
John Spurlock786546e2012-08-08 11:40:20 -040057 private static final int DEFAULT_DOCK = 1;
58
Bernd Holzheybfca3a02010-02-10 17:39:51 +010059 private static final int MSG_DOCK_STATE = 0;
Bernd Holzheybfca3a02010-02-10 17:39:51 +010060
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -070061 private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
Daniel Sandler0e9d2af2010-01-25 11:33:03 -050062 private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
63
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -070064 private boolean mSystemReady;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050065
66 private final Context mContext;
67
Jeff Brown96307042012-07-27 15:51:34 -070068 public DockObserver(Context context) {
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050069 mContext = context;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050070 init(); // set initial status
Tobias Haamel27b28b32010-02-09 23:09:17 +010071
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -070072 startObserving(DOCK_UEVENT_MATCH);
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050073 }
74
75 @Override
76 public void onUEvent(UEventObserver.UEvent event) {
77 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -080078 Slog.v(TAG, "Dock UEVENT: " + event.toString());
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050079 }
80
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -070081 synchronized (this) {
82 try {
83 int newState = Integer.parseInt(event.get("SWITCH_STATE"));
84 if (newState != mDockState) {
Daniel Sandler0e9d2af2010-01-25 11:33:03 -050085 mPreviousDockState = mDockState;
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -070086 mDockState = newState;
87 if (mSystemReady) {
Mike Lockwood1d069922009-11-11 18:09:25 -050088 // Don't force screen on when undocking from the desk dock.
89 // The change in power state will do this anyway.
90 // FIXME - we should be configurable.
Jeff Brown1a693182011-11-08 14:44:16 -080091 if ((mPreviousDockState != Intent.EXTRA_DOCK_STATE_DESK
92 && mPreviousDockState != Intent.EXTRA_DOCK_STATE_LE_DESK
93 && mPreviousDockState != Intent.EXTRA_DOCK_STATE_HE_DESK) ||
Daniel Sandler0e9d2af2010-01-25 11:33:03 -050094 mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
Jeff Brown96307042012-07-27 15:51:34 -070095 PowerManager pm =
96 (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
97 pm.wakeUp(SystemClock.uptimeMillis());
Mike Lockwood1d069922009-11-11 18:09:25 -050098 }
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -070099 update();
100 }
101 }
102 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800103 Slog.e(TAG, "Could not parse switch state from event " + event);
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -0700104 }
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500105 }
106 }
107
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -0700108 private final void init() {
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500109 char[] buffer = new char[1024];
110
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500111 try {
112 FileReader file = new FileReader(DOCK_STATE_PATH);
113 int len = file.read(buffer, 0, 1024);
Brian Carlstromfd9ddd12010-11-04 11:24:58 -0700114 file.close();
Daniel Sandler0e9d2af2010-01-25 11:33:03 -0500115 mPreviousDockState = mDockState = Integer.valueOf((new String(buffer, 0, len)).trim());
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500116 } catch (FileNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800117 Slog.w(TAG, "This kernel does not have dock station support");
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500118 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800119 Slog.e(TAG, "" , e);
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500120 }
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500121 }
122
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -0700123 void systemReady() {
124 synchronized (this) {
125 // don't bother broadcasting undocked here
126 if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
127 update();
128 }
129 mSystemReady = true;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500130 }
131 }
132
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -0700133 private final void update() {
Bernd Holzheybfca3a02010-02-10 17:39:51 +0100134 mHandler.sendEmptyMessage(MSG_DOCK_STATE);
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500135 }
136
John Spurlock786546e2012-08-08 11:40:20 -0400137 private static boolean isScreenSaverActivatedOnDock(Context context) {
138 return 0 != Settings.Secure.getInt(
139 context.getContentResolver(), SCREENSAVER_ACTIVATE_ON_DOCK, DEFAULT_DOCK);
140 }
141
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500142 private final Handler mHandler = new Handler() {
143 @Override
144 public void handleMessage(Message msg) {
Bernd Holzheybfca3a02010-02-10 17:39:51 +0100145 switch (msg.what) {
146 case MSG_DOCK_STATE:
147 synchronized (this) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800148 Slog.i(TAG, "Dock state changed: " + mDockState);
Daniel Sandler0e9d2af2010-01-25 11:33:03 -0500149
Bernd Holzheybfca3a02010-02-10 17:39:51 +0100150 final ContentResolver cr = mContext.getContentResolver();
Daniel Sandler0e9d2af2010-01-25 11:33:03 -0500151
Bernd Holzheybfca3a02010-02-10 17:39:51 +0100152 if (Settings.Secure.getInt(cr,
153 Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800154 Slog.i(TAG, "Device not provisioned, skipping dock broadcast");
Bernd Holzheybfca3a02010-02-10 17:39:51 +0100155 return;
Daniel Sandler0e9d2af2010-01-25 11:33:03 -0500156 }
Bernd Holzheybfca3a02010-02-10 17:39:51 +0100157 // Pack up the values and broadcast them to everyone
158 Intent intent = new Intent(Intent.ACTION_DOCK_EVENT);
159 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
Dianne Hackborn7299c412010-03-04 18:41:49 -0800160 intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
Bernd Holzheybfca3a02010-02-10 17:39:51 +0100161
162 // Check if this is Bluetooth Dock
Jaikumar Ganesh9773ebb2012-01-17 16:51:41 -0800163 // TODO(BT): Get Dock address.
164 String address = null;
Bernd Holzheybfca3a02010-02-10 17:39:51 +0100165 if (address != null)
166 intent.putExtra(BluetoothDevice.EXTRA_DEVICE,
167 BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address));
168
169 // User feedback to confirm dock connection. Particularly
170 // useful for flaky contact pins...
171 if (Settings.System.getInt(cr,
172 Settings.System.DOCK_SOUNDS_ENABLED, 1) == 1)
173 {
174 String whichSound = null;
175 if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500176 if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
177 (mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
178 (mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
Bernd Holzheybfca3a02010-02-10 17:39:51 +0100179 whichSound = Settings.System.DESK_UNDOCK_SOUND;
180 } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) {
181 whichSound = Settings.System.CAR_UNDOCK_SOUND;
182 }
183 } else {
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500184 if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
185 (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
186 (mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
Bernd Holzheybfca3a02010-02-10 17:39:51 +0100187 whichSound = Settings.System.DESK_DOCK_SOUND;
188 } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) {
189 whichSound = Settings.System.CAR_DOCK_SOUND;
190 }
191 }
192
193 if (whichSound != null) {
194 final String soundPath = Settings.System.getString(cr, whichSound);
195 if (soundPath != null) {
196 final Uri soundUri = Uri.parse("file://" + soundPath);
197 if (soundUri != null) {
198 final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
Daniel Sandlerec2c88d2010-02-20 01:04:57 -0500199 if (sfx != null) {
200 sfx.setStreamType(AudioManager.STREAM_SYSTEM);
201 sfx.play();
202 }
Bernd Holzheybfca3a02010-02-10 17:39:51 +0100203 }
204 }
205 }
206 }
207
John Spurlockbc632a22012-07-31 08:28:12 -0400208 IDreamManager mgr = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams"));
209 if (mgr != null) {
210 // dreams feature enabled
211 boolean undocked = mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED;
212 if (undocked) {
213 try {
214 if (mgr.isDreaming()) {
215 mgr.awaken();
216 }
217 } catch (RemoteException e) {
218 Slog.w(TAG, "Unable to awaken!", e);
219 }
220 } else {
John Spurlock786546e2012-08-08 11:40:20 -0400221 if (isScreenSaverActivatedOnDock(mContext)) {
222 try {
223 mgr.dream();
224 } catch (RemoteException e) {
225 Slog.w(TAG, "Unable to dream!", e);
226 }
John Spurlockbc632a22012-07-31 08:28:12 -0400227 }
228 }
229 } else {
230 // dreams feature not enabled, send legacy intent
231 mContext.sendStickyBroadcast(intent);
232 }
Bernd Holzheybfca3a02010-02-10 17:39:51 +0100233 }
234 break;
235 }
236 }
Tobias Haamel27b28b32010-02-09 23:09:17 +0100237 };
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500238}