// 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 "base/values.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" namespace { // Not exposed/exported: const char kIndexKey[] = "index"; // Keys from ModemManager1 const char kModemManager1NumberKey[] = "Number"; const char kModemManager1TextKey[] = "Text"; const char kModemManager1TimestampKey[] = "Timestamp"; // Maximum number of messages stored for RequestUpdate(true). const size_t kMaxReceivedMessages = 100; } // 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, const std::string& service_name, const 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 MessageReceived(const base::DictionaryValue& dictionary); NetworkSmsHandler* host_; std::string service_name_; 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, const std::string& service_name, const dbus::ObjectPath& object_path) : host_(host), service_name_(service_name), object_path_(object_path), deleting_messages_(false), weak_ptr_factory_(this) { // Set the handler for received Sms messaages. DBusThreadManager::Get()->GetGsmSMSClient()->SetSmsReceivedHandler( service_name_, object_path_, base::Bind(&ModemManagerNetworkSmsDeviceHandler::SmsReceivedCallback, weak_ptr_factory_.GetWeakPtr())); // List the existing messages. DBusThreadManager::Get()->GetGsmSMSClient()->List( service_name_, object_path_, base::Bind(&NetworkSmsHandler:: ModemManagerNetworkSmsDeviceHandler::ListCallback, weak_ptr_factory_.GetWeakPtr())); } void NetworkSmsHandler::ModemManagerNetworkSmsDeviceHandler::RequestUpdate() { DBusThreadManager::Get()->GetGsmSMSClient()->RequestUpdate( service_name_, 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; MessageReceived(*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( service_name_, 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( service_name_, object_path_, index, base::Bind(&NetworkSmsHandler:: ModemManagerNetworkSmsDeviceHandler::GetCallback, weak_ptr_factory_.GetWeakPtr(), index)); } void NetworkSmsHandler::ModemManagerNetworkSmsDeviceHandler::GetCallback( uint32 index, const base::DictionaryValue& dictionary) { MessageReceived(dictionary); delete_queue_.push_back(index); if (!deleting_messages_) DeleteMessages(); } void NetworkSmsHandler:: ModemManagerNetworkSmsDeviceHandler::MessageReceived( 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_->MessageReceived(dictionary); } class NetworkSmsHandler::ModemManager1NetworkSmsDeviceHandler : public NetworkSmsHandler::NetworkSmsDeviceHandler { public: ModemManager1NetworkSmsDeviceHandler(NetworkSmsHandler* host, const std::string& service_name, const 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 MessageReceived(const base::DictionaryValue& dictionary); NetworkSmsHandler* host_; std::string service_name_; 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, const std::string& service_name, const dbus::ObjectPath& object_path) : host_(host), service_name_(service_name), 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( service_name_, object_path_, base::Bind( &NetworkSmsHandler:: ModemManager1NetworkSmsDeviceHandler::SmsReceivedCallback, weak_ptr_factory_.GetWeakPtr())); // List the existing messages. DBusThreadManager::Get()->GetModemMessagingClient()->List( service_name_, 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( service_name_, 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( service_name_, 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) { MessageReceived(dictionary); GetMessages(); } void NetworkSmsHandler:: ModemManager1NetworkSmsDeviceHandler::MessageReceived( 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_->MessageReceived(new_dictionary); } /////////////////////////////////////////////////////////////////////////////// // NetworkSmsHandler NetworkSmsHandler::NetworkSmsHandler() : weak_ptr_factory_(this) { } NetworkSmsHandler::~NetworkSmsHandler() { DBusThreadManager::Get()->GetShillManagerClient()-> RemovePropertyChangedObserver(this); } void NetworkSmsHandler::Init() { // Add as an observer here so that new devices added after this call are // recognized. DBusThreadManager::Get()->GetShillManagerClient()->AddPropertyChangedObserver( this); // 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(bool request_existing) { // If we already received messages and |request_existing| is true, send // updates for existing messages. for (ScopedVector::iterator iter = received_messages_.begin(); iter != received_messages_.end(); ++iter) { base::DictionaryValue* message = *iter; NotifyMessageReceived(*message); } // Request updates from each device. 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::OnPropertyChanged(const std::string& name, const base::Value& value) { if (name != shill::kDevicesProperty) return; const base::ListValue* devices = NULL; if (!value.GetAsList(&devices) || !devices) return; UpdateDevices(devices); } // Private methods void NetworkSmsHandler::AddReceivedMessage( const base::DictionaryValue& message) { base::DictionaryValue* new_message = message.DeepCopy(); if (received_messages_.size() >= kMaxReceivedMessages) received_messages_.erase(received_messages_.begin()); received_messages_.push_back(new_message); } void NetworkSmsHandler::NotifyMessageReceived( const base::DictionaryValue& message) { FOR_EACH_OBSERVER(Observer, observers_, MessageReceived(message)); } void NetworkSmsHandler::MessageReceived(const base::DictionaryValue& message) { AddReceivedMessage(message); NotifyMessageReceived(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(shill::kDevicesProperty, &value) || value->GetType() != base::Value::TYPE_LIST) { LOG(ERROR) << "NetworkSmsHandler: No list value for: " << shill::kDevicesProperty; return; } const base::ListValue* devices = static_cast(value); UpdateDevices(devices); } void NetworkSmsHandler::UpdateDevices(const base::ListValue* devices) { 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( shill::kTypeProperty, &device_type)) { LOG(ERROR) << "NetworkSmsHandler: No type for: " << device_path; return; } if (device_type != shill::kTypeCellular) return; std::string service_name; if (!properties.GetStringWithoutPathExpansion( shill::kDBusServiceProperty, &service_name)) { LOG(ERROR) << "Device has no DBusService Property: " << device_path; return; } std::string object_path_string; if (!properties.GetStringWithoutPathExpansion( shill::kDBusObjectProperty, &object_path_string)) { LOG(ERROR) << "Device has no DBusObject Property: " << device_path; return; } dbus::ObjectPath object_path(object_path_string); if (service_name == modemmanager::kModemManager1ServiceName) { device_handlers_.push_back( new ModemManager1NetworkSmsDeviceHandler( this, service_name, object_path)); } else { device_handlers_.push_back( new ModemManagerNetworkSmsDeviceHandler( this, service_name, object_path)); } } } // namespace chromeos