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/chromeos/chromeos_version.h" 13 #include "base/command_line.h" 14 #include "base/file_util.h" 15 #include "base/files/file_path.h" 16 #include "base/memory/ref_counted.h" 17 #include "base/message_loop/message_loop.h" 18 #include "base/process/kill.h" 19 #include "base/process/launch.h" 20 #include "base/process/process_handle.h" 21 #include "base/strings/stringprintf.h" 22 #include "base/task_runner.h" 23 #include "base/threading/sequenced_worker_pool.h" 24 #include "chrome/browser/browser_process.h" 25 #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h" 26 #include "chrome/browser/chromeos/system/statistics_provider.h" 27 #include "chrome/browser/policy/browser_policy_connector.h" 28 #include "chrome/common/pref_names.h" 29 #include "content/public/browser/browser_thread.h" 30 31 namespace chromeos { 32 namespace system { 33 34 namespace { 35 36 const char kTpControl[] = "/opt/google/touchpad/tpcontrol"; 37 const char kMouseControl[] = "/opt/google/mouse/mousecontrol"; 38 39 const char kRemoraRequisition[] = "remora"; 40 41 typedef base::RefCountedData<bool> RefCountedBool; 42 43 bool ScriptExists(const std::string& script) { 44 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 45 return base::PathExists(base::FilePath(script)); 46 } 47 48 // Executes the input control script asynchronously, if it exists. 49 void ExecuteScriptOnFileThread(const std::vector<std::string>& argv) { 50 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 51 DCHECK(!argv.empty()); 52 const std::string& script(argv[0]); 53 54 // Script must exist on device. 55 DCHECK(!base::chromeos::IsRunningOnChromeOS() || ScriptExists(script)); 56 57 if (!ScriptExists(script)) 58 return; 59 60 base::ProcessHandle handle; 61 base::LaunchProcess(CommandLine(argv), base::LaunchOptions(), &handle); 62 base::EnsureProcessGetsReaped(handle); 63 } 64 65 void ExecuteScript(int argc, ...) { 66 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 67 std::vector<std::string> argv; 68 va_list vl; 69 va_start(vl, argc); 70 for (int i = 0; i < argc; ++i) { 71 argv.push_back(va_arg(vl, const char*)); 72 } 73 va_end(vl); 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 SetPointerSensitivity(const char* script, int value) { 86 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 87 DCHECK(value >= kMinPointerSensitivity && value <= kMaxPointerSensitivity); 88 ExecuteScript( 89 3, script, "sensitivity", base::StringPrintf("%d", value).c_str()); 90 } 91 92 void SetTPControl(const char* control, bool enabled) { 93 ExecuteScript(3, kTpControl, control, enabled ? "on" : "off"); 94 } 95 96 void DeviceExistsBlockingPool(const char* script, 97 scoped_refptr<RefCountedBool> exists) { 98 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 99 exists->data = false; 100 if (!ScriptExists(script)) 101 return; 102 103 std::vector<std::string> argv; 104 argv.push_back(script); 105 argv.push_back("status"); 106 std::string output; 107 // Output is empty if the device is not found. 108 exists->data = base::GetAppOutput(CommandLine(argv), &output) && 109 !output.empty(); 110 DVLOG(1) << "DeviceExistsBlockingPool:" << script << "=" << exists->data; 111 } 112 113 void RunCallbackUIThread(scoped_refptr<RefCountedBool> exists, 114 const DeviceExistsCallback& callback) { 115 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 116 DVLOG(1) << "RunCallbackUIThread " << exists->data; 117 callback.Run(exists->data); 118 } 119 120 void DeviceExists(const char* script, const DeviceExistsCallback& callback) { 121 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 122 123 // One or both of the control scripts can apparently hang during shutdown 124 // (http://crbug.com/255546). Run the blocking pool task with 125 // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down. 126 scoped_refptr<RefCountedBool> exists(new RefCountedBool(false)); 127 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); 128 scoped_refptr<base::TaskRunner> runner = 129 pool->GetTaskRunnerWithShutdownBehavior( 130 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); 131 runner->PostTaskAndReply(FROM_HERE, 132 base::Bind(&DeviceExistsBlockingPool, script, exists), 133 base::Bind(&RunCallbackUIThread, exists, callback)); 134 } 135 136 } // namespace 137 138 namespace touchpad_settings { 139 140 void TouchpadExists(const DeviceExistsCallback& callback) { 141 DeviceExists(kTpControl, callback); 142 } 143 144 // Sets the touchpad sensitivity in the range [1, 5]. 145 void SetSensitivity(int value) { 146 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 147 SetPointerSensitivity(kTpControl, value); 148 } 149 150 void SetTapToClick(bool enabled) { 151 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 152 SetTPControl("taptoclick", enabled); 153 } 154 155 void SetThreeFingerClick(bool enabled) { 156 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 157 158 SetTPControl("three_finger_click", enabled); 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 (policy_manager->GetDeviceRequisition() == kRemoraRequisition) 204 return true; 205 206 bool keyboard_driven = false; 207 if (chromeos::system::StatisticsProvider::GetInstance()->GetMachineFlag( 208 kOemKeyboardDrivenOobeKey, &keyboard_driven)) { 209 return keyboard_driven; 210 } 211 212 return false; 213 } 214 215 } // namespace keyboard_settings 216 217 } // namespace system 218 } // namespace chromeos 219