Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(29)

Side by Side Diff: remoting/protocol/pairing_registry.cc

Issue 21128006: Refactored PairingRegistry::Delegate such that it can retrieve/modify for a single client. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebased Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « remoting/protocol/pairing_registry.h ('k') | remoting/protocol/pairing_registry_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/protocol/pairing_registry.h" 5 #include "remoting/protocol/pairing_registry.h"
6 6
7 #include "base/base64.h" 7 #include "base/base64.h"
8 #include "base/bind.h" 8 #include "base/bind.h"
9 #include "base/guid.h" 9 #include "base/guid.h"
10 #include "base/json/json_string_value_serializer.h" 10 #include "base/json/json_string_value_serializer.h"
11 #include "base/location.h"
12 #include "base/single_thread_task_runner.h"
11 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/string_number_conversions.h"
14 #include "base/thread_task_runner_handle.h"
12 #include "base/values.h" 15 #include "base/values.h"
13 #include "crypto/random.h" 16 #include "crypto/random.h"
14 17
15 namespace remoting { 18 namespace remoting {
16 namespace protocol { 19 namespace protocol {
17 20
18 // How many bytes of random data to use for the shared secret. 21 // How many bytes of random data to use for the shared secret.
19 const int kKeySize = 16; 22 const int kKeySize = 16;
20 23
21 const char PairingRegistry::kCreatedTimeKey[] = "createdTime"; 24 const char PairingRegistry::kCreatedTimeKey[] = "createdTime";
22 const char PairingRegistry::kClientIdKey[] = "clientId"; 25 const char PairingRegistry::kClientIdKey[] = "clientId";
23 const char PairingRegistry::kClientNameKey[] = "clientName"; 26 const char PairingRegistry::kClientNameKey[] = "clientName";
24 const char PairingRegistry::kSharedSecretKey[] = "sharedSecret"; 27 const char PairingRegistry::kSharedSecretKey[] = "sharedSecret";
25 28
26 PairingRegistry::Pairing::Pairing() { 29 PairingRegistry::Pairing::Pairing() {
27 } 30 }
28 31
29 PairingRegistry::Pairing::Pairing(const base::Time& created_time, 32 PairingRegistry::Pairing::Pairing(const base::Time& created_time,
30 const std::string& client_name, 33 const std::string& client_name,
31 const std::string& client_id, 34 const std::string& client_id,
32 const std::string& shared_secret) 35 const std::string& shared_secret)
33 : created_time_(created_time), 36 : created_time_(created_time),
34 client_name_(client_name), 37 client_name_(client_name),
35 client_id_(client_id), 38 client_id_(client_id),
36 shared_secret_(shared_secret) { 39 shared_secret_(shared_secret) {
37 } 40 }
38 41
42 PairingRegistry::Pairing::~Pairing() {
43 }
44
39 PairingRegistry::Pairing PairingRegistry::Pairing::Create( 45 PairingRegistry::Pairing PairingRegistry::Pairing::Create(
40 const std::string& client_name) { 46 const std::string& client_name) {
41 base::Time created_time = base::Time::Now(); 47 base::Time created_time = base::Time::Now();
42 std::string client_id = base::GenerateGUID(); 48 std::string client_id = base::GenerateGUID();
43 std::string shared_secret; 49 std::string shared_secret;
44 char buffer[kKeySize]; 50 char buffer[kKeySize];
45 crypto::RandBytes(buffer, arraysize(buffer)); 51 crypto::RandBytes(buffer, arraysize(buffer));
46 if (!base::Base64Encode(base::StringPiece(buffer, arraysize(buffer)), 52 if (!base::Base64Encode(base::StringPiece(buffer, arraysize(buffer)),
47 &shared_secret)) { 53 &shared_secret)) {
48 LOG(FATAL) << "Base64Encode failed."; 54 LOG(FATAL) << "Base64Encode failed.";
49 } 55 }
50 return Pairing(created_time, client_name, client_id, shared_secret); 56 return Pairing(created_time, client_name, client_id, shared_secret);
51 } 57 }
52 58
53 PairingRegistry::Pairing::~Pairing() { 59 PairingRegistry::Pairing PairingRegistry::Pairing::CreateFromValue(
60 const base::Value& pairing_json) {
61 const base::DictionaryValue* pairing = NULL;
62 if (!pairing_json.GetAsDictionary(&pairing)) {
63 LOG(ERROR) << "Failed to load pairing information: not a dictionary.";
64 return Pairing();
65 }
66
67 std::string client_name, client_id;
68 double created_time_value;
69 if (pairing->GetDouble(kCreatedTimeKey, &created_time_value) &&
70 pairing->GetString(kClientNameKey, &client_name) &&
71 pairing->GetString(kClientIdKey, &client_id)) {
72 // The shared secret is optional.
73 std::string shared_secret;
74 pairing->GetString(kSharedSecretKey, &shared_secret);
75 base::Time created_time = base::Time::FromJsTime(created_time_value);
76 return Pairing(created_time, client_name, client_id, shared_secret);
77 }
78
79 LOG(ERROR) << "Failed to load pairing information: unexpected format.";
80 return Pairing();
81 }
82
83 scoped_ptr<base::Value> PairingRegistry::Pairing::ToValue() const {
84 scoped_ptr<base::DictionaryValue> pairing(new base::DictionaryValue());
85 pairing->SetDouble(kCreatedTimeKey, created_time().ToJsTime());
86 pairing->SetString(kClientNameKey, client_name());
87 pairing->SetString(kClientIdKey, client_id());
88 if (!shared_secret().empty())
89 pairing->SetString(kSharedSecretKey, shared_secret());
90 return pairing.PassAs<base::Value>();
54 } 91 }
55 92
56 bool PairingRegistry::Pairing::operator==(const Pairing& other) const { 93 bool PairingRegistry::Pairing::operator==(const Pairing& other) const {
57 return created_time_ == other.created_time_ && 94 return created_time_ == other.created_time_ &&
58 client_id_ == other.client_id_ && 95 client_id_ == other.client_id_ &&
59 client_name_ == other.client_name_ && 96 client_name_ == other.client_name_ &&
60 shared_secret_ == other.shared_secret_; 97 shared_secret_ == other.shared_secret_;
61 } 98 }
62 99
63 bool PairingRegistry::Pairing::is_valid() const { 100 bool PairingRegistry::Pairing::is_valid() const {
64 return !client_id_.empty() && !shared_secret_.empty(); 101 return !client_id_.empty() && !shared_secret_.empty();
65 } 102 }
66 103
67 PairingRegistry::PairingRegistry(scoped_ptr<Delegate> delegate) 104 PairingRegistry::PairingRegistry(
68 : delegate_(delegate.Pass()) { 105 scoped_refptr<base::SingleThreadTaskRunner> delegate_task_runner,
106 scoped_ptr<Delegate> delegate)
107 : caller_task_runner_(base::ThreadTaskRunnerHandle::Get()),
108 delegate_task_runner_(delegate_task_runner),
109 delegate_(delegate.Pass()) {
69 DCHECK(delegate_); 110 DCHECK(delegate_);
70 } 111 }
71 112
72 PairingRegistry::~PairingRegistry() {
73 }
74
75 PairingRegistry::Pairing PairingRegistry::CreatePairing( 113 PairingRegistry::Pairing PairingRegistry::CreatePairing(
76 const std::string& client_name) { 114 const std::string& client_name) {
77 DCHECK(CalledOnValidThread()); 115 DCHECK(caller_task_runner_->BelongsToCurrentThread());
116
78 Pairing result = Pairing::Create(client_name); 117 Pairing result = Pairing::Create(client_name);
79 AddPairing(result); 118 AddPairing(result);
80 return result; 119 return result;
81 } 120 }
82 121
83 void PairingRegistry::GetPairing(const std::string& client_id, 122 void PairingRegistry::GetPairing(const std::string& client_id,
84 const GetPairingCallback& callback) { 123 const GetPairingCallback& callback) {
85 DCHECK(CalledOnValidThread()); 124 DCHECK(caller_task_runner_->BelongsToCurrentThread());
125
86 GetPairingCallback wrapped_callback = base::Bind( 126 GetPairingCallback wrapped_callback = base::Bind(
87 &PairingRegistry::InvokeGetPairingCallbackAndScheduleNext, 127 &PairingRegistry::InvokeGetPairingCallbackAndScheduleNext,
88 this, callback); 128 this, callback);
89 LoadCallback load_callback = base::Bind(
90 &PairingRegistry::DoGetPairing, this, client_id, wrapped_callback);
91 // |Unretained| and |get| are both safe here because the delegate is owned
92 // by the pairing registry and so is guaranteed to exist when the request
93 // is serviced.
94 base::Closure request = base::Bind( 129 base::Closure request = base::Bind(
95 &PairingRegistry::Delegate::Load, 130 &PairingRegistry::DoLoad, this, client_id, wrapped_callback);
96 base::Unretained(delegate_.get()), load_callback);
97 ServiceOrQueueRequest(request); 131 ServiceOrQueueRequest(request);
98 } 132 }
99 133
100 void PairingRegistry::GetAllPairings( 134 void PairingRegistry::GetAllPairings(
101 const GetAllPairingsCallback& callback) { 135 const GetAllPairingsCallback& callback) {
102 DCHECK(CalledOnValidThread()); 136 DCHECK(caller_task_runner_->BelongsToCurrentThread());
137
103 GetAllPairingsCallback wrapped_callback = base::Bind( 138 GetAllPairingsCallback wrapped_callback = base::Bind(
104 &PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext, 139 &PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext,
105 this, callback); 140 this, callback);
106 LoadCallback load_callback = base::Bind( 141 GetAllPairingsCallback sanitize_callback = base::Bind(
107 &PairingRegistry::SanitizePairings, this, wrapped_callback); 142 &PairingRegistry::SanitizePairings,
143 this, wrapped_callback);
108 base::Closure request = base::Bind( 144 base::Closure request = base::Bind(
109 &PairingRegistry::Delegate::Load, 145 &PairingRegistry::DoLoadAll, this, sanitize_callback);
110 base::Unretained(delegate_.get()), load_callback);
111 ServiceOrQueueRequest(request); 146 ServiceOrQueueRequest(request);
112 } 147 }
113 148
114 void PairingRegistry::DeletePairing( 149 void PairingRegistry::DeletePairing(
115 const std::string& client_id, const SaveCallback& callback) { 150 const std::string& client_id, const DoneCallback& callback) {
116 DCHECK(CalledOnValidThread()); 151 DCHECK(caller_task_runner_->BelongsToCurrentThread());
117 SaveCallback wrapped_callback = base::Bind( 152
118 &PairingRegistry::InvokeSaveCallbackAndScheduleNext, 153 DoneCallback wrapped_callback = base::Bind(
154 &PairingRegistry::InvokeDoneCallbackAndScheduleNext,
119 this, callback); 155 this, callback);
120 LoadCallback load_callback = base::Bind(
121 &PairingRegistry::DoDeletePairing, this, client_id, wrapped_callback);
122 base::Closure request = base::Bind( 156 base::Closure request = base::Bind(
123 &PairingRegistry::Delegate::Load, 157 &PairingRegistry::DoDelete, this, client_id, wrapped_callback);
124 base::Unretained(delegate_.get()), load_callback);
125 ServiceOrQueueRequest(request); 158 ServiceOrQueueRequest(request);
126 } 159 }
127 160
128 void PairingRegistry::ClearAllPairings( 161 void PairingRegistry::ClearAllPairings(
129 const SaveCallback& callback) { 162 const DoneCallback& callback) {
130 DCHECK(CalledOnValidThread()); 163 DCHECK(caller_task_runner_->BelongsToCurrentThread());
131 SaveCallback wrapped_callback = base::Bind( 164
132 &PairingRegistry::InvokeSaveCallbackAndScheduleNext, 165 DoneCallback wrapped_callback = base::Bind(
166 &PairingRegistry::InvokeDoneCallbackAndScheduleNext,
133 this, callback); 167 this, callback);
134 base::Closure request = base::Bind( 168 base::Closure request = base::Bind(
135 &PairingRegistry::Delegate::Save, 169 &PairingRegistry::DoDeleteAll, this, wrapped_callback);
136 base::Unretained(delegate_.get()),
137 EncodeJson(PairedClients()),
138 wrapped_callback);
139 ServiceOrQueueRequest(request); 170 ServiceOrQueueRequest(request);
140 } 171 }
141 172
173 PairingRegistry::~PairingRegistry() {
174 }
175
176 void PairingRegistry::PostTask(
177 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
178 const tracked_objects::Location& from_here,
179 const base::Closure& task) {
180 task_runner->PostTask(from_here, task);
181 }
182
142 void PairingRegistry::AddPairing(const Pairing& pairing) { 183 void PairingRegistry::AddPairing(const Pairing& pairing) {
143 SaveCallback callback = base::Bind( 184 DoneCallback wrapped_callback = base::Bind(
144 &PairingRegistry::InvokeSaveCallbackAndScheduleNext, 185 &PairingRegistry::InvokeDoneCallbackAndScheduleNext,
145 this, SaveCallback()); 186 this, DoneCallback());
146 LoadCallback load_callback = base::Bind(
147 &PairingRegistry::MergePairingAndSave, this, pairing, callback);
148 base::Closure request = base::Bind( 187 base::Closure request = base::Bind(
149 &PairingRegistry::Delegate::Load, 188 &PairingRegistry::DoSave, this, pairing, wrapped_callback);
150 base::Unretained(delegate_.get()), load_callback);
151 ServiceOrQueueRequest(request); 189 ServiceOrQueueRequest(request);
152 } 190 }
153 191
154 void PairingRegistry::MergePairingAndSave(const Pairing& pairing, 192 void PairingRegistry::DoLoadAll(
155 const SaveCallback& callback, 193 const protocol::PairingRegistry::GetAllPairingsCallback& callback) {
156 const std::string& pairings_json) { 194 DCHECK(delegate_task_runner_->BelongsToCurrentThread());
157 DCHECK(CalledOnValidThread()); 195
158 PairedClients clients = DecodeJson(pairings_json); 196 scoped_ptr<base::ListValue> pairings = delegate_->LoadAll();
159 clients[pairing.client_id()] = pairing; 197 PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback,
160 std::string new_pairings_json = EncodeJson(clients); 198 base::Passed(&pairings)));
161 delegate_->Save(new_pairings_json, callback);
162 } 199 }
163 200
164 void PairingRegistry::DoGetPairing(const std::string& client_id, 201 void PairingRegistry::DoDeleteAll(
165 const GetPairingCallback& callback, 202 const protocol::PairingRegistry::DoneCallback& callback) {
166 const std::string& pairings_json) { 203 DCHECK(delegate_task_runner_->BelongsToCurrentThread());
167 PairedClients clients = DecodeJson(pairings_json); 204
168 Pairing result = clients[client_id]; 205 bool success = delegate_->DeleteAll();
169 callback.Run(result); 206 PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success));
170 } 207 }
171 208
172 void PairingRegistry::SanitizePairings(const GetAllPairingsCallback& callback, 209 void PairingRegistry::DoLoad(
173 const std::string& pairings_json) { 210 const std::string& client_id,
174 PairedClients clients = DecodeJson(pairings_json); 211 const protocol::PairingRegistry::GetPairingCallback& callback) {
175 callback.Run(ConvertToListValue(clients, false)); 212 DCHECK(delegate_task_runner_->BelongsToCurrentThread());
213
214 Pairing pairing = delegate_->Load(client_id);
215 PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, pairing));
176 } 216 }
177 217
178 void PairingRegistry::DoDeletePairing(const std::string& client_id, 218 void PairingRegistry::DoSave(
179 const SaveCallback& callback, 219 const protocol::PairingRegistry::Pairing& pairing,
180 const std::string& pairings_json) { 220 const protocol::PairingRegistry::DoneCallback& callback) {
181 PairedClients clients = DecodeJson(pairings_json); 221 DCHECK(delegate_task_runner_->BelongsToCurrentThread());
182 clients.erase(client_id); 222
183 std::string new_pairings_json = EncodeJson(clients); 223 bool success = delegate_->Save(pairing);
184 delegate_->Save(new_pairings_json, callback); 224 PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success));
185 } 225 }
186 226
187 void PairingRegistry::InvokeLoadCallbackAndScheduleNext( 227 void PairingRegistry::DoDelete(
188 const LoadCallback& callback, const std::string& pairings_json) { 228 const std::string& client_id,
189 callback.Run(pairings_json); 229 const protocol::PairingRegistry::DoneCallback& callback) {
230 DCHECK(delegate_task_runner_->BelongsToCurrentThread());
231
232 bool success = delegate_->Delete(client_id);
233 PostTask(caller_task_runner_, FROM_HERE, base::Bind(callback, success));
234 }
235
236 void PairingRegistry::InvokeDoneCallbackAndScheduleNext(
237 const DoneCallback& callback, bool success) {
238 // CreatePairing doesn't have a callback, so the callback can be null.
239 if (!callback.is_null())
240 callback.Run(success);
241
190 pending_requests_.pop(); 242 pending_requests_.pop();
191 ServiceNextRequest(); 243 ServiceNextRequest();
192 } 244 }
193
194 void PairingRegistry::InvokeSaveCallbackAndScheduleNext(
195 const SaveCallback& callback, bool success) {
196 // CreatePairing doesn't have a callback, so the callback can be null.
197 if (!callback.is_null()) {
198 callback.Run(success);
199 }
200 pending_requests_.pop();
201 ServiceNextRequest();
202 }
203 245
204 void PairingRegistry::InvokeGetPairingCallbackAndScheduleNext( 246 void PairingRegistry::InvokeGetPairingCallbackAndScheduleNext(
205 const GetPairingCallback& callback, Pairing pairing) { 247 const GetPairingCallback& callback, Pairing pairing) {
206 callback.Run(pairing); 248 callback.Run(pairing);
207 pending_requests_.pop(); 249 pending_requests_.pop();
208 ServiceNextRequest(); 250 ServiceNextRequest();
209 } 251 }
210 252
211 void PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext( 253 void PairingRegistry::InvokeGetAllPairingsCallbackAndScheduleNext(
212 const GetAllPairingsCallback& callback, 254 const GetAllPairingsCallback& callback,
213 scoped_ptr<base::ListValue> pairings) { 255 scoped_ptr<base::ListValue> pairings) {
214 callback.Run(pairings.Pass()); 256 callback.Run(pairings.Pass());
215 pending_requests_.pop(); 257 pending_requests_.pop();
216 ServiceNextRequest(); 258 ServiceNextRequest();
217 } 259 }
218 260
219 // static 261 void PairingRegistry::SanitizePairings(const GetAllPairingsCallback& callback,
220 PairingRegistry::PairedClients PairingRegistry::DecodeJson( 262 scoped_ptr<base::ListValue> pairings) {
221 const std::string& pairings_json) { 263 DCHECK(caller_task_runner_->BelongsToCurrentThread());
222 PairedClients result;
223 264
224 if (pairings_json.empty()) { 265 scoped_ptr<base::ListValue> sanitized_pairings(new base::ListValue());
225 return result; 266 for (size_t i = 0; i < pairings->GetSize(); ++i) {
267 DictionaryValue* pairing_json;
268 if (!pairings->GetDictionary(i, &pairing_json)) {
269 LOG(WARNING) << "A pairing entry is not a dictionary.";
270 continue;
271 }
272
273 // Parse the pairing data.
274 Pairing pairing = Pairing::CreateFromValue(*pairing_json);
275 if (!pairing.is_valid()) {
276 LOG(WARNING) << "Could not parse a pairing entry.";
277 continue;
278 }
279
280 // Clear the shared secrect and append the pairing data to the list.
281 Pairing sanitized_pairing(
282 pairing.created_time(),
283 pairing.client_name(),
284 pairing.client_id(),
285 "");
286 sanitized_pairings->Append(sanitized_pairing.ToValue().release());
226 } 287 }
227 288
228 JSONStringValueSerializer registry(pairings_json); 289 callback.Run(sanitized_pairings.Pass());
229 int error_code;
230 std::string error_message;
231 scoped_ptr<base::Value> root(
232 registry.Deserialize(&error_code, &error_message));
233 if (!root) {
234 LOG(ERROR) << "Failed to load paired clients: " << error_message
235 << " (" << error_code << ").";
236 return result;
237 }
238
239 base::ListValue* root_list = NULL;
240 if (!root->GetAsList(&root_list)) {
241 LOG(ERROR) << "Failed to load paired clients: root node is not a list.";
242 return result;
243 }
244
245 for (size_t i = 0; i < root_list->GetSize(); ++i) {
246 base::DictionaryValue* pairing = NULL;
247 std::string client_name, client_id, shared_secret;
248 double created_time_value;
249 if (root_list->GetDictionary(i, &pairing) &&
250 pairing->GetDouble(kCreatedTimeKey, &created_time_value) &&
251 pairing->GetString(kClientNameKey, &client_name) &&
252 pairing->GetString(kClientIdKey, &client_id) &&
253 pairing->GetString(kSharedSecretKey, &shared_secret)) {
254 base::Time created_time = base::Time::FromJsTime(created_time_value);
255 result[client_id] = Pairing(
256 created_time, client_name, client_id, shared_secret);
257 } else {
258 LOG(ERROR) << "Paired client " << i << " has unexpected format.";
259 }
260 }
261
262 return result;
263 } 290 }
264 291
265 void PairingRegistry::ServiceOrQueueRequest(const base::Closure& request) { 292 void PairingRegistry::ServiceOrQueueRequest(const base::Closure& request) {
266 bool servicing_request = !pending_requests_.empty(); 293 bool servicing_request = !pending_requests_.empty();
267 pending_requests_.push(request); 294 pending_requests_.push(request);
268 if (!servicing_request) { 295 if (!servicing_request) {
269 ServiceNextRequest(); 296 ServiceNextRequest();
270 } 297 }
271 } 298 }
272 299
273 void PairingRegistry::ServiceNextRequest() { 300 void PairingRegistry::ServiceNextRequest() {
274 if (pending_requests_.empty()) { 301 if (pending_requests_.empty())
275 return; 302 return;
276 }
277 base::Closure request = pending_requests_.front();
278 request.Run();
279 }
280 303
281 // static 304 PostTask(delegate_task_runner_, FROM_HERE, pending_requests_.front());
282 std::string PairingRegistry::EncodeJson(const PairedClients& clients) {
283 scoped_ptr<base::ListValue> root = ConvertToListValue(clients, true);
284 std::string result;
285 JSONStringValueSerializer serializer(&result);
286 serializer.Serialize(*root);
287
288 return result;
289 }
290
291 // static
292 scoped_ptr<base::ListValue> PairingRegistry::ConvertToListValue(
293 const PairedClients& clients,
294 bool include_shared_secrets) {
295 scoped_ptr<base::ListValue> root(new base::ListValue());
296 for (PairedClients::const_iterator i = clients.begin();
297 i != clients.end(); ++i) {
298 base::DictionaryValue* pairing = new base::DictionaryValue();
299 pairing->SetDouble(kCreatedTimeKey, i->second.created_time().ToJsTime());
300 pairing->SetString(kClientNameKey, i->second.client_name());
301 pairing->SetString(kClientIdKey, i->second.client_id());
302 if (include_shared_secrets) {
303 pairing->SetString(kSharedSecretKey, i->second.shared_secret());
304 }
305 root->Append(pairing);
306 }
307 return root.Pass();
308 } 305 }
309 306
310 } // namespace protocol 307 } // namespace protocol
311 } // namespace remoting 308 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/protocol/pairing_registry.h ('k') | remoting/protocol/pairing_registry_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698