// Copyright (c) 2012 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 "chromeos/network/network_sms_handler.h" #include #include #include #include #include "base/bind.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/shill_device_client.h" #include "chromeos/dbus/shill_manager_client.h" #include "chromeos/dbus/gsm_sms_client.h" #include "chromeos/dbus/modem_messaging_client.h" #include "chromeos/dbus/sms_client.h" #include "dbus/object_path.h" #include "third_party/cros_system_api/dbus/service_constants.h" // Not exposed/exported. namespace { const char kSmscKey[] = "smsc"; const char kValidityKey[] = "validity"; const char kClassKey[] = "class"; const char kIndexKey[] = "index"; // Keys from ModemManager1 const char kModemManager1NumberKey[] = "Number"; const char kModemManager1TextKey[] = "Text"; const char kModemManager1TimestampKey[] = "Timestamp"; } // namespace namespace chromeos { // static const char NetworkSmsHandler::kNumberKey[] = "number"; const char NetworkSmsHandler::kTextKey[] = "text"; const char NetworkSmsHandler::kTimestampKey[] = "timestamp"; class NetworkSmsHandler::NetworkSmsDeviceHandler { public: NetworkSmsDeviceHandler() {} virtual ~NetworkSmsDeviceHandler() {} virtual void RequestUpdate() = 0; }; class NetworkSmsHandler::ModemManagerNetworkSmsDeviceHandler : public NetworkSmsHandler::NetworkSmsDeviceHandler { public: ModemManagerNetworkSmsDeviceHandler(NetworkSmsHandler* host, std::string dbus_connection, dbus::ObjectPath object_path); virtual void RequestUpdate() OVERRIDE; private: void ListCallback(const base::ListValue& message_list); void SmsReceivedCallback(uint32 index, bool complete); void GetCallback(uint32 index, const base::DictionaryValue& dictionary); void DeleteMessages(); void NotifyMessageReceived(const base::DictionaryValue& dictionary); NetworkSmsHandler* host_; std::string dbus_connection_; dbus::ObjectPath object_path_; bool deleting_messages_; base::WeakPtrFactory weak_ptr_factory_; std::vector delete_queue_; DISALLOW_COPY_AND_ASSIGN(ModemManagerNetworkSmsDeviceHandler); }; NetworkSmsHandler:: ModemManagerNetworkSmsDeviceHandler::ModemManagerNetworkSmsDeviceHandler( NetworkSmsHandler* host, std::string dbus_connection, dbus::ObjectPath object_path) : host_(host), dbus_connection_(dbus_connection), object_path_(object_path), deleting_messages_(false), weak_ptr_factory_(this) { // Set the handler for received Sms messaages. DBusThreadManager::Get()->GetGsmSMSClient()->SetSmsReceivedHandler( dbus_connection_, object_path_, base::Bind(&ModemManagerNetworkSmsDeviceHandler::SmsReceivedCallback, weak_ptr_factory_.GetWeakPtr())); // List the existing messages. DBusThreadManager::Get()->GetGsmSMSClient()->List( dbus_connection_, object_path_, base::Bind(&NetworkSmsHandler:: ModemManagerNetworkSmsDeviceHandler::ListCallback, weak_ptr_factory_.GetWeakPtr())); } void NetworkSmsHandler::ModemManagerNetworkSmsDeviceHandler::RequestUpdate() { DBusThreadManager::Get()->GetGsmSMSClient()->RequestUpdate( dbus_connection_, object_path_); } void NetworkSmsHandler::ModemManagerNetworkSmsDeviceHandler::ListCallback( const base::ListValue& message_list) { // This receives all messages, so clear any pending deletes. delete_queue_.clear(); for (base::ListValue::const_iterator iter = message_list.begin(); iter != message_list.end(); ++iter) { base::DictionaryValue* message = NULL; if (!(*iter)->GetAsDictionary(&message)) continue; NotifyMessageReceived(*message); double index = 0; if (message->GetDoubleWithoutPathExpansion(kIndexKey, &index)) delete_queue_.push_back(static_cast(index)); } DeleteMessages(); } // Messages must be deleted one at a time, since we can not guarantee // the order the deletion will be executed in. Delete messages from // the back of the list so that the indices are valid. void NetworkSmsHandler::ModemManagerNetworkSmsDeviceHandler::DeleteMessages() { if (delete_queue_.empty()) { deleting_messages_ = false; return; } deleting_messages_ = true; uint32 index = delete_queue_.back(); delete_queue_.pop_back(); DBusThreadManager::Get()->GetGsmSMSClient()->Delete( dbus_connection_, object_path_, index, base::Bind(&NetworkSmsHandler:: ModemManagerNetworkSmsDeviceHandler::DeleteMessages, weak_ptr_factory_.GetWeakPtr())); } void NetworkSmsHandler:: ModemManagerNetworkSmsDeviceHandler::SmsReceivedCallback( uint32 index, bool complete) { // Only handle complete messages. if (!complete) return; DBusThreadManager::Get()->GetGsmSMSClient()->Get( dbus_connection_, object_path_, index, base::Bind(&NetworkSmsHandler:: ModemManagerNetworkSmsDeviceHandler::GetCallback, weak_ptr_factory_.GetWeakPtr(), index)); } void NetworkSmsHandler::ModemManagerNetworkSmsDeviceHandler::GetCallback( uint32 index, const base::DictionaryValue& dictionary) { NotifyMessageReceived(dictionary); delete_queue_.push_back(index); if (!deleting_messages_) DeleteMessages(); } void NetworkSmsHandler:: ModemManagerNetworkSmsDeviceHandler::NotifyMessageReceived( const base::DictionaryValue& dictionary) { // The keys of the ModemManager.Modem.Gsm.SMS interface match the // exported keys, so the dictionary used as a notification argument // unchanged. host_->NotifyMessageReceived(dictionary); } class NetworkSmsHandler::ModemManager1NetworkSmsDeviceHandler : public NetworkSmsHandler::NetworkSmsDeviceHandler { public: ModemManager1NetworkSmsDeviceHandler(NetworkSmsHandler* host, std::string dbus_connection, dbus::ObjectPath object_path); virtual void RequestUpdate() OVERRIDE; private: void ListCallback(const std::vector& paths); void SmsReceivedCallback(const dbus::ObjectPath& path, bool complete); void GetCallback(const base::DictionaryValue& dictionary); void DeleteMessages(); void GetMessages(); void NotifyMessageReceived(const base::DictionaryValue& dictionary); NetworkSmsHandler* host_; std::string dbus_connection_; dbus::ObjectPath object_path_; bool deleting_messages_; bool retrieving_messages_; base::WeakPtrFactory weak_ptr_factory_; std::vector delete_queue_; std::deque retrieval_queue_; DISALLOW_COPY_AND_ASSIGN(ModemManager1NetworkSmsDeviceHandler); }; NetworkSmsHandler:: ModemManager1NetworkSmsDeviceHandler::ModemManager1NetworkSmsDeviceHandler( NetworkSmsHandler* host, std::string dbus_connection, dbus::ObjectPath object_path) : host_(host), dbus_connection_(dbus_connection), object_path_(object_path), deleting_messages_(false), retrieving_messages_(false), weak_ptr_factory_(this) { // Set the handler for received Sms messaages. DBusThreadManager::Get()->GetModemMessagingClient()->SetSmsReceivedHandler( dbus_connection_, object_path_, base::Bind( &NetworkSmsHandler:: ModemManager1NetworkSmsDeviceHandler::SmsReceivedCallback, weak_ptr_factory_.GetWeakPtr())); // List the existing messages. DBusThreadManager::Get()->GetModemMessagingClient()->List( dbus_connection_, object_path_, base::Bind(&NetworkSmsHandler:: ModemManager1NetworkSmsDeviceHandler::ListCallback, weak_ptr_factory_.GetWeakPtr())); } void NetworkSmsHandler::ModemManager1NetworkSmsDeviceHandler::RequestUpdate() { // Calling List using the service "AddSMS" causes the stub // implementation to deliver new sms messages. DBusThreadManager::Get()->GetModemMessagingClient()->List( std::string("AddSMS"), dbus::ObjectPath("/"), base::Bind(&NetworkSmsHandler:: ModemManager1NetworkSmsDeviceHandler::ListCallback, weak_ptr_factory_.GetWeakPtr())); } void NetworkSmsHandler::ModemManager1NetworkSmsDeviceHandler::ListCallback( const std::vector& paths) { // This receives all messages, so clear any pending gets and deletes. retrieval_queue_.clear(); delete_queue_.clear(); retrieval_queue_.resize(paths.size()); std::copy(paths.begin(), paths.end(), retrieval_queue_.begin()); if (!retrieving_messages_) GetMessages(); } // Messages must be deleted one at a time, since we can not guarantee // the order the deletion will be executed in. Delete messages from // the back of the list so that the indices are valid. void NetworkSmsHandler::ModemManager1NetworkSmsDeviceHandler::DeleteMessages() { if (delete_queue_.empty()) { deleting_messages_ = false; return; } deleting_messages_ = true; dbus::ObjectPath sms_path = delete_queue_.back(); delete_queue_.pop_back(); DBusThreadManager::Get()->GetModemMessagingClient()->Delete( dbus_connection_, object_path_, sms_path, base::Bind(&NetworkSmsHandler:: ModemManager1NetworkSmsDeviceHandler::DeleteMessages, weak_ptr_factory_.GetWeakPtr())); } // Messages must be fetched one at a time, so that we do not queue too // many requests to a single threaded server. void NetworkSmsHandler::ModemManager1NetworkSmsDeviceHandler::GetMessages() { if (retrieval_queue_.empty()) { retrieving_messages_ = false; if (!deleting_messages_) DeleteMessages(); return; } retrieving_messages_ = true; dbus::ObjectPath sms_path = retrieval_queue_.front(); retrieval_queue_.pop_front(); DBusThreadManager::Get()->GetSMSClient()->GetAll( dbus_connection_, sms_path, base::Bind(&NetworkSmsHandler:: ModemManager1NetworkSmsDeviceHandler::GetCallback, weak_ptr_factory_.GetWeakPtr())); delete_queue_.push_back(sms_path); } void NetworkSmsHandler:: ModemManager1NetworkSmsDeviceHandler::SmsReceivedCallback( const dbus::ObjectPath& sms_path, bool complete) { // Only handle complete messages. if (!complete) return; retrieval_queue_.push_back(sms_path); if (!retrieving_messages_) GetMessages(); } void NetworkSmsHandler::ModemManager1NetworkSmsDeviceHandler::GetCallback( const base::DictionaryValue& dictionary) { NotifyMessageReceived(dictionary); GetMessages(); } void NetworkSmsHandler:: ModemManager1NetworkSmsDeviceHandler::NotifyMessageReceived( const base::DictionaryValue& dictionary) { // The keys of the ModemManager1.SMS interface do not match the // exported keys, so a new dictionary is created with the expected // key namaes. base::DictionaryValue new_dictionary; std::string text, number, timestamp; if (dictionary.GetStringWithoutPathExpansion(kModemManager1NumberKey, &number)) new_dictionary.SetString(kNumberKey, number); if (dictionary.GetStringWithoutPathExpansion(kModemManager1TextKey, &text)) new_dictionary.SetString(kTextKey, text); // TODO(jglasgow): consider normalizing timestamp. if (dictionary.GetStringWithoutPathExpansion(kModemManager1TimestampKey, ×tamp)) new_dictionary.SetString(kTimestampKey, timestamp); host_->NotifyMessageReceived(new_dictionary); } /////////////////////////////////////////////////////////////////////////////// // NetworkSmsHandler NetworkSmsHandler::NetworkSmsHandler() : weak_ptr_factory_(this) { } NetworkSmsHandler::~NetworkSmsHandler() { } void NetworkSmsHandler::Init() { // TODO(stevenjb): This code needs to monitor changes to Manager.Network // so that devices added after Init() is called get added to device_handlers_. // See: crbug.com/133416. // Request network manager properties so that we can get the list of devices. DBusThreadManager::Get()->GetShillManagerClient()->GetProperties( base::Bind(&NetworkSmsHandler::ManagerPropertiesCallback, weak_ptr_factory_.GetWeakPtr())); } void NetworkSmsHandler::RequestUpdate() { for (ScopedVector::iterator iter = device_handlers_.begin(); iter != device_handlers_.end(); ++iter) { (*iter)->RequestUpdate(); } } void NetworkSmsHandler::AddObserver(Observer* observer) { observers_.AddObserver(observer); } void NetworkSmsHandler::RemoveObserver(Observer* observer) { observers_.RemoveObserver(observer); } void NetworkSmsHandler::NotifyMessageReceived( const base::DictionaryValue& message) { FOR_EACH_OBSERVER(Observer, observers_, MessageReceived(message)); } void NetworkSmsHandler::ManagerPropertiesCallback( DBusMethodCallStatus call_status, const base::DictionaryValue& properties) { if (call_status != DBUS_METHOD_CALL_SUCCESS) { LOG(ERROR) << "NetworkSmsHandler: Failed to get manager properties."; return; } const base::Value* value; if (!properties.GetWithoutPathExpansion(flimflam::kDevicesProperty, &value) || value->GetType() != base::Value::TYPE_LIST) { LOG(ERROR) << "NetworkSmsHandler: No list value for: " << flimflam::kDevicesProperty; return; } const base::ListValue* devices = static_cast(value); for (base::ListValue::const_iterator iter = devices->begin(); iter != devices->end(); ++iter) { std::string device_path; (*iter)->GetAsString(&device_path); if (!device_path.empty()) { // Request device properties. VLOG(1) << "GetDeviceProperties: " << device_path; DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties( dbus::ObjectPath(device_path), base::Bind(&NetworkSmsHandler::DevicePropertiesCallback, weak_ptr_factory_.GetWeakPtr(), device_path)); } } } void NetworkSmsHandler::DevicePropertiesCallback( const std::string& device_path, DBusMethodCallStatus call_status, const base::DictionaryValue& properties) { if (call_status != DBUS_METHOD_CALL_SUCCESS) { LOG(ERROR) << "NetworkSmsHandler: ERROR: " << call_status << " For: " << device_path; return; } std::string device_type; if (!properties.GetStringWithoutPathExpansion( flimflam::kTypeProperty, &device_type)) { LOG(ERROR) << "NetworkSmsHandler: No type for: " << device_path; return; } if (device_type != flimflam::kTypeCellular) return; std::string dbus_connection; if (!properties.GetStringWithoutPathExpansion( flimflam::kDBusConnectionProperty, &dbus_connection)) { LOG(ERROR) << "Device has no DBusConnection Property: " << device_path; return; } std::string object_path_string; if (!properties.GetStringWithoutPathExpansion( flimflam::kDBusObjectProperty, &object_path_string)) { LOG(ERROR) << "Device has no DBusObject Property: " << device_path; return; } dbus::ObjectPath object_path(object_path_string); if (object_path_string.compare( 0, sizeof(modemmanager::kModemManager1ServicePath) - 1, modemmanager::kModemManager1ServicePath) == 0) { device_handlers_.push_back( new ModemManager1NetworkSmsDeviceHandler( this, dbus_connection, object_path)); } else { device_handlers_.push_back( new ModemManagerNetworkSmsDeviceHandler( this, dbus_connection, object_path)); } } } // namespace chromeos