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 <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 
     32 namespace chromeos {
     33 namespace system {
     34 
     35 namespace {
     36 
     37 const char kTpControl[] = "/opt/google/touchpad/tpcontrol";
     38 const char kMouseControl[] = "/opt/google/mouse/mousecontrol";
     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(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 
     86 void 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 
     93 void SetTPControl(const char* control, bool enabled) {
     94   ExecuteScript(3, kTpControl, control, enabled ? "on" : "off");
     95 }
     96 
     97 void 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 
    114 void 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 
    121 void 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 
    139 namespace touchpad_settings {
    140 
    141 void TouchpadExists(const DeviceExistsCallback& callback) {
    142   DeviceExists(kTpControl, callback);
    143 }
    144 
    145 // Sets the touchpad sensitivity in the range [1, 5].
    146 void SetSensitivity(int value) {
    147   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    148   SetPointerSensitivity(kTpControl, value);
    149 }
    150 
    151 void SetTapToClick(bool enabled) {
    152   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    153   SetTPControl("taptoclick", enabled);
    154 }
    155 
    156 void 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 
    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 (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