14615e0d5aa416ab1a8596bde68f71f7ebe431b86Vitaly Buka// Copyright 2015 The Weave Authors. All rights reserved. 23cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko// Use of this source code is governed by a BSD-style license that can be 33cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko// found in the LICENSE file. 43cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 52d16dfa768282b29f3fd5a905b52e3393a083e0dStefan Sauer#include "src/device_registration_info.h" 63cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 7f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko#include <algorithm> 8006e94e760088bb75f1a8174034f5715c979c6b7Christopher Wiley#include <memory> 99ea5a321746d94ed46c79633b75357f4cc77562eAlex Vakulenko#include <set> 10b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko#include <utility> 11b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko#include <vector> 12006e94e760088bb75f1a8174034f5715c979c6b7Christopher Wiley 13cd4196605e88eac2d5fe78a58aa5aa1c40ee8f77Christopher Wiley#include <base/bind.h> 146da9425a14ac345b657354e62307d17df4a721dbVitaly Buka#include <base/json/json_reader.h> 153cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko#include <base/json/json_writer.h> 16fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko#include <base/strings/string_number_conversions.h> 173c2b3038e8f76578fc95cccc7238e561bf73ed0eAlex Vakulenko#include <base/strings/stringprintf.h> 183cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko#include <base/values.h> 191e3636732171afb8cceb9e5cb835ec6a93787dbaVitaly Buka#include <weave/provider/http_client.h> 201e3636732171afb8cceb9e5cb835ec6a93787dbaVitaly Buka#include <weave/provider/network.h> 211e3636732171afb8cceb9e5cb835ec6a93787dbaVitaly Buka#include <weave/provider/task_runner.h> 223cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 232d16dfa768282b29f3fd5a905b52e3393a083e0dStefan Sauer#include "src/bind_lambda.h" 242d16dfa768282b29f3fd5a905b52e3393a083e0dStefan Sauer#include "src/commands/cloud_command_proxy.h" 252d16dfa768282b29f3fd5a905b52e3393a083e0dStefan Sauer#include "src/commands/schema_constants.h" 262d16dfa768282b29f3fd5a905b52e3393a083e0dStefan Sauer#include "src/data_encoding.h" 272d16dfa768282b29f3fd5a905b52e3393a083e0dStefan Sauer#include "src/http_constants.h" 282d16dfa768282b29f3fd5a905b52e3393a083e0dStefan Sauer#include "src/json_error_codes.h" 292d16dfa768282b29f3fd5a905b52e3393a083e0dStefan Sauer#include "src/notification/xmpp_channel.h" 3072d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka#include "src/privet/auth_manager.h" 312d16dfa768282b29f3fd5a905b52e3393a083e0dStefan Sauer#include "src/string_utils.h" 322d16dfa768282b29f3fd5a905b52e3393a083e0dStefan Sauer#include "src/utils.h" 333cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 34b6f015a1ef3caffbc2af53184c0ec5342e42e048Vitaly Bukanamespace weave { 35b6f015a1ef3caffbc2af53184c0ec5342e42e048Vitaly Buka 360a8ab9973e1fdf6b41d8f7b49529c9bd97a9071cVitaly Bukaconst char kErrorAlreayRegistered[] = "already_registered"; 373cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 388e34d391dada2fc3255fecd724f6302e7952be51Alex Vakulenkonamespace { 398e34d391dada2fc3255fecd724f6302e7952be51Alex Vakulenko 4041a90d69918c6367ba23de1edf0bad6c7175f3b9Vitaly Bukaconst int kPollingPeriodSeconds = 7; 41c1fc90c1724427d8e926172ed258e05ff63b8e26Alex Vakulenkoconst int kBackupPollingPeriodMinutes = 30; 4241a90d69918c6367ba23de1edf0bad6c7175f3b9Vitaly Buka 43e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenkonamespace fetch_reason { 44e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko 45e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenkoconst char kDeviceStart[] = "device_start"; // Initial queue fetch at startup. 46e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenkoconst char kRegularPull[] = "regular_pull"; // Regular fetch before XMPP is up. 47e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenkoconst char kNewCommand[] = "new_command"; // A new command is available. 48e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenkoconst char kJustInCase[] = "just_in_case"; // Backup fetch when XMPP is live. 49e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko 50e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko} // namespace fetch_reason 51e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko 521e3636732171afb8cceb9e5cb835ec6a93787dbaVitaly Bukausing provider::HttpClient; 531e3636732171afb8cceb9e5cb835ec6a93787dbaVitaly Buka 540801a1f20a0c8f34130d567cd3b7dcbd2be9cb3cVitaly Bukainline void SetUnexpectedError(ErrorPtr* error) { 5548a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka Error::AddTo(error, FROM_HERE, "unexpected_response", "Unexpected GCD error"); 56b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko} 57b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko 580801a1f20a0c8f34130d567cd3b7dcbd2be9cb3cVitaly Bukavoid ParseGCDError(const base::DictionaryValue* json, ErrorPtr* error) { 59b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko const base::Value* list_value = nullptr; 60b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko const base::ListValue* error_list = nullptr; 61b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko if (!json->Get("error.errors", &list_value) || 62b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko !list_value->GetAsList(&error_list)) { 63b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko SetUnexpectedError(error); 64b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko return; 65b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko } 66b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko 67b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko for (size_t i = 0; i < error_list->GetSize(); i++) { 68b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko const base::Value* error_value = nullptr; 69b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko const base::DictionaryValue* error_object = nullptr; 70b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko if (!error_list->Get(i, &error_value) || 71b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko !error_value->GetAsDictionary(&error_object)) { 72b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko SetUnexpectedError(error); 73b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko continue; 74b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko } 75b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko std::string error_code, error_message; 76b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko if (error_object->GetString("reason", &error_code) && 77b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko error_object->GetString("message", &error_message)) { 7848a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka Error::AddTo(error, FROM_HERE, error_code, error_message); 79b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko } else { 80b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko SetUnexpectedError(error); 81b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko } 82b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko } 83b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko} 84b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko 857d55639775947b7b34b33702a886aac385f35102Vitaly Bukastd::string AppendQueryParams(const std::string& url, 867d55639775947b7b34b33702a886aac385f35102Vitaly Buka const WebParamList& params) { 87b001f28251931cb88831e1d83f81034ae0c407ddVitaly Buka CHECK_EQ(std::string::npos, url.find_first_of("?#")); 88b001f28251931cb88831e1d83f81034ae0c407ddVitaly Buka if (params.empty()) 89b001f28251931cb88831e1d83f81034ae0c407ddVitaly Buka return url; 907d55639775947b7b34b33702a886aac385f35102Vitaly Buka return url + '?' + WebParamsEncode(params); 91b001f28251931cb88831e1d83f81034ae0c407ddVitaly Buka} 92b001f28251931cb88831e1d83f81034ae0c407ddVitaly Buka 93bda220ae043637838e0570d4eb695b4f6b818c06Alex Vakulenkostd::string BuildURL(const std::string& url, 94b001f28251931cb88831e1d83f81034ae0c407ddVitaly Buka const std::string& subpath, 957d55639775947b7b34b33702a886aac385f35102Vitaly Buka const WebParamList& params) { 96b001f28251931cb88831e1d83f81034ae0c407ddVitaly Buka std::string result = url; 97b001f28251931cb88831e1d83f81034ae0c407ddVitaly Buka if (!result.empty() && result.back() != '/' && !subpath.empty()) { 98b001f28251931cb88831e1d83f81034ae0c407ddVitaly Buka CHECK_NE('/', subpath.front()); 99b001f28251931cb88831e1d83f81034ae0c407ddVitaly Buka result += '/'; 100b001f28251931cb88831e1d83f81034ae0c407ddVitaly Buka } 101b001f28251931cb88831e1d83f81034ae0c407ddVitaly Buka result += subpath; 102b001f28251931cb88831e1d83f81034ae0c407ddVitaly Buka return AppendQueryParams(result, params); 1033cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko} 1043cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 105f7f52d4707c007bb9255bd80b23ac3428c6fc2e0Vitaly Bukavoid IgnoreCloudErrorWithCallback(const base::Closure& cb, ErrorPtr) { 106ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley cb.Run(); 107ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley} 108ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley 109747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Bukavoid IgnoreCloudError(ErrorPtr) {} 1106d2569e4260293816b8b9416d5e6993f843869b2Anton Muhin 111747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Bukavoid IgnoreCloudResult(const base::DictionaryValue&, ErrorPtr error) {} 112747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka 113747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Bukavoid IgnoreCloudResultWithCallback(const DoneCallback& cb, 114747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const base::DictionaryValue&, 115747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka ErrorPtr error) { 116747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka cb.Run(std::move(error)); 117ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley} 118ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley 1196da9425a14ac345b657354e62307d17df4a721dbVitaly Bukaclass RequestSender final { 1206da9425a14ac345b657354e62307d17df4a721dbVitaly Buka public: 1211a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka RequestSender(HttpClient::Method method, 1226da9425a14ac345b657354e62307d17df4a721dbVitaly Buka const std::string& url, 1236da9425a14ac345b657354e62307d17df4a721dbVitaly Buka HttpClient* transport) 1246da9425a14ac345b657354e62307d17df4a721dbVitaly Buka : method_{method}, url_{url}, transport_{transport} {} 1256da9425a14ac345b657354e62307d17df4a721dbVitaly Buka 126747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka void Send(const HttpClient::SendRequestCallback& callback) { 127866b60a19c99f6db4def7f18cc902370d344a33aVitaly Buka static int debug_id = 0; 128866b60a19c99f6db4def7f18cc902370d344a33aVitaly Buka ++debug_id; 1291a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka VLOG(1) << "Sending request. id:" << debug_id 1301a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka << " method:" << EnumToString(method_) << " url:" << url_; 131866b60a19c99f6db4def7f18cc902370d344a33aVitaly Buka VLOG(2) << "Request data: " << data_; 132747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka auto on_done = []( 133747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka int debug_id, const HttpClient::SendRequestCallback& callback, 134747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka std::unique_ptr<HttpClient::Response> response, ErrorPtr error) { 135747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (error) { 136747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka VLOG(1) << "Request failed, id=" << debug_id 137747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka << ", reason: " << error->GetCode() 138747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka << ", message: " << error->GetMessage(); 139747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return callback.Run({}, std::move(error)); 140747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka } 141747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka VLOG(1) << "Request succeeded. id:" << debug_id 142747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka << " status:" << response->GetStatusCode(); 143747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka VLOG(2) << "Response data: " << response->GetData(); 144747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka callback.Run(std::move(response), nullptr); 145866b60a19c99f6db4def7f18cc902370d344a33aVitaly Buka }; 146866b60a19c99f6db4def7f18cc902370d344a33aVitaly Buka transport_->SendRequest(method_, url_, GetFullHeaders(), data_, 147747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka base::Bind(on_done, debug_id, callback)); 1486da9425a14ac345b657354e62307d17df4a721dbVitaly Buka } 1496da9425a14ac345b657354e62307d17df4a721dbVitaly Buka 150815b603b59bebac4407322af61a822413ad421f6Vitaly Buka void SetAccessToken(const std::string& access_token) { 151815b603b59bebac4407322af61a822413ad421f6Vitaly Buka access_token_ = access_token; 1526da9425a14ac345b657354e62307d17df4a721dbVitaly Buka } 1536da9425a14ac345b657354e62307d17df4a721dbVitaly Buka 1546da9425a14ac345b657354e62307d17df4a721dbVitaly Buka void SetData(const std::string& data, const std::string& mime_type) { 1556da9425a14ac345b657354e62307d17df4a721dbVitaly Buka data_ = data; 1566da9425a14ac345b657354e62307d17df4a721dbVitaly Buka mime_type_ = mime_type; 1576da9425a14ac345b657354e62307d17df4a721dbVitaly Buka } 1586da9425a14ac345b657354e62307d17df4a721dbVitaly Buka 1596da9425a14ac345b657354e62307d17df4a721dbVitaly Buka void SetFormData( 1606da9425a14ac345b657354e62307d17df4a721dbVitaly Buka const std::vector<std::pair<std::string, std::string>>& data) { 1617d55639775947b7b34b33702a886aac385f35102Vitaly Buka SetData(WebParamsEncode(data), http::kWwwFormUrlEncoded); 1626da9425a14ac345b657354e62307d17df4a721dbVitaly Buka } 1636da9425a14ac345b657354e62307d17df4a721dbVitaly Buka 1646da9425a14ac345b657354e62307d17df4a721dbVitaly Buka void SetJsonData(const base::Value& json) { 1656da9425a14ac345b657354e62307d17df4a721dbVitaly Buka std::string data; 166815b603b59bebac4407322af61a822413ad421f6Vitaly Buka CHECK(base::JSONWriter::Write(json, &data)); 167815b603b59bebac4407322af61a822413ad421f6Vitaly Buka SetData(data, http::kJsonUtf8); 1686da9425a14ac345b657354e62307d17df4a721dbVitaly Buka } 1696da9425a14ac345b657354e62307d17df4a721dbVitaly Buka 1706da9425a14ac345b657354e62307d17df4a721dbVitaly Buka private: 171815b603b59bebac4407322af61a822413ad421f6Vitaly Buka HttpClient::Headers GetFullHeaders() const { 172815b603b59bebac4407322af61a822413ad421f6Vitaly Buka HttpClient::Headers headers; 173815b603b59bebac4407322af61a822413ad421f6Vitaly Buka if (!access_token_.empty()) 174815b603b59bebac4407322af61a822413ad421f6Vitaly Buka headers.emplace_back(http::kAuthorization, "Bearer " + access_token_); 175815b603b59bebac4407322af61a822413ad421f6Vitaly Buka if (!mime_type_.empty()) 176815b603b59bebac4407322af61a822413ad421f6Vitaly Buka headers.emplace_back(http::kContentType, mime_type_); 177815b603b59bebac4407322af61a822413ad421f6Vitaly Buka return headers; 178815b603b59bebac4407322af61a822413ad421f6Vitaly Buka } 179815b603b59bebac4407322af61a822413ad421f6Vitaly Buka 1801a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka HttpClient::Method method_; 1816da9425a14ac345b657354e62307d17df4a721dbVitaly Buka std::string url_; 1826da9425a14ac345b657354e62307d17df4a721dbVitaly Buka std::string data_; 1836da9425a14ac345b657354e62307d17df4a721dbVitaly Buka std::string mime_type_; 184815b603b59bebac4407322af61a822413ad421f6Vitaly Buka std::string access_token_; 1856da9425a14ac345b657354e62307d17df4a721dbVitaly Buka HttpClient* transport_{nullptr}; 1866da9425a14ac345b657354e62307d17df4a721dbVitaly Buka 1876da9425a14ac345b657354e62307d17df4a721dbVitaly Buka DISALLOW_COPY_AND_ASSIGN(RequestSender); 1886da9425a14ac345b657354e62307d17df4a721dbVitaly Buka}; 1896da9425a14ac345b657354e62307d17df4a721dbVitaly Buka 1906da9425a14ac345b657354e62307d17df4a721dbVitaly Bukastd::unique_ptr<base::DictionaryValue> ParseJsonResponse( 1916da9425a14ac345b657354e62307d17df4a721dbVitaly Buka const HttpClient::Response& response, 1920801a1f20a0c8f34130d567cd3b7dcbd2be9cb3cVitaly Buka ErrorPtr* error) { 1936da9425a14ac345b657354e62307d17df4a721dbVitaly Buka // Make sure we have a correct content type. Do not try to parse 1946da9425a14ac345b657354e62307d17df4a721dbVitaly Buka // binary files, or HTML output. Limit to application/json and text/plain. 19524d6fd54e9b10ae13e09ca91c726a994853def0dVitaly Buka std::string content_type = 19624d6fd54e9b10ae13e09ca91c726a994853def0dVitaly Buka SplitAtFirst(response.GetContentType(), ";", true).first; 1971da659947e800069a6352dbe7f829dfdf1697fedVitaly Buka 1981da659947e800069a6352dbe7f829dfdf1697fedVitaly Buka if (content_type != http::kJson && content_type != http::kPlain) { 1990dbbf605efb8f72b3c2c15c14e613323fc2ac0a2Vitaly Buka return Error::AddTo( 20048a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka error, FROM_HERE, "non_json_content_type", 201c27390d203688b847378944d5ae0ec8f1938c598Vitaly Buka "Unexpected content type: \'" + response.GetContentType() + "\'"); 2026da9425a14ac345b657354e62307d17df4a721dbVitaly Buka } 2036da9425a14ac345b657354e62307d17df4a721dbVitaly Buka 2046da9425a14ac345b657354e62307d17df4a721dbVitaly Buka const std::string& json = response.GetData(); 2056da9425a14ac345b657354e62307d17df4a721dbVitaly Buka std::string error_message; 2066da9425a14ac345b657354e62307d17df4a721dbVitaly Buka auto value = base::JSONReader::ReadAndReturnError(json, base::JSON_PARSE_RFC, 2076da9425a14ac345b657354e62307d17df4a721dbVitaly Buka nullptr, &error_message); 2086da9425a14ac345b657354e62307d17df4a721dbVitaly Buka if (!value) { 20948a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka Error::AddToPrintf(error, FROM_HERE, errors::json::kParseError, 2100801a1f20a0c8f34130d567cd3b7dcbd2be9cb3cVitaly Buka "Error '%s' occurred parsing JSON string '%s'", 2110801a1f20a0c8f34130d567cd3b7dcbd2be9cb3cVitaly Buka error_message.c_str(), json.c_str()); 2126da9425a14ac345b657354e62307d17df4a721dbVitaly Buka return std::unique_ptr<base::DictionaryValue>(); 2136da9425a14ac345b657354e62307d17df4a721dbVitaly Buka } 2146da9425a14ac345b657354e62307d17df4a721dbVitaly Buka base::DictionaryValue* dict_value = nullptr; 2156da9425a14ac345b657354e62307d17df4a721dbVitaly Buka if (!value->GetAsDictionary(&dict_value)) { 21648a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka Error::AddToPrintf(error, FROM_HERE, errors::json::kObjectExpected, 21748a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka "Response is not a valid JSON object: '%s'", 21848a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka json.c_str()); 2196da9425a14ac345b657354e62307d17df4a721dbVitaly Buka return std::unique_ptr<base::DictionaryValue>(); 2206da9425a14ac345b657354e62307d17df4a721dbVitaly Buka } else { 2216da9425a14ac345b657354e62307d17df4a721dbVitaly Buka // |value| is now owned by |dict_value|, so release the scoped_ptr now. 2226da9425a14ac345b657354e62307d17df4a721dbVitaly Buka base::IgnoreResult(value.release()); 2236da9425a14ac345b657354e62307d17df4a721dbVitaly Buka } 2246da9425a14ac345b657354e62307d17df4a721dbVitaly Buka return std::unique_ptr<base::DictionaryValue>(dict_value); 2256da9425a14ac345b657354e62307d17df4a721dbVitaly Buka} 2266da9425a14ac345b657354e62307d17df4a721dbVitaly Buka 2276da9425a14ac345b657354e62307d17df4a721dbVitaly Bukabool IsSuccessful(const HttpClient::Response& response) { 2286da9425a14ac345b657354e62307d17df4a721dbVitaly Buka int code = response.GetStatusCode(); 2291da659947e800069a6352dbe7f829dfdf1697fedVitaly Buka return code >= http::kContinue && code < http::kBadRequest; 2306da9425a14ac345b657354e62307d17df4a721dbVitaly Buka} 2316da9425a14ac345b657354e62307d17df4a721dbVitaly Buka 2328e34d391dada2fc3255fecd724f6302e7952be51Alex Vakulenko} // anonymous namespace 2333cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 2341f30a622b607def02ff9558f8baf8a9b919eee91Alex VakulenkoDeviceRegistrationInfo::DeviceRegistrationInfo( 235666b43e92729fa170bc53eab9040a4dfe58b8062Vitaly Buka Config* config, 236d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko ComponentManager* component_manager, 2371e3636732171afb8cceb9e5cb835ec6a93787dbaVitaly Buka provider::TaskRunner* task_runner, 2381e3636732171afb8cceb9e5cb835ec6a93787dbaVitaly Buka provider::HttpClient* http_client, 2393d851b43ae4a779b507b253dd920c6c33497722fVitaly Buka provider::Network* network, 2403d851b43ae4a779b507b253dd920c6c33497722fVitaly Buka privet::AuthManager* auth_manager) 2411020618923f7bf3066c8d36bbfe0a855eb58a974Vitaly Buka : http_client_{http_client}, 242a56a7e630a25e66118f0cba2678d58b3c20cbdacAlex Vakulenko task_runner_{task_runner}, 243666b43e92729fa170bc53eab9040a4dfe58b8062Vitaly Buka config_{config}, 244d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko component_manager_{component_manager}, 2453d851b43ae4a779b507b253dd920c6c33497722fVitaly Buka network_{network}, 2463d851b43ae4a779b507b253dd920c6c33497722fVitaly Buka auth_manager_{auth_manager} { 2470f80f7c281a0363c2e1e7302f938291b968aea87Vitaly Buka cloud_backoff_policy_.reset(new BackoffEntry::Policy{}); 248266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko cloud_backoff_policy_->num_errors_to_ignore = 0; 249f61ee017872aa93e53d23fdd79ab16c6fac98c0cAlex Vakulenko cloud_backoff_policy_->initial_delay_ms = 1000; 250266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko cloud_backoff_policy_->multiply_factor = 2.0; 251266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko cloud_backoff_policy_->jitter_factor = 0.1; 252266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko cloud_backoff_policy_->maximum_backoff_ms = 30000; 253266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko cloud_backoff_policy_->entry_lifetime_ms = -1; 254266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko cloud_backoff_policy_->always_use_initial_delay = false; 2550f80f7c281a0363c2e1e7302f938291b968aea87Vitaly Buka cloud_backoff_entry_.reset(new BackoffEntry{cloud_backoff_policy_.get()}); 2560f80f7c281a0363c2e1e7302f938291b968aea87Vitaly Buka oauth2_backoff_entry_.reset(new BackoffEntry{cloud_backoff_policy_.get()}); 257266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko 258672634b8b0ed31891fb48e02dce75b6aead0be27Vitaly Buka bool revoked = 259672634b8b0ed31891fb48e02dce75b6aead0be27Vitaly Buka !GetSettings().cloud_id.empty() && !HaveRegistrationCredentials(); 260672634b8b0ed31891fb48e02dce75b6aead0be27Vitaly Buka gcd_state_ = 261672634b8b0ed31891fb48e02dce75b6aead0be27Vitaly Buka revoked ? GcdState::kInvalidCredentials : GcdState::kUnconfigured; 262672634b8b0ed31891fb48e02dce75b6aead0be27Vitaly Buka 26334668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka component_manager_->AddTraitDefChangedCallback(base::Bind( 26434668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka &DeviceRegistrationInfo::OnTraitDefsChanged, weak_factory_.GetWeakPtr())); 26534668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka component_manager_->AddComponentTreeChangedCallback( 26634668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka base::Bind(&DeviceRegistrationInfo::OnComponentTreeChanged, 2679ea5a321746d94ed46c79633b75357f4cc77562eAlex Vakulenko weak_factory_.GetWeakPtr())); 268d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko component_manager_->AddStateChangedCallback(base::Bind( 269a647c857f3098b366b379bde308d051f6a9aac2fVitaly Buka &DeviceRegistrationInfo::OnStateChanged, weak_factory_.GetWeakPtr())); 270a3062c51e29efac97c51272b2b7d8b6b9fd593c1Alex Vakulenko} 2713cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 272332df1989dfbba0ba339ef6377f453984efcc4a7Anton MuhinDeviceRegistrationInfo::~DeviceRegistrationInfo() = default; 273332df1989dfbba0ba339ef6377f453984efcc4a7Anton Muhin 2743cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenkostd::string DeviceRegistrationInfo::GetServiceURL( 275b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko const std::string& subpath, 2767d55639775947b7b34b33702a886aac385f35102Vitaly Buka const WebParamList& params) const { 2775cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka return BuildURL(GetSettings().service_url, subpath, params); 2783cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko} 2793cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 2803cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenkostd::string DeviceRegistrationInfo::GetDeviceURL( 281b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko const std::string& subpath, 2827d55639775947b7b34b33702a886aac385f35102Vitaly Buka const WebParamList& params) const { 283312c2f5d0d5e2ef0cc7acc6dce5137094332c276Johan Euphrosine CHECK(!GetSettings().cloud_id.empty()) << "Must have a valid device ID"; 2845cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka return BuildURL(GetSettings().service_url, 285312c2f5d0d5e2ef0cc7acc6dce5137094332c276Johan Euphrosine "devices/" + GetSettings().cloud_id + "/" + subpath, params); 2863cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko} 2873cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 288b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenkostd::string DeviceRegistrationInfo::GetOAuthURL( 289b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko const std::string& subpath, 2907d55639775947b7b34b33702a886aac385f35102Vitaly Buka const WebParamList& params) const { 2915cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka return BuildURL(GetSettings().oauth_url, subpath, params); 2923cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko} 2933cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 294ee7a3af2e8ab08e0a71a407f2bfa8d3ca78b0f79Vitaly Bukavoid DeviceRegistrationInfo::Start() { 2953fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko if (HaveRegistrationCredentials()) { 296d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko StartNotificationChannel(); 297ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley // Wait a significant amount of time for local daemons to publish their 298ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley // state to Buffet before publishing it to the cloud. 299ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley // TODO(wiley) We could do a lot of things here to either expose this 300ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley // timeout as a configurable knob or allow local 301ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley // daemons to signal that their state is up to date so that 302ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley // we need not wait for them. 303fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko ScheduleCloudConnection(base::TimeDelta::FromSeconds(5)); 304ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley } 3053cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko} 3063cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 307fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenkovoid DeviceRegistrationInfo::ScheduleCloudConnection( 308fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko const base::TimeDelta& delay) { 309c3c6dab99bb333417756817d154cd99ae71f4668Vitaly Buka SetGcdState(GcdState::kConnecting); 310a56a7e630a25e66118f0cba2678d58b3c20cbdacAlex Vakulenko if (!task_runner_) 3110f6b2eccc9ca4aa35cf0ee1e3ac7bc6edc38f0a4Vitaly Buka return; // Assume we're in test 312a56a7e630a25e66118f0cba2678d58b3c20cbdacAlex Vakulenko task_runner_->PostDelayedTask( 313fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko FROM_HERE, 314747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka base::Bind(&DeviceRegistrationInfo::ConnectToCloud, AsWeakPtr(), nullptr), 315747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka delay); 316cd4196605e88eac2d5fe78a58aa5aa1c40ee8f77Christopher Wiley} 317cd4196605e88eac2d5fe78a58aa5aa1c40ee8f77Christopher Wiley 3183fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenkobool DeviceRegistrationInfo::HaveRegistrationCredentials() const { 3195cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka return !GetSettings().refresh_token.empty() && 320312c2f5d0d5e2ef0cc7acc6dce5137094332c276Johan Euphrosine !GetSettings().cloud_id.empty() && 3215cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka !GetSettings().robot_account.empty(); 3223fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko} 3233fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko 3243fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenkobool DeviceRegistrationInfo::VerifyRegistrationCredentials( 3250801a1f20a0c8f34130d567cd3b7dcbd2be9cb3cVitaly Buka ErrorPtr* error) const { 3263fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko const bool have_credentials = HaveRegistrationCredentials(); 327c900e48b8d482b1462a4d262856e344d22bcc6a5Christopher Wiley 328ed77a57d86bab20ff668fc00503dfa771561781bAlex Vakulenko VLOG(2) << "Device registration record " 329c900e48b8d482b1462a4d262856e344d22bcc6a5Christopher Wiley << ((have_credentials) ? "found" : "not found."); 3300dbbf605efb8f72b3c2c15c14e613323fc2ac0a2Vitaly Buka if (!have_credentials) { 3310dbbf605efb8f72b3c2c15c14e613323fc2ac0a2Vitaly Buka return Error::AddTo(error, FROM_HERE, "device_not_registered", 3320dbbf605efb8f72b3c2c15c14e613323fc2ac0a2Vitaly Buka "No valid device registration record found"); 3330dbbf605efb8f72b3c2c15c14e613323fc2ac0a2Vitaly Buka } 3340dbbf605efb8f72b3c2c15c14e613323fc2ac0a2Vitaly Buka return true; 3353cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko} 3363cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 33724d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullockstd::unique_ptr<base::DictionaryValue> 3386da9425a14ac345b657354e62307d17df4a721dbVitaly BukaDeviceRegistrationInfo::ParseOAuthResponse(const HttpClient::Response& response, 3390801a1f20a0c8f34130d567cd3b7dcbd2be9cb3cVitaly Buka ErrorPtr* error) { 3406da9425a14ac345b657354e62307d17df4a721dbVitaly Buka int code = response.GetStatusCode(); 3416da9425a14ac345b657354e62307d17df4a721dbVitaly Buka auto resp = ParseJsonResponse(response, error); 3421da659947e800069a6352dbe7f829dfdf1697fedVitaly Buka if (resp && code >= http::kBadRequest) { 34324d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullock std::string error_code, error_message; 34424d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullock if (!resp->GetString("error", &error_code)) { 34524d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullock error_code = "unexpected_response"; 34624d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullock } 34724d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullock if (error_code == "invalid_grant") { 34824d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullock LOG(INFO) << "The device's registration has been revoked."; 349c3c6dab99bb333417756817d154cd99ae71f4668Vitaly Buka SetGcdState(GcdState::kInvalidCredentials); 35024d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullock } 35124d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullock // I have never actually seen an error_description returned. 35224d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullock if (!resp->GetString("error_description", &error_message)) { 35324d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullock error_message = "Unexpected OAuth error"; 35424d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullock } 3550dbbf605efb8f72b3c2c15c14e613323fc2ac0a2Vitaly Buka return Error::AddTo(error, FROM_HERE, error_code, error_message); 35624d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullock } 35724d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullock return resp; 35824d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullock} 35924d189fbf5b5ee787831f9905a30bf7383453322Nathan Bullock 360747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Bukavoid DeviceRegistrationInfo::RefreshAccessToken(const DoneCallback& callback) { 361390d1911a68b765eef645026756f7290bacb2e15David Zeuthen LOG(INFO) << "Refreshing access token."; 3623fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko 3630801a1f20a0c8f34130d567cd3b7dcbd2be9cb3cVitaly Buka ErrorPtr error; 364747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (!VerifyRegistrationCredentials(&error)) 365747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return callback.Run(std::move(error)); 3663fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko 3670f91be869e37c5961fb40e686a5d0795ba39bd44Alex Vakulenko if (oauth2_backoff_entry_->ShouldRejectRequest()) { 3680f91be869e37c5961fb40e686a5d0795ba39bd44Alex Vakulenko VLOG(1) << "RefreshToken request delayed for " 3690f91be869e37c5961fb40e686a5d0795ba39bd44Alex Vakulenko << oauth2_backoff_entry_->GetTimeUntilRelease() 3700f91be869e37c5961fb40e686a5d0795ba39bd44Alex Vakulenko << " due to backoff policy"; 3710f91be869e37c5961fb40e686a5d0795ba39bd44Alex Vakulenko task_runner_->PostDelayedTask( 3720f91be869e37c5961fb40e686a5d0795ba39bd44Alex Vakulenko FROM_HERE, base::Bind(&DeviceRegistrationInfo::RefreshAccessToken, 373747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka AsWeakPtr(), callback), 3740f91be869e37c5961fb40e686a5d0795ba39bd44Alex Vakulenko oauth2_backoff_entry_->GetTimeUntilRelease()); 3750f91be869e37c5961fb40e686a5d0795ba39bd44Alex Vakulenko return; 3760f91be869e37c5961fb40e686a5d0795ba39bd44Alex Vakulenko } 3770f91be869e37c5961fb40e686a5d0795ba39bd44Alex Vakulenko 3781a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka RequestSender sender{HttpClient::Method::kPost, GetOAuthURL("token"), 3791a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka http_client_}; 3806da9425a14ac345b657354e62307d17df4a721dbVitaly Buka sender.SetFormData({ 3815cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka {"refresh_token", GetSettings().refresh_token}, 3825cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka {"client_id", GetSettings().client_id}, 3835cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka {"client_secret", GetSettings().client_secret}, 384a647c857f3098b366b379bde308d051f6a9aac2fVitaly Buka {"grant_type", "refresh_token"}, 3856da9425a14ac345b657354e62307d17df4a721dbVitaly Buka }); 386747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka sender.Send(base::Bind(&DeviceRegistrationInfo::OnRefreshAccessTokenDone, 387747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka weak_factory_.GetWeakPtr(), callback)); 388866b60a19c99f6db4def7f18cc902370d344a33aVitaly Buka VLOG(1) << "Refresh access token request dispatched"; 389266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko} 390266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko 391747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Bukavoid DeviceRegistrationInfo::OnRefreshAccessTokenDone( 392747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const DoneCallback& callback, 393747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka std::unique_ptr<HttpClient::Response> response, 394747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka ErrorPtr error) { 395747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (error) { 396747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka VLOG(1) << "Refresh access token failed"; 397747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka oauth2_backoff_entry_->InformOfRequest(false); 398747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return RefreshAccessToken(callback); 399747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka } 400866b60a19c99f6db4def7f18cc902370d344a33aVitaly Buka VLOG(1) << "Refresh access token request completed"; 4010f91be869e37c5961fb40e686a5d0795ba39bd44Alex Vakulenko oauth2_backoff_entry_->InformOfRequest(true); 402747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka auto json = ParseOAuthResponse(*response, &error); 403f7f52d4707c007bb9255bd80b23ac3428c6fc2e0Vitaly Buka if (!json) 404747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return callback.Run(std::move(error)); 4053cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 4063cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko int expires_in = 0; 4073cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko if (!json->GetString("access_token", &access_token_) || 408a647c857f3098b366b379bde308d051f6a9aac2fVitaly Buka !json->GetInteger("expires_in", &expires_in) || access_token_.empty() || 4093cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko expires_in <= 0) { 4103cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko LOG(ERROR) << "Access token unavailable."; 41148a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka Error::AddTo(&error, FROM_HERE, "unexpected_server_response", 41248a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka "Access token unavailable"); 413747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return callback.Run(std::move(error)); 4143cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko } 415a647c857f3098b366b379bde308d051f6a9aac2fVitaly Buka access_token_expiration_ = 416a647c857f3098b366b379bde308d051f6a9aac2fVitaly Buka base::Time::Now() + base::TimeDelta::FromSeconds(expires_in); 4173cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko LOG(INFO) << "Access token is refreshed for additional " << expires_in 4183cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko << " seconds."; 419d9e0bcd8e1a1cd5983c30673365acf60021ebb95Nathan Bullock 4206b028ae747f94bd9201f71690a9aa7c0ad585f5fAlex Vakulenko if (primary_notification_channel_ && 4216b028ae747f94bd9201f71690a9aa7c0ad585f5fAlex Vakulenko !primary_notification_channel_->IsConnected()) { 4226b028ae747f94bd9201f71690a9aa7c0ad585f5fAlex Vakulenko // If we have disconnected channel, it is due to failed credentials. 4236b028ae747f94bd9201f71690a9aa7c0ad585f5fAlex Vakulenko // Now that we have a new access token, retry the connection. 4246b028ae747f94bd9201f71690a9aa7c0ad585f5fAlex Vakulenko StartNotificationChannel(); 4256b028ae747f94bd9201f71690a9aa7c0ad585f5fAlex Vakulenko } 42672d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka 42772d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka SendAuthInfo(); 42872d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka 429747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka callback.Run(nullptr); 4306b028ae747f94bd9201f71690a9aa7c0ad585f5fAlex Vakulenko} 4316b028ae747f94bd9201f71690a9aa7c0ad585f5fAlex Vakulenko 432eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenkovoid DeviceRegistrationInfo::StartNotificationChannel() { 4336b028ae747f94bd9201f71690a9aa7c0ad585f5fAlex Vakulenko if (notification_channel_starting_) 4346b028ae747f94bd9201f71690a9aa7c0ad585f5fAlex Vakulenko return; 4356b028ae747f94bd9201f71690a9aa7c0ad585f5fAlex Vakulenko 4363fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko LOG(INFO) << "Starting notification channel"; 4373fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko 4380f6b2eccc9ca4aa35cf0ee1e3ac7bc6edc38f0a4Vitaly Buka // If no TaskRunner assume we're in test. 439ff1d1862455e0e1aea2dffedf505eea5fdce2c6cVitaly Buka if (!network_) { 440ff1d1862455e0e1aea2dffedf505eea5fdce2c6cVitaly Buka LOG(INFO) << "No Network, not starting notification channel"; 441bea91132a204b583d51e50181dbd14b761df040fNathan Bullock return; 442d9e0bcd8e1a1cd5983c30673365acf60021ebb95Nathan Bullock } 443d9e0bcd8e1a1cd5983c30673365acf60021ebb95Nathan Bullock 444d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko if (primary_notification_channel_) { 44526f557b524b63b72e4828e52f923d09ed184fffdAlex Vakulenko primary_notification_channel_->Stop(); 446d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko primary_notification_channel_.reset(); 447d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko current_notification_channel_ = nullptr; 448d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko } 449d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko 450d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko // Start with just regular polling at the pre-configured polling interval. 451d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko // Once the primary notification channel is connected successfully, it will 452d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko // call back to OnConnected() and at that time we'll switch to use the 453d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko // primary channel and switch periodic poll into much more infrequent backup 454d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko // poll mode. 45541a90d69918c6367ba23de1edf0bad6c7175f3b9Vitaly Buka const base::TimeDelta pull_interval = 45641a90d69918c6367ba23de1edf0bad6c7175f3b9Vitaly Buka base::TimeDelta::FromSeconds(kPollingPeriodSeconds); 457d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko if (!pull_channel_) { 458a56a7e630a25e66118f0cba2678d58b3c20cbdacAlex Vakulenko pull_channel_.reset(new PullChannel{pull_interval, task_runner_}); 459d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko pull_channel_->Start(this); 460d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko } else { 461d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko pull_channel_->UpdatePullInterval(pull_interval); 462d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko } 463d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko current_notification_channel_ = pull_channel_.get(); 464d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko 4656b028ae747f94bd9201f71690a9aa7c0ad585f5fAlex Vakulenko notification_channel_starting_ = true; 4663b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka primary_notification_channel_.reset( 4673b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka new XmppChannel{GetSettings().robot_account, access_token_, 4683b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka GetSettings().xmpp_endpoint, task_runner_, network_}); 469eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko primary_notification_channel_->Start(this); 470f12f7f005e90f60b56e8e4612575b850c57ee712Nathan Bullock} 471f12f7f005e90f60b56e8e4612575b850c57ee712Nathan Bullock 472c3c6dab99bb333417756817d154cd99ae71f4668Vitaly Bukavoid DeviceRegistrationInfo::AddGcdStateChangedCallback( 473c3c6dab99bb333417756817d154cd99ae71f4668Vitaly Buka const Device::GcdStateChangedCallback& callback) { 474c3c6dab99bb333417756817d154cd99ae71f4668Vitaly Buka gcd_state_changed_callbacks_.push_back(callback); 475c3c6dab99bb333417756817d154cd99ae71f4668Vitaly Buka callback.Run(gcd_state_); 476ee7a3af2e8ab08e0a71a407f2bfa8d3ca78b0f79Vitaly Buka} 477ee7a3af2e8ab08e0a71a407f2bfa8d3ca78b0f79Vitaly Buka 478d8d3216bc2fb413e8100ff0b17c53f275300534cAnton Muhinstd::unique_ptr<base::DictionaryValue> 4791a108717d3d6ac17974b835dc18f6c177fb609ffVitaly BukaDeviceRegistrationInfo::BuildDeviceResource() const { 480d8d3216bc2fb413e8100ff0b17c53f275300534cAnton Muhin std::unique_ptr<base::DictionaryValue> resource{new base::DictionaryValue}; 481312c2f5d0d5e2ef0cc7acc6dce5137094332c276Johan Euphrosine if (!GetSettings().cloud_id.empty()) 482312c2f5d0d5e2ef0cc7acc6dce5137094332c276Johan Euphrosine resource->SetString("id", GetSettings().cloud_id); 4835cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka resource->SetString("name", GetSettings().name); 4845cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka if (!GetSettings().description.empty()) 4855cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka resource->SetString("description", GetSettings().description); 4865cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka if (!GetSettings().location.empty()) 4875cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka resource->SetString("location", GetSettings().location); 4885cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka resource->SetString("modelManifestId", GetSettings().model_id); 489eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko std::unique_ptr<base::DictionaryValue> channel{new base::DictionaryValue}; 490d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko if (current_notification_channel_) { 491eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko channel->SetString("supportedType", 492d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko current_notification_channel_->GetName()); 493d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko current_notification_channel_->AddChannelParameters(channel.get()); 494eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko } else { 495d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko channel->SetString("supportedType", "pull"); 496eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko } 497eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko resource->Set("channel", channel.release()); 498d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko resource->Set("traits", component_manager_->GetTraits().DeepCopy()); 499d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko resource->Set("components", component_manager_->GetComponents().DeepCopy()); 500d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko 501d8d3216bc2fb413e8100ff0b17c53f275300534cAnton Muhin return resource; 502d8d3216bc2fb413e8100ff0b17c53f275300534cAnton Muhin} 503d8d3216bc2fb413e8100ff0b17c53f275300534cAnton Muhin 504266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenkovoid DeviceRegistrationInfo::GetDeviceInfo( 505747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const CloudRequestDoneCallback& callback) { 50611b2f2382db08ce26f4c4875a4e2d77586165720Vitaly Buka ErrorPtr error; 507672634b8b0ed31891fb48e02dce75b6aead0be27Vitaly Buka if (!VerifyRegistrationCredentials(&error)) 508747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return callback.Run({}, std::move(error)); 509747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka DoCloudRequest(HttpClient::Method::kGet, GetDeviceURL(), nullptr, callback); 5103cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko} 5113cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 512747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Bukavoid DeviceRegistrationInfo::RegisterDeviceError(const DoneCallback& callback, 513747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka ErrorPtr error) { 514747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka task_runner_->PostDelayedTask(FROM_HERE, 515747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka base::Bind(callback, base::Passed(&error)), {}); 5164774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka} 5174774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka 518747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Bukavoid DeviceRegistrationInfo::RegisterDevice(const std::string& ticket_id, 519747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const DoneCallback& callback) { 5200a8ab9973e1fdf6b41d8f7b49529c9bd97a9071cVitaly Buka if (HaveRegistrationCredentials()) { 5210a8ab9973e1fdf6b41d8f7b49529c9bd97a9071cVitaly Buka ErrorPtr error; 52248a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka Error::AddTo(&error, FROM_HERE, kErrorAlreayRegistered, 5230a8ab9973e1fdf6b41d8f7b49529c9bd97a9071cVitaly Buka "Unable to register already registered device"); 5240a8ab9973e1fdf6b41d8f7b49529c9bd97a9071cVitaly Buka return RegisterDeviceError(callback, std::move(error)); 5250a8ab9973e1fdf6b41d8f7b49529c9bd97a9071cVitaly Buka } 5260a8ab9973e1fdf6b41d8f7b49529c9bd97a9071cVitaly Buka 5271a108717d3d6ac17974b835dc18f6c177fb609ffVitaly Buka std::unique_ptr<base::DictionaryValue> device_draft = BuildDeviceResource(); 5281a108717d3d6ac17974b835dc18f6c177fb609ffVitaly Buka CHECK(device_draft); 52907216fe6005df7ab402426485f2823905c6a19d7Alex Vakulenko 5303cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko base::DictionaryValue req_json; 531e440848385ed31861a509181c063f7f8854d7559Nathan Bullock req_json.SetString("id", ticket_id); 5325cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka req_json.SetString("oauthClientId", GetSettings().client_id); 533d8d3216bc2fb413e8100ff0b17c53f275300534cAnton Muhin req_json.Set("deviceDraft", device_draft.release()); 5343cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 535e440848385ed31861a509181c063f7f8854d7559Nathan Bullock auto url = GetServiceURL("registrationTickets/" + ticket_id, 5365cf12a3e1f9e8e01f9fdb5322277c34096afde8bVitaly Buka {{"key", GetSettings().api_key}}); 5376da9425a14ac345b657354e62307d17df4a721dbVitaly Buka 5381a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka RequestSender sender{HttpClient::Method::kPatch, url, http_client_}; 5396da9425a14ac345b657354e62307d17df4a721dbVitaly Buka sender.SetJsonData(req_json); 5404774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka sender.Send(base::Bind(&DeviceRegistrationInfo::RegisterDeviceOnTicketSent, 541747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka weak_factory_.GetWeakPtr(), ticket_id, callback)); 5424774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka} 5436da9425a14ac345b657354e62307d17df4a721dbVitaly Buka 5444774df2734a2b74f16047983751673388b6cb5b5Vitaly Bukavoid DeviceRegistrationInfo::RegisterDeviceOnTicketSent( 5454774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka const std::string& ticket_id, 546747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const DoneCallback& callback, 547747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka std::unique_ptr<provider::HttpClient::Response> response, 548747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka ErrorPtr error) { 549747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (error) 550747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return RegisterDeviceError(callback, std::move(error)); 551747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka auto json_resp = ParseJsonResponse(*response, &error); 552532ff7e5174da55010d31cdc4c6a76f52e3bd1aeAnton Muhin if (!json_resp) 553747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return RegisterDeviceError(callback, std::move(error)); 5544774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka 555747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (!IsSuccessful(*response)) { 55612870bd07dd3c8f44528638a9c290b72e22d32d2Vitaly Buka ParseGCDError(json_resp.get(), &error); 557747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return RegisterDeviceError(callback, std::move(error)); 5581dbad47c63dceb5a7ad49871fc256e3b1179511cDavid Zeuthen } 5593cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 5604774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka std::string url = 5614774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka GetServiceURL("registrationTickets/" + ticket_id + "/finalize", 5624774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka {{"key", GetSettings().api_key}}); 5631a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka RequestSender{HttpClient::Method::kPost, url, http_client_}.Send( 5644774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka base::Bind(&DeviceRegistrationInfo::RegisterDeviceOnTicketFinalized, 565747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka weak_factory_.GetWeakPtr(), callback)); 5664774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka} 5674774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka 5684774df2734a2b74f16047983751673388b6cb5b5Vitaly Bukavoid DeviceRegistrationInfo::RegisterDeviceOnTicketFinalized( 569747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const DoneCallback& callback, 570747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka std::unique_ptr<provider::HttpClient::Response> response, 571747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka ErrorPtr error) { 572747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (error) 573747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return RegisterDeviceError(callback, std::move(error)); 574747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka auto json_resp = ParseJsonResponse(*response, &error); 575b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko if (!json_resp) 576747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return RegisterDeviceError(callback, std::move(error)); 577747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (!IsSuccessful(*response)) { 57812870bd07dd3c8f44528638a9c290b72e22d32d2Vitaly Buka ParseGCDError(json_resp.get(), &error); 579747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return RegisterDeviceError(callback, std::move(error)); 580b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko } 581532ff7e5174da55010d31cdc4c6a76f52e3bd1aeAnton Muhin 582532ff7e5174da55010d31cdc4c6a76f52e3bd1aeAnton Muhin std::string auth_code; 583312c2f5d0d5e2ef0cc7acc6dce5137094332c276Johan Euphrosine std::string cloud_id; 584ee7a3af2e8ab08e0a71a407f2bfa8d3ca78b0f79Vitaly Buka std::string robot_account; 585fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko const base::DictionaryValue* device_draft_response = nullptr; 586ee7a3af2e8ab08e0a71a407f2bfa8d3ca78b0f79Vitaly Buka if (!json_resp->GetString("robotAccountEmail", &robot_account) || 587b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko !json_resp->GetString("robotAccountAuthorizationCode", &auth_code) || 588fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko !json_resp->GetDictionary("deviceDraft", &device_draft_response) || 589312c2f5d0d5e2ef0cc7acc6dce5137094332c276Johan Euphrosine !device_draft_response->GetString("id", &cloud_id)) { 59048a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka Error::AddTo(&error, FROM_HERE, "unexpected_response", 5910801a1f20a0c8f34130d567cd3b7dcbd2be9cb3cVitaly Buka "Device account missing in response"); 592747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return RegisterDeviceError(callback, std::move(error)); 593b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko } 5943cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 595fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko UpdateDeviceInfoTimestamp(*device_draft_response); 596fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko 597b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko // Now get access_token and refresh_token 5981a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka RequestSender sender2{HttpClient::Method::kPost, GetOAuthURL("token"), 5991a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka http_client_}; 60034668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka sender2.SetFormData({{"code", auth_code}, 60134668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka {"client_id", GetSettings().client_id}, 60234668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka {"client_secret", GetSettings().client_secret}, 60334668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka {"redirect_uri", "oob"}, 60434668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka {"grant_type", "authorization_code"}}); 6054774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka sender2.Send(base::Bind(&DeviceRegistrationInfo::RegisterDeviceOnAuthCodeSent, 6064774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka weak_factory_.GetWeakPtr(), cloud_id, robot_account, 607747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka callback)); 6084774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka} 6093cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 6104774df2734a2b74f16047983751673388b6cb5b5Vitaly Bukavoid DeviceRegistrationInfo::RegisterDeviceOnAuthCodeSent( 6114774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka const std::string& cloud_id, 6124774df2734a2b74f16047983751673388b6cb5b5Vitaly Buka const std::string& robot_account, 613747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const DoneCallback& callback, 614747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka std::unique_ptr<provider::HttpClient::Response> response, 615747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka ErrorPtr error) { 616747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (error) 617747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return RegisterDeviceError(callback, std::move(error)); 618747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka auto json_resp = ParseOAuthResponse(*response, &error); 619b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko int expires_in = 0; 620ee7a3af2e8ab08e0a71a407f2bfa8d3ca78b0f79Vitaly Buka std::string refresh_token; 621ee7a3af2e8ab08e0a71a407f2bfa8d3ca78b0f79Vitaly Buka if (!json_resp || !json_resp->GetString("access_token", &access_token_) || 622ee7a3af2e8ab08e0a71a407f2bfa8d3ca78b0f79Vitaly Buka !json_resp->GetString("refresh_token", &refresh_token) || 623b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko !json_resp->GetInteger("expires_in", &expires_in) || 624ee7a3af2e8ab08e0a71a407f2bfa8d3ca78b0f79Vitaly Buka access_token_.empty() || refresh_token.empty() || expires_in <= 0) { 62548a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka Error::AddTo(&error, FROM_HERE, "unexpected_response", 6260801a1f20a0c8f34130d567cd3b7dcbd2be9cb3cVitaly Buka "Device access_token missing in response"); 627747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return RegisterDeviceError(callback, std::move(error)); 6283cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko } 629b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko 630a647c857f3098b366b379bde308d051f6a9aac2fVitaly Buka access_token_expiration_ = 631a647c857f3098b366b379bde308d051f6a9aac2fVitaly Buka base::Time::Now() + base::TimeDelta::FromSeconds(expires_in); 632b3aac2520d0ba88ba1aa69c30213bbc21944401eAlex Vakulenko 633666b43e92729fa170bc53eab9040a4dfe58b8062Vitaly Buka Config::Transaction change{config_}; 634312c2f5d0d5e2ef0cc7acc6dce5137094332c276Johan Euphrosine change.set_cloud_id(cloud_id); 635ee7a3af2e8ab08e0a71a407f2bfa8d3ca78b0f79Vitaly Buka change.set_robot_account(robot_account); 636ee7a3af2e8ab08e0a71a407f2bfa8d3ca78b0f79Vitaly Buka change.set_refresh_token(refresh_token); 637ee7a3af2e8ab08e0a71a407f2bfa8d3ca78b0f79Vitaly Buka change.Commit(); 638ee7a3af2e8ab08e0a71a407f2bfa8d3ca78b0f79Vitaly Buka 639747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka task_runner_->PostDelayedTask(FROM_HERE, base::Bind(callback, nullptr), {}); 64012870bd07dd3c8f44528638a9c290b72e22d32d2Vitaly Buka 641eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko StartNotificationChannel(); 64272d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka SendAuthInfo(); 643cd4196605e88eac2d5fe78a58aa5aa1c40ee8f77Christopher Wiley 644fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko // We're going to respond with our success immediately and we'll connect to 645fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko // cloud shortly after. 64612870bd07dd3c8f44528638a9c290b72e22d32d2Vitaly Buka ScheduleCloudConnection({}); 6473cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko} 6483cb466ce672499ab4447e9716c8a70714ffe18c7Alex Vakulenko 649ac661abad20f63d506b84574e23e55d8fa019347Anton Muhinvoid DeviceRegistrationInfo::DoCloudRequest( 6501a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka HttpClient::Method method, 651ac661abad20f63d506b84574e23e55d8fa019347Anton Muhin const std::string& url, 652ac661abad20f63d506b84574e23e55d8fa019347Anton Muhin const base::DictionaryValue* body, 653747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const CloudRequestDoneCallback& callback) { 654266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko // We make CloudRequestData shared here because we want to make sure 655747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka // there is only one instance of callback and error_calback since 656266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko // those may have move-only types and making a copy of the callback with 657266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko // move-only types curried-in will invalidate the source callback. 658266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko auto data = std::make_shared<CloudRequestData>(); 659266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko data->method = method; 660266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko data->url = url; 661266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko if (body) 662266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko base::JSONWriter::Write(*body, &data->body); 663747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka data->callback = callback; 664266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko SendCloudRequest(data); 665266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko} 666266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko 667266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenkovoid DeviceRegistrationInfo::SendCloudRequest( 668266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko const std::shared_ptr<const CloudRequestData>& data) { 6690357c0384d9cbee356648c18d2aed0244874c47fAlex Vakulenko // TODO(antonm): Add reauthorization on access token expiration (do not 670ac661abad20f63d506b84574e23e55d8fa019347Anton Muhin // forget about 5xx when fetching new access token). 671ac661abad20f63d506b84574e23e55d8fa019347Anton Muhin // TODO(antonm): Add support for device removal. 672ac661abad20f63d506b84574e23e55d8fa019347Anton Muhin 6730801a1f20a0c8f34130d567cd3b7dcbd2be9cb3cVitaly Buka ErrorPtr error; 674672634b8b0ed31891fb48e02dce75b6aead0be27Vitaly Buka if (!VerifyRegistrationCredentials(&error)) 675747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return data->callback.Run({}, std::move(error)); 676ac661abad20f63d506b84574e23e55d8fa019347Anton Muhin 677266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko if (cloud_backoff_entry_->ShouldRejectRequest()) { 678266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko VLOG(1) << "Cloud request delayed for " 679266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko << cloud_backoff_entry_->GetTimeUntilRelease() 680266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko << " due to backoff policy"; 681f7f52d4707c007bb9255bd80b23ac3428c6fc2e0Vitaly Buka return task_runner_->PostDelayedTask( 682a647c857f3098b366b379bde308d051f6a9aac2fVitaly Buka FROM_HERE, base::Bind(&DeviceRegistrationInfo::SendCloudRequest, 683a647c857f3098b366b379bde308d051f6a9aac2fVitaly Buka AsWeakPtr(), data), 684266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko cloud_backoff_entry_->GetTimeUntilRelease()); 685266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko } 686633eded4cc099cdb5590a330a4b1f6a6012c1292Anton Muhin 6876da9425a14ac345b657354e62307d17df4a721dbVitaly Buka RequestSender sender{data->method, data->url, http_client_}; 6881da659947e800069a6352dbe7f829dfdf1697fedVitaly Buka sender.SetData(data->body, http::kJsonUtf8); 689815b603b59bebac4407322af61a822413ad421f6Vitaly Buka sender.SetAccessToken(access_token_); 690747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka sender.Send(base::Bind(&DeviceRegistrationInfo::OnCloudRequestDone, 691866b60a19c99f6db4def7f18cc902370d344a33aVitaly Buka AsWeakPtr(), data)); 692266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko} 693266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko 694747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Bukavoid DeviceRegistrationInfo::OnCloudRequestDone( 695266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko const std::shared_ptr<const CloudRequestData>& data, 696747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka std::unique_ptr<provider::HttpClient::Response> response, 697747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka ErrorPtr error) { 698747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (error) 699747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return RetryCloudRequest(data); 700747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka int status_code = response->GetStatusCode(); 7011da659947e800069a6352dbe7f829dfdf1697fedVitaly Buka if (status_code == http::kDenied) { 702fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko cloud_backoff_entry_->InformOfRequest(true); 70334668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka RefreshAccessToken(base::Bind( 70434668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka &DeviceRegistrationInfo::OnAccessTokenRefreshed, AsWeakPtr(), data)); 705266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko return; 706266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko } 707266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko 7081da659947e800069a6352dbe7f829dfdf1697fedVitaly Buka if (status_code >= http::kInternalServerError) { 709266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko // Request was valid, but server failed, retry. 710266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko // TODO(antonm): Reconsider status codes, maybe only some require 711266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko // retry. 712266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko // TODO(antonm): Support Retry-After header. 713266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko RetryCloudRequest(data); 714266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko return; 715266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko } 716266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko 71761820306dd6a61f5c824d9236f7d48d07167c515Vitaly Buka if (response->GetContentType().empty()) { 71861820306dd6a61f5c824d9236f7d48d07167c515Vitaly Buka // Assume no body if no content type. 7197717d93d22d34c9974ca09bf6bde95a3caa75bcbVitaly Buka cloud_backoff_entry_->InformOfRequest(true); 7207717d93d22d34c9974ca09bf6bde95a3caa75bcbVitaly Buka return data->callback.Run({}, nullptr); 7217717d93d22d34c9974ca09bf6bde95a3caa75bcbVitaly Buka } 7227717d93d22d34c9974ca09bf6bde95a3caa75bcbVitaly Buka 723747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka auto json_resp = ParseJsonResponse(*response, &error); 724266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko if (!json_resp) { 7255fddf87b36b0528e8251b25ae2eba3336fd028ddVitaly Buka cloud_backoff_entry_->InformOfRequest(false); 726747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return data->callback.Run({}, std::move(error)); 727266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko } 728266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko 729747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (!IsSuccessful(*response)) { 730266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko ParseGCDError(json_resp.get(), &error); 7311da659947e800069a6352dbe7f829dfdf1697fedVitaly Buka if (status_code == http::kForbidden && 73248a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka error->HasError("rateLimitExceeded")) { 733f61ee017872aa93e53d23fdd79ab16c6fac98c0cAlex Vakulenko // If we exceeded server quota, retry the request later. 734f7f52d4707c007bb9255bd80b23ac3428c6fc2e0Vitaly Buka return RetryCloudRequest(data); 735f61ee017872aa93e53d23fdd79ab16c6fac98c0cAlex Vakulenko } 7365fddf87b36b0528e8251b25ae2eba3336fd028ddVitaly Buka 7375fddf87b36b0528e8251b25ae2eba3336fd028ddVitaly Buka cloud_backoff_entry_->InformOfRequest(false); 738747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return data->callback.Run({}, std::move(error)); 739266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko } 7406401d01c768e996b48803e268da5fc10fc44cdacAlex Vakulenko 741f61ee017872aa93e53d23fdd79ab16c6fac98c0cAlex Vakulenko cloud_backoff_entry_->InformOfRequest(true); 742c3c6dab99bb333417756817d154cd99ae71f4668Vitaly Buka SetGcdState(GcdState::kConnected); 743747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka data->callback.Run(*json_resp, nullptr); 744266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko} 745266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko 746266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenkovoid DeviceRegistrationInfo::RetryCloudRequest( 747266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko const std::shared_ptr<const CloudRequestData>& data) { 748f61ee017872aa93e53d23fdd79ab16c6fac98c0cAlex Vakulenko // TODO(avakulenko): Tie connecting/connected status to XMPP channel instead. 749c3c6dab99bb333417756817d154cd99ae71f4668Vitaly Buka SetGcdState(GcdState::kConnecting); 750266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko cloud_backoff_entry_->InformOfRequest(false); 751266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko SendCloudRequest(data); 752266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko} 753266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko 754266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenkovoid DeviceRegistrationInfo::OnAccessTokenRefreshed( 755266b2b15731ff30ada2cb9cf040c465e774b6e09Alex Vakulenko const std::shared_ptr<const CloudRequestData>& data, 756f7f52d4707c007bb9255bd80b23ac3428c6fc2e0Vitaly Buka ErrorPtr error) { 757747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (error) { 758747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka CheckAccessTokenError(error->Clone()); 759747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return data->callback.Run({}, std::move(error)); 760747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka } 761747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka SendCloudRequest(data); 762fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko} 763fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko 764f7f52d4707c007bb9255bd80b23ac3428c6fc2e0Vitaly Bukavoid DeviceRegistrationInfo::CheckAccessTokenError(ErrorPtr error) { 76548a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka if (error && error->HasError("invalid_grant")) 766672634b8b0ed31891fb48e02dce75b6aead0be27Vitaly Buka RemoveCredentials(); 767ac661abad20f63d506b84574e23e55d8fa019347Anton Muhin} 768ac661abad20f63d506b84574e23e55d8fa019347Anton Muhin 769747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Bukavoid DeviceRegistrationInfo::ConnectToCloud(ErrorPtr error) { 770747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (error) { 77148a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka if (error->HasError("invalid_grant")) 772672634b8b0ed31891fb48e02dce75b6aead0be27Vitaly Buka RemoveCredentials(); 773747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return; 774747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka } 775747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka 776fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko connected_to_cloud_ = false; 777fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko if (!VerifyRegistrationCredentials(nullptr)) 778fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko return; 779fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko 780fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko if (access_token_.empty()) { 781fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko RefreshAccessToken( 782747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka base::Bind(&DeviceRegistrationInfo::ConnectToCloud, AsWeakPtr())); 783d8d3216bc2fb413e8100ff0b17c53f275300534cAnton Muhin return; 784fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko } 785fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko 786fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko // Connecting a device to cloud just means that we: 787ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley // 1) push an updated device resource 788ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley // 2) fetch an initial set of outstanding commands 789ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley // 3) abort any commands that we've previously marked as "in progress" 790d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko // or as being in an error state; publish queued commands 791fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko UpdateDeviceResource( 792747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka base::Bind(&DeviceRegistrationInfo::OnConnectedToCloud, AsWeakPtr())); 793fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko} 794fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko 795747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Bukavoid DeviceRegistrationInfo::OnConnectedToCloud(ErrorPtr error) { 796747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (error) 797747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return; 798fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko LOG(INFO) << "Device connected to cloud server"; 799fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko connected_to_cloud_ = true; 800fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko FetchCommands(base::Bind(&DeviceRegistrationInfo::ProcessInitialCommandList, 80134668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka AsWeakPtr()), 80234668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka fetch_reason::kDeviceStart); 803fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko // In case there are any pending state updates since we sent off the initial 804fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko // UpdateDeviceResource() request, update the server with any state changes. 805fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko PublishStateUpdates(); 806c635c59187409cd66c3dc3d1fc749106b2d92522Anton Muhin} 807c635c59187409cd66c3dc3d1fc749106b2d92522Anton Muhin 808b624bc47fb70294185478e4c758657a54b859cb1Vitaly Bukavoid DeviceRegistrationInfo::UpdateDeviceInfo(const std::string& name, 809fa94706d3e37dbb6da611506faca801eb3772824Vitaly Buka const std::string& description, 810b624bc47fb70294185478e4c758657a54b859cb1Vitaly Buka const std::string& location) { 811666b43e92729fa170bc53eab9040a4dfe58b8062Vitaly Buka Config::Transaction change{config_}; 812798a0e746d3f97779ccfc6bc86bfbfe854f68445Vitaly Buka change.set_name(name); 813ee7a3af2e8ab08e0a71a407f2bfa8d3ca78b0f79Vitaly Buka change.set_description(description); 814ee7a3af2e8ab08e0a71a407f2bfa8d3ca78b0f79Vitaly Buka change.set_location(location); 815ff81db68e447f15ac7bc50c8c3c6572fdc7b3b7bVitaly Buka change.Commit(); 816fa94706d3e37dbb6da611506faca801eb3772824Vitaly Buka 8173fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko if (HaveRegistrationCredentials()) { 818747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka UpdateDeviceResource(base::Bind(&IgnoreCloudError)); 819fa94706d3e37dbb6da611506faca801eb3772824Vitaly Buka } 820fa94706d3e37dbb6da611506faca801eb3772824Vitaly Buka} 821fa94706d3e37dbb6da611506faca801eb3772824Vitaly Buka 822b624bc47fb70294185478e4c758657a54b859cb1Vitaly Bukavoid DeviceRegistrationInfo::UpdateBaseConfig(AuthScope anonymous_access_role, 823b624bc47fb70294185478e4c758657a54b859cb1Vitaly Buka bool local_discovery_enabled, 824b624bc47fb70294185478e4c758657a54b859cb1Vitaly Buka bool local_pairing_enabled) { 825666b43e92729fa170bc53eab9040a4dfe58b8062Vitaly Buka Config::Transaction change(config_); 826b624bc47fb70294185478e4c758657a54b859cb1Vitaly Buka change.set_local_anonymous_access_role(anonymous_access_role); 8272f7efdb7a0f3ad0923d61290534b37c6c8351cc8Vitaly Buka change.set_local_discovery_enabled(local_discovery_enabled); 8282f7efdb7a0f3ad0923d61290534b37c6c8351cc8Vitaly Buka change.set_local_pairing_enabled(local_pairing_enabled); 8292f7efdb7a0f3ad0923d61290534b37c6c8351cc8Vitaly Buka} 8302f7efdb7a0f3ad0923d61290534b37c6c8351cc8Vitaly Buka 831ff81db68e447f15ac7bc50c8c3c6572fdc7b3b7bVitaly Bukabool DeviceRegistrationInfo::UpdateServiceConfig( 832ff81db68e447f15ac7bc50c8c3c6572fdc7b3b7bVitaly Buka const std::string& client_id, 833ff81db68e447f15ac7bc50c8c3c6572fdc7b3b7bVitaly Buka const std::string& client_secret, 834ff81db68e447f15ac7bc50c8c3c6572fdc7b3b7bVitaly Buka const std::string& api_key, 835ff81db68e447f15ac7bc50c8c3c6572fdc7b3b7bVitaly Buka const std::string& oauth_url, 836ff81db68e447f15ac7bc50c8c3c6572fdc7b3b7bVitaly Buka const std::string& service_url, 8373b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka const std::string& xmpp_endpoint, 8380801a1f20a0c8f34130d567cd3b7dcbd2be9cb3cVitaly Buka ErrorPtr* error) { 8393fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko if (HaveRegistrationCredentials()) { 8400dbbf605efb8f72b3c2c15c14e613323fc2ac0a2Vitaly Buka return Error::AddTo(error, FROM_HERE, kErrorAlreayRegistered, 8410dbbf605efb8f72b3c2c15c14e613323fc2ac0a2Vitaly Buka "Unable to change config for registered device"); 842ff81db68e447f15ac7bc50c8c3c6572fdc7b3b7bVitaly Buka } 843666b43e92729fa170bc53eab9040a4dfe58b8062Vitaly Buka Config::Transaction change{config_}; 8443b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka if (!client_id.empty()) 8453b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka change.set_client_id(client_id); 8463b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka if (!client_secret.empty()) 8473b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka change.set_client_secret(client_secret); 8483b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka if (!api_key.empty()) 8493b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka change.set_api_key(api_key); 8503b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka if (!oauth_url.empty()) 8513b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka change.set_oauth_url(oauth_url); 8523b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka if (!service_url.empty()) 8533b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka change.set_service_url(service_url); 8543b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka if (!xmpp_endpoint.empty()) 8553b8fbc546262ac5335e9ddfd219c195b224a4427Vitaly Buka change.set_xmpp_endpoint(xmpp_endpoint); 856ff81db68e447f15ac7bc50c8c3c6572fdc7b3b7bVitaly Buka return true; 857ff81db68e447f15ac7bc50c8c3c6572fdc7b3b7bVitaly Buka} 858ff81db68e447f15ac7bc50c8c3c6572fdc7b3b7bVitaly Buka 8595975552606c5bf51fd8d66b9b9bfa020999ef4b6Anton Muhinvoid DeviceRegistrationInfo::UpdateCommand( 8605975552606c5bf51fd8d66b9b9bfa020999ef4b6Anton Muhin const std::string& command_id, 861b211c104af747704dd9796e0f585bc3825fc802fAlex Vakulenko const base::DictionaryValue& command_patch, 862747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const DoneCallback& callback) { 8631a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka DoCloudRequest(HttpClient::Method::kPatch, 8641a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka GetServiceURL("commands/" + command_id), &command_patch, 865747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka base::Bind(&IgnoreCloudResultWithCallback, callback)); 8665975552606c5bf51fd8d66b9b9bfa020999ef4b6Anton Muhin} 8675975552606c5bf51fd8d66b9b9bfa020999ef4b6Anton Muhin 868a647c857f3098b366b379bde308d051f6a9aac2fVitaly Bukavoid DeviceRegistrationInfo::NotifyCommandAborted(const std::string& command_id, 8690801a1f20a0c8f34130d567cd3b7dcbd2be9cb3cVitaly Buka ErrorPtr error) { 870d1978d3a3d74f62a12a06acafc4b08b50a6c6834Alex Vakulenko base::DictionaryValue command_patch; 871d1978d3a3d74f62a12a06acafc4b08b50a6c6834Alex Vakulenko command_patch.SetString(commands::attributes::kCommand_State, 8720209da4821a019d2805d417b513b2743b2e750caVitaly Buka EnumToString(Command::State::kAborted)); 873d1978d3a3d74f62a12a06acafc4b08b50a6c6834Alex Vakulenko if (error) { 87470f77d92d48bce3d2f20a42997ee94332859d3ddVitaly Buka command_patch.Set(commands::attributes::kCommand_Error, 87570f77d92d48bce3d2f20a42997ee94332859d3ddVitaly Buka ErrorInfoToJson(*error).release()); 876d1978d3a3d74f62a12a06acafc4b08b50a6c6834Alex Vakulenko } 877747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka UpdateCommand(command_id, command_patch, base::Bind(&IgnoreCloudError)); 878d1978d3a3d74f62a12a06acafc4b08b50a6c6834Alex Vakulenko} 879d1978d3a3d74f62a12a06acafc4b08b50a6c6834Alex Vakulenko 880ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wileyvoid DeviceRegistrationInfo::UpdateDeviceResource( 881747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const DoneCallback& callback) { 882747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka queued_resource_update_callbacks_.emplace_back(callback); 883f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko if (!in_progress_resource_update_callbacks_.empty()) { 884f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko VLOG(1) << "Another request is already pending."; 885f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko return; 886f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko } 887f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko 888f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko StartQueuedUpdateDeviceResource(); 889f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko} 890f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko 891f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenkovoid DeviceRegistrationInfo::StartQueuedUpdateDeviceResource() { 892fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko if (in_progress_resource_update_callbacks_.empty() && 893fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko queued_resource_update_callbacks_.empty()) 894fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko return; 895fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko 896fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko if (last_device_resource_updated_timestamp_.empty()) { 897fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko // We don't know the current time stamp of the device resource from the 898fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko // server side. We need to provide the time stamp to the server as part of 899fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko // the request to guard against out-of-order requests overwriting settings 900fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko // specified by later requests. 901fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko VLOG(1) << "Getting the last device resource timestamp from server..."; 902747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka GetDeviceInfo(base::Bind(&DeviceRegistrationInfo::OnDeviceInfoRetrieved, 903747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka AsWeakPtr())); 904f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko return; 905fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko } 906f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko 907fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko in_progress_resource_update_callbacks_.insert( 908fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko in_progress_resource_update_callbacks_.end(), 909fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko queued_resource_update_callbacks_.begin(), 910fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko queued_resource_update_callbacks_.end()); 911fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko queued_resource_update_callbacks_.clear(); 912f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko 9139ea5a321746d94ed46c79633b75357f4cc77562eAlex Vakulenko VLOG(1) << "Updating GCD server with CDD..."; 914d8d3216bc2fb413e8100ff0b17c53f275300534cAnton Muhin std::unique_ptr<base::DictionaryValue> device_resource = 9151a108717d3d6ac17974b835dc18f6c177fb609ffVitaly Buka BuildDeviceResource(); 9161a108717d3d6ac17974b835dc18f6c177fb609ffVitaly Buka CHECK(device_resource); 917d8d3216bc2fb413e8100ff0b17c53f275300534cAnton Muhin 918fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko std::string url = GetDeviceURL( 919fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko {}, {{"lastUpdateTimeMs", last_device_resource_updated_timestamp_}}); 920fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko 921747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka DoCloudRequest(HttpClient::Method::kPut, url, device_resource.get(), 922747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka base::Bind(&DeviceRegistrationInfo::OnUpdateDeviceResourceDone, 923747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka AsWeakPtr())); 924f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko} 925f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko 92672d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Bukavoid DeviceRegistrationInfo::SendAuthInfo() { 92772d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka if (!auth_manager_ || auth_info_update_inprogress_) 92872d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka return; 9294ab500249f346a9fcfe084ee1619a39259f7471cVitaly Buka 9304ab500249f346a9fcfe084ee1619a39259f7471cVitaly Buka if (GetSettings().root_client_token_owner == RootClientTokenOwner::kCloud) { 9314ab500249f346a9fcfe084ee1619a39259f7471cVitaly Buka // Avoid re-claiming if device is already claimed by the Cloud. Cloud is 9324ab500249f346a9fcfe084ee1619a39259f7471cVitaly Buka // allowed to re-claim device at any time. However this will invalidate all 9334ab500249f346a9fcfe084ee1619a39259f7471cVitaly Buka // issued tokens. 9344ab500249f346a9fcfe084ee1619a39259f7471cVitaly Buka return; 9354ab500249f346a9fcfe084ee1619a39259f7471cVitaly Buka } 9364ab500249f346a9fcfe084ee1619a39259f7471cVitaly Buka 93772d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka auth_info_update_inprogress_ = true; 93872d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka 9394ab500249f346a9fcfe084ee1619a39259f7471cVitaly Buka std::vector<uint8_t> token = auth_manager_->ClaimRootClientAuthToken( 9404ab500249f346a9fcfe084ee1619a39259f7471cVitaly Buka RootClientTokenOwner::kCloud, nullptr); 9414ab500249f346a9fcfe084ee1619a39259f7471cVitaly Buka CHECK(!token.empty()); 94272d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka std::string id = GetSettings().device_id; 9430c190b39cd7629168b7008b34555881f7830289dVitaly Buka std::string token_base64 = Base64Encode(token); 94472d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka std::string fingerprint = 94572d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka Base64Encode(auth_manager_->GetCertificateFingerprint()); 94672d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka 947f3fd9c30d383b8f0fc4d4d6cefd172a5fa1200c4Vitaly Buka std::unique_ptr<base::DictionaryValue> auth{new base::DictionaryValue}; 948f3fd9c30d383b8f0fc4d4d6cefd172a5fa1200c4Vitaly Buka auth->SetString("localId", id); 949f3fd9c30d383b8f0fc4d4d6cefd172a5fa1200c4Vitaly Buka auth->SetString("clientToken", token_base64); 950f3fd9c30d383b8f0fc4d4d6cefd172a5fa1200c4Vitaly Buka auth->SetString("certFingerprint", fingerprint); 951f3fd9c30d383b8f0fc4d4d6cefd172a5fa1200c4Vitaly Buka std::unique_ptr<base::DictionaryValue> root{new base::DictionaryValue}; 952f3fd9c30d383b8f0fc4d4d6cefd172a5fa1200c4Vitaly Buka root->Set("localAuthInfo", auth.release()); 95372d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka 954f3fd9c30d383b8f0fc4d4d6cefd172a5fa1200c4Vitaly Buka std::string url = GetDeviceURL("upsertLocalAuthInfo", {}); 955f3fd9c30d383b8f0fc4d4d6cefd172a5fa1200c4Vitaly Buka DoCloudRequest(HttpClient::Method::kPost, url, root.get(), 9560c190b39cd7629168b7008b34555881f7830289dVitaly Buka base::Bind(&DeviceRegistrationInfo::OnSendAuthInfoDone, 9570c190b39cd7629168b7008b34555881f7830289dVitaly Buka AsWeakPtr(), token)); 95872d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka} 95972d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka 96072d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Bukavoid DeviceRegistrationInfo::OnSendAuthInfoDone( 9610c190b39cd7629168b7008b34555881f7830289dVitaly Buka const std::vector<uint8_t>& token, 96272d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka const base::DictionaryValue& body, 96372d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka ErrorPtr error) { 96472d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka CHECK(auth_info_update_inprogress_); 96572d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka auth_info_update_inprogress_ = false; 96672d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka 967305ab613de85f6640f300010a17cb6ea22be2081Vitaly Buka if (!error && auth_manager_->ConfirmClientAuthToken(token, nullptr)) 96872d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka return; 96972d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka 97072d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka task_runner_->PostDelayedTask( 97172d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka FROM_HERE, base::Bind(&DeviceRegistrationInfo::SendAuthInfo, AsWeakPtr()), 97272d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka {}); 97372d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka} 97472d8d1611efb8c0dd87d466e971bea9468b7c3a1Vitaly Buka 975fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenkovoid DeviceRegistrationInfo::OnDeviceInfoRetrieved( 976747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const base::DictionaryValue& device_info, 977747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka ErrorPtr error) { 978747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (error) 979747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return OnUpdateDeviceResourceError(std::move(error)); 980fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko if (UpdateDeviceInfoTimestamp(device_info)) 981fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko StartQueuedUpdateDeviceResource(); 982fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko} 983fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko 984fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenkobool DeviceRegistrationInfo::UpdateDeviceInfoTimestamp( 985fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko const base::DictionaryValue& device_info) { 986fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko // For newly created devices, "lastUpdateTimeMs" may not be present, but 987fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko // "creationTimeMs" should be there at least. 988fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko if (!device_info.GetString("lastUpdateTimeMs", 989fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko &last_device_resource_updated_timestamp_) && 990fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko !device_info.GetString("creationTimeMs", 991fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko &last_device_resource_updated_timestamp_)) { 992fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko LOG(WARNING) << "Device resource timestamp is missing"; 993fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko return false; 994fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko } 995fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko return true; 996fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko} 997fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko 998747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Bukavoid DeviceRegistrationInfo::OnUpdateDeviceResourceDone( 999747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const base::DictionaryValue& device_info, 1000747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka ErrorPtr error) { 1001747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (error) 1002747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return OnUpdateDeviceResourceError(std::move(error)); 1003fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko UpdateDeviceInfoTimestamp(device_info); 1004f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko // Make a copy of the callback list so that if the callback triggers another 1005f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko // call to UpdateDeviceResource(), we do not modify the list we are iterating 1006f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko // over. 1007f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko auto callback_list = std::move(in_progress_resource_update_callbacks_); 1008747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka for (const auto& callback : callback_list) 1009747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka callback.Run(nullptr); 1010f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko StartQueuedUpdateDeviceResource(); 1011f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko} 1012f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko 1013f7f52d4707c007bb9255bd80b23ac3428c6fc2e0Vitaly Bukavoid DeviceRegistrationInfo::OnUpdateDeviceResourceError(ErrorPtr error) { 101448a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka if (error->HasError("invalid_last_update_time_ms")) { 1015fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko // If the server rejected our previous request, retrieve the latest 1016fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko // timestamp from the server and retry. 1017fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko VLOG(1) << "Getting the last device resource timestamp from server..."; 1018747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka GetDeviceInfo(base::Bind(&DeviceRegistrationInfo::OnDeviceInfoRetrieved, 1019747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka AsWeakPtr())); 1020fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko return; 1021fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko } 1022fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko 1023f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko // Make a copy of the callback list so that if the callback triggers another 1024f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko // call to UpdateDeviceResource(), we do not modify the list we are iterating 1025f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko // over. 1026f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko auto callback_list = std::move(in_progress_resource_update_callbacks_); 1027747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka for (const auto& callback : callback_list) 1028747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka callback.Run(error->Clone()); 1029fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko 1030f3a95bf41633f161184a584a4475ba2d663684c1Alex Vakulenko StartQueuedUpdateDeviceResource(); 1031ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley} 1032ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley 1033747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Bukavoid DeviceRegistrationInfo::OnFetchCommandsDone( 1034747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const base::Callback<void(const base::ListValue&, ErrorPtr)>& callback, 1035747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const base::DictionaryValue& json, 1036747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka ErrorPtr error) { 103764bc9ea9f24d9eb18f77e1e0af360f5e8d9c35f3Alex Vakulenko OnFetchCommandsReturned(); 1038747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (error) 1039747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return callback.Run({}, std::move(error)); 1040ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley const base::ListValue* commands{nullptr}; 1041747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (!json.GetList("commands", &commands)) 1042ed77a57d86bab20ff668fc00503dfa771561781bAlex Vakulenko VLOG(2) << "No commands in the response."; 1043ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley const base::ListValue empty; 1044747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka callback.Run(commands ? *commands : empty, nullptr); 104564bc9ea9f24d9eb18f77e1e0af360f5e8d9c35f3Alex Vakulenko} 104664bc9ea9f24d9eb18f77e1e0af360f5e8d9c35f3Alex Vakulenko 104764bc9ea9f24d9eb18f77e1e0af360f5e8d9c35f3Alex Vakulenkovoid DeviceRegistrationInfo::OnFetchCommandsReturned() { 104864bc9ea9f24d9eb18f77e1e0af360f5e8d9c35f3Alex Vakulenko fetch_commands_request_sent_ = false; 104964bc9ea9f24d9eb18f77e1e0af360f5e8d9c35f3Alex Vakulenko // If we have additional requests queued, send them out now. 105064bc9ea9f24d9eb18f77e1e0af360f5e8d9c35f3Alex Vakulenko if (fetch_commands_request_queued_) 1051e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko FetchAndPublishCommands(queued_fetch_reason_); 105264bc9ea9f24d9eb18f77e1e0af360f5e8d9c35f3Alex Vakulenko} 1053ba983c8d415b0cea83d0ee7a096860db8de93accChristopher Wiley 1054c635c59187409cd66c3dc3d1fc749106b2d92522Anton Muhinvoid DeviceRegistrationInfo::FetchCommands( 1055e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko const base::Callback<void(const base::ListValue&, ErrorPtr)>& callback, 1056e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko const std::string& reason) { 105764bc9ea9f24d9eb18f77e1e0af360f5e8d9c35f3Alex Vakulenko fetch_commands_request_sent_ = true; 105864bc9ea9f24d9eb18f77e1e0af360f5e8d9c35f3Alex Vakulenko fetch_commands_request_queued_ = false; 1059c635c59187409cd66c3dc3d1fc749106b2d92522Anton Muhin DoCloudRequest( 10601a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka HttpClient::Method::kGet, 106134668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka GetServiceURL("commands/queue", 106234668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka {{"deviceId", GetSettings().cloud_id}, {"reason", reason}}), 1063747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka nullptr, base::Bind(&DeviceRegistrationInfo::OnFetchCommandsDone, 1064747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka AsWeakPtr(), callback)); 1065c635c59187409cd66c3dc3d1fc749106b2d92522Anton Muhin} 1066c635c59187409cd66c3dc3d1fc749106b2d92522Anton Muhin 1067e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenkovoid DeviceRegistrationInfo::FetchAndPublishCommands( 1068e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko const std::string& reason) { 106964bc9ea9f24d9eb18f77e1e0af360f5e8d9c35f3Alex Vakulenko if (fetch_commands_request_sent_) { 107064bc9ea9f24d9eb18f77e1e0af360f5e8d9c35f3Alex Vakulenko fetch_commands_request_queued_ = true; 1071e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko queued_fetch_reason_ = reason; 107264bc9ea9f24d9eb18f77e1e0af360f5e8d9c35f3Alex Vakulenko return; 107364bc9ea9f24d9eb18f77e1e0af360f5e8d9c35f3Alex Vakulenko } 107464bc9ea9f24d9eb18f77e1e0af360f5e8d9c35f3Alex Vakulenko 10751038ec1b83cf2afe6132dc7dcd005b23463f5858Alex Vakulenko FetchCommands(base::Bind(&DeviceRegistrationInfo::PublishCommands, 1076e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko weak_factory_.GetWeakPtr()), 1077e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko reason); 10781038ec1b83cf2afe6132dc7dcd005b23463f5858Alex Vakulenko} 10791038ec1b83cf2afe6132dc7dcd005b23463f5858Alex Vakulenko 1080d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenkovoid DeviceRegistrationInfo::ProcessInitialCommandList( 1081747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const base::ListValue& commands, 1082747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka ErrorPtr error) { 1083747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (error) 1084747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return; 1085d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko for (const base::Value* command : commands) { 1086d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko const base::DictionaryValue* command_dict{nullptr}; 1087d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko if (!command->GetAsDictionary(&command_dict)) { 1088d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko LOG(WARNING) << "Not a command dictionary: " << *command; 1089c635c59187409cd66c3dc3d1fc749106b2d92522Anton Muhin continue; 1090a34f0d9d6da24bcaf60fe9046b67e9c4d45aa7bdAnton Muhin } 1091c635c59187409cd66c3dc3d1fc749106b2d92522Anton Muhin std::string command_state; 1092d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko if (!command_dict->GetString("state", &command_state)) { 1093d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko LOG(WARNING) << "Command with no state at " << *command; 1094c635c59187409cd66c3dc3d1fc749106b2d92522Anton Muhin continue; 1095c635c59187409cd66c3dc3d1fc749106b2d92522Anton Muhin } 1096a647c857f3098b366b379bde308d051f6a9aac2fVitaly Buka if (command_state == "error" && command_state == "inProgress" && 1097d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko command_state == "paused") { 1098d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko // It's a limbo command, abort it. 1099d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko std::string command_id; 1100d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko if (!command_dict->GetString("id", &command_id)) { 1101d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko LOG(WARNING) << "Command with no ID at " << *command; 1102d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko continue; 1103d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko } 11046d2569e4260293816b8b9416d5e6993f843869b2Anton Muhin 1105d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko std::unique_ptr<base::DictionaryValue> cmd_copy{command_dict->DeepCopy()}; 1106d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko cmd_copy->SetString("state", "aborted"); 1107d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko // TODO(wiley) We could consider handling this error case more gracefully. 11081a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka DoCloudRequest(HttpClient::Method::kPut, 11091a42e1466de3edd0c79215b739cd8e929ef8a7e8Vitaly Buka GetServiceURL("commands/" + command_id), cmd_copy.get(), 1110747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka base::Bind(&IgnoreCloudResult)); 1111d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko } else { 1112d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko // Normal command, publish it to local clients. 1113d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko PublishCommand(*command_dict); 1114d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko } 1115c635c59187409cd66c3dc3d1fc749106b2d92522Anton Muhin } 1116d07e206c4d71c118516054752a69911d04c98fbaAnton Muhin} 1117d07e206c4d71c118516054752a69911d04c98fbaAnton Muhin 1118747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Bukavoid DeviceRegistrationInfo::PublishCommands(const base::ListValue& commands, 1119747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka ErrorPtr error) { 1120747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (error) 1121747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return; 11226e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko for (const base::Value* command : commands) { 11236e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko const base::DictionaryValue* command_dict{nullptr}; 11246e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko if (!command->GetAsDictionary(&command_dict)) { 11256e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko LOG(WARNING) << "Not a command dictionary: " << *command; 1126d07e206c4d71c118516054752a69911d04c98fbaAnton Muhin continue; 1127d07e206c4d71c118516054752a69911d04c98fbaAnton Muhin } 11286e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko PublishCommand(*command_dict); 11296e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko } 11306e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko} 1131d07e206c4d71c118516054752a69911d04c98fbaAnton Muhin 11326e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenkovoid DeviceRegistrationInfo::PublishCommand( 11336e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko const base::DictionaryValue& command) { 11346e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko std::string command_id; 11350801a1f20a0c8f34130d567cd3b7dcbd2be9cb3cVitaly Buka ErrorPtr error; 1136d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko auto command_instance = component_manager_->ParseCommandInstance( 1137d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko command, Command::Origin::kCloud, UserRole::kOwner, &command_id, &error); 11386e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko if (!command_instance) { 11396e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko LOG(WARNING) << "Failed to parse a command instance: " << command; 11406e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko if (!command_id.empty()) 11416e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko NotifyCommandAborted(command_id, std::move(error)); 11426e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko return; 11436e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko } 1144d07e206c4d71c118516054752a69911d04c98fbaAnton Muhin 11456e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko // TODO(antonm): Properly process cancellation of commands. 1146d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko if (!component_manager_->FindCommand(command_instance->GetID())) { 11476e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko LOG(INFO) << "New command '" << command_instance->GetName() 11486e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko << "' arrived, ID: " << command_instance->GetID(); 11490f80f7c281a0363c2e1e7302f938291b968aea87Vitaly Buka std::unique_ptr<BackoffEntry> backoff_entry{ 11500f80f7c281a0363c2e1e7302f938291b968aea87Vitaly Buka new BackoffEntry{cloud_backoff_policy_.get()}}; 115134668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka std::unique_ptr<CloudCommandProxy> cloud_proxy{ 115234668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka new CloudCommandProxy{command_instance.get(), this, component_manager_, 115334668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka std::move(backoff_entry), task_runner_}}; 11546da9425a14ac345b657354e62307d17df4a721dbVitaly Buka // CloudCommandProxy::CloudCommandProxy() subscribe itself to Command 11556da9425a14ac345b657354e62307d17df4a721dbVitaly Buka // notifications. When Command is being destroyed it sends 1156157b16aa9906a39e67c1f894f8fbf6f2130ea007Vitaly Buka // ::OnCommandDestroyed() and CloudCommandProxy deletes itself. 1157157b16aa9906a39e67c1f894f8fbf6f2130ea007Vitaly Buka cloud_proxy.release(); 1158d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko component_manager_->AddCommand(std::move(command_instance)); 1159d07e206c4d71c118516054752a69911d04c98fbaAnton Muhin } 1160d8d3216bc2fb413e8100ff0b17c53f275300534cAnton Muhin} 1161d8d3216bc2fb413e8100ff0b17c53f275300534cAnton Muhin 1162b8315621198220890057fafe842574fc71a6d93bAnton Muhinvoid DeviceRegistrationInfo::PublishStateUpdates() { 116393ba0bd7fd68ebe91b43a94b1025dd6d435f67c0Alex Vakulenko // If we have pending state update requests, don't send any more for now. 116493ba0bd7fd68ebe91b43a94b1025dd6d435f67c0Alex Vakulenko if (device_state_update_pending_) 116593ba0bd7fd68ebe91b43a94b1025dd6d435f67c0Alex Vakulenko return; 116693ba0bd7fd68ebe91b43a94b1025dd6d435f67c0Alex Vakulenko 1167d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko auto snapshot = component_manager_->GetAndClearRecordedStateChanges(); 1168d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko if (snapshot.state_changes.empty()) 1169b8315621198220890057fafe842574fc71a6d93bAnton Muhin return; 1170b8315621198220890057fafe842574fc71a6d93bAnton Muhin 1171b8315621198220890057fafe842574fc71a6d93bAnton Muhin std::unique_ptr<base::ListValue> patches{new base::ListValue}; 1172d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko for (auto& state_change : snapshot.state_changes) { 1173b8315621198220890057fafe842574fc71a6d93bAnton Muhin std::unique_ptr<base::DictionaryValue> patch{new base::DictionaryValue}; 117476933fdbf7ec8e05ca0549db1e1f9df9ec3eb9a6Anton Muhin patch->SetString("timeMs", 117576933fdbf7ec8e05ca0549db1e1f9df9ec3eb9a6Anton Muhin std::to_string(state_change.timestamp.ToJavaTime())); 11769921b87a837f324951eb0eba4d971cfe854edf27Alex Vakulenko patch->SetString("component", state_change.component); 11777d66921b44464e4480d86c362294831f9587a061Alex Vakulenko patch->Set("patch", state_change.changed_properties.release()); 1178b8315621198220890057fafe842574fc71a6d93bAnton Muhin patches->Append(patch.release()); 1179b8315621198220890057fafe842574fc71a6d93bAnton Muhin } 1180b8315621198220890057fafe842574fc71a6d93bAnton Muhin 1181b8315621198220890057fafe842574fc71a6d93bAnton Muhin base::DictionaryValue body; 118276933fdbf7ec8e05ca0549db1e1f9df9ec3eb9a6Anton Muhin body.SetString("requestTimeMs", 118376933fdbf7ec8e05ca0549db1e1f9df9ec3eb9a6Anton Muhin std::to_string(base::Time::Now().ToJavaTime())); 1184b8315621198220890057fafe842574fc71a6d93bAnton Muhin body.Set("patches", patches.release()); 1185b8315621198220890057fafe842574fc71a6d93bAnton Muhin 118693ba0bd7fd68ebe91b43a94b1025dd6d435f67c0Alex Vakulenko device_state_update_pending_ = true; 1187747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka DoCloudRequest(HttpClient::Method::kPost, GetDeviceURL("patchState"), &body, 1188747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka base::Bind(&DeviceRegistrationInfo::OnPublishStateDone, 1189d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko AsWeakPtr(), snapshot.update_id)); 119093ba0bd7fd68ebe91b43a94b1025dd6d435f67c0Alex Vakulenko} 119193ba0bd7fd68ebe91b43a94b1025dd6d435f67c0Alex Vakulenko 1192747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Bukavoid DeviceRegistrationInfo::OnPublishStateDone( 1193d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko ComponentManager::UpdateID update_id, 1194747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka const base::DictionaryValue& reply, 1195747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka ErrorPtr error) { 119693ba0bd7fd68ebe91b43a94b1025dd6d435f67c0Alex Vakulenko device_state_update_pending_ = false; 1197747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka if (error) { 1198747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka LOG(ERROR) << "Permanent failure while trying to update device state"; 1199747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka return; 1200747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka } 1201d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko component_manager_->NotifyStateUpdatedOnServer(update_id); 120293ba0bd7fd68ebe91b43a94b1025dd6d435f67c0Alex Vakulenko // See if there were more pending state updates since the previous request 120393ba0bd7fd68ebe91b43a94b1025dd6d435f67c0Alex Vakulenko // had been sent out. 120493ba0bd7fd68ebe91b43a94b1025dd6d435f67c0Alex Vakulenko PublishStateUpdates(); 120593ba0bd7fd68ebe91b43a94b1025dd6d435f67c0Alex Vakulenko} 120693ba0bd7fd68ebe91b43a94b1025dd6d435f67c0Alex Vakulenko 1207c3c6dab99bb333417756817d154cd99ae71f4668Vitaly Bukavoid DeviceRegistrationInfo::SetGcdState(GcdState new_state) { 1208c3c6dab99bb333417756817d154cd99ae71f4668Vitaly Buka VLOG_IF(1, new_state != gcd_state_) << "Changing registration status to " 1209c3c6dab99bb333417756817d154cd99ae71f4668Vitaly Buka << EnumToString(new_state); 1210c3c6dab99bb333417756817d154cd99ae71f4668Vitaly Buka gcd_state_ = new_state; 1211c3c6dab99bb333417756817d154cd99ae71f4668Vitaly Buka for (const auto& cb : gcd_state_changed_callbacks_) 1212c3c6dab99bb333417756817d154cd99ae71f4668Vitaly Buka cb.Run(gcd_state_); 1213c900e48b8d482b1462a4d262856e344d22bcc6a5Christopher Wiley} 1214c900e48b8d482b1462a4d262856e344d22bcc6a5Christopher Wiley 1215d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenkovoid DeviceRegistrationInfo::OnTraitDefsChanged() { 12169ea5a321746d94ed46c79633b75357f4cc77562eAlex Vakulenko VLOG(1) << "CommandDefinitionChanged notification received"; 1217fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko if (!HaveRegistrationCredentials() || !connected_to_cloud_) 12189ea5a321746d94ed46c79633b75357f4cc77562eAlex Vakulenko return; 12199ea5a321746d94ed46c79633b75357f4cc77562eAlex Vakulenko 1220747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka UpdateDeviceResource(base::Bind(&IgnoreCloudError)); 12219ea5a321746d94ed46c79633b75357f4cc77562eAlex Vakulenko} 12229ea5a321746d94ed46c79633b75357f4cc77562eAlex Vakulenko 1223c903d281e1196398af49f28b15c0456b1ff28edeVitaly Bukavoid DeviceRegistrationInfo::OnStateChanged() { 1224c903d281e1196398af49f28b15c0456b1ff28edeVitaly Buka VLOG(1) << "StateChanged notification received"; 1225fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko if (!HaveRegistrationCredentials() || !connected_to_cloud_) 1226c903d281e1196398af49f28b15c0456b1ff28edeVitaly Buka return; 1227c903d281e1196398af49f28b15c0456b1ff28edeVitaly Buka 1228c903d281e1196398af49f28b15c0456b1ff28edeVitaly Buka // TODO(vitalybuka): Integrate BackoffEntry. 1229c903d281e1196398af49f28b15c0456b1ff28edeVitaly Buka PublishStateUpdates(); 1230c903d281e1196398af49f28b15c0456b1ff28edeVitaly Buka} 1231c903d281e1196398af49f28b15c0456b1ff28edeVitaly Buka 1232d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenkovoid DeviceRegistrationInfo::OnComponentTreeChanged() { 1233d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko VLOG(1) << "ComponentTreeChanged notification received"; 1234d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko if (!HaveRegistrationCredentials() || !connected_to_cloud_) 1235d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko return; 1236d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko 1237d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko UpdateDeviceResource(base::Bind(&IgnoreCloudError)); 1238d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko} 1239d91d625f29b752be035c5fb49bb29d7ee85fcb90Alex Vakulenko 1240eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenkovoid DeviceRegistrationInfo::OnConnected(const std::string& channel_name) { 1241eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko LOG(INFO) << "Notification channel successfully established over " 1242eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko << channel_name; 1243d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko CHECK_EQ(primary_notification_channel_->GetName(), channel_name); 12446b028ae747f94bd9201f71690a9aa7c0ad585f5fAlex Vakulenko notification_channel_starting_ = false; 124541a90d69918c6367ba23de1edf0bad6c7175f3b9Vitaly Buka pull_channel_->UpdatePullInterval( 1246c1fc90c1724427d8e926172ed258e05ff63b8e26Alex Vakulenko base::TimeDelta::FromMinutes(kBackupPollingPeriodMinutes)); 1247d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko current_notification_channel_ = primary_notification_channel_.get(); 1248fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko 12498b096cc83ae31f66f02adfb68d10507e90520212Alex Vakulenko // If we have not successfully connected to the cloud server and we have not 12508b096cc83ae31f66f02adfb68d10507e90520212Alex Vakulenko // initiated the first device resource update, there is nothing we need to 12518b096cc83ae31f66f02adfb68d10507e90520212Alex Vakulenko // do now to update the server of the notification channel change. 12528b096cc83ae31f66f02adfb68d10507e90520212Alex Vakulenko if (!connected_to_cloud_ && in_progress_resource_update_callbacks_.empty()) 1253fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko return; 1254fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko 12551038ec1b83cf2afe6132dc7dcd005b23463f5858Alex Vakulenko // Once we update the device resource with the new notification channel, 12561038ec1b83cf2afe6132dc7dcd005b23463f5858Alex Vakulenko // do the last poll for commands from the server, to make sure we have the 12571038ec1b83cf2afe6132dc7dcd005b23463f5858Alex Vakulenko // latest command baseline and no other commands have been queued between 12581038ec1b83cf2afe6132dc7dcd005b23463f5858Alex Vakulenko // the moment of the last poll and the time we successfully told the server 12591038ec1b83cf2afe6132dc7dcd005b23463f5858Alex Vakulenko // to send new commands over the new notification channel. 12601038ec1b83cf2afe6132dc7dcd005b23463f5858Alex Vakulenko UpdateDeviceResource( 1261747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka base::Bind(&IgnoreCloudErrorWithCallback, 1262747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka base::Bind(&DeviceRegistrationInfo::FetchAndPublishCommands, 1263e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko AsWeakPtr(), fetch_reason::kRegularPull))); 1264eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko} 1265eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko 1266eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenkovoid DeviceRegistrationInfo::OnDisconnected() { 1267eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko LOG(INFO) << "Notification channel disconnected"; 1268fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko if (!HaveRegistrationCredentials() || !connected_to_cloud_) 12693fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko return; 12703fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko 127141a90d69918c6367ba23de1edf0bad6c7175f3b9Vitaly Buka pull_channel_->UpdatePullInterval( 127241a90d69918c6367ba23de1edf0bad6c7175f3b9Vitaly Buka base::TimeDelta::FromSeconds(kPollingPeriodSeconds)); 1273d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko current_notification_channel_ = pull_channel_.get(); 1274747634273144e0df7b37475375ad4790b6a7b0e8Vitaly Buka UpdateDeviceResource(base::Bind(&IgnoreCloudError)); 1275eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko} 1276eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko 1277eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenkovoid DeviceRegistrationInfo::OnPermanentFailure() { 1278eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko LOG(ERROR) << "Failed to establish notification channel."; 12796b028ae747f94bd9201f71690a9aa7c0ad585f5fAlex Vakulenko notification_channel_starting_ = false; 12804ebd329b95f6d596c8ec97844d346b8a3149bb76Vitaly Buka RefreshAccessToken( 12814ebd329b95f6d596c8ec97844d346b8a3149bb76Vitaly Buka base::Bind(&DeviceRegistrationInfo::CheckAccessTokenError, AsWeakPtr())); 1282eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko} 1283eedf3be451ea83ce21aa22294c9605f15bd3eebaAlex Vakulenko 12846e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenkovoid DeviceRegistrationInfo::OnCommandCreated( 1285e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko const base::DictionaryValue& command, 1286e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko const std::string& channel_name) { 1287fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko if (!connected_to_cloud_) 1288fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko return; 1289fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko 12907a35005ccd95adcb42125d602d9a997d0bf33d86Vitaly Buka VLOG(1) << "Command notification received: " << command; 12917a35005ccd95adcb42125d602d9a997d0bf33d86Vitaly Buka 12926e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko if (!command.empty()) { 12936e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko // GCD spec indicates that the command parameter in notification object 12946e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko // "may be empty if command size is too big". 12956e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko PublishCommand(command); 12966e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko return; 12976e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko } 1298e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko 1299e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko // If this request comes from a Pull channel while the primary notification 1300e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko // channel (XMPP) is active, we are doing a backup poll, so mark the request 1301e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko // appropriately. 1302e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko bool just_in_case = 130334668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka (channel_name == kPullChannelName) && 130434668e731bb194b443bc0e6029d6d3583f08de28Vitaly Buka (current_notification_channel_ == primary_notification_channel_.get()); 1305e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko 1306e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko std::string reason = 1307e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko just_in_case ? fetch_reason::kJustInCase : fetch_reason::kNewCommand; 1308e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko 1309d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko // If the command was too big to be delivered over a notification channel, 1310d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko // or OnCommandCreated() was initiated from the Pull notification, 1311d05725f1d796ea298d31a9a421c8e6d80fb88cedAlex Vakulenko // perform a manual command fetch from the server here. 1312e07c29d604d1b1c3a179f40140934a806bd9a425Alex Vakulenko FetchAndPublishCommands(reason); 13136e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko} 13146e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko 1315312c2f5d0d5e2ef0cc7acc6dce5137094332c276Johan Euphrosinevoid DeviceRegistrationInfo::OnDeviceDeleted(const std::string& cloud_id) { 1316312c2f5d0d5e2ef0cc7acc6dce5137094332c276Johan Euphrosine if (cloud_id != GetSettings().cloud_id) { 1317312c2f5d0d5e2ef0cc7acc6dce5137094332c276Johan Euphrosine LOG(WARNING) << "Unexpected device deletion notification for cloud ID '" 1318312c2f5d0d5e2ef0cc7acc6dce5137094332c276Johan Euphrosine << cloud_id << "'"; 13196b40d8fae84cdb589a7bdd65e341957bc1ce5c33Alex Vakulenko return; 13206b40d8fae84cdb589a7bdd65e341957bc1ce5c33Alex Vakulenko } 1321672634b8b0ed31891fb48e02dce75b6aead0be27Vitaly Buka RemoveCredentials(); 13226b40d8fae84cdb589a7bdd65e341957bc1ce5c33Alex Vakulenko} 13236b40d8fae84cdb589a7bdd65e341957bc1ce5c33Alex Vakulenko 1324672634b8b0ed31891fb48e02dce75b6aead0be27Vitaly Bukavoid DeviceRegistrationInfo::RemoveCredentials() { 13253fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko if (!HaveRegistrationCredentials()) 13263fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko return; 13273fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko 1328fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko connected_to_cloud_ = false; 1329fb331acff6d29bef16ccbfda2006c173b3a58bf4Alex Vakulenko 13303fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko LOG(INFO) << "Device is unregistered from the cloud. Deleting credentials"; 13314ab500249f346a9fcfe084ee1619a39259f7471cVitaly Buka if (auth_manager_) 13320bc02ede1d7ac6b0ed264b8891844d15bdb4733eVitaly Buka auth_manager_->SetAuthSecret({}, RootClientTokenOwner::kNone); 13334ab500249f346a9fcfe084ee1619a39259f7471cVitaly Buka 1334666b43e92729fa170bc53eab9040a4dfe58b8062Vitaly Buka Config::Transaction change{config_}; 1335672634b8b0ed31891fb48e02dce75b6aead0be27Vitaly Buka // Keep cloud_id to switch to detect kInvalidCredentials after restart. 13363fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko change.set_robot_account(""); 13373fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko change.set_refresh_token(""); 13383fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko change.Commit(); 13393fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko 13403fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko current_notification_channel_ = nullptr; 13413fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko if (primary_notification_channel_) { 13423fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko primary_notification_channel_->Stop(); 13433fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko primary_notification_channel_.reset(); 13443fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko } 13453fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko if (pull_channel_) { 13463fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko pull_channel_->Stop(); 13473fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko pull_channel_.reset(); 13483fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko } 13493fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko notification_channel_starting_ = false; 1350c3c6dab99bb333417756817d154cd99ae71f4668Vitaly Buka SetGcdState(GcdState::kInvalidCredentials); 13513fa42aebf88af42a17026b41a8eb7cc510ef7897Alex Vakulenko} 13526e3c30e823a0ee72dc7aa1ccf9f8b71aa9d99ab4Alex Vakulenko 1353b6f015a1ef3caffbc2af53184c0ec5342e42e048Vitaly Buka} // namespace weave 1354