1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/report/utils/pref_utils.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "chromeos/ash/components/dbus/private_computing/private_computing_service.pb.h"
#include "chromeos/ash/components/report/prefs/fresnel_pref_names.h"
#include "chromeos/ash/components/report/utils/time_utils.h"
#include "components/prefs/pref_service.h"
namespace ash::report::utils {
namespace {
using private_computing::ActiveStatus;
using private_computing::ChurnObservationStatus;
using private_computing::GetStatusResponse;
using private_computing::PrivateComputingUseCase;
using private_computing::SaveStatusRequest;
// UMA histogram names for preserved file read records.
const char kHistogramsPreservedFileRead[] =
"Ash.Report.PreservedFileReadAndParsed";
// |ts| must be defined and not unix epoch time.
void WriteLocalStateTimestampIfValid(PrefService* local_state,
const std::string& pref_name,
base::Time ts) {
if (ts != base::Time::UnixEpoch() || ts != base::Time()) {
local_state->SetTime(pref_name, ts);
}
}
void WriteObservationLastPingTimestampIfValid(PrefService* local_state,
const std::string& pref_name,
base::Time ts) {
if (ts == base::Time::UnixEpoch() || ts == base::Time()) {
LOG(ERROR) << "Observation timestamp is not valid. ts = " << ts;
return;
}
if (local_state->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus0)) {
local_state->SetTime(pref_name, ts);
} else if (local_state->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus1)) {
local_state->SetTime(pref_name, utils::GetPreviousMonth(ts).value());
} else if (local_state->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus2)) {
local_state->SetTime(
pref_name,
utils::GetPreviousMonth(utils::GetPreviousMonth(ts).value()).value());
}
}
} // namespace
void RestoreLocalStateWithPreservedFile(PrefService* local_state,
GetStatusResponse response) {
bool read_success = true;
for (ActiveStatus active_status : response.active_status()) {
base::Time last_ping_ts;
// Parse and validate the ping date before attempting to restore value.
if (active_status.has_last_ping_date()) {
bool success = base::Time::FromUTCString(
active_status.last_ping_date().c_str(), &last_ping_ts);
if (!success) {
read_success = false;
LOG(ERROR) << "Fail to convert last ping date to ts for use case = "
<< PrivateComputingUseCase_Name(active_status.use_case());
continue;
}
}
switch (active_status.use_case()) {
case PrivateComputingUseCase::CROS_FRESNEL_DAILY:
WriteLocalStateTimestampIfValid(
local_state, prefs::kDeviceActiveLastKnown1DayActivePingTimestamp,
last_ping_ts);
break;
case PrivateComputingUseCase::CROS_FRESNEL_28DAY_ACTIVE:
WriteLocalStateTimestampIfValid(
local_state, prefs::kDeviceActiveLastKnown28DayActivePingTimestamp,
last_ping_ts);
break;
case PrivateComputingUseCase::CROS_FRESNEL_CHURN_MONTHLY_COHORT:
WriteLocalStateTimestampIfValid(
local_state, prefs::kDeviceActiveChurnCohortMonthlyPingTimestamp,
last_ping_ts);
if (active_status.has_churn_active_status() &&
active_status.churn_active_status() != 0) {
local_state->SetInteger(
prefs::kDeviceActiveLastKnownChurnActiveStatus,
active_status.churn_active_status());
}
break;
case PrivateComputingUseCase::CROS_FRESNEL_CHURN_MONTHLY_OBSERVATION:
if (active_status.has_period_status()) {
local_state->SetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus0,
active_status.period_status().is_active_current_period_minus_0());
local_state->SetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus1,
active_status.period_status().is_active_current_period_minus_1());
local_state->SetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus2,
active_status.period_status().is_active_current_period_minus_2());
WriteObservationLastPingTimestampIfValid(
local_state,
prefs::kDeviceActiveChurnObservationMonthlyPingTimestamp,
local_state->GetTime(
prefs::kDeviceActiveChurnCohortMonthlyPingTimestamp));
}
break;
default:
read_success = false;
LOG(ERROR) << "Restore local state failed - unknown use case.";
continue;
}
}
base::UmaHistogramBoolean(kHistogramsPreservedFileRead, read_success);
}
SaveStatusRequest CreatePreservedFileContents(PrefService* local_state) {
base::Time one_day_ts = local_state->GetTime(
prefs::kDeviceActiveLastKnown1DayActivePingTimestamp);
base::Time twenty_eight_day_ts = local_state->GetTime(
prefs::kDeviceActiveLastKnown28DayActivePingTimestamp);
base::Time cohort_ts =
local_state->GetTime(prefs::kDeviceActiveChurnCohortMonthlyPingTimestamp);
base::Time observation_ts = local_state->GetTime(
prefs::kDeviceActiveChurnObservationMonthlyPingTimestamp);
int churn_active_status =
local_state->GetInteger(prefs::kDeviceActiveLastKnownChurnActiveStatus);
bool period_0 = local_state->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus0);
bool period_1 = local_state->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus1);
bool period_2 = local_state->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus2);
SaveStatusRequest save_request;
// Store 1-day-active data.
if (one_day_ts != base::Time() && one_day_ts != base::Time::UnixEpoch()) {
ActiveStatus one_day_status;
one_day_status.set_use_case(PrivateComputingUseCase::CROS_FRESNEL_DAILY);
one_day_status.set_last_ping_date(
utils::FormatTimestampToMidnightGMTString(one_day_ts));
*save_request.add_active_status() = one_day_status;
}
// Store 28-day-active data.
if (twenty_eight_day_ts != base::Time() &&
twenty_eight_day_ts != base::Time::UnixEpoch()) {
ActiveStatus twenty_eight_day_status;
twenty_eight_day_status.set_use_case(
PrivateComputingUseCase::CROS_FRESNEL_28DAY_ACTIVE);
twenty_eight_day_status.set_last_ping_date(
utils::FormatTimestampToMidnightGMTString(twenty_eight_day_ts));
*save_request.add_active_status() = twenty_eight_day_status;
}
// Store Churn data.
if (cohort_ts != base::Time() && cohort_ts != base::Time::UnixEpoch()) {
ActiveStatus cohort_status;
cohort_status.set_use_case(
PrivateComputingUseCase::CROS_FRESNEL_CHURN_MONTHLY_COHORT);
cohort_status.set_last_ping_date(
utils::FormatTimestampToMidnightGMTString(cohort_ts));
cohort_status.set_churn_active_status(churn_active_status);
*save_request.add_active_status() = cohort_status;
// Store Monthly Observation data.
//
// Observation active status will only be saved to preserved file,
// if it is aligned with when Cohort use case last pinged.
if (utils::IsSameYearAndMonth(observation_ts, cohort_ts)) {
ActiveStatus observation_status;
observation_status.set_use_case(
PrivateComputingUseCase::CROS_FRESNEL_CHURN_MONTHLY_OBSERVATION);
ChurnObservationStatus* period_status =
observation_status.mutable_period_status();
period_status->set_is_active_current_period_minus_0(period_0);
period_status->set_is_active_current_period_minus_1(period_1);
period_status->set_is_active_current_period_minus_2(period_2);
*save_request.add_active_status() = observation_status;
}
}
return save_request;
}
} // namespace ash::report::utils
|