network_sms_handler.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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 kIndexKey[] = "index";
27
28// Keys from ModemManager1
29const char kModemManager1NumberKey[] = "Number";
30const char kModemManager1TextKey[] = "Text";
31const char kModemManager1TimestampKey[] = "Timestamp";
32
33// Maximum number of messages stored for RequestUpdate(true).
34const size_t kMaxReceivedMessages = 100;
35
36}  // namespace
37
38namespace chromeos {
39
40// static
41const char NetworkSmsHandler::kNumberKey[] = "number";
42const char NetworkSmsHandler::kTextKey[] = "text";
43const char NetworkSmsHandler::kTimestampKey[] = "timestamp";
44
45class NetworkSmsHandler::NetworkSmsDeviceHandler {
46 public:
47  NetworkSmsDeviceHandler() {}
48  virtual ~NetworkSmsDeviceHandler() {}
49
50  virtual void RequestUpdate() = 0;
51};
52
53class 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
79NetworkSmsHandler::
80ModemManagerNetworkSmsDeviceHandler::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
103void NetworkSmsHandler::ModemManagerNetworkSmsDeviceHandler::RequestUpdate() {
104  DBusThreadManager::Get()->GetGsmSMSClient()->RequestUpdate(
105      service_name_, object_path_);
106}
107
108void 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.
128void 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
143void NetworkSmsHandler::
144ModemManagerNetworkSmsDeviceHandler::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
157void 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
166void NetworkSmsHandler::
167ModemManagerNetworkSmsDeviceHandler::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
175class 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
204NetworkSmsHandler::
205ModemManager1NetworkSmsDeviceHandler::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
231void 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
241void 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.
256void 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.
273void 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
291void NetworkSmsHandler::
292ModemManager1NetworkSmsDeviceHandler::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
303void NetworkSmsHandler::ModemManager1NetworkSmsDeviceHandler::GetCallback(
304    const base::DictionaryValue& dictionary) {
305  MessageReceived(dictionary);
306  GetMessages();
307}
308
309void NetworkSmsHandler::
310ModemManager1NetworkSmsDeviceHandler::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                                               &timestamp))
325    new_dictionary.SetString(kTimestampKey, timestamp);
326  host_->MessageReceived(new_dictionary);
327}
328
329///////////////////////////////////////////////////////////////////////////////
330// NetworkSmsHandler
331
332NetworkSmsHandler::NetworkSmsHandler()
333    : weak_ptr_factory_(this) {
334}
335
336NetworkSmsHandler::~NetworkSmsHandler() {
337  DBusThreadManager::Get()->GetShillManagerClient()->
338      RemovePropertyChangedObserver(this);
339}
340
341void 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
352void 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
368void NetworkSmsHandler::AddObserver(Observer* observer) {
369  observers_.AddObserver(observer);
370}
371
372void NetworkSmsHandler::RemoveObserver(Observer* observer) {
373  observers_.RemoveObserver(observer);
374}
375
376void 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
388void 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
396void NetworkSmsHandler::NotifyMessageReceived(
397    const base::DictionaryValue& message) {
398  FOR_EACH_OBSERVER(Observer, observers_, MessageReceived(message));
399}
400
401void NetworkSmsHandler::MessageReceived(const base::DictionaryValue& message) {
402  AddReceivedMessage(message);
403  NotifyMessageReceived(message);
404}
405
406void 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
424void 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
441void 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