OLD | NEW |
| (Empty) |
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 "chrome/browser/chromeos/cros/sms_watcher.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/gsm_sms_client.h" | |
17 #include "chromeos/dbus/modem_messaging_client.h" | |
18 #include "chromeos/dbus/sms_client.h" | |
19 #include "third_party/cros_system_api/dbus/service_constants.h" | |
20 | |
21 namespace chromeos { | |
22 | |
23 namespace { | |
24 | |
25 int decode_bcd(const char *s) { | |
26 return (s[0] - '0') * 10 + s[1] - '0'; | |
27 } | |
28 | |
29 void decode_timestamp(const std::string& sms_timestamp, SMS *sms) { | |
30 base::Time::Exploded exp; | |
31 exp.year = decode_bcd(&sms_timestamp[0]); | |
32 if (exp.year > 95) | |
33 exp.year += 1900; | |
34 else | |
35 exp.year += 2000; | |
36 exp.month = decode_bcd(&sms_timestamp[2]); | |
37 exp.day_of_month = decode_bcd(&sms_timestamp[4]); | |
38 exp.hour = decode_bcd(&sms_timestamp[6]); | |
39 exp.minute = decode_bcd(&sms_timestamp[8]); | |
40 exp.second = decode_bcd(&sms_timestamp[10]); | |
41 exp.millisecond = 0; | |
42 sms->timestamp = base::Time::FromUTCExploded(exp); | |
43 int hours = decode_bcd(&sms_timestamp[13]); | |
44 if (sms_timestamp[12] == '-') | |
45 hours = -hours; | |
46 sms->timestamp -= base::TimeDelta::FromHours(hours); | |
47 } | |
48 | |
49 // Callback for Delete() method. This method does nothing. | |
50 void DeleteSMSCallback() {} | |
51 | |
52 } // namespace | |
53 | |
54 const char SMSWatcher::kNumberKey[] = "number"; | |
55 const char SMSWatcher::kTextKey[] = "text"; | |
56 const char SMSWatcher::kTimestampKey[] = "timestamp"; | |
57 const char SMSWatcher::kSmscKey[] = "smsc"; | |
58 const char SMSWatcher::kValidityKey[] = "validity"; | |
59 const char SMSWatcher::kClassKey[] = "class"; | |
60 const char SMSWatcher::kIndexKey[] = "index"; | |
61 | |
62 const char SMSWatcher::kModemManager1NumberKey[] = "Number"; | |
63 const char SMSWatcher::kModemManager1TextKey[] = "Text"; | |
64 const char SMSWatcher::kModemManager1TimestampKey[] = "Timestamp"; | |
65 const char SMSWatcher::kModemManager1SmscKey[] = "Smsc"; | |
66 const char SMSWatcher::kModemManager1ValidityKey[] = "Validity"; | |
67 const char SMSWatcher::kModemManager1ClassKey[] = "Class"; | |
68 const char SMSWatcher::kModemManager1IndexKey[] = "Index"; | |
69 | |
70 class SMSWatcher::WatcherBase { | |
71 public: | |
72 WatcherBase(const std::string& device_path, | |
73 MonitorSMSCallback callback, | |
74 const std::string& dbus_connection, | |
75 const dbus::ObjectPath& object_path) : | |
76 device_path_(device_path), | |
77 callback_(callback), | |
78 dbus_connection_(dbus_connection), | |
79 object_path_(object_path) {} | |
80 | |
81 virtual ~WatcherBase() {} | |
82 | |
83 protected: | |
84 const std::string device_path_; | |
85 MonitorSMSCallback callback_; | |
86 const std::string dbus_connection_; | |
87 const dbus::ObjectPath object_path_; | |
88 | |
89 DISALLOW_COPY_AND_ASSIGN(WatcherBase); | |
90 }; | |
91 | |
92 namespace { | |
93 | |
94 class GsmWatcher : public SMSWatcher::WatcherBase { | |
95 public: | |
96 GsmWatcher(const std::string& device_path, | |
97 MonitorSMSCallback callback, | |
98 const std::string& dbus_connection, | |
99 const dbus::ObjectPath& object_path) | |
100 : WatcherBase(device_path, callback, dbus_connection, object_path), | |
101 weak_ptr_factory_(this) { | |
102 DBusThreadManager::Get()->GetGsmSMSClient()->SetSmsReceivedHandler( | |
103 dbus_connection_, | |
104 object_path_, | |
105 base::Bind(&GsmWatcher::OnSmsReceived, weak_ptr_factory_.GetWeakPtr())); | |
106 | |
107 DBusThreadManager::Get()->GetGsmSMSClient()->List( | |
108 dbus_connection_, object_path_, | |
109 base::Bind(&GsmWatcher::ListSMSCallback, | |
110 weak_ptr_factory_.GetWeakPtr())); | |
111 } | |
112 | |
113 virtual ~GsmWatcher() { | |
114 DBusThreadManager::Get()->GetGsmSMSClient()->ResetSmsReceivedHandler( | |
115 dbus_connection_, object_path_); | |
116 } | |
117 | |
118 private: | |
119 // Callback for SmsReceived signal from ModemManager.Modem.Gsm.SMS | |
120 void OnSmsReceived(uint32 index, bool complete) { | |
121 // Only handle complete messages. | |
122 if (!complete) | |
123 return; | |
124 DBusThreadManager::Get()->GetGsmSMSClient()->Get( | |
125 dbus_connection_, object_path_, index, | |
126 base::Bind(&GsmWatcher::GetSMSCallback, | |
127 weak_ptr_factory_.GetWeakPtr(), | |
128 index)); | |
129 } | |
130 | |
131 // Runs |callback_| with a SMS. | |
132 void RunCallbackWithSMS(const base::DictionaryValue& sms_dictionary) { | |
133 SMS sms; | |
134 | |
135 if (!sms_dictionary.GetStringWithoutPathExpansion(SMSWatcher::kNumberKey, | |
136 &sms.number)) | |
137 LOG(WARNING) << "SMS did not contain a number"; | |
138 | |
139 if (!sms_dictionary.GetStringWithoutPathExpansion(SMSWatcher::kTextKey, | |
140 &sms.text)) | |
141 LOG(WARNING) << "SMS did not contain message text"; | |
142 | |
143 std::string sms_timestamp; | |
144 if (sms_dictionary.GetStringWithoutPathExpansion(SMSWatcher::kTimestampKey, | |
145 &sms_timestamp)) { | |
146 decode_timestamp(sms_timestamp, &sms); | |
147 } else { | |
148 LOG(WARNING) << "SMS did not contain a timestamp"; | |
149 sms.timestamp = base::Time(); | |
150 } | |
151 | |
152 sms_dictionary.GetStringWithoutPathExpansion(SMSWatcher::kSmscKey, | |
153 &sms.smsc); | |
154 | |
155 double validity = 0; | |
156 if (!sms_dictionary.GetDoubleWithoutPathExpansion(SMSWatcher::kValidityKey, | |
157 &validity)) { | |
158 validity = -1; | |
159 } | |
160 sms.validity = validity; | |
161 | |
162 double msgclass = 0; | |
163 if (!sms_dictionary.GetDoubleWithoutPathExpansion(SMSWatcher::kClassKey, | |
164 &msgclass)) { | |
165 msgclass = -1; | |
166 } | |
167 sms.msgclass = msgclass; | |
168 | |
169 callback_.Run(device_path_, sms); | |
170 } | |
171 | |
172 // Callback for Get() method from ModemManager.Modem.Gsm.SMS | |
173 void GetSMSCallback(uint32 index, | |
174 const base::DictionaryValue& sms_dictionary) { | |
175 RunCallbackWithSMS(sms_dictionary); | |
176 | |
177 DBusThreadManager::Get()->GetGsmSMSClient()->Delete( | |
178 dbus_connection_, object_path_, index, base::Bind(&DeleteSMSCallback)); | |
179 } | |
180 | |
181 // Callback for List() method. | |
182 void ListSMSCallback(const base::ListValue& result) { | |
183 // List() is called only once; no one touches delete_queue_ before List(). | |
184 CHECK(delete_queue_.empty()); | |
185 for (size_t i = 0; i != result.GetSize(); ++i) { | |
186 const base::DictionaryValue* sms_dictionary = NULL; | |
187 if (!result.GetDictionary(i, &sms_dictionary)) { | |
188 LOG(ERROR) << "result[" << i << "] is not a dictionary."; | |
189 continue; | |
190 } | |
191 RunCallbackWithSMS(*sms_dictionary); | |
192 double index = 0; | |
193 if (sms_dictionary->GetDoubleWithoutPathExpansion(SMSWatcher::kIndexKey, | |
194 &index)) { | |
195 delete_queue_.push_back(index); | |
196 } | |
197 } | |
198 DeleteSMSInChain(); | |
199 } | |
200 | |
201 // Deletes SMSs in the queue. | |
202 void DeleteSMSInChain() { | |
203 if (!delete_queue_.empty()) { | |
204 DBusThreadManager::Get()->GetGsmSMSClient()->Delete( | |
205 dbus_connection_, object_path_, delete_queue_.back(), | |
206 base::Bind(&GsmWatcher::DeleteSMSInChain, | |
207 weak_ptr_factory_.GetWeakPtr())); | |
208 delete_queue_.pop_back(); | |
209 } | |
210 } | |
211 | |
212 base::WeakPtrFactory<GsmWatcher> weak_ptr_factory_; | |
213 std::vector<uint32> delete_queue_; | |
214 | |
215 DISALLOW_COPY_AND_ASSIGN(GsmWatcher); | |
216 }; | |
217 | |
218 class ModemManager1Watcher : public SMSWatcher::WatcherBase { | |
219 public: | |
220 ModemManager1Watcher(const std::string& device_path, | |
221 MonitorSMSCallback callback, | |
222 const std::string& dbus_connection, | |
223 const dbus::ObjectPath& object_path) | |
224 : WatcherBase(device_path, callback, dbus_connection, object_path), | |
225 weak_ptr_factory_(this), | |
226 deleting_messages_(false), | |
227 retrieving_messages_(false) { | |
228 DBusThreadManager::Get()->GetModemMessagingClient()->SetSmsReceivedHandler( | |
229 dbus_connection_, | |
230 object_path_, | |
231 base::Bind(&ModemManager1Watcher::OnSmsReceived, | |
232 weak_ptr_factory_.GetWeakPtr())); | |
233 | |
234 DBusThreadManager::Get()->GetModemMessagingClient()->List( | |
235 dbus_connection_, object_path_, | |
236 base::Bind(&ModemManager1Watcher::ListSMSCallback, | |
237 weak_ptr_factory_.GetWeakPtr())); | |
238 } | |
239 | |
240 virtual ~ModemManager1Watcher() { | |
241 DBusThreadManager::Get()->GetModemMessagingClient() | |
242 ->ResetSmsReceivedHandler(dbus_connection_, object_path_); | |
243 } | |
244 | |
245 private: | |
246 void ListSMSCallback( | |
247 const std::vector<dbus::ObjectPath>& paths) { | |
248 // This receives all messages, so clear any pending gets and deletes. | |
249 retrieval_queue_.clear(); | |
250 delete_queue_.clear(); | |
251 | |
252 retrieval_queue_.resize(paths.size()); | |
253 std::copy(paths.begin(), paths.end(), retrieval_queue_.begin()); | |
254 if (!retrieving_messages_) | |
255 GetMessages(); | |
256 } | |
257 | |
258 // Messages must be deleted one at a time, since we can not | |
259 // guarantee the order the deletion will be executed in. Delete | |
260 // messages from the back of the list so that the indices are | |
261 // valid. | |
262 void DeleteMessages() { | |
263 if (delete_queue_.empty()) { | |
264 deleting_messages_ = false; | |
265 return; | |
266 } | |
267 deleting_messages_ = true; | |
268 dbus::ObjectPath sms_path = delete_queue_.back(); | |
269 delete_queue_.pop_back(); | |
270 DBusThreadManager::Get()->GetModemMessagingClient()->Delete( | |
271 dbus_connection_, object_path_, sms_path, | |
272 base::Bind(&ModemManager1Watcher::DeleteMessages, | |
273 weak_ptr_factory_.GetWeakPtr())); | |
274 } | |
275 | |
276 // Messages must be fetched one at a time, so that we do not queue too | |
277 // many requests to a single threaded server. | |
278 void GetMessages() { | |
279 if (retrieval_queue_.empty()) { | |
280 retrieving_messages_ = false; | |
281 if (!deleting_messages_) | |
282 DeleteMessages(); | |
283 return; | |
284 } | |
285 retrieving_messages_ = true; | |
286 dbus::ObjectPath sms_path = retrieval_queue_.front(); | |
287 retrieval_queue_.pop_front(); | |
288 DBusThreadManager::Get()->GetSMSClient()->GetAll( | |
289 dbus_connection_, sms_path, | |
290 base::Bind(&ModemManager1Watcher::GetCallback, | |
291 weak_ptr_factory_.GetWeakPtr())); | |
292 delete_queue_.push_back(sms_path); | |
293 } | |
294 | |
295 // Handles arrival of a new SMS message. | |
296 void OnSmsReceived(const dbus::ObjectPath& sms_path, bool complete) { | |
297 // Only handle complete messages. | |
298 if (!complete) | |
299 return; | |
300 retrieval_queue_.push_back(sms_path); | |
301 if (!retrieving_messages_) | |
302 GetMessages(); | |
303 } | |
304 | |
305 // Runs |callback_| with a SMS. | |
306 void RunCallbackWithSMS(const base::DictionaryValue& sms_dictionary) { | |
307 SMS sms; | |
308 | |
309 if (!sms_dictionary.GetStringWithoutPathExpansion( | |
310 SMSWatcher::kModemManager1NumberKey, &sms.number)) | |
311 LOG(WARNING) << "SMS did not contain a number"; | |
312 | |
313 if (!sms_dictionary.GetStringWithoutPathExpansion( | |
314 SMSWatcher::kModemManager1TextKey, &sms.text)) | |
315 LOG(WARNING) << "SMS did not contain message text"; | |
316 | |
317 std::string sms_timestamp; | |
318 if (sms_dictionary.GetStringWithoutPathExpansion( | |
319 SMSWatcher::kModemManager1TimestampKey, &sms_timestamp)) { | |
320 decode_timestamp(sms_timestamp, &sms); | |
321 } else { | |
322 LOG(WARNING) << "SMS did not contain a timestamp"; | |
323 sms.timestamp = base::Time(); | |
324 } | |
325 | |
326 sms_dictionary.GetStringWithoutPathExpansion( | |
327 SMSWatcher::kModemManager1SmscKey, &sms.smsc); | |
328 | |
329 double validity = 0; | |
330 if (!sms_dictionary.GetDoubleWithoutPathExpansion( | |
331 SMSWatcher::kModemManager1ValidityKey, &validity)) { | |
332 validity = -1; | |
333 } | |
334 sms.validity = validity; | |
335 | |
336 double msgclass = 0; | |
337 if (!sms_dictionary.GetDoubleWithoutPathExpansion( | |
338 SMSWatcher::kModemManager1ClassKey, &msgclass)) { | |
339 msgclass = -1; | |
340 } | |
341 sms.msgclass = msgclass; | |
342 | |
343 callback_.Run(device_path_, sms); | |
344 } | |
345 | |
346 void GetCallback(const base::DictionaryValue& dictionary) { | |
347 RunCallbackWithSMS(dictionary); | |
348 GetMessages(); | |
349 } | |
350 | |
351 base::WeakPtrFactory<ModemManager1Watcher> weak_ptr_factory_; | |
352 bool deleting_messages_; | |
353 bool retrieving_messages_; | |
354 std::vector<dbus::ObjectPath> delete_queue_; | |
355 std::deque<dbus::ObjectPath> retrieval_queue_; | |
356 | |
357 DISALLOW_COPY_AND_ASSIGN(ModemManager1Watcher); | |
358 }; | |
359 | |
360 } // namespace | |
361 | |
362 SMSWatcher::SMSWatcher(const std::string& modem_device_path, | |
363 MonitorSMSCallback callback) | |
364 : weak_ptr_factory_(this), | |
365 device_path_(modem_device_path), | |
366 callback_(callback) { | |
367 DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties( | |
368 dbus::ObjectPath(modem_device_path), | |
369 base::Bind(&SMSWatcher::DevicePropertiesCallback, | |
370 weak_ptr_factory_.GetWeakPtr())); | |
371 } | |
372 | |
373 SMSWatcher::~SMSWatcher() { | |
374 } | |
375 | |
376 void SMSWatcher::DevicePropertiesCallback( | |
377 DBusMethodCallStatus call_status, | |
378 const base::DictionaryValue& properties) { | |
379 if (call_status != DBUS_METHOD_CALL_SUCCESS) | |
380 return; | |
381 | |
382 std::string dbus_connection; | |
383 if (!properties.GetStringWithoutPathExpansion( | |
384 flimflam::kDBusConnectionProperty, &dbus_connection)) { | |
385 LOG(WARNING) << "Modem device properties do not include DBus connection."; | |
386 return; | |
387 } | |
388 | |
389 std::string object_path_string; | |
390 if (!properties.GetStringWithoutPathExpansion( | |
391 flimflam::kDBusObjectProperty, &object_path_string)) { | |
392 LOG(WARNING) << "Modem device properties do not include DBus object."; | |
393 return; | |
394 } | |
395 | |
396 if (object_path_string.compare( | |
397 0, sizeof(modemmanager::kModemManager1ServicePath) - 1, | |
398 modemmanager::kModemManager1ServicePath) == 0) { | |
399 watcher_.reset( | |
400 new ModemManager1Watcher(device_path_, callback_, dbus_connection, | |
401 dbus::ObjectPath(object_path_string))); | |
402 } else { | |
403 watcher_.reset( | |
404 new GsmWatcher(device_path_, callback_, dbus_connection, | |
405 dbus::ObjectPath(object_path_string))); | |
406 } | |
407 } | |
408 | |
409 } // namespace chromeos | |
OLD | NEW |