Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / chromeos / login / enrollment / auto_enrollment_controller.cc
blob7f9fc2541e8d08cc1a51e88c726396ae31922898
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.
5 #include "chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
14 #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
15 #include "chrome/browser/chromeos/policy/server_backed_state_keys_broker.h"
16 #include "chromeos/chromeos_switches.h"
17 #include "chromeos/system/statistics_provider.h"
18 #include "components/policy/core/common/cloud/device_management_service.h"
19 #include "net/url_request/url_request_context_getter.h"
21 namespace chromeos {
23 namespace {
25 // Maximum time to wait before forcing a decision. Note that download time for
26 // state key buckets can be non-negligible, especially on 2G connections.
27 const int kSafeguardTimeoutSeconds = 60;
29 // Returns the int value of the |switch_name| argument, clamped to the [0, 62]
30 // interval. Returns 0 if the argument doesn't exist or isn't an int value.
31 int GetSanitizedArg(const std::string& switch_name) {
32 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
33 if (!command_line->HasSwitch(switch_name))
34 return 0;
35 std::string value = command_line->GetSwitchValueASCII(switch_name);
36 int int_value;
37 if (!base::StringToInt(value, &int_value)) {
38 LOG(ERROR) << "Switch \"" << switch_name << "\" is not a valid int. "
39 << "Defaulting to 0.";
40 return 0;
42 if (int_value < 0) {
43 LOG(ERROR) << "Switch \"" << switch_name << "\" can't be negative. "
44 << "Using 0";
45 return 0;
47 if (int_value > policy::AutoEnrollmentClient::kMaximumPower) {
48 LOG(ERROR) << "Switch \"" << switch_name << "\" can't be greater than "
49 << policy::AutoEnrollmentClient::kMaximumPower << ". Using "
50 << policy::AutoEnrollmentClient::kMaximumPower;
51 return policy::AutoEnrollmentClient::kMaximumPower;
53 return int_value;
56 // Checks whether the device is yet to be set up by the first user in its
57 // lifetime. After first setup, the activation date gets stored in the R/W VPD,
58 // the absence of this key signals the device is factory-fresh. The requirement
59 // for the machine serial number to be present as well is a sanity-check to
60 // ensure that the VPD has actually been read successfully.
61 bool IsFirstDeviceSetup() {
62 std::string activate_date;
63 return !system::StatisticsProvider::GetInstance()->HasMachineStatistic(
64 system::kActivateDateKey) &&
65 !policy::DeviceCloudPolicyManagerChromeOS::GetMachineID().empty();
68 } // namespace
70 const char AutoEnrollmentController::kForcedReEnrollmentAlways[] = "always";
71 const char AutoEnrollmentController::kForcedReEnrollmentNever[] = "never";
72 const char AutoEnrollmentController::kForcedReEnrollmentOfficialBuild[] =
73 "official";
75 AutoEnrollmentController::Mode AutoEnrollmentController::GetMode() {
76 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
78 std::string command_line_mode = command_line->GetSwitchValueASCII(
79 switches::kEnterpriseEnableForcedReEnrollment);
80 if (command_line_mode == kForcedReEnrollmentAlways) {
81 return MODE_FORCED_RE_ENROLLMENT;
82 } else if (command_line_mode.empty() ||
83 command_line_mode == kForcedReEnrollmentOfficialBuild) {
84 #if defined(OFFICIAL_BUILD)
85 std::string firmware_type;
86 const bool non_chrome_firmware =
87 system::StatisticsProvider::GetInstance()->GetMachineStatistic(
88 system::kFirmwareTypeKey, &firmware_type) &&
89 firmware_type == system::kFirmwareTypeValueNonchrome;
91 std::string write_protect_switch_boot;
92 const bool write_protect_switch_off =
93 system::StatisticsProvider::GetInstance()->GetMachineStatistic(
94 system::kWriteProtectSwitchBootKey, &write_protect_switch_boot) &&
95 write_protect_switch_boot == system::kWriteProtectSwitchBootValueOff;
97 return (non_chrome_firmware || write_protect_switch_off)
98 ? MODE_NONE
99 : MODE_FORCED_RE_ENROLLMENT;
100 #else
101 return MODE_NONE;
102 #endif
103 } else if (command_line_mode == kForcedReEnrollmentNever) {
104 return MODE_NONE;
107 LOG(FATAL) << "Unknown auto-enrollment mode " << command_line_mode;
108 return MODE_NONE;
111 AutoEnrollmentController::AutoEnrollmentController()
112 : state_(policy::AUTO_ENROLLMENT_STATE_IDLE),
113 safeguard_timer_(false, false),
114 client_start_weak_factory_(this) {}
116 AutoEnrollmentController::~AutoEnrollmentController() {}
118 void AutoEnrollmentController::Start() {
119 // This method is called at the point in the OOBE/login flow at which the
120 // auto-enrollment check can start. This happens either after the EULA is
121 // accepted, or right after a reboot if the EULA has already been accepted.
123 // Do not communicate auto-enrollment data to the server if
124 // 1. we are running telemetry tests.
125 // 2. modulus configuration is not present.
126 // 3. Auto-enrollment is disabled via the command line.
127 // 4. This is the first boot ever, so re-enrollment checks are pointless. This
128 // also enables factories to start full guest sessions for testing, see
129 // http://crbug.com/397354 for more context.
131 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
132 if (command_line->HasSwitch(chromeos::switches::kDisableGaiaServices) ||
133 (!command_line->HasSwitch(
134 chromeos::switches::kEnterpriseEnrollmentInitialModulus) &&
135 !command_line->HasSwitch(
136 chromeos::switches::kEnterpriseEnrollmentModulusLimit)) ||
137 GetMode() == MODE_NONE ||
138 IsFirstDeviceSetup()) {
139 VLOG(1) << "Auto-enrollment disabled.";
140 UpdateState(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT);
141 return;
144 // If a client is being created or already existing, bail out.
145 if (client_start_weak_factory_.HasWeakPtrs() || client_)
146 return;
148 // Arm the belts-and-suspenders timer to avoid hangs.
149 safeguard_timer_.Start(
150 FROM_HERE, base::TimeDelta::FromSeconds(kSafeguardTimeoutSeconds),
151 base::Bind(&AutoEnrollmentController::Timeout, base::Unretained(this)));
153 // Start by checking if the device has already been owned.
154 UpdateState(policy::AUTO_ENROLLMENT_STATE_PENDING);
155 DeviceSettingsService::Get()->GetOwnershipStatusAsync(
156 base::Bind(&AutoEnrollmentController::OnOwnershipStatusCheckDone,
157 client_start_weak_factory_.GetWeakPtr()));
160 void AutoEnrollmentController::Cancel() {
161 if (client_) {
162 // Cancelling the |client_| allows it to determine whether
163 // its protocol finished before login was complete.
164 client_.release()->CancelAndDeleteSoon();
167 // Make sure to nuke pending |client_| start sequences.
168 client_start_weak_factory_.InvalidateWeakPtrs();
170 safeguard_timer_.Stop();
173 void AutoEnrollmentController::Retry() {
174 if (client_)
175 client_->Retry();
176 else
177 Start();
180 scoped_ptr<AutoEnrollmentController::ProgressCallbackList::Subscription>
181 AutoEnrollmentController::RegisterProgressCallback(
182 const ProgressCallbackList::CallbackType& callback) {
183 return progress_callbacks_.Add(callback);
186 void AutoEnrollmentController::OnOwnershipStatusCheckDone(
187 DeviceSettingsService::OwnershipStatus status) {
188 if (status != DeviceSettingsService::OWNERSHIP_NONE) {
189 // The device is already owned. No need for auto-enrollment checks.
190 VLOG(1) << "Device already owned, skipping auto-enrollment check";
191 UpdateState(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT);
192 return;
195 // Make sure state keys are available.
196 g_browser_process->platform_part()
197 ->browser_policy_connector_chromeos()
198 ->GetStateKeysBroker()
199 ->RequestStateKeys(base::Bind(&AutoEnrollmentController::StartClient,
200 client_start_weak_factory_.GetWeakPtr()));
203 void AutoEnrollmentController::StartClient(
204 const std::vector<std::string>& state_keys) {
205 if (state_keys.empty()) {
206 LOG(WARNING) << "No state keys available!";
207 UpdateState(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT);
208 return;
211 policy::BrowserPolicyConnectorChromeOS* connector =
212 g_browser_process->platform_part()->browser_policy_connector_chromeos();
213 policy::DeviceManagementService* service =
214 connector->device_management_service();
215 service->ScheduleInitialization(0);
217 int power_initial = GetSanitizedArg(
218 chromeos::switches::kEnterpriseEnrollmentInitialModulus);
219 int power_limit = GetSanitizedArg(
220 chromeos::switches::kEnterpriseEnrollmentModulusLimit);
221 if (power_initial > power_limit) {
222 LOG(ERROR) << "Initial auto-enrollment modulus is larger than the limit, "
223 << "clamping to the limit.";
224 power_initial = power_limit;
227 client_.reset(new policy::AutoEnrollmentClient(
228 base::Bind(&AutoEnrollmentController::UpdateState,
229 base::Unretained(this)),
230 service,
231 g_browser_process->local_state(),
232 g_browser_process->system_request_context(),
233 state_keys.front(),
234 power_initial,
235 power_limit));
237 VLOG(1) << "Starting auto-enrollment client.";
238 client_->Start();
241 void AutoEnrollmentController::UpdateState(
242 policy::AutoEnrollmentState new_state) {
243 VLOG(1) << "New state: " << new_state << ".";
244 state_ = new_state;
246 // Stop the safeguard timer once a result comes in.
247 switch (state_) {
248 case policy::AUTO_ENROLLMENT_STATE_IDLE:
249 case policy::AUTO_ENROLLMENT_STATE_PENDING:
250 break;
251 case policy::AUTO_ENROLLMENT_STATE_CONNECTION_ERROR:
252 case policy::AUTO_ENROLLMENT_STATE_SERVER_ERROR:
253 case policy::AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT:
254 case policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT:
255 safeguard_timer_.Stop();
256 break;
259 progress_callbacks_.Notify(state_);
262 void AutoEnrollmentController::Timeout() {
263 // TODO(mnissler): Add UMA to track results of auto-enrollment checks.
264 if (client_start_weak_factory_.HasWeakPtrs()) {
265 // If the callbacks to check ownership status or state keys are still
266 // pending, there's a bug in the code running on the device. No use in
267 // retrying anything, need to fix that bug.
268 LOG(ERROR) << "Failed to start auto-enrollment check, fix the code!";
269 UpdateState(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT);
270 } else {
271 // If the AutoEnrollmentClient didn't manage to come back with a result in
272 // time, blame it on the network. This can actually happen in some cases,
273 // for example when the server just doesn't reply and keeps the connection
274 // open.
275 LOG(ERROR) << "AutoEnrollmentClient didn't complete within time limit.";
276 UpdateState(policy::AUTO_ENROLLMENT_STATE_CONNECTION_ERROR);
279 // Reset state.
280 Cancel();
283 } // namespace chromeos