Home | History | Annotate | Download | only in network
      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/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/gsm_sms_client.h"
     16 #include "chromeos/dbus/modem_messaging_client.h"
     17 #include "chromeos/dbus/shill_device_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
    410