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

Side by Side Diff: components/proximity_auth/cryptauth/cryptauth_client_unittest.cc

Issue 738593002: Introduce CryptAuthClient, a class capable of performing all CryptAuth APIs. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix typo Created 6 years 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
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/proximity_auth/cryptauth/cryptauth_client.h"
6
7 #include "base/command_line.h"
8 #include "base/test/null_task_runner.h"
9 #include "components/proximity_auth/cryptauth/cryptauth_access_token_fetcher.h"
10 #include "components/proximity_auth/cryptauth/cryptauth_api_call_flow.h"
11 #include "components/proximity_auth/cryptauth/proto/cryptauth_api.pb.h"
12 #include "components/proximity_auth/switches.h"
13 #include "net/url_request/test_url_fetcher_factory.h"
14 #include "net/url_request/url_request_test_util.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 using testing::_;
19 using testing::DoAll;
20 using testing::Return;
21 using testing::SaveArg;
22 using testing::StrictMock;
23
24 namespace proximity_auth {
25
26 namespace {
27
28 const char kTestGoogleApisUrl[] = "https://www.testgoogleapis.com";
29 const char kAccessToken[] = "access_token";
30 const char kPublicKey1[] = "public_key1";
31 const char kPublicKey2[] = "public_key2";
32 const char kBluetoothAddress1[] = "AA:AA:AA:AA:AA:AA";
33 const char kBluetoothAddress2[] = "BB:BB:BB:BB:BB:BB";
34
35 // CryptAuthAccessTokenFetcher implementation simply returning a predetermined
36 // access token.
37 class FakeCryptAuthAccessTokenFetcher : public CryptAuthAccessTokenFetcher {
38 public:
39 FakeCryptAuthAccessTokenFetcher() : access_token_(kAccessToken) {}
40
41 void FetchAccessToken(const AccessTokenCallback& callback) override {
42 callback.Run(access_token_);
43 }
44
45 void set_access_token(const std::string& access_token) {
46 access_token_ = access_token;
47 };
48
49 private:
50 std::string access_token_;
51 };
52
53 // Mock CryptAuthApiCallFlow, which handles the HTTP requests to CryptAuth.
54 class MockCryptAuthApiCallFlow : public CryptAuthApiCallFlow {
55 public:
56 MockCryptAuthApiCallFlow() : CryptAuthApiCallFlow(GURL(std::string())) {}
57 virtual ~MockCryptAuthApiCallFlow() {}
58
59 MOCK_METHOD5(Start,
60 void(net::URLRequestContextGetter* context,
61 const std::string& access_token,
62 const std::string& serialized_request,
63 const ResultCallback& result_callback,
64 const ErrorCallback& error_callback));
65
66 private:
67 DISALLOW_COPY_AND_ASSIGN(MockCryptAuthApiCallFlow);
68 };
69
70 // Subclass of CryptAuthClient to use as test harness.
71 class MockCryptAuthClient : public CryptAuthClient {
72 public:
73 // Ownership of |access_token_fetcher| is passed to the superclass. Due to the
74 // limitations of gmock, we need to use a raw pointer argument rather than a
75 // scoped_ptr.
76 MockCryptAuthClient(
77 CryptAuthAccessTokenFetcher* access_token_fetcher,
78 scoped_refptr<net::URLRequestContextGetter> url_request_context)
79 : CryptAuthClient(make_scoped_ptr(access_token_fetcher),
80 url_request_context) {}
81 virtual ~MockCryptAuthClient() {}
82
83 MOCK_METHOD1(CreateFlowProxy, CryptAuthApiCallFlow*(const GURL& request_url));
84
85 scoped_ptr<CryptAuthApiCallFlow> CreateFlow(
86 const GURL& request_url) override {
87 return make_scoped_ptr(CreateFlowProxy(request_url));
88 };
89
90 private:
91 DISALLOW_COPY_AND_ASSIGN(MockCryptAuthClient);
92 };
93
94 // Callback that should never be invoked.
95 template <class T>
96 void NotCalled(const T& type) {
97 EXPECT_TRUE(false);
98 }
99
100 // Callback that saves the result returned by CryptAuthClient.
101 template <class T>
102 void SaveResult(T* out, const T& result) {
103 *out = result;
104 }
105
106 } // namespace
107
108 class ProximityAuthCryptAuthClientTest : public testing::Test {
109 protected:
110 ProximityAuthCryptAuthClientTest()
111 : access_token_fetcher_(new FakeCryptAuthAccessTokenFetcher()),
112 url_request_context_(
113 new net::TestURLRequestContextGetter(new base::NullTaskRunner())),
114 serialized_request_(std::string()) {}
115 virtual ~ProximityAuthCryptAuthClientTest() {}
116
117 void SetUp() override {
118 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
119 switches::kCryptAuthHTTPHost, kTestGoogleApisUrl);
120
121 client_.reset(new StrictMock<MockCryptAuthClient>(access_token_fetcher_,
122 url_request_context_));
123 }
124
125 // Sets up an expectation and captures a CryptAuth API request to
126 // |request_url|.
127 void ExpectRequest(const std::string& request_url) {
128 StrictMock<MockCryptAuthApiCallFlow>* api_call_flow =
129 new StrictMock<MockCryptAuthApiCallFlow>();
130
131 EXPECT_CALL(*client_, CreateFlowProxy(GURL(request_url)))
132 .WillOnce(Return(api_call_flow));
133
134 EXPECT_CALL(*api_call_flow,
135 Start(url_request_context_.get(), kAccessToken, _, _, _))
136 .WillOnce(DoAll(SaveArg<2>(&serialized_request_),
137 SaveArg<3>(&flow_result_callback_),
138 SaveArg<4>(&flow_error_callback_)));
139 }
140
141 // Returns |response_proto| as the result to the current API request.
142 // ExpectResult() must have been called first.
143 void FinishApiCallFlow(const google::protobuf::MessageLite* response_proto) {
144 flow_result_callback_.Run(response_proto->SerializeAsString());
145 }
146
147 // Ends the current API request with |error_message|. ExpectResult() must have
148 // been called first.
149 void FailApiCallFlow(const std::string& error_message) {
150 flow_error_callback_.Run(error_message);
151 }
152
153 protected:
154 // Owned by |client_|.
155 FakeCryptAuthAccessTokenFetcher* access_token_fetcher_;
156
157 scoped_refptr<net::URLRequestContextGetter> url_request_context_;
158 scoped_ptr<StrictMock<MockCryptAuthClient>> client_;
159
160 std::string serialized_request_;
161 CryptAuthApiCallFlow::ResultCallback flow_result_callback_;
162 CryptAuthApiCallFlow::ErrorCallback flow_error_callback_;
163 };
164
165 TEST_F(ProximityAuthCryptAuthClientTest, GetMyDevicesSuccess) {
166 ExpectRequest(
167 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
168 "getmydevices?alt=proto");
169
170 cryptauth::GetMyDevicesResponse result_proto;
171 cryptauth::GetMyDevicesRequest request_proto;
172 request_proto.set_allow_stale_read(true);
173 client_->GetMyDevices(
174 request_proto,
175 base::Bind(&SaveResult<cryptauth::GetMyDevicesResponse>, &result_proto),
176 base::Bind(&NotCalled<std::string>));
177
178 cryptauth::GetMyDevicesRequest expected_request;
179 EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
180 EXPECT_TRUE(expected_request.allow_stale_read());
181
182 // Return two devices, one unlock key and one unlockable device.
183 {
184 cryptauth::GetMyDevicesResponse response_proto;
185 response_proto.add_devices();
186 response_proto.mutable_devices(0)->set_public_key(kPublicKey1);
187 response_proto.mutable_devices(0)->set_unlock_key(true);
188 response_proto.mutable_devices(0)
189 ->set_bluetooth_address(kBluetoothAddress1);
190 response_proto.add_devices();
191 response_proto.mutable_devices(1)->set_public_key(kPublicKey2);
192 response_proto.mutable_devices(1)->set_unlockable(true);
193 FinishApiCallFlow(&response_proto);
194 }
195
196 // Check that the result received in callback is the same as the response.
197 ASSERT_EQ(2, result_proto.devices_size());
198 EXPECT_EQ(kPublicKey1, result_proto.devices(0).public_key());
199 EXPECT_TRUE(result_proto.devices(0).unlock_key());
200 EXPECT_EQ(kBluetoothAddress1, result_proto.devices(0).bluetooth_address());
201 EXPECT_EQ(kPublicKey2, result_proto.devices(1).public_key());
202 EXPECT_TRUE(result_proto.devices(1).unlockable());
203 }
204
205 TEST_F(ProximityAuthCryptAuthClientTest, GetMyDevicesFailure) {
206 ExpectRequest(
207 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
208 "getmydevices?alt=proto");
209
210 std::string error_message;
211 client_->GetMyDevices(cryptauth::GetMyDevicesRequest(),
212 base::Bind(&NotCalled<cryptauth::GetMyDevicesResponse>),
213 base::Bind(&SaveResult<std::string>, &error_message));
214
215 std::string kStatus500Error("HTTP status: 500");
216 FailApiCallFlow(kStatus500Error);
217 EXPECT_EQ(kStatus500Error, error_message);
218 }
219
220 TEST_F(ProximityAuthCryptAuthClientTest, FindEligibleUnlockDevicesSuccess) {
221 ExpectRequest(
222 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
223 "findeligibleunlockdevices?alt=proto");
224
225 cryptauth::FindEligibleUnlockDevicesResponse result_proto;
226 cryptauth::FindEligibleUnlockDevicesRequest request_proto;
227 request_proto.set_callback_bluetooth_address(kBluetoothAddress2);
228 client_->FindEligibleUnlockDevices(
229 request_proto,
230 base::Bind(&SaveResult<cryptauth::FindEligibleUnlockDevicesResponse>,
231 &result_proto),
232 base::Bind(&NotCalled<std::string>));
233
234 cryptauth::FindEligibleUnlockDevicesRequest expected_request;
235 EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
236 EXPECT_EQ(kBluetoothAddress2, expected_request.callback_bluetooth_address());
237
238 // Return a response proto with one eligible and one ineligible device.
239 cryptauth::FindEligibleUnlockDevicesResponse response_proto;
240 response_proto.add_eligible_devices();
241 response_proto.mutable_eligible_devices(0)->set_public_key(kPublicKey1);
242
243 const std::string kIneligibilityReason = "You require more vespine gas.";
244 response_proto.add_ineligible_devices();
245 response_proto.mutable_ineligible_devices(0)
246 ->mutable_device()
247 ->set_public_key(kPublicKey2);
248 response_proto.mutable_ineligible_devices(0)
249 ->add_reasons(kIneligibilityReason);
250 FinishApiCallFlow(&response_proto);
251
252 // Check that the result received in callback is the same as the response.
253 ASSERT_EQ(1, result_proto.eligible_devices_size());
254 EXPECT_EQ(kPublicKey1, result_proto.eligible_devices(0).public_key());
255 ASSERT_EQ(1, result_proto.ineligible_devices_size());
256 EXPECT_EQ(kPublicKey2,
257 result_proto.ineligible_devices(0).device().public_key());
258 ASSERT_EQ(1, result_proto.ineligible_devices(0).reasons_size());
259 EXPECT_EQ(kIneligibilityReason,
260 result_proto.ineligible_devices(0).reasons(0));
261 }
262
263 TEST_F(ProximityAuthCryptAuthClientTest, FindEligibleUnlockDevicesFailure) {
264 ExpectRequest(
265 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
266 "findeligibleunlockdevices?alt=proto");
267
268 std::string error_message;
269 cryptauth::FindEligibleUnlockDevicesRequest request_proto;
270 request_proto.set_callback_bluetooth_address(kBluetoothAddress1);
271 client_->FindEligibleUnlockDevices(
272 request_proto,
273 base::Bind(&NotCalled<cryptauth::FindEligibleUnlockDevicesResponse>),
274 base::Bind(&SaveResult<std::string>, &error_message));
275
276 std::string kStatus403Error("HTTP status: 403");
277 FailApiCallFlow(kStatus403Error);
278 EXPECT_EQ(kStatus403Error, error_message);
279 }
280
281 TEST_F(ProximityAuthCryptAuthClientTest, SendDeviceSyncTickleSuccess) {
282 ExpectRequest(
283 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
284 "senddevicesynctickle?alt=proto");
285
286 cryptauth::SendDeviceSyncTickleResponse result_proto;
287 client_->SendDeviceSyncTickle(
288 cryptauth::SendDeviceSyncTickleRequest(),
289 base::Bind(&SaveResult<cryptauth::SendDeviceSyncTickleResponse>,
290 &result_proto),
291 base::Bind(&NotCalled<std::string>));
292
293 cryptauth::SendDeviceSyncTickleRequest expected_request;
294 EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
295
296 cryptauth::SendDeviceSyncTickleResponse response_proto;
297 FinishApiCallFlow(&response_proto);
298 }
299
300 TEST_F(ProximityAuthCryptAuthClientTest, ToggleEasyUnlockSuccess) {
301 ExpectRequest(
302 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
303 "toggleeasyunlock?alt=proto");
304
305 cryptauth::ToggleEasyUnlockResponse result_proto;
306 cryptauth::ToggleEasyUnlockRequest request_proto;
307 request_proto.set_enable(true);
308 request_proto.set_apply_to_all(false);
309 request_proto.set_public_key(kPublicKey1);
310 client_->ToggleEasyUnlock(
311 request_proto,
312 base::Bind(&SaveResult<cryptauth::ToggleEasyUnlockResponse>,
313 &result_proto),
314 base::Bind(&NotCalled<std::string>));
315
316 cryptauth::ToggleEasyUnlockRequest expected_request;
317 EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
318 EXPECT_TRUE(expected_request.enable());
319 EXPECT_EQ(kPublicKey1, expected_request.public_key());
320 EXPECT_FALSE(expected_request.apply_to_all());
321
322 cryptauth::ToggleEasyUnlockResponse response_proto;
323 FinishApiCallFlow(&response_proto);
324 }
325
326 TEST_F(ProximityAuthCryptAuthClientTest, SetupEnrollmentSuccess) {
327 ExpectRequest(
328 "https://www.testgoogleapis.com/cryptauth/v1/enrollment/"
329 "setupenrollment?alt=proto");
330
331 std::string kApplicationId = "mkaes";
332 std::vector<std::string> supported_protocols;
333 supported_protocols.push_back("gcmV1");
334 supported_protocols.push_back("testProtocol");
335
336 cryptauth::SetupEnrollmentResponse result_proto;
337 cryptauth::SetupEnrollmentRequest request_proto;
338 request_proto.set_application_id(kApplicationId);
339 request_proto.add_types("gcmV1");
340 request_proto.add_types("testProtocol");
341 client_->SetupEnrollment(
342 request_proto, base::Bind(&SaveResult<cryptauth::SetupEnrollmentResponse>,
343 &result_proto),
344 base::Bind(&NotCalled<std::string>));
345
346 cryptauth::SetupEnrollmentRequest expected_request;
347 EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
348 EXPECT_EQ(kApplicationId, expected_request.application_id());
349 ASSERT_EQ(2, expected_request.types_size());
350 EXPECT_EQ("gcmV1", expected_request.types(0));
351 EXPECT_EQ("testProtocol", expected_request.types(1));
352
353 // Return a fake enrollment session.
354 {
355 cryptauth::SetupEnrollmentResponse response_proto;
356 response_proto.set_status("OK");
357 response_proto.add_infos();
358 response_proto.mutable_infos(0)->set_type("gcmV1");
359 response_proto.mutable_infos(0)->set_enrollment_session_id("session_id");
360 response_proto.mutable_infos(0)->set_server_ephemeral_key("ephemeral_key");
361 FinishApiCallFlow(&response_proto);
362 }
363
364 // Check that the returned proto is the same as the one just created.
365 EXPECT_EQ("OK", result_proto.status());
366 ASSERT_EQ(1, result_proto.infos_size());
367 EXPECT_EQ("gcmV1", result_proto.infos(0).type());
368 EXPECT_EQ("session_id", result_proto.infos(0).enrollment_session_id());
369 EXPECT_EQ("ephemeral_key", result_proto.infos(0).server_ephemeral_key());
370 }
371
372 TEST_F(ProximityAuthCryptAuthClientTest, FinishEnrollmentSuccess) {
373 ExpectRequest(
374 "https://www.testgoogleapis.com/cryptauth/v1/enrollment/"
375 "finishenrollment?alt=proto");
376
377 const char kEnrollmentSessionId[] = "enrollment_session_id";
378 const char kEnrollmentMessage[] = "enrollment_message";
379 const char kDeviceEphemeralKey[] = "device_ephermal_key";
380 cryptauth::FinishEnrollmentResponse result_proto;
381 cryptauth::FinishEnrollmentRequest request_proto;
382 request_proto.set_enrollment_session_id(kEnrollmentSessionId);
383 request_proto.set_enrollment_message(kEnrollmentMessage);
384 request_proto.set_device_ephemeral_key(kDeviceEphemeralKey);
385 client_->FinishEnrollment(
386 request_proto,
387 base::Bind(&SaveResult<cryptauth::FinishEnrollmentResponse>,
388 &result_proto),
389 base::Bind(&NotCalled<const std::string&>));
390
391 cryptauth::FinishEnrollmentRequest expected_request;
392 EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
393 EXPECT_EQ(kEnrollmentSessionId, expected_request.enrollment_session_id());
394 EXPECT_EQ(kEnrollmentMessage, expected_request.enrollment_message());
395 EXPECT_EQ(kDeviceEphemeralKey, expected_request.device_ephemeral_key());
396
397 {
398 cryptauth::FinishEnrollmentResponse response_proto;
399 response_proto.set_status("OK");
400 FinishApiCallFlow(&response_proto);
401 }
402 EXPECT_EQ("OK", result_proto.status());
403 }
404
405 TEST_F(ProximityAuthCryptAuthClientTest, FetchAccessTokenFailure) {
406 access_token_fetcher_->set_access_token("");
407
408 std::string error_message;
409 client_->GetMyDevices(cryptauth::GetMyDevicesRequest(),
410 base::Bind(&NotCalled<cryptauth::GetMyDevicesResponse>),
411 base::Bind(&SaveResult<std::string>, &error_message));
412
413 EXPECT_EQ("Failed to get a valid access token.", error_message);
414 }
415
416 TEST_F(ProximityAuthCryptAuthClientTest, ParseResponseProtoFailure) {
417 ExpectRequest(
418 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
419 "getmydevices?alt=proto");
420
421 std::string error_message;
422 client_->GetMyDevices(cryptauth::GetMyDevicesRequest(),
423 base::Bind(&NotCalled<cryptauth::GetMyDevicesResponse>),
424 base::Bind(&SaveResult<std::string>, &error_message));
425
426 flow_result_callback_.Run("Not a valid serialized response message.");
427 EXPECT_EQ("Failed to parse response proto.", error_message);
428 }
429
430 TEST_F(ProximityAuthCryptAuthClientTest,
431 MakeSecondRequestBeforeFirstRequestSucceeds) {
432 ExpectRequest(
433 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
434 "getmydevices?alt=proto");
435
436 // Make first request.
437 cryptauth::GetMyDevicesResponse result_proto;
438 client_->GetMyDevices(
439 cryptauth::GetMyDevicesRequest(),
440 base::Bind(&SaveResult<cryptauth::GetMyDevicesResponse>, &result_proto),
441 base::Bind(&NotCalled<std::string>));
442
443 // With request pending, make second request.
444 {
445 std::string error_message;
446 client_->FindEligibleUnlockDevices(
447 cryptauth::FindEligibleUnlockDevicesRequest(),
448 base::Bind(&NotCalled<cryptauth::FindEligibleUnlockDevicesResponse>),
449 base::Bind(&SaveResult<std::string>, &error_message));
450 EXPECT_EQ("Client has been used for another request. Do not reuse.",
451 error_message);
452 }
453
454 // Complete first request.
455 {
456 cryptauth::GetMyDevicesResponse response_proto;
457 response_proto.add_devices();
458 response_proto.mutable_devices(0)->set_public_key(kPublicKey1);
459 FinishApiCallFlow(&response_proto);
460 }
461
462 ASSERT_EQ(1, result_proto.devices_size());
463 EXPECT_EQ(kPublicKey1, result_proto.devices(0).public_key());
464 }
465
466 TEST_F(ProximityAuthCryptAuthClientTest,
467 MakeSecondRequestBeforeFirstRequestFails) {
468 ExpectRequest(
469 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
470 "getmydevices?alt=proto");
471
472 // Make first request.
473 std::string error_message;
474 client_->GetMyDevices(cryptauth::GetMyDevicesRequest(),
475 base::Bind(&NotCalled<cryptauth::GetMyDevicesResponse>),
476 base::Bind(&SaveResult<std::string>, &error_message));
477
478 // With request pending, make second request.
479 {
480 std::string error_message;
481 client_->FindEligibleUnlockDevices(
482 cryptauth::FindEligibleUnlockDevicesRequest(),
483 base::Bind(&NotCalled<cryptauth::FindEligibleUnlockDevicesResponse>),
484 base::Bind(&SaveResult<std::string>, &error_message));
485 EXPECT_EQ("Client has been used for another request. Do not reuse.",
486 error_message);
487 }
488
489 // Fail first request.
490 std::string kStatus429Error = "HTTP status: 429";
491 FailApiCallFlow(kStatus429Error);
492 EXPECT_EQ(kStatus429Error, error_message);
493 }
494
495 TEST_F(ProximityAuthCryptAuthClientTest,
496 MakeSecondRequestAfterFirstRequestSucceeds) {
497 // Make first request successfully.
498 {
499 ExpectRequest(
500 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
501 "getmydevices?alt=proto");
502 cryptauth::GetMyDevicesResponse result_proto;
503 client_->GetMyDevices(
504 cryptauth::GetMyDevicesRequest(),
505 base::Bind(&SaveResult<cryptauth::GetMyDevicesResponse>, &result_proto),
506 base::Bind(&NotCalled<std::string>));
507
508 cryptauth::GetMyDevicesResponse response_proto;
509 response_proto.add_devices();
510 response_proto.mutable_devices(0)->set_public_key(kPublicKey1);
511 FinishApiCallFlow(&response_proto);
512 ASSERT_EQ(1, result_proto.devices_size());
513 EXPECT_EQ(kPublicKey1, result_proto.devices(0).public_key());
514 }
515
516 // Second request fails.
517 {
518 std::string error_message;
519 client_->FindEligibleUnlockDevices(
520 cryptauth::FindEligibleUnlockDevicesRequest(),
521 base::Bind(&NotCalled<cryptauth::FindEligibleUnlockDevicesResponse>),
522 base::Bind(&SaveResult<std::string>, &error_message));
523 EXPECT_EQ("Client has been used for another request. Do not reuse.",
524 error_message);
525 }
526 }
527
528 } // namespace proximity_auth
OLDNEW
« no previous file with comments | « components/proximity_auth/cryptauth/cryptauth_client.cc ('k') | components/proximity_auth/cryptauth/proto/cryptauth_api.proto » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698