blob: c89cfd5af189d469f25bfeba3d625b2a71835013 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/system_logs/touch_log_source.h"
#include <stddef.h>
#include "ash/touch/touch_hud_debug.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/json/json_string_value_serializer.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/task/post_task.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part_chromeos.h"
#include "content/public/browser/browser_thread.h"
#include "services/ws/public/cpp/input_devices/input_device_controller_client.h"
#include "ui/base/ui_base_features.h"
using content::BrowserThread;
namespace {
const char kHUDLogDataKey[] = "hud_log";
// The prefix "hack-33025" was historically chosen in http://crbug.com/139715.
// We continue to go with it in order to be compatible with the existing touch
// log processing toolchain.
const char kDeviceStatusLogDataKey[] = "hack-33025-touchpad";
const char kTouchpadEventLogDataKey[] = "hack-33025-touchpad_activity";
const char kTouchscreenEventLogDataKey[] = "hack-33025-touchscreen_activity";
// Directory for temp touch event logs.
const char kTouchEventLogDir[] = "/home/chronos/user/log";
// Prefixes of touch event logs.
const char kTouchpadGestureLogPrefix[] = "touchpad_activity_";
const char kTouchscreenLogPrefix[] = "evdev_input_events_";
const char kTouchpadEvdevLogPrefix[] = "cmt_input_events_";
// Binary paths.
const char kShellCommand[] = "/bin/sh";
const char kTarCommand[] = "/bin/tar cf -";
const char kUuencodeCommand[] = "/usr/bin/uuencode";
const int kMaxDeviceTouchEventLogs = 7;
// Clean up intermediate log files dumped during feedback creation.
void CleanupEventLog(const std::vector<base::FilePath>& log_paths) {
for (const base::FilePath& path : log_paths)
base::DeleteFile(path, false);
}
// Check for all known log paths and find the ones whose filenames match a
// prefix. Concatenate their filenames into one string. |max_log_count| is
// the maximum number of logs that we will collect.
//
// This is used to distinguish touchpad/mice logs from touchscreen logs.
std::string GetEventLogListOfOnePrefix(
const std::vector<base::FilePath>& log_paths,
const std::string& prefix,
const int max_log_count) {
int collected = 0;
std::string log_list;
for (size_t i = 0; i < log_paths.size(); ++i) {
const std::string basename = log_paths[i].BaseName().value();
if (base::StartsWith(basename, prefix, base::CompareCase::SENSITIVE)) {
log_list.append(" " + log_paths[i].value());
// Limit the max number of collected logs to shorten the log collection
// process.
if (++collected >= max_log_count)
break;
}
}
return log_list;
}
// Pack the collected event logs in a way that is compatible with the log
// analysis tools.
void PackEventLog(system_logs::SystemLogsResponse* response,
const std::vector<base::FilePath>& log_paths) {
// Combine logs with a command line call that tars them up and uuencode the
// result in one string.
std::vector<std::pair<std::string, base::CommandLine>> commands;
base::CommandLine command = base::CommandLine(base::FilePath(kShellCommand));
command.AppendArg("-c");
// Make a list that contains touchpad (and mouse) event logs only.
const std::string touchpad_log_list =
GetEventLogListOfOnePrefix(log_paths, kTouchpadGestureLogPrefix,
kMaxDeviceTouchEventLogs) +
GetEventLogListOfOnePrefix(log_paths, kTouchpadEvdevLogPrefix,
kMaxDeviceTouchEventLogs);
command.AppendArg(std::string(kTarCommand) + touchpad_log_list +
" 2>/dev/null | " + kUuencodeCommand +
" -m touchpad_activity_log.tar");
commands.push_back(std::make_pair(kTouchpadEventLogDataKey, command));
base::CommandLine ts_command =
base::CommandLine(base::FilePath(kShellCommand));
ts_command.AppendArg("-c");
// Make a list that contains touchscreen event logs only.
const std::string touchscreen_log_list = GetEventLogListOfOnePrefix(
log_paths, kTouchscreenLogPrefix, kMaxDeviceTouchEventLogs);
ts_command.AppendArg(std::string(kTarCommand) + touchscreen_log_list +
" 2>/dev/null | " + kUuencodeCommand +
" -m touchscreen_activity_log.tar");
commands.push_back(std::make_pair(kTouchscreenEventLogDataKey, ts_command));
// For now only touchpad (and mouse) logs are actually collected.
for (size_t i = 0; i < commands.size(); ++i) {
std::string output;
base::GetAppOutput(commands[i].second, &output);
(*response)[commands[i].first] = output;
}
// Cleanup these temporary log files.
base::PostTaskWithTraits(FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(CleanupEventLog, log_paths));
}
// Callback for handing the outcome of GetTouchEventLog().
//
// This is the end of the whole touch log collection process.
void OnEventLogCollected(
std::unique_ptr<system_logs::SystemLogsResponse> response,
system_logs::SysLogsSourceCallback callback,
const std::vector<base::FilePath>& log_paths) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
system_logs::SystemLogsResponse* response_ptr = response.get();
base::PostTaskWithTraitsAndReply(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&PackEventLog, response_ptr, log_paths),
base::BindOnce(std::move(callback), std::move(response)));
}
// Callback for handing the outcome of GetTouchDeviceStatus().
//
// Appends the collected log to the SystemLogsResponse map. Also goes on to
// collect touch event logs.
void OnStatusLogCollected(
std::unique_ptr<system_logs::SystemLogsResponse> response,
system_logs::SysLogsSourceCallback callback,
const std::string& log) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
(*response)[kDeviceStatusLogDataKey] = log;
// Collect touch event logs.
const base::FilePath kBaseLogPath(kTouchEventLogDir);
ws::InputDeviceControllerClient* input_device_controller_client =
g_browser_process->platform_part()->GetInputDeviceControllerClient();
input_device_controller_client->GetTouchEventLog(
kBaseLogPath, base::BindOnce(&OnEventLogCollected, std::move(response),
std::move(callback)));
}
// Collect touch HUD debug logs. This needs to be done on the UI thread.
void CollectTouchHudDebugLog(system_logs::SystemLogsResponse* response) {
// TODO(crbug.com/807408): Collect this data from window server over mojo.
if (features::IsMultiProcessMash()) {
NOTIMPLEMENTED();
return;
}
std::unique_ptr<base::DictionaryValue> dictionary =
ash::TouchHudDebug::GetAllAsDictionary();
if (!dictionary->empty()) {
std::string touch_log;
JSONStringValueSerializer json(&touch_log);
json.set_pretty_print(true);
if (json.Serialize(*dictionary) && !touch_log.empty())
(*response)[kHUDLogDataKey] = touch_log;
}
}
} // namespace
namespace system_logs {
void TouchLogSource::Fetch(SysLogsSourceCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!callback.is_null());
auto response = std::make_unique<SystemLogsResponse>();
CollectTouchHudDebugLog(response.get());
// Collect touch device status logs.
ws::InputDeviceControllerClient* input_device_controller_client =
g_browser_process->platform_part()->GetInputDeviceControllerClient();
input_device_controller_client->GetTouchDeviceStatus(base::BindOnce(
&OnStatusLogCollected, std::move(response), std::move(callback)));
}
} // namespace system_logs