DockObserver.java revision a2910d0abbbe18ba1710dfd4a31af45769632255
1/* 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 19import static android.provider.Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK; 20import static android.provider.Settings.Secure.SCREENSAVER_ENABLED; 21 22import android.content.ContentResolver; 23import android.content.Context; 24import android.content.Intent; 25import android.media.AudioManager; 26import android.media.Ringtone; 27import android.media.RingtoneManager; 28import android.net.Uri; 29import android.os.Handler; 30import android.os.Looper; 31import android.os.Message; 32import android.os.RemoteException; 33import android.os.ServiceManager; 34import android.os.PowerManager; 35import android.os.SystemClock; 36import android.os.UEventObserver; 37import android.provider.Settings; 38import android.service.dreams.IDreamManager; 39import android.util.Log; 40import android.util.Slog; 41 42import java.io.FileNotFoundException; 43import java.io.FileReader; 44 45/** 46 * <p>DockObserver monitors for a docking station. 47 */ 48final class DockObserver extends UEventObserver { 49 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 55 private static final int DEFAULT_SCREENSAVER_ENABLED = 1; 56 private static final int DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK = 1; 57 58 private static final int MSG_DOCK_STATE_CHANGED = 0; 59 60 private final Object mLock = new Object(); 61 62 private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 63 private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 64 65 private boolean mSystemReady; 66 67 private final Context mContext; 68 69 public DockObserver(Context context) { 70 mContext = context; 71 init(); // set initial status 72 73 startObserving(DOCK_UEVENT_MATCH); 74 } 75 76 @Override 77 public void onUEvent(UEventObserver.UEvent event) { 78 if (Log.isLoggable(TAG, Log.VERBOSE)) { 79 Slog.v(TAG, "Dock UEVENT: " + event.toString()); 80 } 81 82 synchronized (mLock) { 83 try { 84 int newState = Integer.parseInt(event.get("SWITCH_STATE")); 85 if (newState != mDockState) { 86 mPreviousDockState = mDockState; 87 mDockState = newState; 88 if (mSystemReady) { 89 // 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. 92 if ((mPreviousDockState != Intent.EXTRA_DOCK_STATE_DESK 93 && mPreviousDockState != Intent.EXTRA_DOCK_STATE_LE_DESK 94 && mPreviousDockState != Intent.EXTRA_DOCK_STATE_HE_DESK) || 95 mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { 96 PowerManager pm = 97 (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); 98 pm.wakeUp(SystemClock.uptimeMillis()); 99 } 100 updateLocked(); 101 } 102 } 103 } catch (NumberFormatException e) { 104 Slog.e(TAG, "Could not parse switch state from event " + event); 105 } 106 } 107 } 108 109 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 } 126 } 127 } 128 129 void systemReady() { 130 synchronized (mLock) { 131 // don't bother broadcasting undocked here 132 if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { 133 updateLocked(); 134 } 135 mSystemReady = true; 136 } 137 } 138 139 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 { 219 if (isScreenSaverEnabled(mContext) && isScreenSaverActivatedOnDock(mContext)) { 220 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 } 232 } 233 234 private static boolean isScreenSaverEnabled(Context context) { 235 return Settings.Secure.getInt(context.getContentResolver(), 236 SCREENSAVER_ENABLED, DEFAULT_SCREENSAVER_ENABLED) != 0; 237 } 238 239 private static boolean isScreenSaverActivatedOnDock(Context context) { 240 return Settings.Secure.getInt(context.getContentResolver(), 241 SCREENSAVER_ACTIVATE_ON_DOCK, DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK) != 0; 242 } 243 244 private final Handler mHandler = new Handler(true /*async*/) { 245 @Override 246 public void handleMessage(Message msg) { 247 switch (msg.what) { 248 case MSG_DOCK_STATE_CHANGED: 249 handleDockStateChange(); 250 break; 251 } 252 } 253 }; 254} 255