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/message_loop.h"
15#include "base/stl_util.h"
16#include "base/strings/stringprintf.h"
17#include "base/values.h"
18#include "chromeos/chromeos_switches.h"
19#include "chromeos/dbus/fake_gsm_sms_client.h"
20#include "dbus/bus.h"
21#include "dbus/message.h"
22#include "dbus/object_proxy.h"
23#include "dbus/values_util.h"
24#include "third_party/cros_system_api/dbus/service_constants.h"
25
26namespace chromeos {
27
28namespace {
29
30// A class actually making method calls for SMS services, used by
31// GsmSMSClientImpl.
32class SMSProxy {
33 public:
34  typedef GsmSMSClient::SmsReceivedHandler SmsReceivedHandler;
35  typedef GsmSMSClient::DeleteCallback DeleteCallback;
36  typedef GsmSMSClient::GetCallback GetCallback;
37  typedef GsmSMSClient::ListCallback ListCallback;
38
39  SMSProxy(dbus::Bus* bus,
40           const std::string& service_name,
41           const dbus::ObjectPath& object_path)
42      : proxy_(bus->GetObjectProxy(service_name, object_path)),
43        weak_ptr_factory_(this) {
44    proxy_->ConnectToSignal(
45        modemmanager::kModemManagerSMSInterface,
46        modemmanager::kSMSReceivedSignal,
47        base::Bind(&SMSProxy::OnSmsReceived, weak_ptr_factory_.GetWeakPtr()),
48        base::Bind(&SMSProxy::OnSignalConnected,
49                   weak_ptr_factory_.GetWeakPtr()));
50  }
51
52  // Sets SmsReceived signal handler.
53  void SetSmsReceivedHandler(const SmsReceivedHandler& handler) {
54    DCHECK(sms_received_handler_.is_null());
55    sms_received_handler_ = handler;
56  }
57
58  // Resets SmsReceived signal handler.
59  void ResetSmsReceivedHandler() {
60    sms_received_handler_.Reset();
61  }
62
63  // Calls Delete method.
64  void Delete(uint32 index, const DeleteCallback& callback) {
65    dbus::MethodCall method_call(modemmanager::kModemManagerSMSInterface,
66                                 modemmanager::kSMSDeleteFunction);
67    dbus::MessageWriter writer(&method_call);
68    writer.AppendUint32(index);
69    proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
70                       base::Bind(&SMSProxy::OnDelete,
71                                  weak_ptr_factory_.GetWeakPtr(),
72                                  callback));
73  }
74
75  // Calls Get method.
76  void Get(uint32 index, const GetCallback& callback) {
77    dbus::MethodCall method_call(modemmanager::kModemManagerSMSInterface,
78                                 modemmanager::kSMSGetFunction);
79    dbus::MessageWriter writer(&method_call);
80    writer.AppendUint32(index);
81    proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
82                       base::Bind(&SMSProxy::OnGet,
83                                  weak_ptr_factory_.GetWeakPtr(),
84                                  callback));
85  }
86
87  // Calls List method.
88  void List(const ListCallback& callback) {
89    dbus::MethodCall method_call(modemmanager::kModemManagerSMSInterface,
90                                 modemmanager::kSMSListFunction);
91    proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
92                       base::Bind(&SMSProxy::OnList,
93                                  weak_ptr_factory_.GetWeakPtr(),
94                                  callback));
95  }
96
97 private:
98  // Handles SmsReceived signal.
99  void OnSmsReceived(dbus::Signal* signal) {
100    uint32 index = 0;
101    bool complete = false;
102    dbus::MessageReader reader(signal);
103    if (!reader.PopUint32(&index) ||
104        !reader.PopBool(&complete)) {
105      LOG(ERROR) << "Invalid signal: " << signal->ToString();
106      return;
107    }
108    if (!sms_received_handler_.is_null())
109      sms_received_handler_.Run(index, complete);
110  }
111
112  // Handles the result of signal connection setup.
113  void OnSignalConnected(const std::string& interface,
114                         const std::string& signal,
115                         bool succeeded) {
116    LOG_IF(ERROR, !succeeded) << "Connect to " << interface << " " <<
117        signal << " failed.";
118  }
119
120  // Handles responses of Delete method calls.
121  void OnDelete(const DeleteCallback& callback, dbus::Response* response) {
122    if (!response)
123      return;
124    callback.Run();
125  }
126
127  // Handles responses of Get method calls.
128  void OnGet(const GetCallback& callback, dbus::Response* response) {
129    if (!response)
130      return;
131    dbus::MessageReader reader(response);
132    scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
133    base::DictionaryValue* dictionary_value = NULL;
134    if (!value.get() || !value->GetAsDictionary(&dictionary_value)) {
135      LOG(WARNING) << "Invalid response: " << response->ToString();
136      return;
137    }
138    callback.Run(*dictionary_value);
139  }
140
141  // Handles responses of List method calls.
142  void OnList(const ListCallback& callback, dbus::Response* response) {
143    if (!response)
144      return;
145    dbus::MessageReader reader(response);
146    scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
147    base::ListValue* list_value = NULL;
148    if (!value.get() || !value->GetAsList(&list_value)) {
149      LOG(WARNING) << "Invalid response: " << response->ToString();
150      return;
151    }
152    callback.Run(*list_value);
153  }
154
155  dbus::ObjectProxy* proxy_;
156  SmsReceivedHandler sms_received_handler_;
157
158  // Note: This should remain the last member so it'll be destroyed and
159  // invalidate its weak pointers before any other members are destroyed.
160  base::WeakPtrFactory<SMSProxy> weak_ptr_factory_;
161
162  DISALLOW_COPY_AND_ASSIGN(SMSProxy);
163};
164
165// The GsmSMSClient implementation.
166class GsmSMSClientImpl : public GsmSMSClient {
167 public:
168  explicit GsmSMSClientImpl(dbus::Bus* bus)
169      : bus_(bus),
170        proxies_deleter_(&proxies_) {
171  }
172
173  // GsmSMSClient override.
174  virtual void SetSmsReceivedHandler(
175      const std::string& service_name,
176      const dbus::ObjectPath& object_path,
177      const SmsReceivedHandler& handler) OVERRIDE {
178    GetProxy(service_name, object_path)->SetSmsReceivedHandler(handler);
179  }
180
181  // GsmSMSClient override.
182  virtual void ResetSmsReceivedHandler(
183      const std::string& service_name,
184      const dbus::ObjectPath& object_path) OVERRIDE {
185    GetProxy(service_name, object_path)->ResetSmsReceivedHandler();
186  }
187
188  // GsmSMSClient override.
189  virtual void Delete(const std::string& service_name,
190                      const dbus::ObjectPath& object_path,
191                      uint32 index,
192                      const DeleteCallback& callback) OVERRIDE {
193    GetProxy(service_name, object_path)->Delete(index, callback);
194  }
195
196  // GsmSMSClient override.
197  virtual void Get(const std::string& service_name,
198                   const dbus::ObjectPath& object_path,
199                   uint32 index,
200                   const GetCallback& callback) OVERRIDE {
201    GetProxy(service_name, object_path)->Get(index, callback);
202  }
203
204  // GsmSMSClient override.
205  virtual void List(const std::string& service_name,
206                    const dbus::ObjectPath& object_path,
207                    const ListCallback& callback) OVERRIDE {
208    GetProxy(service_name, object_path)->List(callback);
209  }
210
211  // GsmSMSClient override.
212  virtual void RequestUpdate(const std::string& service_name,
213                             const dbus::ObjectPath& object_path) OVERRIDE {
214  }
215
216 private:
217  typedef std::map<std::pair<std::string, std::string>, SMSProxy*> ProxyMap;
218
219  // Returns a SMSProxy for the given service name and object path.
220  SMSProxy* GetProxy(const std::string& service_name,
221                     const dbus::ObjectPath& object_path) {
222    const ProxyMap::key_type key(service_name, object_path.value());
223    ProxyMap::iterator it = proxies_.find(key);
224    if (it != proxies_.end())
225      return it->second;
226
227    // There is no proxy for the service_name and object_path, create it.
228    SMSProxy* proxy = new SMSProxy(bus_, service_name, object_path);
229    proxies_.insert(ProxyMap::value_type(key, proxy));
230    return proxy;
231  }
232
233  dbus::Bus* bus_;
234  ProxyMap proxies_;
235  STLValueDeleter<ProxyMap> proxies_deleter_;
236
237  DISALLOW_COPY_AND_ASSIGN(GsmSMSClientImpl);
238};
239
240}  // namespace
241
242////////////////////////////////////////////////////////////////////////////////
243// GsmSMSClient
244
245GsmSMSClient::GsmSMSClient() {}
246
247GsmSMSClient::~GsmSMSClient() {}
248
249// static
250GsmSMSClient* GsmSMSClient::Create(DBusClientImplementationType type,
251                                   dbus::Bus* bus) {
252  if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
253    return new GsmSMSClientImpl(bus);
254  DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
255
256  FakeGsmSMSClient* fake = new FakeGsmSMSClient();
257  fake->set_sms_test_message_switch_present(
258      CommandLine::ForCurrentProcess()->HasSwitch(
259          chromeos::switches::kSmsTestMessages));
260  return fake;
261}
262
263}  // namespace chromeos
264