device_activity_client.cc 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/components/device_activity/device_activity_client.h"
  5. #include "ash/components/device_activity/device_active_use_case.h"
  6. #include "ash/components/device_activity/fresnel_service.pb.h"
  7. #include "ash/constants/ash_features.h"
  8. #include "base/check.h"
  9. // TODO(https://crbug.com/1269900): Migrate to use SFUL library.
  10. #include "base/metrics/histogram_functions.h"
  11. #include "base/strings/strcat.h"
  12. #include "base/time/time.h"
  13. #include "chromeos/ash/components/dbus/system_clock/system_clock_sync_observation.h"
  14. #include "services/network/public/cpp/shared_url_loader_factory.h"
  15. #include "services/network/public/cpp/simple_url_loader.h"
  16. #include "services/network/public/mojom/url_response_head.mojom.h"
  17. #include "third_party/private_membership/src/private_membership_rlwe_client.h"
  18. namespace ash {
  19. namespace device_activity {
  20. namespace psm_rlwe = private_membership::rlwe;
  21. namespace {
  22. // Amount of time to wait before retriggering repeating timer.
  23. constexpr base::TimeDelta kTimeToRepeat = base::Hours(1);
  24. // General upper bound of expected Fresnel response size in bytes.
  25. constexpr size_t kMaxFresnelResponseSizeBytes = 5 << 20; // 5MB;
  26. // Maximum time to wait for time sync before not reporting device as active
  27. // in current attempt.
  28. // This corresponds to at least seven TCP retransmissions attempts to
  29. // the remote server used to update the system clock.
  30. constexpr base::TimeDelta kSystemClockSyncWaitTimeout = base::Seconds(45);
  31. // Timeout for each Fresnel request.
  32. constexpr base::TimeDelta kHealthCheckRequestTimeout = base::Seconds(10);
  33. constexpr base::TimeDelta kImportRequestTimeout = base::Seconds(15);
  34. constexpr base::TimeDelta kOprfRequestTimeout = base::Seconds(15);
  35. constexpr base::TimeDelta kQueryRequestTimeout = base::Seconds(65);
  36. // TODO(https://crbug.com/1272922): Move shared configuration constants to
  37. // separate file.
  38. const char kFresnelHealthCheckEndpoint[] = "/v1/fresnel/healthCheck";
  39. const char kFresnelImportRequestEndpoint[] = "/v1/fresnel/psmRlweImport";
  40. const char kFresnelOprfRequestEndpoint[] = "/v1/fresnel/psmRlweOprf";
  41. const char kFresnelQueryRequestEndpoint[] = "/v1/fresnel/psmRlweQuery";
  42. // UMA histograms defined in:
  43. // //tools/metrics/histograms/metadata/ash/histograms.xml.
  44. //
  45. // Count number of times a state has been entered.
  46. const char kHistogramStateCount[] = "Ash.DeviceActiveClient.StateCount";
  47. // Duration histogram uses State variant in order to create
  48. // unique histograms measuring durations by State.
  49. const char kHistogramDurationPrefix[] = "Ash.DeviceActiveClient.Duration";
  50. // Response histogram uses State variant in order to create
  51. // unique histograms measuring responses by State.
  52. const char kHistogramResponsePrefix[] = "Ash.DeviceActiveClient.Response";
  53. // Count the number of boolean membership request results.
  54. const char kDeviceActiveClientQueryMembershipResult[] =
  55. "Ash.DeviceActiveClient.QueryMembershipResult";
  56. // Record the minute the device activity client transitions out of idle.
  57. const char kDeviceActiveClientTransitionOutOfIdleMinute[] =
  58. "Ash.DeviceActiveClient.RecordedTransitionOutOfIdleMinute";
  59. // Record the minute the device activity client transitions to check in.
  60. const char kDeviceActiveClientTransitionToCheckInMinute[] =
  61. "Ash.DeviceActiveClient.RecordedTransitionToCheckInMinute";
  62. // Traffic annotation for check device activity status
  63. const net::NetworkTrafficAnnotationTag check_membership_traffic_annotation =
  64. net::DefineNetworkTrafficAnnotation(
  65. "device_activity_client_check_membership",R"(
  66. semantics {
  67. sender: "Device Activity"
  68. description:
  69. "Check the status of the Chrome OS devices in a private "
  70. "set, through Private Set Membership (PSM) services."
  71. trigger: "Chrome OS client makes this network request and records "
  72. "the device activity when the default network changes"
  73. data: "Google API Key."
  74. destination: GOOGLE_OWNED_SERVICE
  75. }
  76. policy {
  77. cookies_allowed: NO
  78. setting: "This feature cannot be disabled in settings."
  79. policy_exception_justification: "Not implemented."
  80. })");
  81. // Generates the full histogram name for histogram variants based on state.
  82. std::string HistogramVariantName(const std::string& histogram_prefix,
  83. DeviceActivityClient::State state) {
  84. switch (state) {
  85. case DeviceActivityClient::State::kIdle:
  86. return base::StrCat({histogram_prefix, ".Idle"});
  87. case DeviceActivityClient::State::kCheckingMembershipOprf:
  88. return base::StrCat({histogram_prefix, ".CheckingMembershipOprf"});
  89. case DeviceActivityClient::State::kCheckingMembershipQuery:
  90. return base::StrCat({histogram_prefix, ".CheckingMembershipQuery"});
  91. case DeviceActivityClient::State::kCheckingIn:
  92. return base::StrCat({histogram_prefix, ".CheckingIn"});
  93. case DeviceActivityClient::State::kHealthCheck:
  94. return base::StrCat({histogram_prefix, ".HealthCheck"});
  95. default:
  96. NOTREACHED() << "Invalid State.";
  97. return base::StrCat({histogram_prefix, ".Unknown"});
  98. }
  99. }
  100. void RecordStateCountMetric(DeviceActivityClient::State state) {
  101. base::UmaHistogramEnumeration(kHistogramStateCount, state);
  102. }
  103. void RecordQueryMembershipResultBoolean(bool is_member) {
  104. base::UmaHistogramBoolean(kDeviceActiveClientQueryMembershipResult,
  105. is_member);
  106. }
  107. // Return the minute of the current UTC time.
  108. int GetCurrentMinute() {
  109. base::Time cur_time = base::Time::Now();
  110. // Extract minute from exploded |cur_time| in UTC.
  111. base::Time::Exploded exploded_utc;
  112. cur_time.UTCExplode(&exploded_utc);
  113. return exploded_utc.minute;
  114. }
  115. void RecordTransitionOutOfIdleMinute() {
  116. base::UmaHistogramCustomCounts(kDeviceActiveClientTransitionOutOfIdleMinute,
  117. GetCurrentMinute(), 0, 59,
  118. 60 /* number of histogram buckets */);
  119. }
  120. void RecordTransitionToCheckInMinute() {
  121. base::UmaHistogramCustomCounts(kDeviceActiveClientTransitionToCheckInMinute,
  122. GetCurrentMinute(), 0, 59,
  123. 60 /* number of histogram buckets */);
  124. }
  125. // Histogram sliced by duration and state.
  126. void RecordDurationStateMetric(DeviceActivityClient::State state,
  127. const base::TimeDelta duration) {
  128. std::string duration_state_histogram_name =
  129. HistogramVariantName(kHistogramDurationPrefix, state);
  130. base::UmaHistogramCustomTimes(duration_state_histogram_name, duration,
  131. base::Milliseconds(1), base::Seconds(100),
  132. 100 /* number of histogram buckets */);
  133. }
  134. // Histogram slices by PSM response and state.
  135. void RecordResponseStateMetric(DeviceActivityClient::State state,
  136. int net_code) {
  137. // Mapping status code to PsmResponse is used to record UMA histograms
  138. // for responses by state.
  139. DeviceActivityClient::PsmResponse response;
  140. switch (net_code) {
  141. case net::OK:
  142. response = DeviceActivityClient::PsmResponse::kSuccess;
  143. break;
  144. case net::ERR_TIMED_OUT:
  145. response = DeviceActivityClient::PsmResponse::kTimeout;
  146. break;
  147. default:
  148. response = DeviceActivityClient::PsmResponse::kError;
  149. break;
  150. }
  151. base::UmaHistogramEnumeration(
  152. HistogramVariantName(kHistogramResponsePrefix, state), response);
  153. }
  154. std::unique_ptr<network::ResourceRequest> GenerateResourceRequest(
  155. const std::string& request_method,
  156. const GURL& url,
  157. const std::string& api_key) {
  158. auto resource_request = std::make_unique<network::ResourceRequest>();
  159. resource_request->url = url;
  160. resource_request->method = request_method;
  161. resource_request->headers.SetHeader("x-goog-api-key", api_key);
  162. resource_request->headers.SetHeader(net::HttpRequestHeaders::kContentType,
  163. "application/x-protobuf");
  164. return resource_request;
  165. }
  166. } // namespace
  167. // static
  168. void DeviceActivityClient::RecordDeviceActivityMethodCalled(
  169. DeviceActivityMethod method_name) {
  170. // Record the device activity method calls.
  171. const char kDeviceActivityMethodCalled[] = "Ash.DeviceActivity.MethodCalled";
  172. base::UmaHistogramEnumeration(kDeviceActivityMethodCalled, method_name);
  173. }
  174. DeviceActivityClient::DeviceActivityClient(
  175. NetworkStateHandler* handler,
  176. scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
  177. std::unique_ptr<PsmDelegate> psm_delegate,
  178. std::unique_ptr<base::RepeatingTimer> report_timer,
  179. const std::string& fresnel_base_url,
  180. const std::string& api_key,
  181. std::vector<std::unique_ptr<DeviceActiveUseCase>> use_cases)
  182. : network_state_handler_(handler),
  183. url_loader_factory_(url_loader_factory),
  184. psm_delegate_(std::move(psm_delegate)),
  185. report_timer_(std::move(report_timer)),
  186. fresnel_base_url_(fresnel_base_url),
  187. api_key_(api_key),
  188. use_cases_(std::move(use_cases)) {
  189. RecordDeviceActivityMethodCalled(DeviceActivityClient::DeviceActivityMethod::
  190. kDeviceActivityClientConstructor);
  191. DCHECK(network_state_handler_);
  192. DCHECK(url_loader_factory_);
  193. DCHECK(psm_delegate_);
  194. DCHECK(report_timer_);
  195. DCHECK(!use_cases_.empty());
  196. report_timer_->Start(FROM_HERE, kTimeToRepeat, this,
  197. &DeviceActivityClient::ReportingTriggeredByTimer);
  198. network_state_handler_->AddObserver(this, FROM_HERE);
  199. DefaultNetworkChanged(network_state_handler_->DefaultNetwork());
  200. }
  201. DeviceActivityClient::~DeviceActivityClient() {
  202. RecordDeviceActivityMethodCalled(DeviceActivityClient::DeviceActivityMethod::
  203. kDeviceActivityClientDestructor);
  204. network_state_handler_->RemoveObserver(this, FROM_HERE);
  205. }
  206. base::RepeatingTimer* DeviceActivityClient::GetReportTimer() {
  207. return report_timer_.get();
  208. }
  209. // Method gets called when the state of the default (primary)
  210. // network OR properties of the default network changes.
  211. void DeviceActivityClient::DefaultNetworkChanged(const NetworkState* network) {
  212. bool was_connected = network_connected_;
  213. network_connected_ = network && network->IsOnline();
  214. if (network_connected_ == was_connected)
  215. return;
  216. if (network_connected_)
  217. OnNetworkOnline();
  218. else
  219. OnNetworkOffline();
  220. }
  221. DeviceActivityClient::State DeviceActivityClient::GetState() const {
  222. return state_;
  223. }
  224. std::vector<DeviceActiveUseCase*> DeviceActivityClient::GetUseCases() const {
  225. std::vector<DeviceActiveUseCase*> use_cases_ptr;
  226. for (auto& use_case : use_cases_) {
  227. use_cases_ptr.push_back(use_case.get());
  228. }
  229. return use_cases_ptr;
  230. }
  231. void DeviceActivityClient::ReportingTriggeredByTimer() {
  232. RecordDeviceActivityMethodCalled(
  233. DeviceActivityClient::DeviceActivityMethod::
  234. kDeviceActivityClientReportingTriggeredByTimer);
  235. // Terminate if the state of the client while reporting device actives is not
  236. // what is expected. This may occur if the client is in the middle of
  237. // reporting actives or is disconnected from the network.
  238. if (!network_connected_ || state_ != State::kIdle ||
  239. !pending_use_cases_.empty()) {
  240. TransitionToIdle(nullptr);
  241. return;
  242. }
  243. OnNetworkOnline();
  244. }
  245. void DeviceActivityClient::OnNetworkOnline() {
  246. RecordDeviceActivityMethodCalled(DeviceActivityClient::DeviceActivityMethod::
  247. kDeviceActivityClientOnNetworkOnline);
  248. // Asynchronously wait for the system clock to synchronize on network
  249. // connection.
  250. system_clock_sync_observation_ =
  251. SystemClockSyncObservation::WaitForSystemClockSync(
  252. SystemClockClient::Get(), kSystemClockSyncWaitTimeout,
  253. base::BindOnce(&DeviceActivityClient::OnSystemClockSyncResult,
  254. weak_factory_.GetWeakPtr()));
  255. }
  256. void DeviceActivityClient::OnSystemClockSyncResult(
  257. bool system_clock_synchronized) {
  258. RecordDeviceActivityMethodCalled(
  259. DeviceActivityClient::DeviceActivityMethod::
  260. kDeviceActivityClientOnSystemClockSyncResult);
  261. if (system_clock_synchronized)
  262. ReportUseCases();
  263. else
  264. TransitionToIdle(nullptr);
  265. }
  266. void DeviceActivityClient::OnNetworkOffline() {
  267. RecordDeviceActivityMethodCalled(DeviceActivityClient::DeviceActivityMethod::
  268. kDeviceActivityClientOnNetworkOffline);
  269. CancelUseCases();
  270. }
  271. GURL DeviceActivityClient::GetFresnelURL() const {
  272. GURL base_url(fresnel_base_url_);
  273. GURL::Replacements replacements;
  274. switch (state_) {
  275. case State::kCheckingMembershipOprf:
  276. replacements.SetPathStr(kFresnelOprfRequestEndpoint);
  277. break;
  278. case State::kCheckingMembershipQuery:
  279. replacements.SetPathStr(kFresnelQueryRequestEndpoint);
  280. break;
  281. case State::kCheckingIn:
  282. replacements.SetPathStr(kFresnelImportRequestEndpoint);
  283. break;
  284. case State::kHealthCheck:
  285. replacements.SetPathStr(kFresnelHealthCheckEndpoint);
  286. break;
  287. case State::kIdle: // Fallthrough to |kUnknown| case.
  288. [[fallthrough]];
  289. case State::kUnknown:
  290. NOTREACHED();
  291. break;
  292. }
  293. return base_url.ReplaceComponents(replacements);
  294. }
  295. // TODO(https://crbug.com/1262189): Add callback to report actives only after
  296. // synchronizing the system clock.
  297. void DeviceActivityClient::ReportUseCases() {
  298. RecordDeviceActivityMethodCalled(DeviceActivityClient::DeviceActivityMethod::
  299. kDeviceActivityClientReportUseCases);
  300. DCHECK(!use_cases_.empty());
  301. if (!network_connected_ || state_ != State::kIdle) {
  302. TransitionToIdle(nullptr);
  303. return;
  304. }
  305. // The network is connected and the client |state_| is kIdle.
  306. last_transition_out_of_idle_time_ = base::Time::Now();
  307. for (auto& use_case : use_cases_) {
  308. // Ownership of the use cases will be maintained by the |use_cases_| vector.
  309. pending_use_cases_.push(use_case.get());
  310. }
  311. // Pop from |pending_use_cases_| queue in |TransitionToIdle|, after the
  312. // use case has tried to be reported.
  313. TransitionOutOfIdle(pending_use_cases_.front());
  314. }
  315. void DeviceActivityClient::CancelUseCases() {
  316. RecordDeviceActivityMethodCalled(DeviceActivityClient::DeviceActivityMethod::
  317. kDeviceActivityClientCancelUseCases);
  318. // Use RAII to reset |url_loader_| after current function scope.
  319. // Delete |url_loader_| before the callback is invoked cancels the sent out
  320. // request.
  321. // No callback will be invoked in the case a network request is sent,
  322. // and the device internet disconnects.
  323. auto url_loader = std::move(url_loader_);
  324. // Use RAII to clear the queue.
  325. std::queue<DeviceActiveUseCase*> pending_use_cases;
  326. std::swap(pending_use_cases_, pending_use_cases);
  327. for (auto* use_case : GetUseCases()) {
  328. // Setting the window identifier resets the object to a fresh state.
  329. use_case->SetWindowIdentifier(absl::nullopt);
  330. }
  331. TransitionToIdle(nullptr);
  332. }
  333. void DeviceActivityClient::TransitionOutOfIdle(
  334. DeviceActiveUseCase* current_use_case) {
  335. RecordTransitionOutOfIdleMinute();
  336. RecordDeviceActivityMethodCalled(
  337. DeviceActivityClient::DeviceActivityMethod::
  338. kDeviceActivityClientTransitionOutOfIdle);
  339. DCHECK(current_use_case);
  340. // Begin phase one of checking membership if the device has not pinged yet
  341. // within the given use case window.
  342. // TODO(https://crbug.com/1262187): Remove hardcoded use case when adding
  343. // support for additional use cases (i.e MONTHLY, ALL_TIME, etc.).
  344. if (current_use_case->IsDevicePingRequired(
  345. last_transition_out_of_idle_time_)) {
  346. current_use_case->SetWindowIdentifier(
  347. current_use_case->GenerateUTCWindowIdentifier(
  348. last_transition_out_of_idle_time_));
  349. auto current_psm_id = current_use_case->GetPsmIdentifier();
  350. // Check if the PSM id is generated.
  351. if (!current_psm_id.has_value()) {
  352. TransitionToIdle(current_use_case);
  353. return;
  354. }
  355. std::vector<psm_rlwe::RlwePlaintextId> psm_rlwe_ids = {
  356. current_psm_id.value()};
  357. auto status_or_client = psm_delegate_->CreatePsmClient(
  358. current_use_case->GetPsmUseCase(), psm_rlwe_ids);
  359. if (!status_or_client.ok()) {
  360. TransitionToIdle(current_use_case);
  361. return;
  362. }
  363. current_use_case->SetPsmRlweClient(std::move(status_or_client.value()));
  364. switch (current_use_case->GetPsmUseCase()) {
  365. case psm_rlwe::RlweUseCase::CROS_FRESNEL_DAILY:
  366. // Check membership continues when the cached local state pref
  367. // is not set. The local state pref may not be set if the device is
  368. // new, powerwashed, recovered, RMA, or the local state was corrupted.
  369. if (base::FeatureList::IsEnabled(
  370. features::kDeviceActiveClientDailyCheckMembership) &&
  371. !current_use_case->IsLastKnownPingTimestampSet()) {
  372. TransitionToCheckMembershipOprf(current_use_case);
  373. return;
  374. } else {
  375. // |TransitionToCheckIn| if the local state pref is set.
  376. TransitionToCheckIn(current_use_case);
  377. return;
  378. }
  379. case psm_rlwe::RlweUseCase::CROS_FRESNEL_MONTHLY:
  380. // Check membership continues when the cached local state pref is not
  381. // set. The local state pref may not be set if the device is
  382. // new, powerwashed, recovered, RMA, or the local state was corrupted.
  383. if (base::FeatureList::IsEnabled(
  384. features::kDeviceActiveClientMonthlyCheckMembership) &&
  385. !current_use_case->IsLastKnownPingTimestampSet()) {
  386. TransitionToCheckMembershipOprf(current_use_case);
  387. return;
  388. }
  389. // |TransitionToCheckIn| if the local state pref is set.
  390. if (base::FeatureList::IsEnabled(
  391. features::kDeviceActiveClientMonthlyCheckIn)) {
  392. // During rollout, we perform CheckIn without CheckMembership for
  393. // powerwash, recovery, or RMA devices.
  394. TransitionToCheckIn(current_use_case);
  395. return;
  396. }
  397. break;
  398. default:
  399. VLOG(1) << "Use case is not supported yet. "
  400. << psm_rlwe::RlweUseCase_Name(
  401. current_use_case->GetPsmUseCase());
  402. TransitionToIdle(current_use_case);
  403. return;
  404. }
  405. }
  406. TransitionToIdle(current_use_case);
  407. }
  408. void DeviceActivityClient::TransitionToHealthCheck() {
  409. RecordDeviceActivityMethodCalled(
  410. DeviceActivityClient::DeviceActivityMethod::
  411. kDeviceActivityClientTransitionToHealthCheck);
  412. DCHECK_EQ(state_, State::kIdle);
  413. DCHECK(!url_loader_);
  414. const net::NetworkTrafficAnnotationTag traffic_annotation =
  415. net::DefineNetworkTrafficAnnotation("device_activity_client_health_check", R"(
  416. semantics {
  417. sender: "Device Activity Health Check"
  418. description:
  419. "Send Health Check network request of Chrome OS device. "
  420. "Provide a health check service for client. "
  421. "The health check will include cpu utilization, "
  422. "memory usage and disk space. "
  423. "The server will return health status of the service immediately. "
  424. "The health status will include if the device is actively running "
  425. "or the device is not successfully sending heartbeats to servers "
  426. "or the device is not eligible for health monitoring. "
  427. trigger: "This request is deprecated, and never happens."
  428. data: "Google API Key."
  429. destination: GOOGLE_OWNED_SERVICE
  430. }
  431. policy {
  432. cookies_allowed: NO
  433. setting: "This feature cannot be disabled in settings."
  434. policy_exception_justification: "Not implemented."
  435. })");
  436. state_timer_ = base::ElapsedTimer();
  437. // |state_| must be set correctly in order to generate correct URL.
  438. state_ = State::kHealthCheck;
  439. // Report UMA histogram for transitioning state to |kHealthCheck|.
  440. RecordStateCountMetric(state_);
  441. auto resource_request = GenerateResourceRequest(
  442. net::HttpRequestHeaders::kGetMethod, GetFresnelURL(), api_key_);
  443. // TODO(https://crbug.com/1266972): Refactor |url_loader_| network request
  444. // call to a shared helper method.
  445. url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
  446. traffic_annotation);
  447. url_loader_->SetTimeoutDuration(kHealthCheckRequestTimeout);
  448. url_loader_->DownloadToString(
  449. url_loader_factory_.get(),
  450. base::BindOnce(&DeviceActivityClient::OnHealthCheckDone,
  451. weak_factory_.GetWeakPtr()),
  452. kMaxFresnelResponseSizeBytes);
  453. }
  454. void DeviceActivityClient::OnHealthCheckDone(
  455. std::unique_ptr<std::string> response_body) {
  456. RecordDeviceActivityMethodCalled(DeviceActivityClient::DeviceActivityMethod::
  457. kDeviceActivityClientOnHealthCheckDone);
  458. DCHECK_EQ(state_, State::kHealthCheck);
  459. // Use RAII to reset |url_loader_| after current function scope.
  460. // Resetting |url_loader_| also invalidates the |response_info| variable.
  461. auto url_loader = std::move(url_loader_);
  462. int net_code = url_loader->NetError();
  463. RecordResponseStateMetric(state_, net_code);
  464. // Record duration of |kHealthCheck| state.
  465. RecordDurationStateMetric(state_, state_timer_.Elapsed());
  466. // Transition back to kIdle state after performing a health check on servers.
  467. TransitionToIdle(nullptr);
  468. }
  469. void DeviceActivityClient::TransitionToCheckMembershipOprf(
  470. DeviceActiveUseCase* current_use_case) {
  471. RecordDeviceActivityMethodCalled(
  472. DeviceActivityClient::DeviceActivityMethod::
  473. kDeviceActivityClientTransitionToCheckMembershipOprf);
  474. DCHECK_EQ(state_, State::kIdle);
  475. DCHECK(!url_loader_);
  476. state_timer_ = base::ElapsedTimer();
  477. // |state_| must be set correctly in order to generate correct URL.
  478. state_ = State::kCheckingMembershipOprf;
  479. // Report UMA histogram for transitioning state to |kCheckingMembershipOprf|.
  480. RecordStateCountMetric(state_);
  481. // Generate PSM Oprf request body.
  482. const auto status_or_oprf_request =
  483. current_use_case->GetPsmRlweClient()->CreateOprfRequest();
  484. if (!status_or_oprf_request.ok()) {
  485. RecordDurationStateMetric(state_, state_timer_.Elapsed());
  486. TransitionToIdle(current_use_case);
  487. return;
  488. }
  489. psm_rlwe::PrivateMembershipRlweOprfRequest oprf_request =
  490. status_or_oprf_request.value();
  491. // Wrap PSM Oprf request body by FresnelPsmRlweOprfRequest proto.
  492. // This proto is expected by the Fresnel service.
  493. device_activity::FresnelPsmRlweOprfRequest fresnel_oprf_request;
  494. *fresnel_oprf_request.mutable_rlwe_oprf_request() = oprf_request;
  495. std::string request_body;
  496. fresnel_oprf_request.SerializeToString(&request_body);
  497. auto resource_request = GenerateResourceRequest(
  498. net::HttpRequestHeaders::kPostMethod, GetFresnelURL(), api_key_);
  499. // TODO(https://crbug.com/1266972): Refactor |url_loader_| network request
  500. // call to a shared helper method.
  501. url_loader_ = network
  502. ::SimpleURLLoader
  503. ::Create(std::move(resource_request),
  504. check_membership_traffic_annotation);
  505. url_loader_->AttachStringForUpload(request_body, "application/x-protobuf");
  506. url_loader_->SetTimeoutDuration(kOprfRequestTimeout);
  507. url_loader_->DownloadToString(
  508. url_loader_factory_.get(),
  509. base::BindOnce(&DeviceActivityClient::OnCheckMembershipOprfDone,
  510. weak_factory_.GetWeakPtr(), current_use_case),
  511. kMaxFresnelResponseSizeBytes);
  512. }
  513. void DeviceActivityClient::OnCheckMembershipOprfDone(
  514. DeviceActiveUseCase* current_use_case,
  515. std::unique_ptr<std::string> response_body) {
  516. RecordDeviceActivityMethodCalled(
  517. DeviceActivityClient::DeviceActivityMethod::
  518. kDeviceActivityClientOnCheckMembershipOprfDone);
  519. DCHECK_EQ(state_, State::kCheckingMembershipOprf);
  520. // Use RAII to reset |url_loader_| after current function scope.
  521. // Resetting |url_loader_| also invalidates the |response_info| variable.
  522. auto url_loader = std::move(url_loader_);
  523. int net_code = url_loader->NetError();
  524. RecordResponseStateMetric(state_, net_code);
  525. // Convert serialized response body to oprf response protobuf.
  526. FresnelPsmRlweOprfResponse psm_oprf_response;
  527. if (!response_body || !psm_oprf_response.ParseFromString(*response_body)) {
  528. RecordDurationStateMetric(state_, state_timer_.Elapsed());
  529. TransitionToIdle(current_use_case);
  530. return;
  531. }
  532. // Parse |fresnel_oprf_response| for oprf_response.
  533. if (!psm_oprf_response.has_rlwe_oprf_response()) {
  534. RecordDurationStateMetric(state_, state_timer_.Elapsed());
  535. TransitionToIdle(current_use_case);
  536. return;
  537. }
  538. psm_rlwe::PrivateMembershipRlweOprfResponse oprf_response =
  539. psm_oprf_response.rlwe_oprf_response();
  540. RecordDurationStateMetric(state_, state_timer_.Elapsed());
  541. TransitionToCheckMembershipQuery(oprf_response, current_use_case);
  542. }
  543. void DeviceActivityClient::TransitionToCheckMembershipQuery(
  544. const psm_rlwe::PrivateMembershipRlweOprfResponse& oprf_response,
  545. DeviceActiveUseCase* current_use_case) {
  546. RecordDeviceActivityMethodCalled(
  547. DeviceActivityClient::DeviceActivityMethod::
  548. kDeviceActivityClientTransitionToCheckMembershipQuery);
  549. DCHECK_EQ(state_, State::kCheckingMembershipOprf);
  550. DCHECK(!url_loader_);
  551. state_timer_ = base::ElapsedTimer();
  552. // |state_| must be set correctly in order to generate correct URL.
  553. state_ = State::kCheckingMembershipQuery;
  554. // Report UMA histogram for transitioning state to |kCheckingMembershipQuery|.
  555. RecordStateCountMetric(state_);
  556. // Generate PSM Query request body.
  557. const auto status_or_query_request =
  558. current_use_case->GetPsmRlweClient()->CreateQueryRequest(oprf_response);
  559. if (!status_or_query_request.ok()) {
  560. RecordDurationStateMetric(state_, state_timer_.Elapsed());
  561. TransitionToIdle(current_use_case);
  562. return;
  563. }
  564. psm_rlwe::PrivateMembershipRlweQueryRequest query_request =
  565. status_or_query_request.value();
  566. // Wrap PSM Query request body by FresnelPsmRlweQueryRequest proto.
  567. // This proto is expected by the Fresnel service.
  568. device_activity::FresnelPsmRlweQueryRequest fresnel_query_request;
  569. *fresnel_query_request.mutable_rlwe_query_request() = query_request;
  570. std::string request_body;
  571. fresnel_query_request.SerializeToString(&request_body);
  572. auto resource_request = GenerateResourceRequest(
  573. net::HttpRequestHeaders::kPostMethod, GetFresnelURL(), api_key_);
  574. // TODO(https://crbug.com/1266972): Refactor |url_loader_| network request
  575. // call to a shared helper method.
  576. url_loader_ = network
  577. ::SimpleURLLoader
  578. ::Create(std::move(resource_request),
  579. check_membership_traffic_annotation);
  580. url_loader_->AttachStringForUpload(request_body, "application/x-protobuf");
  581. url_loader_->SetTimeoutDuration(kQueryRequestTimeout);
  582. url_loader_->DownloadToString(
  583. url_loader_factory_.get(),
  584. base::BindOnce(&DeviceActivityClient::OnCheckMembershipQueryDone,
  585. weak_factory_.GetWeakPtr(), current_use_case),
  586. kMaxFresnelResponseSizeBytes);
  587. }
  588. void DeviceActivityClient::OnCheckMembershipQueryDone(
  589. DeviceActiveUseCase* current_use_case,
  590. std::unique_ptr<std::string> response_body) {
  591. RecordDeviceActivityMethodCalled(
  592. DeviceActivityClient::DeviceActivityMethod::
  593. kDeviceActivityClientOnCheckMembershipQueryDone);
  594. DCHECK_EQ(state_, State::kCheckingMembershipQuery);
  595. // Use RAII to reset |url_loader_| after current function scope.
  596. // Resetting |url_loader_| also invalidates the |response_info| variable.
  597. auto url_loader = std::move(url_loader_);
  598. int net_code = url_loader->NetError();
  599. RecordResponseStateMetric(state_, net_code);
  600. // Convert serialized response body to fresnel query response protobuf.
  601. FresnelPsmRlweQueryResponse psm_query_response;
  602. if (!response_body || !psm_query_response.ParseFromString(*response_body)) {
  603. RecordDurationStateMetric(state_, state_timer_.Elapsed());
  604. TransitionToIdle(current_use_case);
  605. return;
  606. }
  607. // Parse |fresnel_query_response| for psm query_response.
  608. if (!psm_query_response.has_rlwe_query_response()) {
  609. RecordDurationStateMetric(state_, state_timer_.Elapsed());
  610. TransitionToIdle(current_use_case);
  611. return;
  612. }
  613. psm_rlwe::PrivateMembershipRlweQueryResponse query_response =
  614. psm_query_response.rlwe_query_response();
  615. auto status_or_response =
  616. current_use_case->GetPsmRlweClient()->ProcessQueryResponse(
  617. query_response);
  618. if (!status_or_response.ok()) {
  619. RecordDurationStateMetric(state_, state_timer_.Elapsed());
  620. TransitionToIdle(current_use_case);
  621. return;
  622. }
  623. // Ensure the existence of one membership response. Then, verify that it is
  624. // regarding the current PSM ID.
  625. psm_rlwe::RlweMembershipResponses rlwe_membership_responses =
  626. status_or_response.value();
  627. if (rlwe_membership_responses.membership_responses_size() != 1) {
  628. RecordDurationStateMetric(state_, state_timer_.Elapsed());
  629. TransitionToIdle(current_use_case);
  630. return;
  631. }
  632. private_membership::MembershipResponse membership_response =
  633. rlwe_membership_responses.membership_responses(0).membership_response();
  634. bool is_psm_id_member = membership_response.is_member();
  635. // Record the query membership result to UMA histogram.
  636. RecordQueryMembershipResultBoolean(is_psm_id_member);
  637. if (!is_psm_id_member) {
  638. RecordDurationStateMetric(state_, state_timer_.Elapsed());
  639. TransitionToCheckIn(current_use_case);
  640. return;
  641. } else {
  642. // Update local state to signal ping has already been sent for use case
  643. // window.
  644. current_use_case->SetLastKnownPingTimestamp(
  645. last_transition_out_of_idle_time_);
  646. RecordDurationStateMetric(state_, state_timer_.Elapsed());
  647. TransitionToIdle(current_use_case);
  648. return;
  649. }
  650. }
  651. void DeviceActivityClient::TransitionToCheckIn(
  652. DeviceActiveUseCase* current_use_case) {
  653. RecordTransitionToCheckInMinute();
  654. RecordDeviceActivityMethodCalled(
  655. DeviceActivityClient::DeviceActivityMethod::
  656. kDeviceActivityClientTransitionToCheckIn);
  657. DCHECK(!url_loader_);
  658. const net::NetworkTrafficAnnotationTag traffic_annotation =
  659. net::DefineNetworkTrafficAnnotation("device_activity_client_check_in", R"(
  660. semantics {
  661. sender: "Device Activity"
  662. description:
  663. "After checking that the Chrome device doesn't have the "
  664. "membership of PSM, Chrome devices make an 'import network' "
  665. "request which lets Fresnel Service import data into "
  666. "PSM storage and Google web server Logs. Fresnel Service "
  667. "is operating system images to be retrieved and provisioned "
  668. "from anywhere internet access is available. So when a new "
  669. "Chrome OS device joins a LAN, it gets added to the Private "
  670. "Set of that LAN. After that, it can view the health status "
  671. "(CPU/RAM/disk usage) of other Chrome OS devices "
  672. "on the same LAN."
  673. trigger: "Chrome OS client makes this network request and records "
  674. "the device activity when the default network changes"
  675. data: "Google API Key."
  676. destination: GOOGLE_OWNED_SERVICE
  677. }
  678. policy {
  679. cookies_allowed: NO
  680. setting: "This feature cannot be disabled in settings."
  681. policy_exception_justification: "Not implemented."
  682. })");
  683. state_timer_ = base::ElapsedTimer();
  684. // |state_| must be set correctly in order to generate correct URL.
  685. state_ = State::kCheckingIn;
  686. // Report UMA histogram for transitioning state to |kCheckingIn|.
  687. RecordStateCountMetric(state_);
  688. // Generate Fresnel PSM import request body.
  689. device_activity::ImportDataRequest import_request =
  690. current_use_case->GenerateImportRequestBody();
  691. std::string request_body;
  692. import_request.SerializeToString(&request_body);
  693. auto resource_request = GenerateResourceRequest(
  694. net::HttpRequestHeaders::kPostMethod, GetFresnelURL(), api_key_);
  695. // TODO(https://crbug.com/1266972): Refactor |url_loader_| network request
  696. // call to a shared helper method.
  697. url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
  698. traffic_annotation);
  699. url_loader_->AttachStringForUpload(request_body, "application/x-protobuf");
  700. url_loader_->SetTimeoutDuration(kImportRequestTimeout);
  701. url_loader_->DownloadToString(
  702. url_loader_factory_.get(),
  703. base::BindOnce(&DeviceActivityClient::OnCheckInDone,
  704. weak_factory_.GetWeakPtr(), current_use_case),
  705. kMaxFresnelResponseSizeBytes);
  706. }
  707. void DeviceActivityClient::OnCheckInDone(
  708. DeviceActiveUseCase* current_use_case,
  709. std::unique_ptr<std::string> response_body) {
  710. RecordDeviceActivityMethodCalled(DeviceActivityClient::DeviceActivityMethod::
  711. kDeviceActivityClientOnCheckInDone);
  712. DCHECK_EQ(state_, State::kCheckingIn);
  713. // Use RAII to reset |url_loader_| after current function scope.
  714. // Resetting |url_loader_| also invalidates the |response_info| variable.
  715. auto url_loader = std::move(url_loader_);
  716. int net_code = url_loader->NetError();
  717. RecordResponseStateMetric(state_, net_code);
  718. // Successful import request - PSM ID was imported successfully.
  719. if (net_code == net::OK) {
  720. // Update local state pref to record reporting device active.
  721. current_use_case->SetLastKnownPingTimestamp(
  722. last_transition_out_of_idle_time_);
  723. }
  724. RecordDurationStateMetric(state_, state_timer_.Elapsed());
  725. TransitionToIdle(current_use_case);
  726. }
  727. void DeviceActivityClient::TransitionToIdle(
  728. DeviceActiveUseCase* current_use_case) {
  729. RecordDeviceActivityMethodCalled(DeviceActivityClient::DeviceActivityMethod::
  730. kDeviceActivityClientTransitionToIdle);
  731. DCHECK(!url_loader_);
  732. state_ = State::kIdle;
  733. if (current_use_case) {
  734. // This will also reset the |current_use_case| psm_id field.
  735. current_use_case->SetWindowIdentifier(absl::nullopt);
  736. current_use_case = nullptr;
  737. // Pop the front of the queue since the use case has tried reporting.
  738. if (!pending_use_cases_.empty())
  739. pending_use_cases_.pop();
  740. }
  741. // Try to report the remaining pending use cases.
  742. if (!pending_use_cases_.empty()) {
  743. TransitionOutOfIdle(pending_use_cases_.front());
  744. return;
  745. }
  746. // Report UMA histogram for transitioning state back to |kIdle|.
  747. RecordStateCountMetric(state_);
  748. }
  749. } // namespace device_activity
  750. } // namespace ash