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:
NetworkSmsDeviceHandler()47 NetworkSmsDeviceHandler() {}
~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::
ModemManagerNetworkSmsDeviceHandler(NetworkSmsHandler * host,const std::string & service_name,const dbus::ObjectPath & object_path)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
RequestUpdate()103 void NetworkSmsHandler::ModemManagerNetworkSmsDeviceHandler::RequestUpdate() {
104 DBusThreadManager::Get()->GetGsmSMSClient()->RequestUpdate(
105 service_name_, object_path_);
106 }
107
ListCallback(const base::ListValue & message_list)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.
DeleteMessages()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::
SmsReceivedCallback(uint32 index,bool complete)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
GetCallback(uint32 index,const base::DictionaryValue & dictionary)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::
MessageReceived(const base::DictionaryValue & dictionary)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::
ModemManager1NetworkSmsDeviceHandler(NetworkSmsHandler * host,const std::string & service_name,const dbus::ObjectPath & object_path)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
RequestUpdate()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
ListCallback(const std::vector<dbus::ObjectPath> & paths)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.
DeleteMessages()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.
GetMessages()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::
SmsReceivedCallback(const dbus::ObjectPath & sms_path,bool complete)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
GetCallback(const base::DictionaryValue & dictionary)303 void NetworkSmsHandler::ModemManager1NetworkSmsDeviceHandler::GetCallback(
304 const base::DictionaryValue& dictionary) {
305 MessageReceived(dictionary);
306 GetMessages();
307 }
308
309 void NetworkSmsHandler::
MessageReceived(const base::DictionaryValue & dictionary)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
NetworkSmsHandler()332 NetworkSmsHandler::NetworkSmsHandler()
333 : weak_ptr_factory_(this) {
334 }
335
~NetworkSmsHandler()336 NetworkSmsHandler::~NetworkSmsHandler() {
337 DBusThreadManager::Get()->GetShillManagerClient()->
338 RemovePropertyChangedObserver(this);
339 }
340
Init()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
RequestUpdate(bool request_existing)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
AddObserver(Observer * observer)368 void NetworkSmsHandler::AddObserver(Observer* observer) {
369 observers_.AddObserver(observer);
370 }
371
RemoveObserver(Observer * observer)372 void NetworkSmsHandler::RemoveObserver(Observer* observer) {
373 observers_.RemoveObserver(observer);
374 }
375
OnPropertyChanged(const std::string & name,const base::Value & value)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
AddReceivedMessage(const base::DictionaryValue & message)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
NotifyMessageReceived(const base::DictionaryValue & message)396 void NetworkSmsHandler::NotifyMessageReceived(
397 const base::DictionaryValue& message) {
398 FOR_EACH_OBSERVER(Observer, observers_, MessageReceived(message));
399 }
400
MessageReceived(const base::DictionaryValue & message)401 void NetworkSmsHandler::MessageReceived(const base::DictionaryValue& message) {
402 AddReceivedMessage(message);
403 NotifyMessageReceived(message);
404 }
405
ManagerPropertiesCallback(DBusMethodCallStatus call_status,const base::DictionaryValue & properties)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
UpdateDevices(const base::ListValue * devices)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
DevicePropertiesCallback(const std::string & device_path,DBusMethodCallStatus call_status,const base::DictionaryValue & properties)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