blob: 3f3f4c10b967ce373023f2dbdf7f9129526313f1 [file] [log] [blame]
Christopher Wiley4b5f04c2014-03-27 14:45:37 -07001// Copyright 2014 The Chromium OS 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 "buffet/manager.h"
6
Alex Vakulenko7c3226e2014-05-07 17:35:24 -07007#include <map>
Alex Vakulenkoec41a0c2015-04-17 15:35:34 -07008#include <set>
Alex Vakulenko7c3226e2014-05-07 17:35:24 -07009#include <string>
10
Christopher Wiley4b5f04c2014-03-27 14:45:37 -070011#include <base/bind.h>
12#include <base/bind_helpers.h>
Alex Vakulenko2915a7b2015-10-07 17:04:00 -070013#include <base/files/file_enumerator.h>
14#include <base/files/file_util.h>
Alex Vakulenko4f7778e2014-09-11 16:57:24 -070015#include <base/json/json_reader.h>
Christopher Wiley2ffa0042014-05-05 16:09:16 -070016#include <base/json/json_writer.h>
Alex Vakulenkof7754542015-06-26 12:59:50 -070017#include <base/message_loop/message_loop.h>
Christopher Wiley8994e012015-02-06 17:51:43 -080018#include <base/time/time.h>
Alex Vakulenko0022b752015-10-02 11:09:59 -070019#include <chromeos/bind_lambda.h>
Alex Vakulenko24e5f5d2014-08-27 11:00:57 -070020#include <chromeos/dbus/async_event_sequencer.h>
21#include <chromeos/dbus/exported_object_manager.h>
22#include <chromeos/errors/error.h>
Garret Kelly89ba0362015-05-29 15:56:52 -040023#include <chromeos/http/http_transport.h>
Alex Vakulenko0022b752015-10-02 11:09:59 -070024#include <chromeos/http/http_utils.h>
Anton Muhin53e882a2014-11-22 05:59:14 +040025#include <chromeos/key_value_store.h>
Vitaly Bukae74c8722015-08-13 00:33:00 -070026#include <chromeos/message_loops/message_loop.h>
Alex Vakulenko0022b752015-10-02 11:09:59 -070027#include <chromeos/mime_utils.h>
Christopher Wiley2ffa0042014-05-05 16:09:16 -070028#include <dbus/bus.h>
Christopher Wiley54028f92014-04-01 17:33:29 -070029#include <dbus/object_path.h>
Alex Vakulenkof3d77e52014-04-15 11:36:32 -070030#include <dbus/values_util.h>
Vitaly Bukae2713ac2015-08-03 13:50:01 -070031#include <weave/enum_to_string.h>
Christopher Wiley4b5f04c2014-03-27 14:45:37 -070032
Robert Gindaf86123d2015-09-11 16:10:43 -070033#include "buffet/bluetooth_client.h"
Vitaly Buka1175a9b2015-08-15 10:42:17 -070034#include "buffet/buffet_config.h"
Vitaly Bukaa0305d32015-07-27 16:08:51 -070035#include "buffet/dbus_command_dispatcher.h"
36#include "buffet/dbus_conversion.h"
Vitaly Buka00882b72015-08-06 00:32:11 -070037#include "buffet/http_transport_client.h"
Alex Vakulenkof0f55342015-08-18 15:51:40 -070038#include "buffet/mdns_client.h"
Peter Qiu786a9062015-10-02 11:45:01 -070039#include "buffet/shill_client.h"
Vitaly Buka4f771532015-08-14 14:58:39 -070040#include "buffet/weave_error_conversion.h"
Alex Vakulenko1642bec2015-08-19 09:34:58 -070041#include "buffet/webserv_client.h"
Vitaly Buka03319c22015-07-17 14:48:30 -070042
Christopher Wiley68c07cc2014-07-29 14:07:10 -070043using chromeos::dbus_utils::AsyncEventSequencer;
Alex Vakulenkobe39e932015-10-09 08:10:36 -070044using chromeos::dbus_utils::DBusMethodResponse;
Christopher Wiley1aa980e2014-08-11 10:51:20 -070045using chromeos::dbus_utils::ExportedObjectManager;
Christopher Wiley4b5f04c2014-03-27 14:45:37 -070046
47namespace buffet {
48
Alex Vakulenkoecf961a2014-10-28 13:50:16 -070049namespace {
Vitaly Buka7b3ba792015-06-09 17:01:54 -070050
Vitaly Buka7b3ba792015-06-09 17:01:54 -070051const char kPairingSessionIdKey[] = "sessionId";
52const char kPairingModeKey[] = "mode";
53const char kPairingCodeKey[] = "code";
54
Vitaly Buka0c6dcd22015-07-10 00:12:25 -070055const char kErrorDomain[] = "buffet";
Alex Vakulenko2915a7b2015-10-07 17:04:00 -070056const char kFileReadError[] = "file_read_error";
57
58bool LoadFile(const base::FilePath& file_path,
59 std::string* data,
60 chromeos::ErrorPtr* error) {
61 if (!base::ReadFileToString(file_path, data)) {
62 chromeos::errors::system::AddSystemError(error, FROM_HERE, errno);
63 chromeos::Error::AddToPrintf(error, FROM_HERE, kErrorDomain, kFileReadError,
64 "Failed to read file '%s'",
65 file_path.value().c_str());
66 return false;
67 }
68 return true;
69}
70
71void LoadCommandDefinitions(const BuffetConfig::Options& options,
72 weave::Device* device) {
73 auto load_packages = [device](const base::FilePath& root,
74 const base::FilePath::StringType& pattern) {
75 base::FilePath dir{root.Append("commands")};
76 LOG(INFO) << "Looking for command schemas in " << dir.value();
77 base::FileEnumerator enumerator(dir, false, base::FileEnumerator::FILES,
78 pattern);
79 for (base::FilePath path = enumerator.Next(); !path.empty();
80 path = enumerator.Next()) {
81 LOG(INFO) << "Loading command schema from " << path.value();
82 std::string json;
83 CHECK(LoadFile(path, &json, nullptr));
84 device->AddCommandDefinitionsFromJson(json);
85 }
86 };
87 load_packages(options.definitions, FILE_PATH_LITERAL("*.json"));
88 load_packages(options.test_definitions, FILE_PATH_LITERAL("*test.json"));
89}
90
91void LoadStateDefinitions(const BuffetConfig::Options& options,
92 weave::Device* device) {
93 // Load component-specific device state definitions.
94 base::FilePath dir{options.definitions.Append("states")};
95 LOG(INFO) << "Looking for state definitions in " << dir.value();
96 base::FileEnumerator enumerator(dir, false, base::FileEnumerator::FILES,
97 FILE_PATH_LITERAL("*.schema.json"));
98 std::vector<std::string> result;
99 for (base::FilePath path = enumerator.Next(); !path.empty();
100 path = enumerator.Next()) {
101 LOG(INFO) << "Loading state definition from " << path.value();
102 std::string json;
103 CHECK(LoadFile(path, &json, nullptr));
104 device->AddStateDefinitionsFromJson(json);
105 }
106}
107
108void LoadStateDefaults(const BuffetConfig::Options& options,
109 weave::Device* device) {
110 // Load component-specific device state defaults.
111 base::FilePath dir{options.definitions.Append("states")};
112 LOG(INFO) << "Looking for state defaults in " << dir.value();
113 base::FileEnumerator enumerator(dir, false, base::FileEnumerator::FILES,
114 FILE_PATH_LITERAL("*.defaults.json"));
115 std::vector<std::string> result;
116 for (base::FilePath path = enumerator.Next(); !path.empty();
117 path = enumerator.Next()) {
118 LOG(INFO) << "Loading state defaults from " << path.value();
119 std::string json;
120 CHECK(LoadFile(path, &json, nullptr));
121 CHECK(device->SetStatePropertiesFromJson(json, nullptr));
122 }
123}
Vitaly Buka0c6dcd22015-07-10 00:12:25 -0700124
Alex Vakulenkobe39e932015-10-09 08:10:36 -0700125void RegisterDeviceSuccess(
126 const std::shared_ptr<DBusMethodResponse<std::string>>& response,
127 weave::Device* device) {
128 LOG(INFO) << "Device registered: " << device->GetSettings().cloud_id;
129 response->Return(device->GetSettings().cloud_id);
130}
131
132void RegisterDeviceError(
133 const std::shared_ptr<DBusMethodResponse<std::string>>& response,
134 const weave::Error* weave_error) {
135 chromeos::ErrorPtr error;
136 ConvertError(*weave_error, &error);
137 response->ReplyWithError(error.get());
138}
139
Alex Vakulenkoecf961a2014-10-28 13:50:16 -0700140} // anonymous namespace
141
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700142class Manager::TaskRunner : public weave::provider::TaskRunner {
Vitaly Bukae74c8722015-08-13 00:33:00 -0700143 public:
144 void PostDelayedTask(const tracked_objects::Location& from_here,
145 const base::Closure& task,
146 base::TimeDelta delay) override {
147 chromeos::MessageLoop::current()->PostDelayedTask(from_here, task, delay);
148 }
149};
150
Alex Vakulenko2915a7b2015-10-07 17:04:00 -0700151Manager::Manager(const Options& options,
152 const base::WeakPtr<ExportedObjectManager>& object_manager)
153 : options_{options},
154 dbus_object_(object_manager.get(),
Alex Vakulenkob6351532014-08-15 11:49:35 -0700155 object_manager->GetBus(),
Alex Vakulenko2915a7b2015-10-07 17:04:00 -0700156 com::android::Weave::ManagerAdaptor::GetObjectPath()) {}
Christopher Wiley4b5f04c2014-03-27 14:45:37 -0700157
Vitaly Buka91cc7152015-03-20 09:46:57 -0700158Manager::~Manager() {
159}
Alex Vakulenkoecf961a2014-10-28 13:50:16 -0700160
Alex Vakulenko2915a7b2015-10-07 17:04:00 -0700161void Manager::Start(AsyncEventSequencer* sequencer) {
162 RestartWeave(sequencer);
163
164 dbus_adaptor_.RegisterWithDBusObject(&dbus_object_);
165 dbus_object_.RegisterAsync(
166 sequencer->GetHandler("Manager.RegisterAsync() failed.", true));
167}
168
169void Manager::RestartWeave(AsyncEventSequencer* sequencer) {
170 Stop();
171
Vitaly Bukae74c8722015-08-13 00:33:00 -0700172 task_runner_.reset(new TaskRunner{});
Alex Vakulenko2915a7b2015-10-07 17:04:00 -0700173 config_.reset(new BuffetConfig{options_.config_options});
Vitaly Buka00882b72015-08-06 00:32:11 -0700174 http_client_.reset(new HttpTransportClient);
Alex Vakulenko2915a7b2015-10-07 17:04:00 -0700175 shill_client_.reset(new ShillClient{dbus_object_.GetBus(),
176 options_.device_whitelist,
177 !options_.xmpp_enabled});
Alex Vakulenko0022b752015-10-02 11:09:59 -0700178 weave::provider::HttpServer* http_server{nullptr};
Christopher Wileye925bb32015-08-03 20:09:18 -0700179#ifdef BUFFET_USE_WIFI_BOOTSTRAPPING
Alex Vakulenko2915a7b2015-10-07 17:04:00 -0700180 if (!options_.disable_privet) {
Robert Gindacf92c662015-08-20 09:30:11 -0700181 mdns_client_ = MdnsClient::CreateInstance(dbus_object_.GetBus());
Alex Vakulenkobe39e932015-10-09 08:10:36 -0700182 web_serv_client_.reset(new WebServClient{
183 dbus_object_.GetBus(), sequencer,
184 base::Bind(&Manager::CreateDevice, weak_ptr_factory_.GetWeakPtr())});
Robert Gindaf86123d2015-09-11 16:10:43 -0700185 bluetooth_client_ = BluetoothClient::CreateInstance();
Alex Vakulenko0022b752015-10-02 11:09:59 -0700186 http_server = web_serv_client_.get();
187
Alex Vakulenko2915a7b2015-10-07 17:04:00 -0700188 if (options_.enable_ping) {
Alex Vakulenkobe39e932015-10-09 08:10:36 -0700189 auto ping_handler = base::Bind(
190 [](std::unique_ptr<weave::provider::HttpServer::Request> request) {
191 request->SendReply(chromeos::http::status_code::Ok, "Hello, world!",
192 chromeos::mime::text::kPlain);
193 });
194 http_server->AddHttpRequestHandler("/privet/ping", ping_handler);
195 http_server->AddHttpsRequestHandler("/privet/ping", ping_handler);
Alex Vakulenko0022b752015-10-02 11:09:59 -0700196 }
Alex Vakulenkof0f55342015-08-18 15:51:40 -0700197 }
Christopher Wileye925bb32015-08-03 20:09:18 -0700198#endif // BUFFET_USE_WIFI_BOOTSTRAPPING
Vitaly Buka7042c582015-07-30 17:02:14 -0700199
Alex Vakulenkobe39e932015-10-09 08:10:36 -0700200 if (!http_server)
201 CreateDevice();
202}
203
204void Manager::CreateDevice() {
205 if (device_)
206 return;
207
Alex Vakulenko0022b752015-10-02 11:09:59 -0700208 device_ = weave::Device::Create(config_.get(), task_runner_.get(),
209 http_client_.get(), shill_client_.get(),
210 mdns_client_.get(), web_serv_client_.get(),
211 shill_client_.get(), bluetooth_client_.get());
Vitaly Buka1175a9b2015-08-15 10:42:17 -0700212
Alex Vakulenko2915a7b2015-10-07 17:04:00 -0700213 LoadCommandDefinitions(options_.config_options, device_.get());
214 LoadStateDefinitions(options_.config_options, device_.get());
215 LoadStateDefaults(options_.config_options, device_.get());
216
Alex Vakulenko0022b752015-10-02 11:09:59 -0700217 device_->AddSettingsChangedCallback(
218 base::Bind(&Manager::OnConfigChanged, weak_ptr_factory_.GetWeakPtr()));
Vitaly Buka0c6dcd22015-07-10 00:12:25 -0700219
Alex Vakulenko2915a7b2015-10-07 17:04:00 -0700220 command_dispatcher_.reset(
221 new DBusCommandDispacher{dbus_object_.GetObjectManager(), device_.get()});
Vitaly Bukae8c3bab2015-07-29 16:39:34 -0700222
Alex Vakulenko0022b752015-10-02 11:09:59 -0700223 device_->AddStateChangedCallback(
Vitaly Buka2b30e7a2015-05-27 09:27:08 -0700224 base::Bind(&Manager::OnStateChanged, weak_ptr_factory_.GetWeakPtr()));
Vitaly Bukabf4ba652015-05-14 16:57:23 -0700225
Alex Vakulenko0022b752015-10-02 11:09:59 -0700226 device_->AddGcdStateChangedCallback(
227 base::Bind(&Manager::OnGcdStateChanged, weak_ptr_factory_.GetWeakPtr()));
Vitaly Bukae74fe3c2015-05-13 13:48:59 -0700228
Alex Vakulenko2915a7b2015-10-07 17:04:00 -0700229 device_->AddPairingChangedCallbacks(
230 base::Bind(&Manager::OnPairingStart, weak_ptr_factory_.GetWeakPtr()),
231 base::Bind(&Manager::OnPairingEnd, weak_ptr_factory_.GetWeakPtr()));
Vitaly Buka84fd6dd2015-06-09 17:22:18 -0700232}
233
234void Manager::Stop() {
Alex Vakulenko2915a7b2015-10-07 17:04:00 -0700235 command_dispatcher_.reset();
Vitaly Buka0c6dcd22015-07-10 00:12:25 -0700236 device_.reset();
Alex Vakulenko2915a7b2015-10-07 17:04:00 -0700237#ifdef BUFFET_USE_WIFI_BOOTSTRAPPING
238 web_serv_client_.reset();
239 mdns_client_.reset();
240#endif // BUFFET_USE_WIFI_BOOTSTRAPPING
241 shill_client_.reset();
242 http_client_.reset();
243 config_.reset();
244 task_runner_.reset();
Christopher Wiley4b5f04c2014-03-27 14:45:37 -0700245}
246
Alex Vakulenko6c375262015-06-19 11:01:42 -0700247void Manager::RegisterDevice(DBusMethodResponsePtr<std::string> response,
Vitaly Bukacbadabe2015-05-14 23:33:32 -0700248 const std::string& ticket_id) {
Anton Muhin9cc03fd2014-10-16 18:59:57 +0400249 LOG(INFO) << "Received call to Manager.RegisterDevice()";
Alex Vakulenkof3d77e52014-04-15 11:36:32 -0700250
Alex Vakulenkobe39e932015-10-09 08:10:36 -0700251 std::shared_ptr<DBusMethodResponse<std::string>> shared_response =
252 std::move(response);
253
254 device_->Register(
255 ticket_id,
256 base::Bind(&RegisterDeviceSuccess, shared_response, device_.get()),
257 base::Bind(&RegisterDeviceError, shared_response));
Christopher Wiley4b5f04c2014-03-27 14:45:37 -0700258}
259
Alex Vakulenko6c375262015-06-19 11:01:42 -0700260void Manager::UpdateState(DBusMethodResponsePtr<> response,
Alex Vakulenko12e2c1a2014-11-21 08:57:57 -0800261 const chromeos::VariantDictionary& property_set) {
Vitaly Buka4f771532015-08-14 14:58:39 -0700262 chromeos::ErrorPtr chromeos_error;
263 auto properties =
264 DictionaryFromDBusVariantDictionary(property_set, &chromeos_error);
Vitaly Bukafb2584b2015-07-28 21:39:45 -0700265 if (!properties)
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700266 return response->ReplyWithError(chromeos_error.get());
Vitaly Bukafb2584b2015-07-28 21:39:45 -0700267
Vitaly Buka4f771532015-08-14 14:58:39 -0700268 weave::ErrorPtr error;
Alex Vakulenko0022b752015-10-02 11:09:59 -0700269 if (!device_->SetStateProperties(*properties, &error)) {
Vitaly Buka4f771532015-08-14 14:58:39 -0700270 ConvertError(*error, &chromeos_error);
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700271 return response->ReplyWithError(chromeos_error.get());
Vitaly Buka4f771532015-08-14 14:58:39 -0700272 }
273 response->Return();
Christopher Wiley4b5f04c2014-03-27 14:45:37 -0700274}
275
Alex Vakulenkoceab1772015-01-20 10:50:04 -0800276bool Manager::GetState(chromeos::ErrorPtr* error, std::string* state) {
Alex Vakulenko0022b752015-10-02 11:09:59 -0700277 auto json = device_->GetState();
Vitaly Bukacd5e5562015-07-28 15:33:55 -0700278 CHECK(json);
Nathan Bullockf5b91bf2015-04-01 15:32:58 -0400279 base::JSONWriter::WriteWithOptions(
Alex Vakulenkoab4e4ff2015-06-15 12:53:22 -0700280 *json, base::JSONWriter::OPTIONS_PRETTY_PRINT, state);
Alex Vakulenkoceab1772015-01-20 10:50:04 -0800281 return true;
282}
283
Alex Vakulenko6c375262015-06-19 11:01:42 -0700284void Manager::AddCommand(DBusMethodResponsePtr<std::string> response,
Alex Vakulenko0022b752015-10-02 11:09:59 -0700285 const std::string& json_command) {
Alex Vakulenko4f7778e2014-09-11 16:57:24 -0700286 std::string error_message;
Alex Vakulenkoab4e4ff2015-06-15 12:53:22 -0700287 std::unique_ptr<base::Value> value(
288 base::JSONReader::ReadAndReturnError(json_command, base::JSON_PARSE_RFC,
289 nullptr, &error_message)
290 .release());
Vitaly Bukaa4e8d7f2015-06-09 09:46:53 -0700291 const base::DictionaryValue* command{nullptr};
292 if (!value || !value->GetAsDictionary(&command)) {
293 return response->ReplyWithError(FROM_HERE, chromeos::errors::json::kDomain,
294 chromeos::errors::json::kParseError,
295 error_message);
Alex Vakulenko4f7778e2014-09-11 16:57:24 -0700296 }
Vitaly Bukaa4e8d7f2015-06-09 09:46:53 -0700297
Vitaly Bukaa4e8d7f2015-06-09 09:46:53 -0700298 std::string id;
Vitaly Buka4f771532015-08-14 14:58:39 -0700299 weave::ErrorPtr error;
Alex Vakulenko2915a7b2015-10-07 17:04:00 -0700300 if (!device_->AddCommand(*command, &id, &error)) {
Alex Vakulenko0022b752015-10-02 11:09:59 -0700301 chromeos::ErrorPtr chromeos_error;
Vitaly Buka4f771532015-08-14 14:58:39 -0700302 ConvertError(*error, &chromeos_error);
303 return response->ReplyWithError(chromeos_error.get());
304 }
Vitaly Bukac03ec2b2015-05-31 23:32:46 -0700305
Vitaly Buka59af7ac2015-03-24 12:42:24 -0700306 response->Return(id);
Alex Vakulenko4f7778e2014-09-11 16:57:24 -0700307}
308
Alex Vakulenko12e2c1a2014-11-21 08:57:57 -0800309std::string Manager::TestMethod(const std::string& message) {
Alex Vakulenko35e3bab2014-08-15 11:45:46 -0700310 LOG(INFO) << "Received call to test method: " << message;
311 return message;
Christopher Wiley2ffa0042014-05-05 16:09:16 -0700312}
313
Vitaly Buka2b30e7a2015-05-27 09:27:08 -0700314void Manager::OnStateChanged() {
Alex Vakulenko0022b752015-10-02 11:09:59 -0700315 auto state = device_->GetState();
Vitaly Buka2b30e7a2015-05-27 09:27:08 -0700316 CHECK(state);
317 std::string json;
318 base::JSONWriter::WriteWithOptions(
Alex Vakulenkoab4e4ff2015-06-15 12:53:22 -0700319 *state, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
Vitaly Buka2b30e7a2015-05-27 09:27:08 -0700320 dbus_adaptor_.SetState(json);
321}
322
Alex Vakulenko0022b752015-10-02 11:09:59 -0700323void Manager::OnGcdStateChanged(weave::GcdState state) {
324 dbus_adaptor_.SetStatus(weave::EnumToString(state));
Vitaly Bukabf4ba652015-05-14 16:57:23 -0700325}
326
Vitaly Buka00b83492015-07-20 00:37:48 -0700327void Manager::OnConfigChanged(const weave::Settings& settings) {
Alex Vakulenkoe4ef2612015-09-29 09:53:39 -0700328 dbus_adaptor_.SetDeviceId(settings.cloud_id);
Vitaly Buka00b83492015-07-20 00:37:48 -0700329 dbus_adaptor_.SetOemName(settings.oem_name);
330 dbus_adaptor_.SetModelName(settings.model_name);
331 dbus_adaptor_.SetModelId(settings.model_id);
332 dbus_adaptor_.SetName(settings.name);
333 dbus_adaptor_.SetDescription(settings.description);
334 dbus_adaptor_.SetLocation(settings.location);
Vitaly Buka7b3ba792015-06-09 17:01:54 -0700335}
336
337void Manager::OnPairingStart(const std::string& session_id,
Vitaly Buka0c6dcd22015-07-10 00:12:25 -0700338 weave::PairingType pairing_type,
Vitaly Buka7b3ba792015-06-09 17:01:54 -0700339 const std::vector<uint8_t>& code) {
340 // For now, just overwrite the exposed PairInfo with
341 // the most recent pairing attempt.
342 dbus_adaptor_.SetPairingInfo(chromeos::VariantDictionary{
343 {kPairingSessionIdKey, session_id},
Vitaly Buka03319c22015-07-17 14:48:30 -0700344 {kPairingModeKey, weave::EnumToString(pairing_type)},
Vitaly Buka7b3ba792015-06-09 17:01:54 -0700345 {kPairingCodeKey, code},
346 });
347}
348
349void Manager::OnPairingEnd(const std::string& session_id) {
350 auto exposed_pairing_attempt = dbus_adaptor_.GetPairingInfo();
351 auto it = exposed_pairing_attempt.find(kPairingSessionIdKey);
352 if (it == exposed_pairing_attempt.end()) {
353 return;
354 }
355 std::string exposed_session{it->second.TryGet<std::string>()};
356 if (exposed_session == session_id) {
357 dbus_adaptor_.SetPairingInfo(chromeos::VariantDictionary{});
358 }
359}
360
Christopher Wiley4b5f04c2014-03-27 14:45:37 -0700361} // namespace buffet