blob: f1ff27ff351b55339e772fbf565da5a57428c709 [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;
John Spurlock1a868b72012-08-22 09:56:51 -040020import static android.provider.Settings.Secure.SCREENSAVER_ENABLED;
John Spurlock786546e2012-08-08 11:40:20 -040021
Daniel Sandler0e9d2af2010-01-25 11:33:03 -050022import android.content.ContentResolver;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050023import android.content.Context;
24import android.content.Intent;
Daniel Sandlerec2c88d2010-02-20 01:04:57 -050025import android.media.AudioManager;
Daniel Sandler0e9d2af2010-01-25 11:33:03 -050026import android.media.Ringtone;
27import android.media.RingtoneManager;
28import android.net.Uri;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050029import android.os.Handler;
Jeff Brown008b1762012-08-20 20:15:34 -070030import android.os.Looper;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050031import android.os.Message;
John Spurlockbc632a22012-07-31 08:28:12 -040032import android.os.RemoteException;
33import android.os.ServiceManager;
Jeff Brown96307042012-07-27 15:51:34 -070034import android.os.PowerManager;
Ken Schultzf02c0742009-09-10 18:37:37 -050035import android.os.SystemClock;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050036import android.os.UEventObserver;
Dianne Hackborn49493342009-10-02 10:44:41 -070037import android.provider.Settings;
John Spurlockbc632a22012-07-31 08:28:12 -040038import android.service.dreams.IDreamManager;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050039import android.util.Log;
Joe Onorato8a9b2202010-02-26 18:56:32 -080040import android.util.Slog;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050041
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050042import java.io.FileNotFoundException;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080043import java.io.FileReader;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050044
45/**
46 * <p>DockObserver monitors for a docking station.
47 */
Jeff Brown008b1762012-08-20 20:15:34 -070048final class DockObserver extends UEventObserver {
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050049 private static final String TAG = DockObserver.class.getSimpleName();
50 private static final boolean LOG = false;
51
52 private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
53 private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
54
John Spurlock1a868b72012-08-22 09:56:51 -040055 private static final int DEFAULT_SCREENSAVER_ENABLED = 1;
56 private static final int DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK = 1;
John Spurlock786546e2012-08-08 11:40:20 -040057
Jeff Brown008b1762012-08-20 20:15:34 -070058 private static final int MSG_DOCK_STATE_CHANGED = 0;
59
60 private final Object mLock = new Object();
Bernd Holzheybfca3a02010-02-10 17:39:51 +010061
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -070062 private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
Daniel Sandler0e9d2af2010-01-25 11:33:03 -050063 private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
64
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -070065 private boolean mSystemReady;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050066
67 private final Context mContext;
68
Jeff Brown96307042012-07-27 15:51:34 -070069 public DockObserver(Context context) {
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050070 mContext = context;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050071 init(); // set initial status
Tobias Haamel27b28b32010-02-09 23:09:17 +010072
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -070073 startObserving(DOCK_UEVENT_MATCH);
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050074 }
75
76 @Override
77 public void onUEvent(UEventObserver.UEvent event) {
78 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -080079 Slog.v(TAG, "Dock UEVENT: " + event.toString());
Dan Murphyc9f4eaf2009-08-12 15:15:43 -050080 }
81
Jeff Brown008b1762012-08-20 20:15:34 -070082 synchronized (mLock) {
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -070083 try {
84 int newState = Integer.parseInt(event.get("SWITCH_STATE"));
85 if (newState != mDockState) {
Daniel Sandler0e9d2af2010-01-25 11:33:03 -050086 mPreviousDockState = mDockState;
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -070087 mDockState = newState;
88 if (mSystemReady) {
Mike Lockwood1d069922009-11-11 18:09:25 -050089 // Don't force screen on when undocking from the desk dock.
90 // The change in power state will do this anyway.
91 // FIXME - we should be configurable.
Jeff Brown1a693182011-11-08 14:44:16 -080092 if ((mPreviousDockState != Intent.EXTRA_DOCK_STATE_DESK
93 && mPreviousDockState != Intent.EXTRA_DOCK_STATE_LE_DESK
94 && mPreviousDockState != Intent.EXTRA_DOCK_STATE_HE_DESK) ||
Daniel Sandler0e9d2af2010-01-25 11:33:03 -050095 mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
Jeff Brown96307042012-07-27 15:51:34 -070096 PowerManager pm =
97 (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
98 pm.wakeUp(SystemClock.uptimeMillis());
Mike Lockwood1d069922009-11-11 18:09:25 -050099 }
Jeff Brown008b1762012-08-20 20:15:34 -0700100 updateLocked();
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -0700101 }
102 }
103 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800104 Slog.e(TAG, "Could not parse switch state from event " + event);
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -0700105 }
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500106 }
107 }
108
Jeff Brown008b1762012-08-20 20:15:34 -0700109 private void init() {
110 synchronized (mLock) {
111 try {
112 char[] buffer = new char[1024];
113 FileReader file = new FileReader(DOCK_STATE_PATH);
114 try {
115 int len = file.read(buffer, 0, 1024);
116 mDockState = Integer.valueOf((new String(buffer, 0, len)).trim());
117 mPreviousDockState = mDockState;
118 } finally {
119 file.close();
120 }
121 } catch (FileNotFoundException e) {
122 Slog.w(TAG, "This kernel does not have dock station support");
123 } catch (Exception e) {
124 Slog.e(TAG, "" , e);
125 }
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500126 }
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500127 }
128
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -0700129 void systemReady() {
Jeff Brown008b1762012-08-20 20:15:34 -0700130 synchronized (mLock) {
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -0700131 // don't bother broadcasting undocked here
132 if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
Jeff Brown008b1762012-08-20 20:15:34 -0700133 updateLocked();
Mike Lockwoodd0e82ce2009-08-27 16:19:07 -0700134 }
135 mSystemReady = true;
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500136 }
137 }
138
Jeff Brown008b1762012-08-20 20:15:34 -0700139 private void updateLocked() {
140 mHandler.sendEmptyMessage(MSG_DOCK_STATE_CHANGED);
141 }
142
143 private void handleDockStateChange() {
144 synchronized (mLock) {
145 Slog.i(TAG, "Dock state changed: " + mDockState);
146
147 final ContentResolver cr = mContext.getContentResolver();
148
149 if (Settings.Secure.getInt(cr,
150 Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
151 Slog.i(TAG, "Device not provisioned, skipping dock broadcast");
152 return;
153 }
154
155 // Pack up the values and broadcast them to everyone
156 Intent intent = new Intent(Intent.ACTION_DOCK_EVENT);
157 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
158 intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
159
160 // Check if this is Bluetooth Dock
161 // TODO(BT): Get Dock address.
162 // String address = null;
163 // if (address != null) {
164 // intent.putExtra(BluetoothDevice.EXTRA_DEVICE,
165 // BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address));
166 // }
167
168 // User feedback to confirm dock connection. Particularly
169 // useful for flaky contact pins...
170 if (Settings.System.getInt(cr,
171 Settings.System.DOCK_SOUNDS_ENABLED, 1) == 1) {
172 String whichSound = null;
173 if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
174 if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
175 (mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
176 (mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
177 whichSound = Settings.System.DESK_UNDOCK_SOUND;
178 } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) {
179 whichSound = Settings.System.CAR_UNDOCK_SOUND;
180 }
181 } else {
182 if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
183 (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
184 (mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
185 whichSound = Settings.System.DESK_DOCK_SOUND;
186 } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) {
187 whichSound = Settings.System.CAR_DOCK_SOUND;
188 }
189 }
190
191 if (whichSound != null) {
192 final String soundPath = Settings.System.getString(cr, whichSound);
193 if (soundPath != null) {
194 final Uri soundUri = Uri.parse("file://" + soundPath);
195 if (soundUri != null) {
196 final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
197 if (sfx != null) {
198 sfx.setStreamType(AudioManager.STREAM_SYSTEM);
199 sfx.play();
200 }
201 }
202 }
203 }
204 }
205
206 IDreamManager mgr = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams"));
207 if (mgr != null) {
208 // dreams feature enabled
209 boolean undocked = mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED;
210 if (undocked) {
211 try {
212 if (mgr.isDreaming()) {
213 mgr.awaken();
214 }
215 } catch (RemoteException e) {
216 Slog.w(TAG, "Unable to awaken!", e);
217 }
218 } else {
John Spurlock1a868b72012-08-22 09:56:51 -0400219 if (isScreenSaverEnabled(mContext) && isScreenSaverActivatedOnDock(mContext)) {
Jeff Brown008b1762012-08-20 20:15:34 -0700220 try {
221 mgr.dream();
222 } catch (RemoteException e) {
223 Slog.w(TAG, "Unable to dream!", e);
224 }
225 }
226 }
227 } else {
228 // dreams feature not enabled, send legacy intent
229 mContext.sendStickyBroadcast(intent);
230 }
231 }
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500232 }
233
John Spurlock1a868b72012-08-22 09:56:51 -0400234 private static boolean isScreenSaverEnabled(Context context) {
235 return Settings.Secure.getInt(context.getContentResolver(),
236 SCREENSAVER_ENABLED, DEFAULT_SCREENSAVER_ENABLED) != 0;
237 }
238
John Spurlock786546e2012-08-08 11:40:20 -0400239 private static boolean isScreenSaverActivatedOnDock(Context context) {
Jeff Brown008b1762012-08-20 20:15:34 -0700240 return Settings.Secure.getInt(context.getContentResolver(),
John Spurlock1a868b72012-08-22 09:56:51 -0400241 SCREENSAVER_ACTIVATE_ON_DOCK, DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK) != 0;
John Spurlock786546e2012-08-08 11:40:20 -0400242 }
243
Jeff Browna2910d02012-08-25 12:29:46 -0700244 private final Handler mHandler = new Handler(true /*async*/) {
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500245 @Override
246 public void handleMessage(Message msg) {
Bernd Holzheybfca3a02010-02-10 17:39:51 +0100247 switch (msg.what) {
Jeff Brown008b1762012-08-20 20:15:34 -0700248 case MSG_DOCK_STATE_CHANGED:
249 handleDockStateChange();
Bernd Holzheybfca3a02010-02-10 17:39:51 +0100250 break;
251 }
252 }
Tobias Haamel27b28b32010-02-09 23:09:17 +0100253 };
Dan Murphyc9f4eaf2009-08-12 15:15:43 -0500254}