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
|
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/federated/federated_service_controller_impl.h"
#include <string>
#include "ash/constants/ash_features.h"
#include "ash/login_status.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "base/containers/fixed_flat_map.h"
#include "base/containers/flat_map.h"
#include "base/feature_list.h"
#include "base/i18n/timezone.h"
#include "base/memory/raw_ptr.h"
#include "base/no_destructor.h"
#include "chromeos/ash/services/federated/public/cpp/federated_example_util.h"
#include "chromeos/ash/services/federated/public/cpp/service_connection.h"
#include "chromeos/ash/services/federated/public/mojom/federated_service.mojom.h"
#include "chromeos/ash/services/federated/public/mojom/tables.mojom.h"
#include "components/user_manager/user_type.h"
namespace ash::federated {
namespace {
using chromeos::federated::mojom::ClientScheduleConfig;
using chromeos::federated::mojom::ClientScheduleConfigPtr;
using chromeos::federated::mojom::FederatedExampleTableId;
// Local client config struct that can be converted to mojom
// ClientScheduleConfig.
struct LocalClientConfig {
// A client uses its client_name and launch stage to make up the task group
// identifier when checking in with the server.
std::string_view client_name;
// The table_id is defined in
// chromeos/ash/services/federated/public/mojom/tables.mojom
FederatedExampleTableId table_id;
// The associated_feature is usually defined in
// ash/constants/ash_features.h/cc
raw_ptr<const base::Feature> associated_feature;
// The hardcoded_stage is set when a client is fully launched and no longer
// needs to be changed, in sucn cases the associated_featur should be nullptr,
// e.g.
// {"foo_client_name", FederatedExampleTableId::BAR_TABLE, nullptr, "prod"}
std::optional<std::string_view> hardcoded_stage;
};
// Each federated client should have an entry in `kLocalClientConfigs` that
// contains its client name, example table id and a feature with launch stage as
// the associated parameter, or a hardcoded launch stage.
const std::array<LocalClientConfig, 4> kLocalClientConfigs = {{
{"input_autocorrect_phh", FederatedExampleTableId::INPUT_AUTOCORRECT,
&features::kAutocorrectFederatedPhh},
{"launcher_query_analytics_v1", FederatedExampleTableId::LAUNCHER_QUERY,
nullptr},
{"launcher_query_analytics_v2", FederatedExampleTableId::LAUNCHER_QUERY_V2,
&features::kFederatedLauncherQueryAnalyticsVersion2Task},
{"timezone_code_phh", FederatedExampleTableId::TIMEZONE_CODE, nullptr},
}};
// Converts a LocalClientConfig to mojom ClientScheduleConfigPtr that can be
// used for scheduling tasks via mojom interface.
// The major logic here is to obtain a valid launch stage. If a client's
// associated_feature is not null, tries to get the launch_stage from the
// feature's parameter. Otherwise tries to use the hardcoded stage. If
// eventually no valid launch_stage, the client is ignored.
std::optional<ClientScheduleConfigPtr> ConvertLocalConfigToSchedulingConfig(
const LocalClientConfig& local_config) {
std::string launch_stage;
if (local_config.associated_feature != nullptr) {
base::FeatureParam<std::string> launch_stage_param{
local_config.associated_feature, "launch_stage", ""};
launch_stage = launch_stage_param.Get();
} else if (local_config.hardcoded_stage.has_value()) {
launch_stage = local_config.hardcoded_stage.value();
}
if (launch_stage.empty()) {
DVLOG(1) << "client " << local_config.client_name
<< " has no valid launch stage, ignored.";
return std::nullopt;
}
auto schedule_config = ClientScheduleConfig::New();
schedule_config->client_name = local_config.client_name;
schedule_config->example_storage_table_id = local_config.table_id;
schedule_config->launch_stage = launch_stage;
return schedule_config;
}
// Prepare client configs for scheduling tasks.
std::vector<ClientScheduleConfigPtr> PrepareClientScheduleConfigs() {
std::vector<ClientScheduleConfigPtr> client_schedule_configs;
for (const auto& local_config : kLocalClientConfigs) {
auto maybe_schedule_config =
ConvertLocalConfigToSchedulingConfig(local_config);
if (maybe_schedule_config.has_value()) {
client_schedule_configs.push_back(
std::move(maybe_schedule_config.value()));
}
}
return client_schedule_configs;
}
chromeos::federated::mojom::ExamplePtr CreateBrellaAnalyticsExamplePtr() {
auto example = chromeos::federated::mojom::Example::New();
example->features = chromeos::federated::mojom::Features::New();
auto& feature_map = example->features->feature;
feature_map["timezone_code"] =
federated::CreateStringList({base::CountryCodeForCurrentTimezone()});
return example;
}
// Returns whether federated can run for this type of logged-in user.
bool IsValidPrimaryUserType(const user_manager::UserType user_type) {
// Primary user session must have user_type = regular or child (v.s. guest,
// public account, kiosk app).
return user_type == user_manager::UserType::kRegular ||
user_type == user_manager::UserType::kChild;
}
} // namespace
FederatedServiceControllerImpl::FederatedServiceControllerImpl() {
SessionControllerImpl* session_controller =
Shell::Get()->session_controller();
DCHECK(session_controller);
session_observation_.Observe(session_controller);
}
FederatedServiceControllerImpl::~FederatedServiceControllerImpl() = default;
void FederatedServiceControllerImpl::OnLoginStatusChanged(
LoginStatus login_status) {
// Federated service daemon uses cryptohome as example store and we only
// treat it available when a proper primary user type has signed in.
// Actually once `federated_service_` gets bound, even if availability is
// set false because of subsequent LoginStatus changes, it keeps bound and
// it's safe to call `federated_service_->ReportExampleToTable()`. But on the
// ChromeOS daemon side it loses a valid cryptohome hence no valid example
// storage, all reported examples are abandoned.
auto* primary_user_session =
Shell::Get()->session_controller()->GetPrimaryUserSession();
service_available_ =
primary_user_session != nullptr &&
IsValidPrimaryUserType(primary_user_session->user_info.type);
if (service_available_ && !federated_service_.is_bound()) {
federated::ServiceConnection::GetInstance()->BindReceiver(
federated_service_.BindNewPipeAndPassReceiver());
federated_service_->StartSchedulingWithConfig(
PrepareClientScheduleConfigs());
// On session first login, reports one example for "timezone_code_phh", a
// trivial F.A. task for prove-out purpose.
if (!reported_) {
federated_service_->ReportExampleToTable(
FederatedExampleTableId::TIMEZONE_CODE,
CreateBrellaAnalyticsExamplePtr());
reported_ = true;
}
}
}
bool FederatedServiceControllerImpl::IsServiceAvailable() const {
return service_available_;
}
} // namespace ash::federated
|