• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ScriptExists(const std::string & script)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.
ExecuteScriptOnFileThread(const std::vector<std::string> & argv)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 
ExecuteScript(const std::vector<std::string> & argv)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 
AddSensitivityArguments(const char * device_type,int value,std::vector<std::string> * argv)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 
AddTPControlArguments(const char * control,bool enabled,std::vector<std::string> * argv)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 
DeviceExistsBlockingPool(const char * device_type,scoped_refptr<RefCountedBool> exists)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 
RunCallbackUIThread(scoped_refptr<RefCountedBool> exists,const InputDeviceSettings::DeviceExistsCallback & callback)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 
DeviceExists(const char * script,const InputDeviceSettings::DeviceExistsCallback & callback)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 
InputDeviceSettingsImpl()170 InputDeviceSettingsImpl::InputDeviceSettingsImpl() {}
171 
TouchpadExists(const DeviceExistsCallback & callback)172 void InputDeviceSettingsImpl::TouchpadExists(
173     const DeviceExistsCallback& callback) {
174   DeviceExists(kDeviceTypeTouchpad, callback);
175 }
176 
UpdateTouchpadSettings(const TouchpadSettings & settings)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 
SetTouchpadSensitivity(int value)184 void InputDeviceSettingsImpl::SetTouchpadSensitivity(int value) {
185   TouchpadSettings settings;
186   settings.SetSensitivity(value);
187   UpdateTouchpadSettings(settings);
188 }
189 
SetNaturalScroll(bool enabled)190 void InputDeviceSettingsImpl::SetNaturalScroll(bool enabled) {
191   TouchpadSettings settings;
192   settings.SetNaturalScroll(enabled);
193   UpdateTouchpadSettings(settings);
194 }
195 
SetTapToClick(bool enabled)196 void InputDeviceSettingsImpl::SetTapToClick(bool enabled) {
197   TouchpadSettings settings;
198   settings.SetTapToClick(enabled);
199   UpdateTouchpadSettings(settings);
200 }
201 
SetThreeFingerClick(bool enabled)202 void InputDeviceSettingsImpl::SetThreeFingerClick(bool enabled) {
203   // For Alex/ZGB.
204   TouchpadSettings settings;
205   settings.SetThreeFingerClick(enabled);
206   UpdateTouchpadSettings(settings);
207 }
208 
SetTapDragging(bool enabled)209 void InputDeviceSettingsImpl::SetTapDragging(bool enabled) {
210   TouchpadSettings settings;
211   settings.SetTapDragging(enabled);
212   UpdateTouchpadSettings(settings);
213 }
214 
MouseExists(const DeviceExistsCallback & callback)215 void InputDeviceSettingsImpl::MouseExists(
216     const DeviceExistsCallback& callback) {
217   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
218   DeviceExists(kDeviceTypeMouse, callback);
219 }
220 
UpdateMouseSettings(const MouseSettings & update)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 
SetMouseSensitivity(int value)227 void InputDeviceSettingsImpl::SetMouseSensitivity(int value) {
228   MouseSettings settings;
229   settings.SetSensitivity(value);
230   UpdateMouseSettings(settings);
231 }
232 
SetPrimaryButtonRight(bool right)233 void InputDeviceSettingsImpl::SetPrimaryButtonRight(bool right) {
234   MouseSettings settings;
235   settings.SetPrimaryButtonRight(right);
236   UpdateMouseSettings(settings);
237 }
238 
ForceKeyboardDrivenUINavigation()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 
ReapplyTouchpadSettings()264 void InputDeviceSettingsImpl::ReapplyTouchpadSettings() {
265   TouchpadSettings settings = current_touchpad_settings_;
266   current_touchpad_settings_ = TouchpadSettings();
267   UpdateTouchpadSettings(settings);
268 }
269 
ReapplyMouseSettings()270 void InputDeviceSettingsImpl::ReapplyMouseSettings() {
271   MouseSettings settings = current_mouse_settings_;
272   current_mouse_settings_ = MouseSettings();
273   UpdateMouseSettings(settings);
274 }
275 
276 }  // namespace
277 
TouchpadSettings()278 TouchpadSettings::TouchpadSettings() {}
279 
operator =(const TouchpadSettings & other)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 
SetSensitivity(int value)291 void TouchpadSettings::SetSensitivity(int value) {
292   sensitivity_.Set(value);
293 }
294 
GetSensitivity() const295 int TouchpadSettings::GetSensitivity() const {
296   return sensitivity_.value();
297 }
298 
SetTapToClick(bool enabled)299 void TouchpadSettings::SetTapToClick(bool enabled) {
300   tap_to_click_.Set(enabled);
301 }
302 
GetTapToClick() const303 bool TouchpadSettings::GetTapToClick() const {
304   return tap_to_click_.value();
305 }
306 
SetNaturalScroll(bool enabled)307 void TouchpadSettings::SetNaturalScroll(bool enabled) {
308   natural_scroll_.Set(enabled);
309 }
310 
GetNaturalScroll() const311 bool TouchpadSettings::GetNaturalScroll() const {
312   return natural_scroll_.value();
313 }
314 
SetThreeFingerClick(bool enabled)315 void TouchpadSettings::SetThreeFingerClick(bool enabled) {
316   three_finger_click_.Set(enabled);
317 }
318 
GetThreeFingerClick() const319 bool TouchpadSettings::GetThreeFingerClick() const {
320   return three_finger_click_.value();
321 }
322 
SetTapDragging(bool enabled)323 void TouchpadSettings::SetTapDragging(bool enabled) {
324   tap_dragging_.Set(enabled);
325 }
326 
GetTapDragging() const327 bool TouchpadSettings::GetTapDragging() const {
328   return tap_dragging_.value();
329 }
330 
Update(const TouchpadSettings & settings,std::vector<std::string> * argv)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 
MouseSettings()367 MouseSettings::MouseSettings() {}
368 
operator =(const MouseSettings & other)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 
SetSensitivity(int value)377 void MouseSettings::SetSensitivity(int value) {
378   sensitivity_.Set(value);
379 }
380 
GetSensitivity() const381 int MouseSettings::GetSensitivity() const {
382   return sensitivity_.value();
383 }
384 
SetPrimaryButtonRight(bool right)385 void MouseSettings::SetPrimaryButtonRight(bool right) {
386   primary_button_right_.Set(right);
387 }
388 
GetPrimaryButtonRight() const389 bool MouseSettings::GetPrimaryButtonRight() const {
390   return primary_button_right_.value();
391 }
392 
Update(const MouseSettings & settings,std::vector<std::string> * argv)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
Get()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
SetSettingsForTesting(InputDeviceSettings * test_settings)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