gsm_sms_client.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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#include "chromeos/dbus/gsm_sms_client.h"
5
6#include <map>
7#include <utility>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/command_line.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/memory/weak_ptr.h"
14#include "base/message_loop.h"
15#include "base/stringprintf.h"
16#include "base/stl_util.h"
17#include "base/values.h"
18#include "chromeos/chromeos_switches.h"
19#include "dbus/bus.h"
20#include "dbus/message.h"
21#include "dbus/object_proxy.h"
22#include "dbus/values_util.h"
23#include "third_party/cros_system_api/dbus/service_constants.h"
24
25namespace chromeos {
26
27namespace {
28
29// A class actually making method calls for SMS services, used by
30// GsmSMSClientImpl.
31class SMSProxy {
32 public:
33  typedef GsmSMSClient::SmsReceivedHandler SmsReceivedHandler;
34  typedef GsmSMSClient::DeleteCallback DeleteCallback;
35  typedef GsmSMSClient::GetCallback GetCallback;
36  typedef GsmSMSClient::ListCallback ListCallback;
37
38  SMSProxy(dbus::Bus* bus,
39           const std::string& service_name,
40           const dbus::ObjectPath& object_path)
41      : proxy_(bus->GetObjectProxy(service_name, object_path)),
42        weak_ptr_factory_(this) {
43    proxy_->ConnectToSignal(
44        modemmanager::kModemManagerSMSInterface,
45        modemmanager::kSMSReceivedSignal,
46        base::Bind(&SMSProxy::OnSmsReceived, weak_ptr_factory_.GetWeakPtr()),
47        base::Bind(&SMSProxy::OnSignalConnected,
48                   weak_ptr_factory_.GetWeakPtr()));
49  }
50
51  // Sets SmsReceived signal handler.
52  void SetSmsReceivedHandler(const SmsReceivedHandler& handler) {
53    DCHECK(sms_received_handler_.is_null());
54    sms_received_handler_ = handler;
55  }
56
57  // Resets SmsReceived signal handler.
58  void ResetSmsReceivedHandler() {
59    sms_received_handler_.Reset();
60  }
61
62  // Calls Delete method.
63  void Delete(uint32 index, const DeleteCallback& callback) {
64    dbus::MethodCall method_call(modemmanager::kModemManagerSMSInterface,
65                                 modemmanager::kSMSDeleteFunction);
66    dbus::MessageWriter writer(&method_call);
67    writer.AppendUint32(index);
68    proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
69                       base::Bind(&SMSProxy::OnDelete,
70                                  weak_ptr_factory_.GetWeakPtr(),
71                                  callback));
72  }
73
74  // Calls Get method.
75  void Get(uint32 index, const GetCallback& callback) {
76    dbus::MethodCall method_call(modemmanager::kModemManagerSMSInterface,
77                                 modemmanager::kSMSGetFunction);
78    dbus::MessageWriter writer(&method_call);
79    writer.AppendUint32(index);
80    proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
81                       base::Bind(&SMSProxy::OnGet,
82                                  weak_ptr_factory_.GetWeakPtr(),
83                                  callback));
84  }
85
86  // Calls List method.
87  void List(const ListCallback& callback) {
88    dbus::MethodCall method_call(modemmanager::kModemManagerSMSInterface,
89                                 modemmanager::kSMSListFunction);
90    proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
91                       base::Bind(&SMSProxy::OnList,
92                                  weak_ptr_factory_.GetWeakPtr(),
93                                  callback));
94  }
95
96 private:
97  // Handles SmsReceived signal.
98  void OnSmsReceived(dbus::Signal* signal) {
99    uint32 index = 0;
100    bool complete = false;
101    dbus::MessageReader reader(signal);
102    if (!reader.PopUint32(&index) ||
103        !reader.PopBool(&complete)) {
104      LOG(ERROR) << "Invalid signal: " << signal->ToString();
105      return;
106    }
107    if (!sms_received_handler_.is_null())
108      sms_received_handler_.Run(index, complete);
109  }
110
111  // Handles the result of signal connection setup.
112  void OnSignalConnected(const std::string& interface,
113                         const std::string& signal,
114                         bool succeeded) {
115    LOG_IF(ERROR, !succeeded) << "Connect to " << interface << " " <<
116        signal << " failed.";
117  }
118
119  // Handles responses of Delete method calls.
120  void OnDelete(const DeleteCallback& callback, dbus::Response* response) {
121    if (!response)
122      return;
123    callback.Run();
124  }
125
126  // Handles responses of Get method calls.
127  void OnGet(const GetCallback& callback, dbus::Response* response) {
128    if (!response)
129      return;
130    dbus::MessageReader reader(response);
131    scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
132    base::DictionaryValue* dictionary_value = NULL;
133    if (!value.get() || !value->GetAsDictionary(&dictionary_value)) {
134      LOG(WARNING) << "Invalid response: " << response->ToString();
135      return;
136    }
137    callback.Run(*dictionary_value);
138  }
139
140  // Handles responses of List method calls.
141  void OnList(const ListCallback& callback, dbus::Response* response) {
142    if (!response)
143      return;
144    dbus::MessageReader reader(response);
145    scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
146    base::ListValue* list_value = NULL;
147    if (!value.get() || !value->GetAsList(&list_value)) {
148      LOG(WARNING) << "Invalid response: " << response->ToString();
149      return;
150    }
151    callback.Run(*list_value);
152  }
153
154  dbus::ObjectProxy* proxy_;
155  SmsReceivedHandler sms_received_handler_;
156
157  // Note: This should remain the last member so it'll be destroyed and
158  // invalidate its weak pointers before any other members are destroyed.
159  base::WeakPtrFactory<SMSProxy> weak_ptr_factory_;
160
161  DISALLOW_COPY_AND_ASSIGN(SMSProxy);
162};
163
164// The GsmSMSClient implementation.
165class GsmSMSClientImpl : public GsmSMSClient {
166 public:
167  explicit GsmSMSClientImpl(dbus::Bus* bus)
168      : bus_(bus),
169        proxies_deleter_(&proxies_) {
170  }
171
172  // GsmSMSClient override.
173  virtual void SetSmsReceivedHandler(
174      const std::string& service_name,
175      const dbus::ObjectPath& object_path,
176      const SmsReceivedHandler& handler) OVERRIDE {
177    GetProxy(service_name, object_path)->SetSmsReceivedHandler(handler);
178  }
179
180  // GsmSMSClient override.
181  virtual void ResetSmsReceivedHandler(
182      const std::string& service_name,
183      const dbus::ObjectPath& object_path) OVERRIDE {
184    GetProxy(service_name, object_path)->ResetSmsReceivedHandler();
185  }
186
187  // GsmSMSClient override.
188  virtual void Delete(const std::string& service_name,
189                      const dbus::ObjectPath& object_path,
190                      uint32 index,
191                      const DeleteCallback& callback) OVERRIDE {
192    GetProxy(service_name, object_path)->Delete(index, callback);
193  }
194
195  // GsmSMSClient override.
196  virtual void Get(const std::string& service_name,
197                   const dbus::ObjectPath& object_path,
198                   uint32 index,
199                   const GetCallback& callback) OVERRIDE {
200    GetProxy(service_name, object_path)->Get(index, callback);
201  }
202
203  // GsmSMSClient override.
204  virtual void List(const std::string& service_name,
205                    const dbus::ObjectPath& object_path,
206                    const ListCallback& callback) OVERRIDE {
207    GetProxy(service_name, object_path)->List(callback);
208  }
209
210  // GsmSMSClient override.
211  virtual void RequestUpdate(const std::string& service_name,
212                             const dbus::ObjectPath& object_path) OVERRIDE {
213  }
214
215 private:
216  typedef std::map<std::pair<std::string, std::string>, SMSProxy*> ProxyMap;
217
218  // Returns a SMSProxy for the given service name and object path.
219  SMSProxy* GetProxy(const std::string& service_name,
220                     const dbus::ObjectPath& object_path) {
221    const ProxyMap::key_type key(service_name, object_path.value());
222    ProxyMap::iterator it = proxies_.find(key);
223    if (it != proxies_.end())
224      return it->second;
225
226    // There is no proxy for the service_name and object_path, create it.
227    SMSProxy* proxy = new SMSProxy(bus_, service_name, object_path);
228    proxies_.insert(ProxyMap::value_type(key, proxy));
229    return proxy;
230  }
231
232  dbus::Bus* bus_;
233  ProxyMap proxies_;
234  STLValueDeleter<ProxyMap> proxies_deleter_;
235
236  DISALLOW_COPY_AND_ASSIGN(GsmSMSClientImpl);
237};
238
239// A stub implementaion of GsmSMSClient.
240class GsmSMSClientStubImpl : public GsmSMSClient {
241 public:
242  GsmSMSClientStubImpl() : test_index_(-1), weak_ptr_factory_(this) {
243    test_messages_.push_back("Test Message 0");
244    test_messages_.push_back("Test Message 1");
245    test_messages_.push_back("Test a relatively long message 2");
246    test_messages_.push_back("Test a very, the quick brown fox jumped"
247                             " over the lazy dog, long message 3");
248    test_messages_.push_back("Test Message 4");
249    test_messages_.push_back("Test Message 5");
250    test_messages_.push_back("Test Message 6");
251  }
252
253  virtual ~GsmSMSClientStubImpl() {}
254
255  // GsmSMSClient override.
256  virtual void SetSmsReceivedHandler(
257      const std::string& service_name,
258      const dbus::ObjectPath& object_path,
259      const SmsReceivedHandler& handler) OVERRIDE {
260    handler_ = handler;
261  }
262
263  // GsmSMSClient override.
264  virtual void ResetSmsReceivedHandler(
265      const std::string& service_name,
266      const dbus::ObjectPath& object_path) OVERRIDE {
267    handler_.Reset();
268  }
269
270  // GsmSMSClient override.
271  virtual void Delete(const std::string& service_name,
272                      const dbus::ObjectPath& object_path,
273                      uint32 index,
274                      const DeleteCallback& callback) OVERRIDE {
275    message_list_.Remove(index, NULL);
276    callback.Run();
277  }
278
279  // GsmSMSClient override.
280  virtual void Get(const std::string& service_name,
281                   const dbus::ObjectPath& object_path,
282                   uint32 index,
283                   const GetCallback& callback) OVERRIDE {
284    base::DictionaryValue* dictionary = NULL;
285    if (message_list_.GetDictionary(index, &dictionary)) {
286      callback.Run(*dictionary);
287      return;
288    }
289    base::DictionaryValue empty_dictionary;
290    callback.Run(empty_dictionary);
291  }
292
293  // GsmSMSClient override.
294  virtual void List(const std::string& service_name,
295                    const dbus::ObjectPath& object_path,
296                    const ListCallback& callback) OVERRIDE {
297    callback.Run(message_list_);
298  }
299
300  // GsmSMSClient override.
301  virtual void RequestUpdate(const std::string& service_name,
302                             const dbus::ObjectPath& object_path) OVERRIDE {
303    if (!CommandLine::ForCurrentProcess()->HasSwitch(
304            chromeos::switches::kSmsTestMessages))
305      return;
306    if (test_index_ >= 0)
307      return;
308    test_index_ = 0;
309    // Call PushTestMessageChain asynchronously so that the handler_ callback
310    // does not get called from the update request.
311    MessageLoop::current()->PostTask(
312        FROM_HERE,
313        base::Bind(&GsmSMSClientStubImpl::PushTestMessageChain,
314                   weak_ptr_factory_.GetWeakPtr()));
315  }
316
317 private:
318  void PushTestMessageChain() {
319    if (PushTestMessage())
320      PushTestMessageDelayed();
321  }
322
323  void PushTestMessageDelayed() {
324    const int kSmsMessageDelaySeconds = 5;
325    MessageLoop::current()->PostDelayedTask(
326        FROM_HERE,
327        base::Bind(&GsmSMSClientStubImpl::PushTestMessageChain,
328                   weak_ptr_factory_.GetWeakPtr()),
329        base::TimeDelta::FromSeconds(kSmsMessageDelaySeconds));
330  }
331
332  bool PushTestMessage() {
333    if (test_index_ >= static_cast<int>(test_messages_.size()))
334      return false;
335    base::DictionaryValue* message = new base::DictionaryValue;
336    message->SetString("number", "000-000-0000");
337    message->SetString("text", test_messages_[test_index_]);
338    message->SetInteger("index", test_index_);
339    int msg_index = message_list_.GetSize();
340    message_list_.Append(message);
341    if (!handler_.is_null())
342      handler_.Run(msg_index, true);
343    ++test_index_;
344    return true;
345  }
346
347  int test_index_;
348  std::vector<std::string> test_messages_;
349  base::ListValue message_list_;
350  SmsReceivedHandler handler_;
351  base::WeakPtrFactory<GsmSMSClientStubImpl> weak_ptr_factory_;
352
353  DISALLOW_COPY_AND_ASSIGN(GsmSMSClientStubImpl);
354};
355
356}  // namespace
357
358////////////////////////////////////////////////////////////////////////////////
359// GsmSMSClient
360
361GsmSMSClient::GsmSMSClient() {}
362
363GsmSMSClient::~GsmSMSClient() {}
364
365// static
366GsmSMSClient* GsmSMSClient::Create(DBusClientImplementationType type,
367                                   dbus::Bus* bus) {
368  if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
369    return new GsmSMSClientImpl(bus);
370  DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
371  return new GsmSMSClientStubImpl();
372}
373
374}  // namespace chromeos
375