| // Copyright (c) 2012 The Chromium OS 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 <dbus/dbus-shared.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <linux/vt.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/ioctl.h> |
| #include <sys/wait.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/file_util.h" |
| #include "base/string_split.h" |
| #include "base/string_util.h" |
| #include "base/stringprintf.h" |
| #include "chromeos/dbus/dbus.h" |
| #include "chromeos/dbus/service_constants.h" |
| #include "power_manager/backlight_interface.h" |
| #include "power_manager/power_constants.h" |
| #include "power_manager/powerman.h" |
| #include "power_manager/util.h" |
| #include "power_manager/util_dbus.h" |
| |
| using base::TimeDelta; |
| using base::TimeTicks; |
| using std::string; |
| |
| namespace { |
| |
| const int kCheckLidClosedSeconds = 10; |
| const unsigned int kCancelDBusLidOpenedSecs = 5; |
| |
| } // namespace |
| |
| namespace power_manager { |
| |
| PowerManDaemon::PowerManDaemon(PowerPrefs* prefs, |
| MetricsLibraryInterface* metrics_lib, |
| BacklightInterface* backlight, |
| const FilePath& run_dir) |
| : loop_(NULL), |
| use_input_for_lid_(1), |
| prefs_(prefs), |
| lidstate_(LID_STATE_OPENED), |
| metrics_lib_(metrics_lib), |
| backlight_(backlight), |
| retry_suspend_count_(0), |
| suspend_pid_(0), |
| lid_id_(0), |
| powerd_id_(0), |
| session_state_(kSessionStopped), |
| powerd_state_(kPowerManagerUnknown), |
| run_dir_(run_dir), |
| console_fd_(-1) {} |
| |
| PowerManDaemon::~PowerManDaemon() { |
| if (console_fd_ >= 0) { |
| close(console_fd_); |
| } |
| } |
| |
| void PowerManDaemon::Init() { |
| int input_lidstate = 0; |
| int64 use_input_for_lid; |
| string wakeup_inputs_str; |
| std::vector<string> wakeup_inputs; |
| |
| if (prefs_->GetString(kWakeupInputPref, &wakeup_inputs_str)) |
| base::SplitString(wakeup_inputs_str, '\n', &wakeup_inputs); |
| CHECK(prefs_->GetInt64(kRetrySuspendMsPref, &retry_suspend_ms_)); |
| CHECK(prefs_->GetInt64(kRetrySuspendAttemptsPref, &retry_suspend_attempts_)); |
| CHECK(prefs_->GetInt64(kUseLidPref, &use_input_for_lid)); |
| // Retrys will occur no more than once a minute. |
| CHECK_GE(retry_suspend_ms_, 60000); |
| // Only 1-10 retries prior to just shutting down. |
| CHECK_GT(retry_suspend_attempts_, 0); |
| CHECK_LE(retry_suspend_attempts_, 10); |
| use_input_for_lid_ = (use_input_for_lid == 1); |
| loop_ = g_main_loop_new(NULL, false); |
| // Acquire a handle to the console for VT switch locking ioctl. |
| CHECK(GetConsole()); |
| input_.RegisterHandler(&(PowerManDaemon::OnInputEvent), this); |
| CHECK(input_.Init(wakeup_inputs)) << "Cannot initialize input interface."; |
| lid_open_file_ = FilePath(run_dir_).Append(kLidOpenFile); |
| if (input_.num_lid_events() > 0) { |
| input_.QueryLidState(&input_lidstate); |
| lidstate_ = GetLidState(input_lidstate); |
| lid_ticks_ = TimeTicks::Now(); |
| LOG(INFO) << "PowerM Daemon Init - lid " |
| << (lidstate_ == LID_STATE_CLOSED ? "closed." : "opened."); |
| if (lidstate_ == LID_STATE_CLOSED) { |
| input_.DisableWakeInputs(); |
| LOG(INFO) << "PowerM Daemon Init - lid is closed; generating event"; |
| OnInputEvent(this, INPUT_LID, input_lidstate); |
| } else { |
| input_.EnableWakeInputs(); |
| } |
| } |
| RegisterDBusMessageHandler(); |
| } |
| |
| void PowerManDaemon::Run() { |
| g_main_loop_run(loop_); |
| } |
| |
| gboolean PowerManDaemon::CheckLidClosed(unsigned int lid_id, |
| unsigned int powerd_id) { |
| unsigned int wakeup_count; |
| // Same lid closed event and powerd state has changed. |
| if ((lidstate_ == LID_STATE_CLOSED) && (lid_id_ == lid_id) && |
| ((powerd_state_ != kPowerManagerAlive) || (powerd_id_ != powerd_id))) { |
| LOG(INFO) << "Forced suspend, powerd unstable with pending suspend"; |
| if (!util::GetWakeupCount(&wakeup_count)) { |
| LOG(ERROR) << "Could not get wakeup count trying to suspend"; |
| Suspend(); |
| } else { |
| Suspend(wakeup_count); |
| } |
| } |
| // lid closed events will re-trigger if necessary so always false return. |
| return false; |
| } |
| |
| gboolean PowerManDaemon::RetrySuspend(unsigned int lid_id) { |
| unsigned int wakeup_count; |
| |
| if (lidstate_ == LID_STATE_CLOSED) { |
| if (lid_id_ == lid_id) { |
| retry_suspend_count_++; |
| if (retry_suspend_count_ > retry_suspend_attempts_) { |
| LOG(ERROR) << "Retry suspend attempts failed ... shutting down"; |
| Shutdown(); |
| } else { |
| LOG(WARNING) << "Retry suspend " << retry_suspend_count_; |
| if (!util::GetWakeupCount(&wakeup_count)) { |
| LOG(ERROR) << "Could not get wakeup count retrying suspend"; |
| Suspend(); |
| } else { |
| Suspend(wakeup_count); |
| } |
| } |
| } else { |
| LOG(INFO) << "Retry suspend sequence number changed, retry delayed"; |
| } |
| } else { |
| DLOG(INFO) << "Retry suspend ... lid is open"; |
| } |
| // Return false so the event trigger does not repeat. |
| return false; |
| } |
| |
| void PowerManDaemon::OnInputEvent(void* object, InputType type, int value) { |
| PowerManDaemon* daemon = static_cast<PowerManDaemon*>(object); |
| switch (type) { |
| case INPUT_LID: { |
| daemon->lidstate_ = daemon->GetLidState(value); |
| daemon->lid_id_++; |
| daemon->lid_ticks_ = TimeTicks::Now(); |
| LOG(INFO) << "PowerM Daemon - lid " |
| << (daemon->lidstate_ == LID_STATE_CLOSED ? |
| "closed." : "opened.") |
| << " powerd " |
| << (daemon->powerd_state_ == kPowerManagerDead ? |
| "dead" : (daemon->powerd_state_ == kPowerManagerAlive ? |
| "alive" : "unknown")) |
| << ". session " |
| << (daemon->session_state_ == kSessionStarted ? |
| "started." : "stopped"); |
| if (!daemon->use_input_for_lid_) { |
| LOG(INFO) << "Ignoring lid."; |
| break; |
| } |
| if (daemon->lidstate_ == LID_STATE_CLOSED) { |
| daemon->DisableTouchDevices(); |
| daemon->input_.DisableWakeInputs(); |
| util::SendSignalToPowerD(kLidClosed); |
| // Check that powerd stuck around to act on this event. If not, |
| // callback will assume suspend responsibilities. |
| g_timeout_add_seconds(kCheckLidClosedSeconds, CheckLidClosedThunk, |
| CreateCheckLidClosedArgs(daemon, |
| daemon->lid_id_, |
| daemon->powerd_id_)); |
| } else { |
| daemon->EnableTouchDevices(true); |
| daemon->input_.EnableWakeInputs(); |
| util::CreateStatusFile(daemon->lid_open_file_); |
| util::SendSignalToPowerD(kLidOpened); |
| } |
| break; |
| } |
| case INPUT_POWER_BUTTON: |
| daemon->HandlePowerButtonEvent(GetButtonState(value)); |
| break; |
| case INPUT_LOCK_BUTTON: |
| daemon->SendButtonEventSignal(kLockButtonName, GetButtonState(value)); |
| break; |
| case INPUT_KEY_LEFT_CTRL: |
| daemon->SendButtonEventSignal(kKeyLeftCtrl, GetButtonState(value)); |
| break; |
| case INPUT_KEY_RIGHT_CTRL: |
| daemon->SendButtonEventSignal(kKeyRightCtrl, GetButtonState(value)); |
| break; |
| case INPUT_KEY_LEFT_ALT: |
| daemon->SendButtonEventSignal(kKeyLeftAlt, GetButtonState(value)); |
| break; |
| case INPUT_KEY_RIGHT_ALT: |
| daemon->SendButtonEventSignal(kKeyRightAlt, GetButtonState(value)); |
| break; |
| case INPUT_KEY_LEFT_SHIFT: |
| daemon->SendButtonEventSignal(kKeyLeftShift, GetButtonState(value)); |
| break; |
| case INPUT_KEY_RIGHT_SHIFT: |
| daemon->SendButtonEventSignal(kKeyRightShift, GetButtonState(value)); |
| break; |
| case INPUT_KEY_F4: |
| daemon->SendButtonEventSignal(kKeyF4, GetButtonState(value)); |
| break; |
| default: { |
| LOG(ERROR) << "Bad input type."; |
| NOTREACHED(); |
| break; |
| } |
| } |
| } |
| |
| bool PowerManDaemon::CancelDBusRequest() { |
| TimeDelta delta = TimeTicks::Now() - lid_ticks_; |
| |
| bool cancel = (lidstate_ == LID_STATE_OPENED) && |
| (delta.InSeconds() < kCancelDBusLidOpenedSecs); |
| LOG(INFO) << (cancel ? "Canceled" : "Continuing") |
| << " DBus activated suspend. Lid is " |
| << (lidstate_ == LID_STATE_CLOSED ? "closed." : "open."); |
| return cancel; |
| } |
| |
| DBusHandlerResult PowerManDaemon::MainDBusMethodHandler( |
| DBusConnection* connection, DBusMessage* message, void* data) { |
| if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) |
| return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| |
| PowerManDaemon* daemon = static_cast<PowerManDaemon*>(data); |
| CHECK(daemon); |
| |
| // Look up and call the corresponding dbus message handler. |
| std::string interface = dbus_message_get_interface(message); |
| std::string member = dbus_message_get_member(message); |
| DBusInterfaceMemberPair dbus_message_pair = std::make_pair(interface, member); |
| DBusMethodHandlerTable::iterator iter = |
| daemon->dbus_method_handler_table_.find(dbus_message_pair); |
| if (iter == daemon->dbus_method_handler_table_.end()) { |
| LOG(ERROR) << "Could not find handler for " << interface << ":" << member |
| << " in method handler table."; |
| return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| } |
| |
| LOG(INFO) << "Got " << member << " method call"; |
| DBusMethodHandler callback = iter->second; |
| DBusMessage* reply = (daemon->*callback)(message); |
| |
| // Must send a reply if it is a message. |
| if (!reply) |
| reply = util::CreateEmptyDBusReply(message); |
| // If the send reply fails, it is due to lack of memory. |
| CHECK(dbus_connection_send(connection, reply, NULL)); |
| dbus_message_unref(reply); |
| |
| return DBUS_HANDLER_RESULT_HANDLED; |
| } |
| |
| DBusHandlerResult PowerManDaemon::MainDBusSignalHandler( |
| DBusConnection* connection, DBusMessage* message, void* data) { |
| if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) |
| return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| |
| PowerManDaemon* daemon = static_cast<PowerManDaemon*>(data); |
| CHECK(daemon); |
| // Look up and call the corresponding dbus message handler. |
| std::string interface = dbus_message_get_interface(message); |
| std::string member = dbus_message_get_member(message); |
| DBusInterfaceMemberPair dbus_message_pair = std::make_pair(interface, member); |
| DBusSignalHandlerTable::iterator iter = |
| daemon->dbus_signal_handler_table_.find(dbus_message_pair); |
| // Quietly skip this signal if it has no handler. |
| if (iter == daemon->dbus_signal_handler_table_.end()) |
| return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| |
| LOG(INFO) << "Got " << member << " signal"; |
| DBusSignalHandler callback = iter->second; |
| |
| (daemon->*callback)(message); |
| return DBUS_HANDLER_RESULT_HANDLED; |
| } |
| |
| void PowerManDaemon::HandlePowerButtonEvent(ButtonState value) { |
| // Forward the signal to be handled by powerd and chrome |
| SendButtonEventSignal(kPowerButtonName, value); |
| |
| // On button down, since the user may be doing a long press to cause a |
| // hardware shutdown, sync our state. |
| if (value == BUTTON_DOWN) { |
| LOG(INFO) << "Syncing state due to power button down event"; |
| util::Launch("sync"); |
| } |
| } |
| |
| void PowerManDaemon::HandleCheckLidStateSignal(DBusMessage*) { // NOLINT |
| if (lidstate_ == LID_STATE_CLOSED) { |
| util::SendSignalToPowerD(kLidClosed); |
| } |
| } |
| |
| void PowerManDaemon::HandleSuspendSignal(DBusMessage* message) { |
| Suspend(message); |
| } |
| |
| void PowerManDaemon::HandleShutdownSignal(DBusMessage*) { // NOLINT |
| Shutdown(); |
| } |
| |
| void PowerManDaemon::HandleRestartSignal(DBusMessage*) { // NOLINT |
| Restart(); |
| } |
| |
| void PowerManDaemon::HandleRequestCleanShutdownSignal(DBusMessage*) { // NOLINT |
| util::Launch("initctl emit power-manager-clean-shutdown"); |
| } |
| |
| void PowerManDaemon::HandlePowerStateChangedSignal(DBusMessage* message) { |
| const char* state = '\0'; |
| int32 power_rc = -1; |
| DBusError error; |
| dbus_error_init(&error); |
| if (dbus_message_get_args(message, &error, |
| DBUS_TYPE_STRING, &state, |
| DBUS_TYPE_INT32, &power_rc, |
| DBUS_TYPE_INVALID)) { |
| // on == resume via powerd_suspend |
| if (g_str_equal(state, "on") == TRUE) { |
| LOG(INFO) << "Resuming has commenced"; |
| if (power_rc == 0) { |
| GenerateMetricsOnResumeEvent(); |
| retry_suspend_count_ = 0; |
| } else { |
| LOG(INFO) << "Suspend attempt failed"; |
| } |
| #ifdef SUSPEND_LOCK_VT |
| UnlockVTSwitch(); // Allow virtual terminal switching again. |
| #endif |
| } else { |
| DLOG(INFO) << "Saw arg:" << state << " for " << kPowerStateChanged; |
| } |
| } else { |
| LOG(WARNING) << "Unable to read " << kPowerStateChanged << " args"; |
| dbus_error_free(&error); |
| } |
| } |
| |
| void PowerManDaemon::HandleSessionManagerStateChangedSignal( |
| DBusMessage* message) { |
| DBusError error; |
| dbus_error_init(&error); |
| const char* state = NULL; |
| const char* user = NULL; |
| if (dbus_message_get_args(message, &error, |
| DBUS_TYPE_STRING, &state, |
| DBUS_TYPE_STRING, &user, |
| DBUS_TYPE_INVALID)) { |
| if (strcmp(state, "started") == 0) |
| session_state_ = kSessionStarted; |
| else if (strcmp(state, "stopping") == 0) |
| session_state_ = kSessionStopping; |
| else if (strcmp(state, "stopped") == 0) |
| session_state_ = kSessionStopped; |
| else |
| LOG(WARNING) << "Unknown session state : " << state; |
| } else { |
| LOG(WARNING) << "Unable to read " |
| << login_manager::kSessionManagerSessionStateChanged |
| << " args"; |
| dbus_error_free(&error); |
| } |
| } |
| |
| DBusMessage* PowerManDaemon::HandleExternalBacklightGetMethod( |
| DBusMessage* message) { |
| int64 current_level = 0; |
| int64 max_level = 0; |
| dbus_bool_t result = FALSE; |
| if (backlight_) { |
| result = backlight_->GetCurrentBrightnessLevel(¤t_level) && |
| backlight_->GetMaxBrightnessLevel(&max_level); |
| } |
| |
| DBusMessage* reply = dbus_message_new_method_return(message); |
| CHECK(reply); |
| dbus_message_append_args(reply, |
| DBUS_TYPE_INT64, ¤t_level, |
| DBUS_TYPE_INT64, &max_level, |
| DBUS_TYPE_BOOLEAN, &result, |
| DBUS_TYPE_INVALID); |
| return reply; |
| } |
| |
| DBusMessage* PowerManDaemon::HandleExternalBacklightSetMethod( |
| DBusMessage* message) { |
| int64 level = 0; |
| DBusError error; |
| dbus_error_init(&error); |
| if (dbus_message_get_args(message, &error, |
| DBUS_TYPE_INT64, &level, |
| DBUS_TYPE_INVALID)) { |
| if (backlight_) |
| backlight_->SetBrightnessLevel(level); |
| } else { |
| LOG(WARNING) << "Unable to read " << kExternalBacklightSetMethod << " args"; |
| dbus_error_free(&error); |
| } |
| return dbus_message_new_method_return(message); |
| } |
| |
| DBusMessage* PowerManDaemon::HandleDisableTouchDevicesMethod( |
| DBusMessage* message) { |
| DBusError error; |
| dbus_error_init(&error); |
| |
| DisableTouchDevices(); |
| return dbus_message_new_method_return(message); |
| } |
| |
| DBusMessage* PowerManDaemon::HandleEnableTouchDevicesMethod( |
| DBusMessage* message) { |
| bool display_on = true; |
| DBusError error; |
| dbus_error_init(&error); |
| if (dbus_message_get_args(message, &error, |
| DBUS_TYPE_BOOLEAN, &display_on, |
| DBUS_TYPE_INVALID)) { |
| EnableTouchDevices(display_on); |
| } else { |
| LOG(WARNING) << "Unable to read " << kEnableTouchDevicesMethod << " args"; |
| dbus_error_free(&error); |
| } |
| return dbus_message_new_method_return(message); |
| } |
| |
| void PowerManDaemon::AddDBusSignalHandler(const std::string& interface, |
| const std::string& member, |
| DBusSignalHandler handler) { |
| DBusConnection* connection = dbus_g_connection_get_connection( |
| chromeos::dbus::GetSystemBusConnection().g_connection()); |
| CHECK(connection); |
| util::AddDBusSignalMatch(connection, interface, member); |
| dbus_signal_handler_table_[std::make_pair(interface, member)] = handler; |
| } |
| |
| void PowerManDaemon::AddDBusMethodHandler(const std::string& interface, |
| const std::string& member, |
| DBusMethodHandler handler) { |
| DBusConnection* connection = dbus_g_connection_get_connection( |
| chromeos::dbus::GetSystemBusConnection().g_connection()); |
| CHECK(connection); |
| dbus_method_handler_table_[std::make_pair(interface, member)] = handler; |
| } |
| |
| void PowerManDaemon::DBusNameOwnerChangedHandler( |
| DBusGProxy*, const gchar* name, const gchar* old_owner, |
| const gchar* new_owner, void* data) { |
| PowerManDaemon* daemon = static_cast<PowerManDaemon*>(data); |
| if (!name || !new_owner || !old_owner) { |
| LOG(ERROR) << "NameOwnerChanged with Null name."; |
| return; |
| } |
| if (strcmp(name, kPowerManagerInterface) == 0) { |
| DLOG(INFO) << "name:" << name << " old_owner:" << old_owner |
| << " new_owner:" << new_owner; |
| daemon->powerd_id_++; |
| if (strlen(new_owner) == 0) { |
| daemon->powerd_state_ = kPowerManagerDead; |
| LOG(WARNING) << "Powerd has stopped"; |
| } else if (strlen(old_owner) == 0) { |
| daemon->powerd_state_ = kPowerManagerAlive; |
| LOG(INFO) << "Powerd has started"; |
| if (daemon->use_input_for_lid_ && |
| daemon->lidstate_ == LID_STATE_CLOSED) { |
| LOG(INFO) << "Lid is closed. Sending message to powerd on respawn."; |
| util::SendSignalToPowerD(kLidClosed); |
| } |
| } else { |
| daemon->powerd_state_ = kPowerManagerUnknown; |
| LOG(WARNING) << "Unrecognized DBus NameOwnerChanged transition of powerd"; |
| } |
| } |
| } |
| |
| void PowerManDaemon::RegisterDBusMessageHandler() { |
| DBusConnection* connection = dbus_g_connection_get_connection( |
| chromeos::dbus::GetSystemBusConnection().g_connection()); |
| CHECK(connection); |
| DBusError error; |
| dbus_error_init(&error); |
| if (dbus_bus_request_name(connection, |
| kRootPowerManagerServiceName, |
| 0, |
| &error) < 0) { |
| LOG(FATAL) << "Failed to register name \"" |
| << kRootPowerManagerServiceName << "\": " |
| << (dbus_error_is_set(&error) ? error.message : "Unknown error"); |
| } |
| |
| AddDBusSignalHandler(kRootPowerManagerInterface, kCheckLidStateSignal, |
| &PowerManDaemon::HandleCheckLidStateSignal); |
| AddDBusSignalHandler(kRootPowerManagerInterface, kSuspendSignal, |
| &PowerManDaemon::HandleSuspendSignal); |
| AddDBusSignalHandler(kRootPowerManagerInterface, kShutdownSignal, |
| &PowerManDaemon::HandleShutdownSignal); |
| AddDBusSignalHandler(kRootPowerManagerInterface, kRestartSignal, |
| &PowerManDaemon::HandleRestartSignal); |
| AddDBusSignalHandler(kRootPowerManagerInterface, kRequestCleanShutdown, |
| &PowerManDaemon::HandleRequestCleanShutdownSignal); |
| AddDBusSignalHandler(kPowerManagerInterface, kPowerStateChanged, |
| &PowerManDaemon::HandlePowerStateChangedSignal); |
| AddDBusSignalHandler(login_manager::kSessionManagerInterface, |
| login_manager::kSessionManagerSessionStateChanged, |
| &PowerManDaemon::HandleSessionManagerStateChangedSignal); |
| CHECK(dbus_connection_add_filter( |
| connection, &MainDBusSignalHandler, this, NULL)); |
| |
| AddDBusMethodHandler(kRootPowerManagerInterface, kExternalBacklightGetMethod, |
| &PowerManDaemon::HandleExternalBacklightGetMethod); |
| AddDBusMethodHandler(kRootPowerManagerInterface, kExternalBacklightSetMethod, |
| &PowerManDaemon::HandleExternalBacklightSetMethod); |
| AddDBusMethodHandler(kRootPowerManagerInterface, kDisableTouchDevicesMethod, |
| &PowerManDaemon::HandleDisableTouchDevicesMethod); |
| AddDBusMethodHandler(kRootPowerManagerInterface, kEnableTouchDevicesMethod, |
| &PowerManDaemon::HandleEnableTouchDevicesMethod); |
| |
| DBusObjectPathVTable vtable; |
| memset(&vtable, 0, sizeof(vtable)); |
| vtable.message_function = &MainDBusMethodHandler; |
| CHECK(dbus_connection_register_object_path(connection, |
| kPowerManagerServicePath, |
| &vtable, |
| this)); |
| |
| DBusGProxy* proxy = dbus_g_proxy_new_for_name( |
| chromeos::dbus::GetSystemBusConnection().g_connection(), |
| DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); |
| |
| if (NULL == proxy) { |
| LOG(ERROR) << "Failed to connect to freedesktop dbus server."; |
| NOTREACHED(); |
| } |
| dbus_g_proxy_add_signal(proxy, "NameOwnerChanged", G_TYPE_STRING, |
| G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); |
| dbus_g_proxy_connect_signal(proxy, "NameOwnerChanged", |
| G_CALLBACK(DBusNameOwnerChangedHandler), |
| this, NULL); |
| LOG(INFO) << "DBus monitoring started"; |
| } |
| |
| void PowerManDaemon::SendButtonEventSignal(const std::string& button_name, |
| ButtonState state) { |
| if (state == BUTTON_REPEAT) |
| return; |
| LOG(INFO) << "Sending button event signal: " << button_name << " is " |
| << (state == BUTTON_UP ? "up" : "down"); |
| |
| // This signal is used by both Chrome and powerd. |
| // Both must be updated if it is changed. |
| const gchar* button_name_param = button_name.c_str(); |
| dbus_bool_t down_param = (state == BUTTON_DOWN) ? TRUE : FALSE; |
| dbus_int64_t timestamp_param = base::TimeTicks::Now().ToInternalValue(); |
| chromeos::dbus::Proxy proxy(chromeos::dbus::GetSystemBusConnection(), |
| kPowerManagerServicePath, |
| kPowerManagerInterface); |
| DBusMessage* signal = dbus_message_new_signal(kPowerManagerServicePath, |
| kPowerManagerInterface, |
| kButtonEventSignal); |
| CHECK(signal); |
| dbus_message_append_args(signal, |
| DBUS_TYPE_STRING, &button_name_param, |
| DBUS_TYPE_BOOLEAN, &down_param, |
| DBUS_TYPE_INT64, ×tamp_param, |
| DBUS_TYPE_INVALID); |
| dbus_g_proxy_send(proxy.gproxy(), signal, NULL); |
| dbus_message_unref(signal); |
| } |
| |
| void PowerManDaemon::Shutdown() { |
| util::Launch("shutdown -P now"); |
| } |
| |
| void PowerManDaemon::Restart() { |
| util::Launch("shutdown -r now"); |
| } |
| |
| void PowerManDaemon::Suspend(unsigned int wakeup_count, |
| bool wakeup_count_valid) { |
| LOG(INFO) << "Launching Suspend"; |
| if ((suspend_pid_ > 0) && !kill(-suspend_pid_, 0)) { |
| LOG(ERROR) << "Previous retry suspend pid:" |
| << suspend_pid_ << " is still running"; |
| } |
| g_timeout_add_seconds(retry_suspend_ms_ / 1000, RetrySuspendThunk, |
| CreateRetrySuspendArgs(this, lid_id_)); |
| |
| // create command line |
| char suspend_command[60]; |
| if (wakeup_count_valid && snprintf(suspend_command, sizeof(suspend_command), |
| "powerd_suspend --wakeup_count %d", |
| wakeup_count) == sizeof(suspend_command)) { |
| LOG(ERROR) << "Command line exceeded size limit: " |
| << sizeof(suspend_command); |
| // We shouldn't ever exceed 60 chars (leaves 40 chars for the int) |
| // If we do, exit out and let the retry handle it |
| return; |
| } |
| |
| #ifdef SUSPEND_LOCK_VT |
| LockVTSwitch(); // Do not let suspend change the console terminal. |
| #endif |
| |
| // Remove lid opened flag, so suspend will occur providing the lid isn't |
| // re-opened prior to completing powerd_suspend |
| util::RemoveStatusFile(lid_open_file_); |
| // Detach to allow suspend to be retried and metrics gathered |
| pid_t pid = fork(); |
| if (pid == 0) { |
| setsid(); |
| if (fork() == 0) { |
| wait(NULL); |
| if (CancelDBusRequest()) { |
| exit(system("powerd_suspend --cancel")); |
| } else if (wakeup_count_valid) { |
| exit(system(suspend_command)); |
| } else { |
| exit(system("powerd_suspend")); |
| } |
| } else { |
| exit(0); |
| } |
| } else if (pid > 0) { |
| suspend_pid_ = pid; |
| waitpid(pid, NULL, 0); |
| } else { |
| LOG(ERROR) << "Fork for suspend failed"; |
| } |
| } |
| |
| void PowerManDaemon::Suspend() { |
| Suspend(0, false); |
| } |
| |
| void PowerManDaemon::Suspend(unsigned int wakeup_count) { |
| Suspend(wakeup_count, true); |
| } |
| |
| void PowerManDaemon::Suspend(DBusMessage* message) { |
| unsigned int wakeup_count; |
| DBusError error; |
| dbus_error_init(&error); |
| if (!dbus_message_get_args(message, &error, |
| DBUS_TYPE_UINT32, &wakeup_count, |
| DBUS_TYPE_INVALID)) { |
| LOG(ERROR) << "Suspend message missing wakeup_count: " |
| << error.name << " (" << error.message << ")"; |
| dbus_error_free(&error); |
| Suspend(); |
| } else { |
| Suspend(wakeup_count); |
| } |
| } |
| |
| void PowerManDaemon::LockVTSwitch() { |
| CHECK_GE(console_fd_, 0); |
| if (ioctl(console_fd_, VT_LOCKSWITCH)) |
| LOG(ERROR) << "Error in ioctl(VT_LOCKSWITCH): " << errno; |
| else |
| LOG(INFO) << "Invoked ioctl(VT_LOCKSWITCH)"; |
| } |
| |
| void PowerManDaemon::UnlockVTSwitch() { |
| CHECK_GE(console_fd_, 0); |
| if (ioctl(console_fd_, VT_UNLOCKSWITCH)) |
| LOG(ERROR) << "Error in ioctl(VT_UNLOCKSWITCH): " << errno; |
| else |
| LOG(INFO) << "Invoked ioctl(VT_UNLOCKSWITCH)"; |
| } |
| |
| void PowerManDaemon::DisableTouchDevices() { |
| #ifdef TOUCH_DEVICE |
| util::Run("/opt/google/touch/touch-control.sh --disable"); |
| #endif // TOUCH_DEVICE |
| } |
| |
| void PowerManDaemon::EnableTouchDevices(bool display_on) { |
| #ifdef TOUCH_DEVICE |
| if (lidstate_ == LID_STATE_CLOSED) { |
| // Do not allow the touchscreen to be enabled when the lid is closed. |
| return; |
| } |
| if (display_on) |
| util::Launch("/opt/google/touch/touch-control.sh --enable --display=on"); |
| else |
| util::Launch("/opt/google/touch/touch-control.sh --enable --display=off"); |
| #endif // TOUCH_DEVICE |
| } |
| |
| bool PowerManDaemon::GetConsole() { |
| bool result = true; |
| FilePath file_path("/dev/tty0"); |
| if ((console_fd_ = open(file_path.value().c_str(), O_RDWR)) < 0) { |
| LOG(ERROR) << "Failed to open " << file_path.value().c_str() |
| << ", errno = " << errno; |
| result = false; |
| } else { |
| LOG(INFO) << "Opened console " << file_path.value().c_str() |
| << " with file id = " << console_fd_; |
| } |
| return result; |
| } |
| |
| } // namespace power_manager |