network_sms_handler.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
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                                               &timestamp))
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 != shill::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(shill::kDevicesProperty, &value) ||
418      value->GetType() != base::Value::TYPE_LIST) {
419    LOG(ERROR) << "NetworkSmsHandler: No list value for: "
420               << shill::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          shill::kTypeProperty, &device_type)) {
457    LOG(ERROR) << "NetworkSmsHandler: No type for: " << device_path;
458    return;
459  }
460  if (device_type != shill::kTypeCellular)
461    return;
462
463  std::string dbus_connection;
464  if (!properties.GetStringWithoutPathExpansion(
465          shill::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          shill::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