1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chromeos/network/network_sms_handler.h" 6 7#include <algorithm> 8#include <deque> 9#include <string> 10#include <vector> 11 12#include "base/bind.h" 13#include "base/values.h" 14#include "chromeos/dbus/dbus_thread_manager.h" 15#include "chromeos/dbus/shill_device_client.h" 16#include "chromeos/dbus/shill_manager_client.h" 17#include "chromeos/dbus/gsm_sms_client.h" 18#include "chromeos/dbus/modem_messaging_client.h" 19#include "chromeos/dbus/sms_client.h" 20#include "dbus/object_path.h" 21#include "third_party/cros_system_api/dbus/service_constants.h" 22 23namespace { 24 25// Not exposed/exported: 26const char kSmscKey[] = "smsc"; 27const char kValidityKey[] = "validity"; 28const char kClassKey[] = "class"; 29const char kIndexKey[] = "index"; 30 31// Keys from ModemManager1 32const char kModemManager1NumberKey[] = "Number"; 33const char kModemManager1TextKey[] = "Text"; 34const char kModemManager1TimestampKey[] = "Timestamp"; 35 36// Maximum number of messages stored for RequestUpdate(true). 37const size_t kMaxReceivedMessages = 100; 38 39} // namespace 40 41namespace chromeos { 42 43// static 44const char NetworkSmsHandler::kNumberKey[] = "number"; 45const char NetworkSmsHandler::kTextKey[] = "text"; 46const char NetworkSmsHandler::kTimestampKey[] = "timestamp"; 47 48class NetworkSmsHandler::NetworkSmsDeviceHandler { 49 public: 50 NetworkSmsDeviceHandler() {} 51 virtual ~NetworkSmsDeviceHandler() {} 52 53 virtual void RequestUpdate() = 0; 54}; 55 56class NetworkSmsHandler::ModemManagerNetworkSmsDeviceHandler 57 : public NetworkSmsHandler::NetworkSmsDeviceHandler { 58 public: 59 ModemManagerNetworkSmsDeviceHandler(NetworkSmsHandler* host, 60 std::string dbus_connection, 61 dbus::ObjectPath object_path); 62 63 virtual void RequestUpdate() OVERRIDE; 64 65 private: 66 void ListCallback(const base::ListValue& message_list); 67 void SmsReceivedCallback(uint32 index, bool complete); 68 void GetCallback(uint32 index, const base::DictionaryValue& dictionary); 69 void DeleteMessages(); 70 void MessageReceived(const base::DictionaryValue& dictionary); 71 72 NetworkSmsHandler* host_; 73 std::string dbus_connection_; 74 dbus::ObjectPath object_path_; 75 bool deleting_messages_; 76 base::WeakPtrFactory<ModemManagerNetworkSmsDeviceHandler> weak_ptr_factory_; 77 std::vector<uint32> delete_queue_; 78 79 DISALLOW_COPY_AND_ASSIGN(ModemManagerNetworkSmsDeviceHandler); 80}; 81 82NetworkSmsHandler:: 83ModemManagerNetworkSmsDeviceHandler::ModemManagerNetworkSmsDeviceHandler( 84 NetworkSmsHandler* host, 85 std::string dbus_connection, 86 dbus::ObjectPath object_path) 87 : host_(host), 88 dbus_connection_(dbus_connection), 89 object_path_(object_path), 90 deleting_messages_(false), 91 weak_ptr_factory_(this) { 92 // Set the handler for received Sms messaages. 93 DBusThreadManager::Get()->GetGsmSMSClient()->SetSmsReceivedHandler( 94 dbus_connection_, object_path_, 95 base::Bind(&ModemManagerNetworkSmsDeviceHandler::SmsReceivedCallback, 96 weak_ptr_factory_.GetWeakPtr())); 97 98 // List the existing messages. 99 DBusThreadManager::Get()->GetGsmSMSClient()->List( 100 dbus_connection_, object_path_, 101 base::Bind(&NetworkSmsHandler:: 102 ModemManagerNetworkSmsDeviceHandler::ListCallback, 103 weak_ptr_factory_.GetWeakPtr())); 104} 105 106void NetworkSmsHandler::ModemManagerNetworkSmsDeviceHandler::RequestUpdate() { 107 DBusThreadManager::Get()->GetGsmSMSClient()->RequestUpdate( 108 dbus_connection_, object_path_); 109} 110 111void NetworkSmsHandler::ModemManagerNetworkSmsDeviceHandler::ListCallback( 112 const base::ListValue& message_list) { 113 // This receives all messages, so clear any pending deletes. 114 delete_queue_.clear(); 115 for (base::ListValue::const_iterator iter = message_list.begin(); 116 iter != message_list.end(); ++iter) { 117 base::DictionaryValue* message = NULL; 118 if (!(*iter)->GetAsDictionary(&message)) 119 continue; 120 MessageReceived(*message); 121 double index = 0; 122 if (message->GetDoubleWithoutPathExpansion(kIndexKey, &index)) 123 delete_queue_.push_back(static_cast<uint32>(index)); 124 } 125 DeleteMessages(); 126} 127 128// Messages must be deleted one at a time, since we can not guarantee 129// the order the deletion will be executed in. Delete messages from 130// the back of the list so that the indices are valid. 131void NetworkSmsHandler::ModemManagerNetworkSmsDeviceHandler::DeleteMessages() { 132 if (delete_queue_.empty()) { 133 deleting_messages_ = false; 134 return; 135 } 136 deleting_messages_ = true; 137 uint32 index = delete_queue_.back(); 138 delete_queue_.pop_back(); 139 DBusThreadManager::Get()->GetGsmSMSClient()->Delete( 140 dbus_connection_, object_path_, index, 141 base::Bind(&NetworkSmsHandler:: 142 ModemManagerNetworkSmsDeviceHandler::DeleteMessages, 143 weak_ptr_factory_.GetWeakPtr())); 144} 145 146void NetworkSmsHandler:: 147ModemManagerNetworkSmsDeviceHandler::SmsReceivedCallback( 148 uint32 index, 149 bool complete) { 150 // Only handle complete messages. 151 if (!complete) 152 return; 153 DBusThreadManager::Get()->GetGsmSMSClient()->Get( 154 dbus_connection_, object_path_, index, 155 base::Bind(&NetworkSmsHandler:: 156 ModemManagerNetworkSmsDeviceHandler::GetCallback, 157 weak_ptr_factory_.GetWeakPtr(), index)); 158} 159 160void NetworkSmsHandler::ModemManagerNetworkSmsDeviceHandler::GetCallback( 161 uint32 index, 162 const base::DictionaryValue& dictionary) { 163 MessageReceived(dictionary); 164 delete_queue_.push_back(index); 165 if (!deleting_messages_) 166 DeleteMessages(); 167} 168 169void NetworkSmsHandler:: 170ModemManagerNetworkSmsDeviceHandler::MessageReceived( 171 const base::DictionaryValue& dictionary) { 172 // The keys of the ModemManager.Modem.Gsm.SMS interface match the 173 // exported keys, so the dictionary used as a notification argument 174 // unchanged. 175 host_->MessageReceived(dictionary); 176} 177 178class NetworkSmsHandler::ModemManager1NetworkSmsDeviceHandler 179 : public NetworkSmsHandler::NetworkSmsDeviceHandler { 180 public: 181 ModemManager1NetworkSmsDeviceHandler(NetworkSmsHandler* host, 182 std::string dbus_connection, 183 dbus::ObjectPath object_path); 184 185 virtual void RequestUpdate() OVERRIDE; 186 187 private: 188 void ListCallback(const std::vector<dbus::ObjectPath>& paths); 189 void SmsReceivedCallback(const dbus::ObjectPath& path, bool complete); 190 void GetCallback(const base::DictionaryValue& dictionary); 191 void DeleteMessages(); 192 void GetMessages(); 193 void MessageReceived(const base::DictionaryValue& dictionary); 194 195 NetworkSmsHandler* host_; 196 std::string dbus_connection_; 197 dbus::ObjectPath object_path_; 198 bool deleting_messages_; 199 bool retrieving_messages_; 200 base::WeakPtrFactory<ModemManager1NetworkSmsDeviceHandler> weak_ptr_factory_; 201 std::vector<dbus::ObjectPath> delete_queue_; 202 std::deque<dbus::ObjectPath> retrieval_queue_; 203 204 DISALLOW_COPY_AND_ASSIGN(ModemManager1NetworkSmsDeviceHandler); 205}; 206 207NetworkSmsHandler:: 208ModemManager1NetworkSmsDeviceHandler::ModemManager1NetworkSmsDeviceHandler( 209 NetworkSmsHandler* host, 210 std::string dbus_connection, 211 dbus::ObjectPath object_path) 212 : host_(host), 213 dbus_connection_(dbus_connection), 214 object_path_(object_path), 215 deleting_messages_(false), 216 retrieving_messages_(false), 217 weak_ptr_factory_(this) { 218 // Set the handler for received Sms messaages. 219 DBusThreadManager::Get()->GetModemMessagingClient()->SetSmsReceivedHandler( 220 dbus_connection_, object_path_, 221 base::Bind( 222 &NetworkSmsHandler:: 223 ModemManager1NetworkSmsDeviceHandler::SmsReceivedCallback, 224 weak_ptr_factory_.GetWeakPtr())); 225 226 // List the existing messages. 227 DBusThreadManager::Get()->GetModemMessagingClient()->List( 228 dbus_connection_, object_path_, 229 base::Bind(&NetworkSmsHandler:: 230 ModemManager1NetworkSmsDeviceHandler::ListCallback, 231 weak_ptr_factory_.GetWeakPtr())); 232} 233 234void NetworkSmsHandler::ModemManager1NetworkSmsDeviceHandler::RequestUpdate() { 235 // Calling List using the service "AddSMS" causes the stub 236 // implementation to deliver new sms messages. 237 DBusThreadManager::Get()->GetModemMessagingClient()->List( 238 std::string("AddSMS"), dbus::ObjectPath("/"), 239 base::Bind(&NetworkSmsHandler:: 240 ModemManager1NetworkSmsDeviceHandler::ListCallback, 241 weak_ptr_factory_.GetWeakPtr())); 242} 243 244void NetworkSmsHandler::ModemManager1NetworkSmsDeviceHandler::ListCallback( 245 const std::vector<dbus::ObjectPath>& paths) { 246 // This receives all messages, so clear any pending gets and deletes. 247 retrieval_queue_.clear(); 248 delete_queue_.clear(); 249 250 retrieval_queue_.resize(paths.size()); 251 std::copy(paths.begin(), paths.end(), retrieval_queue_.begin()); 252 if (!retrieving_messages_) 253 GetMessages(); 254} 255 256// Messages must be deleted one at a time, since we can not guarantee 257// the order the deletion will be executed in. Delete messages from 258// the back of the list so that the indices are valid. 259void NetworkSmsHandler::ModemManager1NetworkSmsDeviceHandler::DeleteMessages() { 260 if (delete_queue_.empty()) { 261 deleting_messages_ = false; 262 return; 263 } 264 deleting_messages_ = true; 265 dbus::ObjectPath sms_path = delete_queue_.back(); 266 delete_queue_.pop_back(); 267 DBusThreadManager::Get()->GetModemMessagingClient()->Delete( 268 dbus_connection_, object_path_, sms_path, 269 base::Bind(&NetworkSmsHandler:: 270 ModemManager1NetworkSmsDeviceHandler::DeleteMessages, 271 weak_ptr_factory_.GetWeakPtr())); 272} 273 274// Messages must be fetched one at a time, so that we do not queue too 275// many requests to a single threaded server. 276void NetworkSmsHandler::ModemManager1NetworkSmsDeviceHandler::GetMessages() { 277 if (retrieval_queue_.empty()) { 278 retrieving_messages_ = false; 279 if (!deleting_messages_) 280 DeleteMessages(); 281 return; 282 } 283 retrieving_messages_ = true; 284 dbus::ObjectPath sms_path = retrieval_queue_.front(); 285 retrieval_queue_.pop_front(); 286 DBusThreadManager::Get()->GetSMSClient()->GetAll( 287 dbus_connection_, sms_path, 288 base::Bind(&NetworkSmsHandler:: 289 ModemManager1NetworkSmsDeviceHandler::GetCallback, 290 weak_ptr_factory_.GetWeakPtr())); 291 delete_queue_.push_back(sms_path); 292} 293 294void NetworkSmsHandler:: 295ModemManager1NetworkSmsDeviceHandler::SmsReceivedCallback( 296 const dbus::ObjectPath& sms_path, 297 bool complete) { 298 // Only handle complete messages. 299 if (!complete) 300 return; 301 retrieval_queue_.push_back(sms_path); 302 if (!retrieving_messages_) 303 GetMessages(); 304} 305 306void NetworkSmsHandler::ModemManager1NetworkSmsDeviceHandler::GetCallback( 307 const base::DictionaryValue& dictionary) { 308 MessageReceived(dictionary); 309 GetMessages(); 310} 311 312void NetworkSmsHandler:: 313ModemManager1NetworkSmsDeviceHandler::MessageReceived( 314 const base::DictionaryValue& dictionary) { 315 // The keys of the ModemManager1.SMS interface do not match the 316 // exported keys, so a new dictionary is created with the expected 317 // key namaes. 318 base::DictionaryValue new_dictionary; 319 std::string text, number, timestamp; 320 if (dictionary.GetStringWithoutPathExpansion(kModemManager1NumberKey, 321 &number)) 322 new_dictionary.SetString(kNumberKey, number); 323 if (dictionary.GetStringWithoutPathExpansion(kModemManager1TextKey, &text)) 324 new_dictionary.SetString(kTextKey, text); 325 // TODO(jglasgow): consider normalizing timestamp. 326 if (dictionary.GetStringWithoutPathExpansion(kModemManager1TimestampKey, 327 ×tamp)) 328 new_dictionary.SetString(kTimestampKey, timestamp); 329 host_->MessageReceived(new_dictionary); 330} 331 332/////////////////////////////////////////////////////////////////////////////// 333// NetworkSmsHandler 334 335NetworkSmsHandler::NetworkSmsHandler() 336 : weak_ptr_factory_(this) { 337} 338 339NetworkSmsHandler::~NetworkSmsHandler() { 340 DBusThreadManager::Get()->GetShillManagerClient()-> 341 RemovePropertyChangedObserver(this); 342} 343 344void NetworkSmsHandler::Init() { 345 // Add as an observer here so that new devices added after this call are 346 // recognized. 347 DBusThreadManager::Get()->GetShillManagerClient()->AddPropertyChangedObserver( 348 this); 349 // Request network manager properties so that we can get the list of devices. 350 DBusThreadManager::Get()->GetShillManagerClient()->GetProperties( 351 base::Bind(&NetworkSmsHandler::ManagerPropertiesCallback, 352 weak_ptr_factory_.GetWeakPtr())); 353} 354 355void NetworkSmsHandler::RequestUpdate(bool request_existing) { 356 // If we already received messages and |request_existing| is true, send 357 // updates for existing messages. 358 for (ScopedVector<base::DictionaryValue>::iterator iter = 359 received_messages_.begin(); 360 iter != received_messages_.end(); ++iter) { 361 base::DictionaryValue* message = *iter; 362 NotifyMessageReceived(*message); 363 } 364 // Request updates from each device. 365 for (ScopedVector<NetworkSmsDeviceHandler>::iterator iter = 366 device_handlers_.begin(); iter != device_handlers_.end(); ++iter) { 367 (*iter)->RequestUpdate(); 368 } 369} 370 371void NetworkSmsHandler::AddObserver(Observer* observer) { 372 observers_.AddObserver(observer); 373} 374 375void NetworkSmsHandler::RemoveObserver(Observer* observer) { 376 observers_.RemoveObserver(observer); 377} 378 379void NetworkSmsHandler::OnPropertyChanged(const std::string& name, 380 const base::Value& value) { 381 if (name != flimflam::kDevicesProperty) 382 return; 383 const base::ListValue* devices = NULL; 384 if (!value.GetAsList(&devices) || !devices) 385 return; 386 UpdateDevices(devices); 387} 388 389// Private methods 390 391void NetworkSmsHandler::AddReceivedMessage( 392 const base::DictionaryValue& message) { 393 base::DictionaryValue* new_message = message.DeepCopy(); 394 if (received_messages_.size() >= kMaxReceivedMessages) 395 received_messages_.erase(received_messages_.begin()); 396 received_messages_.push_back(new_message); 397} 398 399void NetworkSmsHandler::NotifyMessageReceived( 400 const base::DictionaryValue& message) { 401 FOR_EACH_OBSERVER(Observer, observers_, MessageReceived(message)); 402} 403 404void NetworkSmsHandler::MessageReceived(const base::DictionaryValue& message) { 405 AddReceivedMessage(message); 406 NotifyMessageReceived(message); 407} 408 409void NetworkSmsHandler::ManagerPropertiesCallback( 410 DBusMethodCallStatus call_status, 411 const base::DictionaryValue& properties) { 412 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 413 LOG(ERROR) << "NetworkSmsHandler: Failed to get manager properties."; 414 return; 415 } 416 const base::Value* value; 417 if (!properties.GetWithoutPathExpansion(flimflam::kDevicesProperty, &value) || 418 value->GetType() != base::Value::TYPE_LIST) { 419 LOG(ERROR) << "NetworkSmsHandler: No list value for: " 420 << flimflam::kDevicesProperty; 421 return; 422 } 423 const base::ListValue* devices = static_cast<const base::ListValue*>(value); 424 UpdateDevices(devices); 425} 426 427void NetworkSmsHandler::UpdateDevices(const base::ListValue* devices) { 428 for (base::ListValue::const_iterator iter = devices->begin(); 429 iter != devices->end(); ++iter) { 430 std::string device_path; 431 (*iter)->GetAsString(&device_path); 432 if (!device_path.empty()) { 433 // Request device properties. 434 VLOG(1) << "GetDeviceProperties: " << device_path; 435 DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties( 436 dbus::ObjectPath(device_path), 437 base::Bind(&NetworkSmsHandler::DevicePropertiesCallback, 438 weak_ptr_factory_.GetWeakPtr(), 439 device_path)); 440 } 441 } 442} 443 444void NetworkSmsHandler::DevicePropertiesCallback( 445 const std::string& device_path, 446 DBusMethodCallStatus call_status, 447 const base::DictionaryValue& properties) { 448 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 449 LOG(ERROR) << "NetworkSmsHandler: ERROR: " << call_status 450 << " For: " << device_path; 451 return; 452 } 453 454 std::string device_type; 455 if (!properties.GetStringWithoutPathExpansion( 456 flimflam::kTypeProperty, &device_type)) { 457 LOG(ERROR) << "NetworkSmsHandler: No type for: " << device_path; 458 return; 459 } 460 if (device_type != flimflam::kTypeCellular) 461 return; 462 463 std::string dbus_connection; 464 if (!properties.GetStringWithoutPathExpansion( 465 flimflam::kDBusConnectionProperty, &dbus_connection)) { 466 LOG(ERROR) << "Device has no DBusConnection Property: " << device_path; 467 return; 468 } 469 470 std::string object_path_string; 471 if (!properties.GetStringWithoutPathExpansion( 472 flimflam::kDBusObjectProperty, &object_path_string)) { 473 LOG(ERROR) << "Device has no DBusObject Property: " << device_path; 474 return; 475 } 476 dbus::ObjectPath object_path(object_path_string); 477 if (object_path_string.compare( 478 0, sizeof(modemmanager::kModemManager1ServicePath) - 1, 479 modemmanager::kModemManager1ServicePath) == 0) { 480 device_handlers_.push_back( 481 new ModemManager1NetworkSmsDeviceHandler( 482 this, dbus_connection, object_path)); 483 } else { 484 device_handlers_.push_back( 485 new ModemManagerNetworkSmsDeviceHandler( 486 this, dbus_connection, object_path)); 487 } 488} 489 490 491} // namespace chromeos 492