input_device_settings.cc revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
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
32namespace chromeos {
33namespace system {
34
35namespace {
36
37const char kTpControl[] = "/opt/google/touchpad/tpcontrol";
38const char kMouseControl[] = "/opt/google/mouse/mousecontrol";
39
40const char kRemoraRequisition[] = "remora";
41
42typedef base::RefCountedData<bool> RefCountedBool;
43
44bool 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.
50void 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
66void 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
86void 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
93void SetTPControl(const char* control, bool enabled) {
94  ExecuteScript(3, kTpControl, control, enabled ? "on" : "off");
95}
96
97void 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
114void 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
121void 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
139namespace touchpad_settings {
140
141void TouchpadExists(const DeviceExistsCallback& callback) {
142  DeviceExists(kTpControl, callback);
143}
144
145// Sets the touchpad sensitivity in the range [1, 5].
146void SetSensitivity(int value) {
147  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
148  SetPointerSensitivity(kTpControl, value);
149}
150
151void SetTapToClick(bool enabled) {
152  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
153  SetTPControl("taptoclick", enabled);
154}
155
156void 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
163void SetTapDragging(bool enabled) {
164  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
165  SetTPControl("tap_dragging", enabled);
166}
167
168}  // namespace touchpad_settings
169
170namespace mouse_settings {
171
172void 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].
178void SetSensitivity(int value) {
179  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
180  SetPointerSensitivity(kMouseControl, value);
181}
182
183void 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
190namespace keyboard_settings {
191
192bool 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