Home | History | Annotate | Download | only in system
      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