DockObserver.java revision 1c633fc89bae9bf0af6fe643ac7ad2e744f27bed
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 android.app.Activity; 20import android.app.KeyguardManager; 21import android.bluetooth.BluetoothAdapter; 22import android.bluetooth.BluetoothDevice; 23import android.content.ActivityNotFoundException; 24import android.content.BroadcastReceiver; 25import android.content.Context; 26import android.content.Intent; 27import android.os.Handler; 28import android.os.Message; 29import android.os.SystemClock; 30import android.os.UEventObserver; 31import android.provider.Settings; 32import android.server.BluetoothService; 33import android.util.Log; 34 35import com.android.internal.widget.LockPatternUtils; 36 37import java.io.FileNotFoundException; 38import java.io.FileReader; 39 40/** 41 * <p>DockObserver monitors for a docking station. 42 */ 43class DockObserver extends UEventObserver { 44 private static final String TAG = DockObserver.class.getSimpleName(); 45 private static final boolean LOG = false; 46 47 private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock"; 48 private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state"; 49 50 private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; 51 private boolean mSystemReady; 52 53 private final Context mContext; 54 55 private PowerManagerService mPowerManager; 56 57 private KeyguardManager.KeyguardLock mKeyguardLock; 58 private boolean mKeyguardDisabled; 59 private LockPatternUtils mLockPatternUtils; 60 61 // The broadcast receiver which receives the result of the ordered broadcast sent when 62 // the dock state changes. The original ordered broadcast is sent with an initial result 63 // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g., 64 // to RESULT_CANCELED, then the intent to start a dock app will not be sent. 65 private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() { 66 @Override 67 public void onReceive(Context context, Intent intent) { 68 if (getResultCode() != Activity.RESULT_OK) { 69 return; 70 } 71 72 // Launch a dock activity 73 String category; 74 switch (mDockState) { 75 case Intent.EXTRA_DOCK_STATE_CAR: 76 category = Intent.CATEGORY_CAR_DOCK; 77 break; 78 case Intent.EXTRA_DOCK_STATE_DESK: 79 category = Intent.CATEGORY_DESK_DOCK; 80 break; 81 default: 82 category = null; 83 break; 84 } 85 if (category != null) { 86 intent = new Intent(Intent.ACTION_MAIN); 87 intent.addCategory(category); 88 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 89 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 90 try { 91 mContext.startActivity(intent); 92 } catch (ActivityNotFoundException e) { 93 Log.w(TAG, e.getCause()); 94 } 95 } 96 } 97 }; 98 99 public DockObserver(Context context, PowerManagerService pm) { 100 mContext = context; 101 mPowerManager = pm; 102 mLockPatternUtils = new LockPatternUtils(context.getContentResolver()); 103 init(); // set initial status 104 startObserving(DOCK_UEVENT_MATCH); 105 } 106 107 @Override 108 public void onUEvent(UEventObserver.UEvent event) { 109 if (Log.isLoggable(TAG, Log.VERBOSE)) { 110 Log.v(TAG, "Dock UEVENT: " + event.toString()); 111 } 112 113 synchronized (this) { 114 try { 115 int newState = Integer.parseInt(event.get("SWITCH_STATE")); 116 if (newState != mDockState) { 117 int oldState = mDockState; 118 mDockState = newState; 119 if (mSystemReady) { 120 // Don't force screen on when undocking from the desk dock. 121 // The change in power state will do this anyway. 122 // FIXME - we should be configurable. 123 if (oldState != Intent.EXTRA_DOCK_STATE_DESK || 124 newState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { 125 mPowerManager.userActivityWithForce(SystemClock.uptimeMillis(), 126 false, true); 127 } 128 update(); 129 } 130 } 131 } catch (NumberFormatException e) { 132 Log.e(TAG, "Could not parse switch state from event " + event); 133 } 134 } 135 } 136 137 private final void init() { 138 char[] buffer = new char[1024]; 139 140 try { 141 FileReader file = new FileReader(DOCK_STATE_PATH); 142 int len = file.read(buffer, 0, 1024); 143 mDockState = Integer.valueOf((new String(buffer, 0, len)).trim()); 144 145 } catch (FileNotFoundException e) { 146 Log.w(TAG, "This kernel does not have dock station support"); 147 } catch (Exception e) { 148 Log.e(TAG, "" , e); 149 } 150 } 151 152 void systemReady() { 153 synchronized (this) { 154 KeyguardManager keyguardManager = 155 (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE); 156 mKeyguardLock = keyguardManager.newKeyguardLock(TAG); 157 158 // don't bother broadcasting undocked here 159 if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { 160 update(); 161 } 162 mSystemReady = true; 163 } 164 } 165 166 private final void update() { 167 mHandler.sendEmptyMessage(0); 168 } 169 170 private final Handler mHandler = new Handler() { 171 @Override 172 public void handleMessage(Message msg) { 173 synchronized (this) { 174 Log.i(TAG, "Dock state changed: " + mDockState); 175 if (Settings.Secure.getInt(mContext.getContentResolver(), 176 Settings.Secure.DEVICE_PROVISIONED, 0) == 0) { 177 Log.i(TAG, "Device not provisioned, skipping dock broadcast"); 178 return; 179 } 180 // Pack up the values and broadcast them to everyone 181 Intent intent = new Intent(Intent.ACTION_DOCK_EVENT); 182 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 183 intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState); 184 185 // Check if this is Bluetooth Dock 186 String address = BluetoothService.readDockBluetoothAddress(); 187 if (address != null) 188 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, 189 BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address)); 190 191 // Send the ordered broadcast; the result receiver will receive after all 192 // broadcasts have been sent. If any broadcast receiver changes the result 193 // code from the initial value of RESULT_OK, then the result receiver will 194 // not launch the corresponding dock application. This gives apps a chance 195 // to override the behavior and stay in their app even when the device is 196 // placed into a dock. 197 mContext.sendStickyOrderedBroadcast( 198 intent, mResultReceiver, null, Activity.RESULT_OK, null, null); 199 } 200 } 201 }; 202} 203