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