1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/chromeos/system/input_device_settings.h" 6 7 #include "base/bind.h" 8 #include "base/command_line.h" 9 #include "base/file_util.h" 10 #include "base/files/file_path.h" 11 #include "base/memory/ref_counted.h" 12 #include "base/message_loop/message_loop.h" 13 #include "base/process/kill.h" 14 #include "base/process/launch.h" 15 #include "base/process/process_handle.h" 16 #include "base/strings/string_util.h" 17 #include "base/strings/stringprintf.h" 18 #include "base/sys_info.h" 19 #include "base/task_runner.h" 20 #include "base/threading/sequenced_worker_pool.h" 21 #include "chrome/browser/browser_process.h" 22 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" 23 #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h" 24 #include "chrome/common/pref_names.h" 25 #include "chromeos/system/statistics_provider.h" 26 #include "content/public/browser/browser_thread.h" 27 28 namespace chromeos { 29 namespace system { 30 31 namespace { 32 33 InputDeviceSettings* g_instance_; 34 InputDeviceSettings* g_test_instance_; 35 36 const char kDeviceTypeTouchpad[] = "touchpad"; 37 const char kDeviceTypeMouse[] = "mouse"; 38 const char kInputControl[] = "/opt/google/input/inputcontrol"; 39 40 const char kRemoraRequisition[] = "remora"; 41 42 typedef base::RefCountedData<bool> RefCountedBool; 43 44 bool ScriptExists(const std::string& script) { 45 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 46 return base::PathExists(base::FilePath(script)); 47 } 48 49 // Executes the input control script asynchronously, if it exists. 50 void ExecuteScriptOnFileThread(const std::vector<std::string>& argv) { 51 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 52 DCHECK(!argv.empty()); 53 const std::string& script(argv[0]); 54 55 // Script must exist on device. 56 DCHECK(!base::SysInfo::IsRunningOnChromeOS() || ScriptExists(script)); 57 58 if (!ScriptExists(script)) 59 return; 60 61 base::ProcessHandle handle; 62 base::LaunchProcess(CommandLine(argv), base::LaunchOptions(), &handle); 63 base::EnsureProcessGetsReaped(handle); 64 } 65 66 void ExecuteScript(const std::vector<std::string>& argv) { 67 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 68 69 if (argv.size() == 1) 70 return; 71 72 VLOG(1) << "About to launch: \"" 73 << CommandLine(argv).GetCommandLineString() << "\""; 74 75 // Control scripts can take long enough to cause SIGART during shutdown 76 // (http://crbug.com/261426). Run the blocking pool task with 77 // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down. 78 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); 79 scoped_refptr<base::TaskRunner> runner = 80 pool->GetTaskRunnerWithShutdownBehavior( 81 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); 82 runner->PostTask(FROM_HERE, base::Bind(&ExecuteScriptOnFileThread, argv)); 83 } 84 85 void AddSensitivityArguments(const char* device_type, int value, 86 std::vector<std::string>* argv) { 87 DCHECK(value >= kMinPointerSensitivity && value <= kMaxPointerSensitivity); 88 argv->push_back(base::StringPrintf("--%s_sensitivity=%d", 89 device_type, value)); 90 } 91 92 void AddTPControlArguments(const char* control, 93 bool enabled, 94 std::vector<std::string>* argv) { 95 argv->push_back(base::StringPrintf("--%s=%d", control, enabled ? 1 : 0)); 96 } 97 98 void DeviceExistsBlockingPool(const char* device_type, 99 scoped_refptr<RefCountedBool> exists) { 100 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 101 exists->data = false; 102 if (!ScriptExists(kInputControl)) 103 return; 104 105 std::vector<std::string> argv; 106 argv.push_back(kInputControl); 107 argv.push_back(base::StringPrintf("--type=%s", device_type)); 108 argv.push_back("--list"); 109 std::string output; 110 // Output is empty if the device is not found. 111 exists->data = base::GetAppOutput(CommandLine(argv), &output) && 112 !output.empty(); 113 DVLOG(1) << "DeviceExistsBlockingPool:" << device_type << "=" << exists->data; 114 } 115 116 void RunCallbackUIThread( 117 scoped_refptr<RefCountedBool> exists, 118 const InputDeviceSettings::DeviceExistsCallback& callback) { 119 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 120 DVLOG(1) << "RunCallbackUIThread " << exists->data; 121 callback.Run(exists->data); 122 } 123 124 void DeviceExists(const char* script, 125 const InputDeviceSettings::DeviceExistsCallback& callback) { 126 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 127 128 // One or both of the control scripts can apparently hang during shutdown 129 // (http://crbug.com/255546). Run the blocking pool task with 130 // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down. 131 scoped_refptr<RefCountedBool> exists(new RefCountedBool(false)); 132 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); 133 scoped_refptr<base::TaskRunner> runner = 134 pool->GetTaskRunnerWithShutdownBehavior( 135 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); 136 runner->PostTaskAndReply(FROM_HERE, 137 base::Bind(&DeviceExistsBlockingPool, script, exists), 138 base::Bind(&RunCallbackUIThread, exists, callback)); 139 } 140 141 class InputDeviceSettingsImpl : public InputDeviceSettings { 142 public: 143 InputDeviceSettingsImpl(); 144 145 private: 146 // Overridden from InputDeviceSettings. 147 virtual void TouchpadExists(const DeviceExistsCallback& callback) OVERRIDE; 148 virtual void UpdateTouchpadSettings(const TouchpadSettings& settings) 149 OVERRIDE; 150 virtual void SetTouchpadSensitivity(int value) OVERRIDE; 151 virtual void SetTapToClick(bool enabled) OVERRIDE; 152 virtual void SetThreeFingerClick(bool enabled) OVERRIDE; 153 virtual void SetTapDragging(bool enabled) OVERRIDE; 154 virtual void SetNaturalScroll(bool enabled) OVERRIDE; 155 virtual void MouseExists(const DeviceExistsCallback& callback) OVERRIDE; 156 virtual void UpdateMouseSettings(const MouseSettings& update) OVERRIDE; 157 virtual void SetMouseSensitivity(int value) OVERRIDE; 158 virtual void SetPrimaryButtonRight(bool right) OVERRIDE; 159 virtual bool ForceKeyboardDrivenUINavigation() OVERRIDE; 160 virtual void ReapplyTouchpadSettings() OVERRIDE; 161 virtual void ReapplyMouseSettings() OVERRIDE; 162 163 private: 164 TouchpadSettings current_touchpad_settings_; 165 MouseSettings current_mouse_settings_; 166 167 DISALLOW_COPY_AND_ASSIGN(InputDeviceSettingsImpl); 168 }; 169 170 InputDeviceSettingsImpl::InputDeviceSettingsImpl() {} 171 172 void InputDeviceSettingsImpl::TouchpadExists( 173 const DeviceExistsCallback& callback) { 174 DeviceExists(kDeviceTypeTouchpad, callback); 175 } 176 177 void InputDeviceSettingsImpl::UpdateTouchpadSettings( 178 const TouchpadSettings& settings) { 179 std::vector<std::string> argv; 180 if (current_touchpad_settings_.Update(settings, &argv)) 181 ExecuteScript(argv); 182 } 183 184 void InputDeviceSettingsImpl::SetTouchpadSensitivity(int value) { 185 TouchpadSettings settings; 186 settings.SetSensitivity(value); 187 UpdateTouchpadSettings(settings); 188 } 189 190 void InputDeviceSettingsImpl::SetNaturalScroll(bool enabled) { 191 TouchpadSettings settings; 192 settings.SetNaturalScroll(enabled); 193 UpdateTouchpadSettings(settings); 194 } 195 196 void InputDeviceSettingsImpl::SetTapToClick(bool enabled) { 197 TouchpadSettings settings; 198 settings.SetTapToClick(enabled); 199 UpdateTouchpadSettings(settings); 200 } 201 202 void InputDeviceSettingsImpl::SetThreeFingerClick(bool enabled) { 203 // For Alex/ZGB. 204 TouchpadSettings settings; 205 settings.SetThreeFingerClick(enabled); 206 UpdateTouchpadSettings(settings); 207 } 208 209 void InputDeviceSettingsImpl::SetTapDragging(bool enabled) { 210 TouchpadSettings settings; 211 settings.SetTapDragging(enabled); 212 UpdateTouchpadSettings(settings); 213 } 214 215 void InputDeviceSettingsImpl::MouseExists( 216 const DeviceExistsCallback& callback) { 217 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 218 DeviceExists(kDeviceTypeMouse, callback); 219 } 220 221 void InputDeviceSettingsImpl::UpdateMouseSettings(const MouseSettings& update) { 222 std::vector<std::string> argv; 223 if (current_mouse_settings_.Update(update, &argv)) 224 ExecuteScript(argv); 225 } 226 227 void InputDeviceSettingsImpl::SetMouseSensitivity(int value) { 228 MouseSettings settings; 229 settings.SetSensitivity(value); 230 UpdateMouseSettings(settings); 231 } 232 233 void InputDeviceSettingsImpl::SetPrimaryButtonRight(bool right) { 234 MouseSettings settings; 235 settings.SetPrimaryButtonRight(right); 236 UpdateMouseSettings(settings); 237 } 238 239 bool InputDeviceSettingsImpl::ForceKeyboardDrivenUINavigation() { 240 policy::BrowserPolicyConnectorChromeOS* connector = 241 g_browser_process->platform_part()->browser_policy_connector_chromeos(); 242 if (!connector) 243 return false; 244 245 policy::DeviceCloudPolicyManagerChromeOS* policy_manager = 246 connector->GetDeviceCloudPolicyManager(); 247 if (!policy_manager) 248 return false; 249 250 if (base::strcasecmp(policy_manager->GetDeviceRequisition().c_str(), 251 kRemoraRequisition) == 0) { 252 return true; 253 } 254 255 bool keyboard_driven = false; 256 if (chromeos::system::StatisticsProvider::GetInstance()->GetMachineFlag( 257 kOemKeyboardDrivenOobeKey, &keyboard_driven)) { 258 return keyboard_driven; 259 } 260 261 return false; 262 } 263 264 void InputDeviceSettingsImpl::ReapplyTouchpadSettings() { 265 TouchpadSettings settings = current_touchpad_settings_; 266 current_touchpad_settings_ = TouchpadSettings(); 267 UpdateTouchpadSettings(settings); 268 } 269 270 void InputDeviceSettingsImpl::ReapplyMouseSettings() { 271 MouseSettings settings = current_mouse_settings_; 272 current_mouse_settings_ = MouseSettings(); 273 UpdateMouseSettings(settings); 274 } 275 276 } // namespace 277 278 TouchpadSettings::TouchpadSettings() {} 279 280 TouchpadSettings& TouchpadSettings::operator=(const TouchpadSettings& other) { 281 if (&other != this) { 282 sensitivity_ = other.sensitivity_; 283 tap_to_click_ = other.tap_to_click_; 284 three_finger_click_ = other.three_finger_click_; 285 tap_dragging_ = other.tap_dragging_; 286 natural_scroll_ = other.natural_scroll_; 287 } 288 return *this; 289 } 290 291 void TouchpadSettings::SetSensitivity(int value) { 292 sensitivity_.Set(value); 293 } 294 295 int TouchpadSettings::GetSensitivity() const { 296 return sensitivity_.value(); 297 } 298 299 void TouchpadSettings::SetTapToClick(bool enabled) { 300 tap_to_click_.Set(enabled); 301 } 302 303 bool TouchpadSettings::GetTapToClick() const { 304 return tap_to_click_.value(); 305 } 306 307 void TouchpadSettings::SetNaturalScroll(bool enabled) { 308 natural_scroll_.Set(enabled); 309 } 310 311 bool TouchpadSettings::GetNaturalScroll() const { 312 return natural_scroll_.value(); 313 } 314 315 void TouchpadSettings::SetThreeFingerClick(bool enabled) { 316 three_finger_click_.Set(enabled); 317 } 318 319 bool TouchpadSettings::GetThreeFingerClick() const { 320 return three_finger_click_.value(); 321 } 322 323 void TouchpadSettings::SetTapDragging(bool enabled) { 324 tap_dragging_.Set(enabled); 325 } 326 327 bool TouchpadSettings::GetTapDragging() const { 328 return tap_dragging_.value(); 329 } 330 331 bool TouchpadSettings::Update(const TouchpadSettings& settings, 332 std::vector<std::string>* argv) { 333 if (argv) 334 argv->push_back(kInputControl); 335 bool updated = false; 336 if (sensitivity_.Update(settings.sensitivity_)) { 337 updated = true; 338 if (argv) 339 AddSensitivityArguments(kDeviceTypeTouchpad, sensitivity_.value(), argv); 340 } 341 if (tap_to_click_.Update(settings.tap_to_click_)) { 342 updated = true; 343 if (argv) 344 AddTPControlArguments("tapclick", tap_to_click_.value(), argv); 345 } 346 if (three_finger_click_.Update(settings.three_finger_click_)) { 347 updated = true; 348 if (argv) 349 AddTPControlArguments("t5r2_three_finger_click", 350 three_finger_click_.value(), 351 argv); 352 } 353 if (tap_dragging_.Update(settings.tap_dragging_)) { 354 updated = true; 355 if (argv) 356 AddTPControlArguments("tapdrag", tap_dragging_.value(), argv); 357 } 358 if (natural_scroll_.Update(settings.natural_scroll_)) { 359 updated = true; 360 if (argv) 361 AddTPControlArguments("australian_scrolling", natural_scroll_.value(), 362 argv); 363 } 364 return updated; 365 } 366 367 MouseSettings::MouseSettings() {} 368 369 MouseSettings& MouseSettings::operator=(const MouseSettings& other) { 370 if (&other != this) { 371 sensitivity_ = other.sensitivity_; 372 primary_button_right_ = other.primary_button_right_; 373 } 374 return *this; 375 } 376 377 void MouseSettings::SetSensitivity(int value) { 378 sensitivity_.Set(value); 379 } 380 381 int MouseSettings::GetSensitivity() const { 382 return sensitivity_.value(); 383 } 384 385 void MouseSettings::SetPrimaryButtonRight(bool right) { 386 primary_button_right_.Set(right); 387 } 388 389 bool MouseSettings::GetPrimaryButtonRight() const { 390 return primary_button_right_.value(); 391 } 392 393 bool MouseSettings::Update(const MouseSettings& settings, 394 std::vector<std::string>* argv) { 395 if (argv) 396 argv->push_back(kInputControl); 397 bool updated = false; 398 if (sensitivity_.Update(settings.sensitivity_)) { 399 updated = true; 400 if (argv) 401 AddSensitivityArguments(kDeviceTypeMouse, sensitivity_.value(), argv); 402 } 403 if (primary_button_right_.Update(settings.primary_button_right_)) { 404 updated = true; 405 if (argv) { 406 AddTPControlArguments("mouse_swap_lr", primary_button_right_.value(), 407 argv); 408 } 409 } 410 return updated; 411 } 412 413 // static 414 InputDeviceSettings* InputDeviceSettings::Get() { 415 if (g_test_instance_) 416 return g_test_instance_; 417 if (!g_instance_) 418 g_instance_ = new InputDeviceSettingsImpl; 419 return g_instance_; 420 } 421 422 // static 423 void InputDeviceSettings::SetSettingsForTesting( 424 InputDeviceSettings* test_settings) { 425 if (g_test_instance_ == test_settings) 426 return; 427 delete g_test_instance_; 428 g_test_instance_ = test_settings; 429 } 430 431 } // namespace system 432 } // namespace chromeos 433