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 <stdarg.h> 8 #include <string> 9 #include <vector> 10 11 #include "base/bind.h" 12 #include "base/command_line.h" 13 #include "base/file_util.h" 14 #include "base/files/file_path.h" 15 #include "base/memory/ref_counted.h" 16 #include "base/message_loop/message_loop.h" 17 #include "base/process/kill.h" 18 #include "base/process/launch.h" 19 #include "base/process/process_handle.h" 20 #include "base/strings/string_util.h" 21 #include "base/strings/stringprintf.h" 22 #include "base/sys_info.h" 23 #include "base/task_runner.h" 24 #include "base/threading/sequenced_worker_pool.h" 25 #include "chrome/browser/browser_process.h" 26 #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h" 27 #include "chrome/browser/policy/browser_policy_connector.h" 28 #include "chrome/common/pref_names.h" 29 #include "chromeos/system/statistics_provider.h" 30 #include "content/public/browser/browser_thread.h" 31 32 namespace chromeos { 33 namespace system { 34 35 namespace { 36 37 const char kTpControl[] = "/opt/google/touchpad/tpcontrol"; 38 const char kMouseControl[] = "/opt/google/mouse/mousecontrol"; 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(int argc, ...) { 67 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 68 std::vector<std::string> argv; 69 va_list vl; 70 va_start(vl, argc); 71 for (int i = 0; i < argc; ++i) { 72 argv.push_back(va_arg(vl, const char*)); 73 } 74 va_end(vl); 75 76 // Control scripts can take long enough to cause SIGART during shutdown 77 // (http://crbug.com/261426). Run the blocking pool task with 78 // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down. 79 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); 80 scoped_refptr<base::TaskRunner> runner = 81 pool->GetTaskRunnerWithShutdownBehavior( 82 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); 83 runner->PostTask(FROM_HERE, base::Bind(&ExecuteScriptOnFileThread, argv)); 84 } 85 86 void SetPointerSensitivity(const char* script, int value) { 87 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 88 DCHECK(value >= kMinPointerSensitivity && value <= kMaxPointerSensitivity); 89 ExecuteScript( 90 3, script, "sensitivity", base::StringPrintf("%d", value).c_str()); 91 } 92 93 void SetTPControl(const char* control, bool enabled) { 94 ExecuteScript(3, kTpControl, control, enabled ? "on" : "off"); 95 } 96 97 void DeviceExistsBlockingPool(const char* script, 98 scoped_refptr<RefCountedBool> exists) { 99 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 100 exists->data = false; 101 if (!ScriptExists(script)) 102 return; 103 104 std::vector<std::string> argv; 105 argv.push_back(script); 106 argv.push_back("status"); 107 std::string output; 108 // Output is empty if the device is not found. 109 exists->data = base::GetAppOutput(CommandLine(argv), &output) && 110 !output.empty(); 111 DVLOG(1) << "DeviceExistsBlockingPool:" << script << "=" << exists->data; 112 } 113 114 void RunCallbackUIThread(scoped_refptr<RefCountedBool> exists, 115 const DeviceExistsCallback& callback) { 116 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 117 DVLOG(1) << "RunCallbackUIThread " << exists->data; 118 callback.Run(exists->data); 119 } 120 121 void DeviceExists(const char* script, const DeviceExistsCallback& callback) { 122 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 123 124 // One or both of the control scripts can apparently hang during shutdown 125 // (http://crbug.com/255546). Run the blocking pool task with 126 // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down. 127 scoped_refptr<RefCountedBool> exists(new RefCountedBool(false)); 128 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); 129 scoped_refptr<base::TaskRunner> runner = 130 pool->GetTaskRunnerWithShutdownBehavior( 131 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); 132 runner->PostTaskAndReply(FROM_HERE, 133 base::Bind(&DeviceExistsBlockingPool, script, exists), 134 base::Bind(&RunCallbackUIThread, exists, callback)); 135 } 136 137 } // namespace 138 139 namespace touchpad_settings { 140 141 void TouchpadExists(const DeviceExistsCallback& callback) { 142 DeviceExists(kTpControl, callback); 143 } 144 145 // Sets the touchpad sensitivity in the range [1, 5]. 146 void SetSensitivity(int value) { 147 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 148 SetPointerSensitivity(kTpControl, value); 149 } 150 151 void SetTapToClick(bool enabled) { 152 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 153 SetTPControl("taptoclick", enabled); 154 } 155 156 void SetThreeFingerClick(bool enabled) { 157 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 158 159 // For Alex/ZGB. 160 SetTPControl("t5r2_three_finger_click", enabled); 161 } 162 163 void SetTapDragging(bool enabled) { 164 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 165 SetTPControl("tap_dragging", enabled); 166 } 167 168 } // namespace touchpad_settings 169 170 namespace mouse_settings { 171 172 void MouseExists(const DeviceExistsCallback& callback) { 173 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 174 DeviceExists(kMouseControl, callback); 175 } 176 177 // Sets the touchpad sensitivity in the range [1, 5]. 178 void SetSensitivity(int value) { 179 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 180 SetPointerSensitivity(kMouseControl, value); 181 } 182 183 void SetPrimaryButtonRight(bool right) { 184 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 185 ExecuteScript(3, kMouseControl, "swap_left_right", right ? "1" : "0"); 186 } 187 188 } // namespace mouse_settings 189 190 namespace keyboard_settings { 191 192 bool ForceKeyboardDrivenUINavigation() { 193 policy::BrowserPolicyConnector* connector = 194 g_browser_process->browser_policy_connector(); 195 if (!connector) 196 return false; 197 198 policy::DeviceCloudPolicyManagerChromeOS* policy_manager = 199 connector->GetDeviceCloudPolicyManager(); 200 if (!policy_manager) 201 return false; 202 203 if (base::strcasecmp(policy_manager->GetDeviceRequisition().c_str(), 204 kRemoraRequisition) == 0) { 205 return true; 206 } 207 208 bool keyboard_driven = false; 209 if (chromeos::system::StatisticsProvider::GetInstance()->GetMachineFlag( 210 kOemKeyboardDrivenOobeKey, &keyboard_driven)) { 211 return keyboard_driven; 212 } 213 214 return false; 215 } 216 217 } // namespace keyboard_settings 218 219 } // namespace system 220 } // namespace chromeos 221