| // Copyright (c) 2010 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 "cromo_server.h" |
| |
| #include <chromeos/dbus/service_constants.h> |
| |
| #include <dbus-c++/glib-integration.h> |
| #include <dbus/dbus.h> |
| #include <glog/logging.h> |
| #include <mm/mm-modem.h> |
| |
| #include "carrier.h" |
| #include "modem_handler.h" |
| #include "plugin_manager.h" |
| |
| using std::vector; |
| |
| const char* CromoServer::kServiceName = "org.chromium.ModemManager"; |
| const char* CromoServer::kServicePath = "/org/chromium/ModemManager"; |
| |
| static const char* kDBusInterface = "org.freedesktop.DBus"; |
| static const char* kDBusPath = "/org/freedesktop/DBus"; |
| static const char* kDBusListNames = "ListNames"; |
| |
| // Returns the current time, in milliseconds, from an unspecified epoch. |
| // TODO(ellyjones): duplicated in some plugins. Figure out how to refactor that. |
| static unsigned long long time_ms(void) { |
| struct timespec ts; |
| unsigned long long rv; |
| |
| clock_gettime(CLOCK_MONOTONIC, &ts); |
| rv = ts.tv_sec; |
| rv *= 1000; |
| rv += ts.tv_nsec / 1000000; |
| return rv; |
| } |
| |
| CromoServer::CromoServer(DBus::Connection& connection) |
| : DBus::ObjectAdaptor(connection, kServicePath), |
| conn_(connection), |
| powerd_up_(false), |
| max_suspend_delay_(0), |
| metrics_lib_(new MetricsLibrary()) { |
| metrics_lib_->Init(); |
| } |
| |
| CromoServer::~CromoServer() { |
| for (ModemHandlers::iterator it = modem_handlers_.begin(); |
| it != modem_handlers_.end(); ++it) { |
| delete *it; |
| } |
| modem_handlers_.clear(); |
| |
| for (CarrierMap::iterator it = carriers_.begin(); |
| it != carriers_.end(); ++it) { |
| delete it->second; |
| } |
| carriers_.clear(); |
| } |
| |
| vector<DBus::Path> CromoServer::EnumerateDevices(DBus::Error& error) { |
| vector<DBus::Path> allpaths; |
| |
| for (vector<ModemHandler*>::iterator it = modem_handlers_.begin(); |
| it != modem_handlers_.end(); it++) { |
| vector<DBus::Path> paths = (*it)->EnumerateDevices(error); |
| allpaths.insert(allpaths.end(), paths.begin(), paths.end()); |
| } |
| return allpaths; |
| } |
| |
| void CromoServer::AddModemHandler(ModemHandler* handler) { |
| LOG(INFO) << "AddModemHandler(" << handler->vendor_tag() << ")"; |
| modem_handlers_.push_back(handler); |
| } |
| |
| void CromoServer::PowerDaemonUp() { |
| LOG(INFO) << "Power daemon: up"; |
| if (!powerd_up_) { |
| powerd_up_ = true; |
| RegisterSuspendDelay(); |
| } |
| } |
| |
| void CromoServer::PowerDaemonDown() { |
| LOG(INFO) << "Power daemon: down"; |
| if (powerd_up_) { |
| powerd_up_ = false; |
| } |
| } |
| |
| void CromoServer::CheckForPowerDaemon() { |
| DBus::CallMessage msg; |
| DBus::MessageIter iter; |
| |
| LOG(INFO) << "Checking for power daemon..."; |
| msg.destination(kDBusInterface); |
| msg.interface(kDBusInterface); |
| msg.member(kDBusListNames); |
| msg.path(kDBusPath); |
| // In RegisterSuspendDelayCallback, we catch any exceptions send_blocking() |
| // throws, since that is how it announces that the target of your message is |
| // gone. Here the target is the dbus bus itself - if this fails, we're |
| // completely hosed and should just abort, which the exception will do for us. |
| DBus::Message ret = conn_.send_blocking(msg, -1); |
| iter = ret.reader(); |
| iter = iter.recurse(); |
| while (!iter.at_end()) { |
| if (!powerd_up_ && |
| strcmp(iter.get_string(), power_manager::kPowerManagerInterface) == 0) { |
| PowerDaemonUp(); |
| } |
| iter++; |
| } |
| } |
| |
| void CromoServer::SuspendReady() { |
| unsigned long duration = time_ms() - suspend_start_time_; |
| metrics_lib_->SendToUMA("Network.3G.SuspendTime", duration, 0, 10000, 20); |
| DBus::SignalMessage msg("/", power_manager::kPowerManagerInterface, |
| power_manager::kSuspendReady); |
| LOG(INFO) << "SuspendReady: " << suspend_nonce_; |
| msg.destination(power_manager::kPowerManagerInterface); |
| msg.writer().append_uint32(suspend_nonce_); |
| conn_.send(msg, NULL); |
| } |
| |
| bool CromoServer::CheckSuspendReady() { |
| bool okay_to_suspend = suspend_ok_hooks_.Run(); |
| if (okay_to_suspend) { |
| SuspendReady(); |
| } |
| // We return whether we need to run again. We only run again if the suspend |
| // wasn't ready. |
| return okay_to_suspend; |
| } |
| |
| gboolean test_for_suspend(void *arg) { |
| CromoServer *srv = static_cast<CromoServer*>(arg); |
| return !srv->CheckSuspendReady(); |
| } |
| |
| void CromoServer::SuspendDelay(unsigned int nonce) { |
| LOG(INFO) << "SuspendDelay: " << nonce; |
| suspend_nonce_ = nonce; |
| suspend_start_time_ = time_ms(); |
| start_suspend_hooks_.Run(); |
| if (CheckSuspendReady()) { |
| return; |
| } |
| g_timeout_add_seconds(1, test_for_suspend, static_cast<void*>(this)); |
| } |
| |
| void CromoServer::RegisterStartSuspend(const std::string& name, |
| bool (*func)(void *), void *arg, |
| unsigned int maxdelay) { |
| suspend_delays_[name] = maxdelay; |
| if (max_suspend_delay_ < maxdelay) |
| max_suspend_delay_ = maxdelay; |
| start_suspend_hooks_.Add(name, func, arg); |
| if (powerd_up_) |
| RegisterSuspendDelay(); |
| } |
| |
| gboolean CromoServer::RegisterSuspendDelayCallback(gpointer arg) { |
| CromoServer* srv = static_cast<CromoServer*>(arg); |
| DBus::CallMessage* call = new DBus::CallMessage(); |
| call->destination(power_manager::kPowerManagerInterface); |
| call->interface(power_manager::kPowerManagerInterface); |
| call->path("/"); |
| call->member(power_manager::kRegisterSuspendDelay); |
| call->append(DBUS_TYPE_UINT32, &srv->max_suspend_delay_, DBUS_TYPE_INVALID); |
| call->terminate(); |
| // dbus-c++ rudely throws an exception here if the target of the call is gone. |
| // It doesn't cause problems for us that powerd is gone, but we have to |
| // violate the style guide a bit and catch the exception or it'll abort() the |
| // program. |
| try { |
| DBus::Message reply = srv->conn_.send_blocking(*call, -1); |
| if (reply.is_error()) |
| LOG(WARNING) << "Can't register for suspend delay: " |
| << srv->max_suspend_delay_; |
| else |
| LOG(INFO) << "Registered for suspend delay: " << srv->max_suspend_delay_; |
| return FALSE; |
| } catch (DBus::Error e) { |
| LOG(ERROR) << "dbus error " << e; |
| return FALSE; |
| } |
| } |
| |
| void CromoServer::RegisterSuspendDelay() { |
| g_idle_add(RegisterSuspendDelayCallback, this); |
| } |
| |
| void CromoServer::UnregisterStartSuspend(const std::string& name) { |
| suspend_delays_.erase(name); |
| start_suspend_hooks_.Del(name); |
| max_suspend_delay_ = MaxSuspendDelay(); |
| RegisterSuspendDelay(); |
| } |
| |
| unsigned int CromoServer::MaxSuspendDelay() { |
| SuspendDelayMap::iterator it; |
| unsigned int max = 0; |
| for (it = suspend_delays_.begin(); it != suspend_delays_.end(); it++) { |
| if (it->second > max) |
| max = it->second; |
| } |
| return max; |
| } |
| |
| void CromoServer::AddCarrier(Carrier *carrier) { |
| delete carriers_[carrier->name()]; |
| carriers_[carrier->name()] = carrier; |
| } |
| |
| Carrier* CromoServer::FindCarrierByName(const std::string &name) { |
| return carriers_[name]; |
| } |
| |
| Carrier* CromoServer::FindCarrierByCarrierId(unsigned long id) { |
| for (CarrierMap::iterator i = carriers_.begin(); |
| i != carriers_.end(); |
| ++i) { |
| if (i->second && |
| i->second->carrier_id() == id) { |
| return i->second; |
| } |
| } |
| return NULL; |
| } |
| |
| Carrier* CromoServer::FindCarrierNoOp() { |
| if (carrier_no_op_.get() == NULL) { |
| carrier_no_op_.reset(new Carrier( |
| "no_op_name", "invalid", -1, MM_MODEM_TYPE_GSM, Carrier::kNone, NULL)); |
| } |
| return carrier_no_op_.get(); |
| } |