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/files/file_path.h"
     10 #include "base/files/file_util.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 typedef base::RefCountedData<bool> RefCountedBool;
     41 
     42 bool ScriptExists(const std::string& script) {
     43   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
     44   return base::PathExists(base::FilePath(script));
     45 }
     46 
     47 // Executes the input control script asynchronously, if it exists.
     48 void ExecuteScriptOnFileThread(const std::vector<std::string>& argv) {
     49   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
     50   DCHECK(!argv.empty());
     51   const std::string& script(argv[0]);
     52 
     53   // Script must exist on device.
     54   DCHECK(!base::SysInfo::IsRunningOnChromeOS() || ScriptExists(script));
     55 
     56   if (!ScriptExists(script))
     57     return;
     58 
     59   base::ProcessHandle handle;
     60   base::LaunchProcess(CommandLine(argv), base::LaunchOptions(), &handle);
     61   base::EnsureProcessGetsReaped(handle);
     62 }
     63 
     64 void ExecuteScript(const std::vector<std::string>& argv) {
     65   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     66 
     67   if (argv.size() == 1)
     68     return;
     69 
     70   VLOG(1) << "About to launch: \""
     71           << CommandLine(argv).GetCommandLineString() << "\"";
     72 
     73   // Control scripts can take long enough to cause SIGART during shutdown
     74   // (http://crbug.com/261426). Run the blocking pool task with
     75   // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down.
     76   base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
     77   scoped_refptr<base::TaskRunner> runner =
     78       pool->GetTaskRunnerWithShutdownBehavior(
     79           base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
     80   runner->PostTask(FROM_HERE, base::Bind(&ExecuteScriptOnFileThread, argv));
     81 }
     82 
     83 void AddSensitivityArguments(const char* device_type, int value,
     84                              std::vector<std::string>* argv) {
     85   DCHECK(value >= kMinPointerSensitivity && value <= kMaxPointerSensitivity);
     86   argv->push_back(base::StringPrintf("--%s_sensitivity=%d",
     87                                      device_type, value));
     88 }
     89 
     90 void AddTPControlArguments(const char* control,
     91                            bool enabled,
     92                            std::vector<std::string>* argv) {
     93   argv->push_back(base::StringPrintf("--%s=%d", control, enabled ? 1 : 0));
     94 }
     95 
     96 void DeviceExistsBlockingPool(const char* device_type,
     97                               scoped_refptr<RefCountedBool> exists) {
     98   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
     99   exists->data = false;
    100   if (!ScriptExists(kInputControl))
    101     return;
    102 
    103   std::vector<std::string> argv;
    104   argv.push_back(kInputControl);
    105   argv.push_back(base::StringPrintf("--type=%s", device_type));
    106   argv.push_back("--list");
    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:" << device_type << "=" << exists->data;
    112 }
    113 
    114 void RunCallbackUIThread(
    115     scoped_refptr<RefCountedBool> exists,
    116     const InputDeviceSettings::DeviceExistsCallback& callback) {
    117   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    118   DVLOG(1) << "RunCallbackUIThread " << exists->data;
    119   callback.Run(exists->data);
    120 }
    121 
    122 void DeviceExists(const char* script,
    123                   const InputDeviceSettings::DeviceExistsCallback& callback) {
    124   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    125 
    126   // One or both of the control scripts can apparently hang during shutdown
    127   // (http://crbug.com/255546). Run the blocking pool task with
    128   // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down.
    129   scoped_refptr<RefCountedBool> exists(new RefCountedBool(false));
    130   base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
    131   scoped_refptr<base::TaskRunner> runner =
    132       pool->GetTaskRunnerWithShutdownBehavior(
    133           base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
    134   runner->PostTaskAndReply(FROM_HERE,
    135       base::Bind(&DeviceExistsBlockingPool, script, exists),
    136       base::Bind(&RunCallbackUIThread, exists, callback));
    137 }
    138 
    139 class InputDeviceSettingsImpl : public InputDeviceSettings {
    140  public:
    141   InputDeviceSettingsImpl();
    142 
    143  private:
    144   // Overridden from InputDeviceSettings.
    145   virtual void TouchpadExists(const DeviceExistsCallback& callback) OVERRIDE;
    146   virtual void UpdateTouchpadSettings(const TouchpadSettings& settings)
    147       OVERRIDE;
    148   virtual void SetTouchpadSensitivity(int value) OVERRIDE;
    149   virtual void SetTapToClick(bool enabled) OVERRIDE;
    150   virtual void SetThreeFingerClick(bool enabled) OVERRIDE;
    151   virtual void SetTapDragging(bool enabled) OVERRIDE;
    152   virtual void SetNaturalScroll(bool enabled) OVERRIDE;
    153   virtual void MouseExists(const DeviceExistsCallback& callback) OVERRIDE;
    154   virtual void UpdateMouseSettings(const MouseSettings& update) OVERRIDE;
    155   virtual void SetMouseSensitivity(int value) OVERRIDE;
    156   virtual void SetPrimaryButtonRight(bool right) OVERRIDE;
    157   virtual bool ForceKeyboardDrivenUINavigation() OVERRIDE;
    158   virtual void ReapplyTouchpadSettings() OVERRIDE;
    159   virtual void ReapplyMouseSettings() OVERRIDE;
    160 
    161  private:
    162   TouchpadSettings current_touchpad_settings_;
    163   MouseSettings current_mouse_settings_;
    164 
    165   DISALLOW_COPY_AND_ASSIGN(InputDeviceSettingsImpl);
    166 };
    167 
    168 InputDeviceSettingsImpl::InputDeviceSettingsImpl() {}
    169 
    170 void InputDeviceSettingsImpl::TouchpadExists(
    171     const DeviceExistsCallback& callback) {
    172   DeviceExists(kDeviceTypeTouchpad, callback);
    173 }
    174 
    175 void InputDeviceSettingsImpl::UpdateTouchpadSettings(
    176     const TouchpadSettings& settings) {
    177   std::vector<std::string> argv;
    178   if (current_touchpad_settings_.Update(settings, &argv))
    179     ExecuteScript(argv);
    180 }
    181 
    182 void InputDeviceSettingsImpl::SetTouchpadSensitivity(int value) {
    183   TouchpadSettings settings;
    184   settings.SetSensitivity(value);
    185   UpdateTouchpadSettings(settings);
    186 }
    187 
    188 void InputDeviceSettingsImpl::SetNaturalScroll(bool enabled) {
    189   TouchpadSettings settings;
    190   settings.SetNaturalScroll(enabled);
    191   UpdateTouchpadSettings(settings);
    192 }
    193 
    194 void InputDeviceSettingsImpl::SetTapToClick(bool enabled) {
    195   TouchpadSettings settings;
    196   settings.SetTapToClick(enabled);
    197   UpdateTouchpadSettings(settings);
    198 }
    199 
    200 void InputDeviceSettingsImpl::SetThreeFingerClick(bool enabled) {
    201   // For Alex/ZGB.
    202   TouchpadSettings settings;
    203   settings.SetThreeFingerClick(enabled);
    204   UpdateTouchpadSettings(settings);
    205 }
    206 
    207 void InputDeviceSettingsImpl::SetTapDragging(bool enabled) {
    208   TouchpadSettings settings;
    209   settings.SetTapDragging(enabled);
    210   UpdateTouchpadSettings(settings);
    211 }
    212 
    213 void InputDeviceSettingsImpl::MouseExists(
    214     const DeviceExistsCallback& callback) {
    215   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    216   DeviceExists(kDeviceTypeMouse, callback);
    217 }
    218 
    219 void InputDeviceSettingsImpl::UpdateMouseSettings(const MouseSettings& update) {
    220   std::vector<std::string> argv;
    221   if (current_mouse_settings_.Update(update, &argv))
    222     ExecuteScript(argv);
    223 }
    224 
    225 void InputDeviceSettingsImpl::SetMouseSensitivity(int value) {
    226   MouseSettings settings;
    227   settings.SetSensitivity(value);
    228   UpdateMouseSettings(settings);
    229 }
    230 
    231 void InputDeviceSettingsImpl::SetPrimaryButtonRight(bool right) {
    232   MouseSettings settings;
    233   settings.SetPrimaryButtonRight(right);
    234   UpdateMouseSettings(settings);
    235 }
    236 
    237 bool InputDeviceSettingsImpl::ForceKeyboardDrivenUINavigation() {
    238   policy::BrowserPolicyConnectorChromeOS* connector =
    239       g_browser_process->platform_part()->browser_policy_connector_chromeos();
    240   if (!connector)
    241     return false;
    242 
    243   policy::DeviceCloudPolicyManagerChromeOS* policy_manager =
    244       connector->GetDeviceCloudPolicyManager();
    245   if (!policy_manager)
    246     return false;
    247 
    248   if (policy_manager->IsRemoraRequisition() ||
    249       policy_manager->IsSharkRequisition()) {
    250     return true;
    251   }
    252 
    253   bool keyboard_driven = false;
    254   if (chromeos::system::StatisticsProvider::GetInstance()->GetMachineFlag(
    255           kOemKeyboardDrivenOobeKey, &keyboard_driven)) {
    256     return keyboard_driven;
    257   }
    258 
    259   return false;
    260 }
    261 
    262 void InputDeviceSettingsImpl::ReapplyTouchpadSettings() {
    263   TouchpadSettings settings = current_touchpad_settings_;
    264   current_touchpad_settings_ = TouchpadSettings();
    265   UpdateTouchpadSettings(settings);
    266 }
    267 
    268 void InputDeviceSettingsImpl::ReapplyMouseSettings() {
    269   MouseSettings settings = current_mouse_settings_;
    270   current_mouse_settings_ = MouseSettings();
    271   UpdateMouseSettings(settings);
    272 }
    273 
    274 }  // namespace
    275 
    276 TouchpadSettings::TouchpadSettings() {}
    277 
    278 TouchpadSettings& TouchpadSettings::operator=(const TouchpadSettings& other) {
    279   if (&other != this) {
    280     sensitivity_ = other.sensitivity_;
    281     tap_to_click_ = other.tap_to_click_;
    282     three_finger_click_ = other.three_finger_click_;
    283     tap_dragging_ = other.tap_dragging_;
    284     natural_scroll_ = other.natural_scroll_;
    285   }
    286   return *this;
    287 }
    288 
    289 void TouchpadSettings::SetSensitivity(int value) {
    290   sensitivity_.Set(value);
    291 }
    292 
    293 int TouchpadSettings::GetSensitivity() const {
    294   return sensitivity_.value();
    295 }
    296 
    297 void TouchpadSettings::SetTapToClick(bool enabled) {
    298   tap_to_click_.Set(enabled);
    299 }
    300 
    301 bool TouchpadSettings::GetTapToClick() const {
    302   return tap_to_click_.value();
    303 }
    304 
    305 void TouchpadSettings::SetNaturalScroll(bool enabled) {
    306   natural_scroll_.Set(enabled);
    307 }
    308 
    309 bool TouchpadSettings::GetNaturalScroll() const {
    310   return natural_scroll_.value();
    311 }
    312 
    313 void TouchpadSettings::SetThreeFingerClick(bool enabled) {
    314   three_finger_click_.Set(enabled);
    315 }
    316 
    317 bool TouchpadSettings::GetThreeFingerClick() const {
    318   return three_finger_click_.value();
    319 }
    320 
    321 void TouchpadSettings::SetTapDragging(bool enabled) {
    322   tap_dragging_.Set(enabled);
    323 }
    324 
    325 bool TouchpadSettings::GetTapDragging() const {
    326   return tap_dragging_.value();
    327 }
    328 
    329 bool TouchpadSettings::Update(const TouchpadSettings& settings,
    330                               std::vector<std::string>* argv) {
    331   if (argv)
    332     argv->push_back(kInputControl);
    333   bool updated = false;
    334   if (sensitivity_.Update(settings.sensitivity_)) {
    335     updated = true;
    336     if (argv)
    337       AddSensitivityArguments(kDeviceTypeTouchpad, sensitivity_.value(), argv);
    338   }
    339   if (tap_to_click_.Update(settings.tap_to_click_)) {
    340     updated = true;
    341     if (argv)
    342       AddTPControlArguments("tapclick", tap_to_click_.value(), argv);
    343   }
    344   if (three_finger_click_.Update(settings.three_finger_click_)) {
    345     updated = true;
    346     if (argv)
    347       AddTPControlArguments("t5r2_three_finger_click",
    348                             three_finger_click_.value(),
    349                             argv);
    350   }
    351   if (tap_dragging_.Update(settings.tap_dragging_)) {
    352     updated = true;
    353     if (argv)
    354       AddTPControlArguments("tapdrag", tap_dragging_.value(), argv);
    355   }
    356   natural_scroll_.Update(settings.natural_scroll_);
    357   // Always send natural scrolling to the shell command, as a workaround.
    358   // See crbug.com/406480
    359   if (natural_scroll_.is_set()) {
    360     updated = true;
    361     if (argv)
    362       AddTPControlArguments("australian_scrolling", natural_scroll_.value(),
    363                             argv);
    364   }
    365   return updated;
    366 }
    367 
    368 MouseSettings::MouseSettings() {}
    369 
    370 MouseSettings& MouseSettings::operator=(const MouseSettings& other) {
    371   if (&other != this) {
    372     sensitivity_ = other.sensitivity_;
    373     primary_button_right_ = other.primary_button_right_;
    374   }
    375   return *this;
    376 }
    377 
    378 void MouseSettings::SetSensitivity(int value) {
    379   sensitivity_.Set(value);
    380 }
    381 
    382 int MouseSettings::GetSensitivity() const {
    383   return sensitivity_.value();
    384 }
    385 
    386 void MouseSettings::SetPrimaryButtonRight(bool right) {
    387   primary_button_right_.Set(right);
    388 }
    389 
    390 bool MouseSettings::GetPrimaryButtonRight() const {
    391   return primary_button_right_.value();
    392 }
    393 
    394 bool MouseSettings::Update(const MouseSettings& settings,
    395                            std::vector<std::string>* argv) {
    396   if (argv)
    397     argv->push_back(kInputControl);
    398   bool updated = false;
    399   if (sensitivity_.Update(settings.sensitivity_)) {
    400     updated = true;
    401     if (argv)
    402       AddSensitivityArguments(kDeviceTypeMouse, sensitivity_.value(), argv);
    403   }
    404   if (primary_button_right_.Update(settings.primary_button_right_)) {
    405     updated = true;
    406     if (argv) {
    407       AddTPControlArguments("mouse_swap_lr", primary_button_right_.value(),
    408                             argv);
    409     }
    410   }
    411   return updated;
    412 }
    413 
    414 // static
    415 InputDeviceSettings* InputDeviceSettings::Get() {
    416   if (g_test_instance_)
    417     return g_test_instance_;
    418   if (!g_instance_)
    419     g_instance_ = new InputDeviceSettingsImpl;
    420   return g_instance_;
    421 }
    422 
    423 // static
    424 void InputDeviceSettings::SetSettingsForTesting(
    425     InputDeviceSettings* test_settings) {
    426   if (g_test_instance_ == test_settings)
    427     return;
    428   delete g_test_instance_;
    429   g_test_instance_ = test_settings;
    430 }
    431 
    432 }  // namespace system
    433 }  // namespace chromeos
    434