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/chromeos/chromeos_version.h"
     13 #include "base/command_line.h"
     14 #include "base/file_util.h"
     15 #include "base/files/file_path.h"
     16 #include "base/memory/ref_counted.h"
     17 #include "base/message_loop/message_loop.h"
     18 #include "base/process/kill.h"
     19 #include "base/process/launch.h"
     20 #include "base/process/process_handle.h"
     21 #include "base/strings/stringprintf.h"
     22 #include "base/task_runner.h"
     23 #include "base/threading/sequenced_worker_pool.h"
     24 #include "chrome/browser/browser_process.h"
     25 #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
     26 #include "chrome/browser/chromeos/system/statistics_provider.h"
     27 #include "chrome/browser/policy/browser_policy_connector.h"
     28 #include "chrome/common/pref_names.h"
     29 #include "content/public/browser/browser_thread.h"
     30 
     31 namespace chromeos {
     32 namespace system {
     33 
     34 namespace {
     35 
     36 const char kTpControl[] = "/opt/google/touchpad/tpcontrol";
     37 const char kMouseControl[] = "/opt/google/mouse/mousecontrol";
     38 
     39 const char kRemoraRequisition[] = "remora";
     40 
     41 typedef base::RefCountedData<bool> RefCountedBool;
     42 
     43 bool ScriptExists(const std::string& script) {
     44   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
     45   return base::PathExists(base::FilePath(script));
     46 }
     47 
     48 // Executes the input control script asynchronously, if it exists.
     49 void ExecuteScriptOnFileThread(const std::vector<std::string>& argv) {
     50   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
     51   DCHECK(!argv.empty());
     52   const std::string& script(argv[0]);
     53 
     54   // Script must exist on device.
     55   DCHECK(!base::chromeos::IsRunningOnChromeOS() || ScriptExists(script));
     56 
     57   if (!ScriptExists(script))
     58     return;
     59 
     60   base::ProcessHandle handle;
     61   base::LaunchProcess(CommandLine(argv), base::LaunchOptions(), &handle);
     62   base::EnsureProcessGetsReaped(handle);
     63 }
     64 
     65 void ExecuteScript(int argc, ...) {
     66   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     67   std::vector<std::string> argv;
     68   va_list vl;
     69   va_start(vl, argc);
     70   for (int i = 0; i < argc; ++i) {
     71     argv.push_back(va_arg(vl, const char*));
     72   }
     73   va_end(vl);
     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 SetPointerSensitivity(const char* script, int value) {
     86   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     87   DCHECK(value >= kMinPointerSensitivity && value <= kMaxPointerSensitivity);
     88   ExecuteScript(
     89       3, script, "sensitivity", base::StringPrintf("%d", value).c_str());
     90 }
     91 
     92 void SetTPControl(const char* control, bool enabled) {
     93   ExecuteScript(3, kTpControl, control, enabled ? "on" : "off");
     94 }
     95 
     96 void DeviceExistsBlockingPool(const char* script,
     97                               scoped_refptr<RefCountedBool> exists) {
     98   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
     99   exists->data = false;
    100   if (!ScriptExists(script))
    101     return;
    102 
    103   std::vector<std::string> argv;
    104   argv.push_back(script);
    105   argv.push_back("status");
    106   std::string output;
    107   // Output is empty if the device is not found.
    108   exists->data = base::GetAppOutput(CommandLine(argv), &output) &&
    109       !output.empty();
    110   DVLOG(1) << "DeviceExistsBlockingPool:" << script << "=" << exists->data;
    111 }
    112 
    113 void RunCallbackUIThread(scoped_refptr<RefCountedBool> exists,
    114                          const DeviceExistsCallback& callback) {
    115   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    116   DVLOG(1) << "RunCallbackUIThread " << exists->data;
    117   callback.Run(exists->data);
    118 }
    119 
    120 void DeviceExists(const char* script, const DeviceExistsCallback& callback) {
    121   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    122 
    123   // One or both of the control scripts can apparently hang during shutdown
    124   // (http://crbug.com/255546). Run the blocking pool task with
    125   // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down.
    126   scoped_refptr<RefCountedBool> exists(new RefCountedBool(false));
    127   base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
    128   scoped_refptr<base::TaskRunner> runner =
    129       pool->GetTaskRunnerWithShutdownBehavior(
    130           base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
    131   runner->PostTaskAndReply(FROM_HERE,
    132       base::Bind(&DeviceExistsBlockingPool, script, exists),
    133       base::Bind(&RunCallbackUIThread, exists, callback));
    134 }
    135 
    136 }  // namespace
    137 
    138 namespace touchpad_settings {
    139 
    140 void TouchpadExists(const DeviceExistsCallback& callback) {
    141   DeviceExists(kTpControl, callback);
    142 }
    143 
    144 // Sets the touchpad sensitivity in the range [1, 5].
    145 void SetSensitivity(int value) {
    146   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    147   SetPointerSensitivity(kTpControl, value);
    148 }
    149 
    150 void SetTapToClick(bool enabled) {
    151   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    152   SetTPControl("taptoclick", enabled);
    153 }
    154 
    155 void SetThreeFingerClick(bool enabled) {
    156   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    157 
    158   SetTPControl("three_finger_click", enabled);
    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 (policy_manager->GetDeviceRequisition() == kRemoraRequisition)
    204     return true;
    205 
    206   bool keyboard_driven = false;
    207   if (chromeos::system::StatisticsProvider::GetInstance()->GetMachineFlag(
    208       kOemKeyboardDrivenOobeKey, &keyboard_driven)) {
    209     return keyboard_driven;
    210   }
    211 
    212   return false;
    213 }
    214 
    215 }  // namespace keyboard_settings
    216 
    217 }  // namespace system
    218 }  // namespace chromeos
    219