auto_enrollment_client.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/chromeos/policy/auto_enrollment_client.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/guid.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/location.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
12b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "base/message_loop/message_loop_proxy.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/metrics/histogram.h"
14b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "base/metrics/sparse_histogram.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_registry_simple.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_service.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/browser_process.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/policy/browser_policy_connector.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/policy/cloud/device_management_service.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h"
23b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "chromeos/chromeos_switches.h"
2468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "crypto/sha2.h"
2668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "net/url_request/url_request_context_getter.h"
2768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
2868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)using content::BrowserThread;
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace em = enterprise_management;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// UMA histogram names.
35b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char kUMAProtocolTime[] = "Enterprise.AutoEnrollmentProtocolTime";
36b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char kUMAExtraTime[] = "Enterprise.AutoEnrollmentExtraTime";
37b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char kUMARequestStatus[] = "Enterprise.AutoEnrollmentRequestStatus";
38b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char kUMANetworkErrorCode[] =
39b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    "Enterprise.AutoEnrollmentRequestNetworkErrorCode";
40b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The modulus value is sent in an int64 field in the protobuf, whose maximum
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// value is 2^63-1. So 2^64 and 2^63 can't be represented as moduli and the
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// max is 2^62 (when the moduli are restricted to powers-of-2).
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kMaximumPower = 62;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the int value of the |switch_name| argument, clamped to the [0, 62]
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// interval. Returns 0 if the argument doesn't exist or isn't an int value.
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int GetSanitizedArg(const std::string& switch_name) {
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CommandLine* command_line = CommandLine::ForCurrentProcess();
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!command_line->HasSwitch(switch_name))
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string value = command_line->GetSwitchValueASCII(switch_name);
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int int_value;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!base::StringToInt(value, &int_value)) {
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Switch \"" << switch_name << "\" is not a valid int. "
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "Defaulting to 0.";
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (int_value < 0) {
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Switch \"" << switch_name << "\" can't be negative. "
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "Using 0";
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (int_value > kMaximumPower) {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Switch \"" << switch_name << "\" can't be greater than "
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << kMaximumPower << ". Using " << kMaximumPower;
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return kMaximumPower;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return int_value;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the power of the next power-of-2 starting at |value|.
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int NextPowerOf2(int64 value) {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i <= kMaximumPower; ++i) {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((GG_INT64_C(1) << i) >= value)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return i;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // No other value can be represented in an int64.
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return kMaximumPower + 1;
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace policy {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AutoEnrollmentClient::AutoEnrollmentClient(const base::Closure& callback,
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           DeviceManagementService* service,
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           PrefService* local_state,
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           const std::string& serial_number,
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           int power_initial,
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           int power_limit)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : completion_callback_(callback),
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      should_auto_enroll_(false),
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      device_id_(base::GenerateGUID()),
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      power_initial_(power_initial),
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      power_limit_(power_limit),
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      requests_sent_(0),
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      device_management_service_(service),
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      local_state_(local_state) {
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_LE(power_initial_, power_limit_);
101bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  DCHECK(!completion_callback_.is_null());
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!serial_number.empty())
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    serial_number_hash_ = crypto::SHA256HashString(serial_number);
104bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
107bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben MurdochAutoEnrollmentClient::~AutoEnrollmentClient() {
108bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
109bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch}
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AutoEnrollmentClient::RegisterPrefs(PrefRegistrySimple* registry) {
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registry->RegisterBooleanPref(prefs::kShouldAutoEnroll, false);
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registry->RegisterIntegerPref(prefs::kAutoEnrollmentPowerLimit, -1);
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool AutoEnrollmentClient::IsDisabled() {
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CommandLine* command_line = CommandLine::ForCurrentProcess();
120b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return !command_line->HasSwitch(
121b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)             chromeos::switches::kEnterpriseEnrollmentInitialModulus) &&
122b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)         !command_line->HasSwitch(
123b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)             chromeos::switches::kEnterpriseEnrollmentModulusLimit);
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AutoEnrollmentClient* AutoEnrollmentClient::Create(
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const base::Closure& completion_callback) {
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The client won't do anything if |service| is NULL.
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DeviceManagementService* service = NULL;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsDisabled()) {
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VLOG(1) << "Auto-enrollment is disabled";
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string url = BrowserPolicyConnector::GetDeviceManagementUrl();
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!url.empty()) {
13668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      service = new DeviceManagementService(
13768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          g_browser_process->system_request_context(),
13868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          url);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      service->ScheduleInitialization(0);
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int power_initial = GetSanitizedArg(
144b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      chromeos::switches::kEnterpriseEnrollmentInitialModulus);
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int power_limit = GetSanitizedArg(
146b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      chromeos::switches::kEnterpriseEnrollmentModulusLimit);
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (power_initial > power_limit) {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Initial auto-enrollment modulus is larger than the limit, "
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "clamping to the limit.";
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    power_initial = power_limit;
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new AutoEnrollmentClient(
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      completion_callback,
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      service,
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      g_browser_process->local_state(),
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DeviceCloudPolicyManagerChromeOS::GetMachineID(),
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      power_initial,
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      power_limit);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AutoEnrollmentClient::CancelAutoEnrollment() {
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrefService* local_state = g_browser_process->local_state();
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local_state->SetBoolean(prefs::kShouldAutoEnroll, false);
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local_state->CommitPendingWrite();
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AutoEnrollmentClient::Start() {
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Drop the previous job and reset state.
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_job_.reset();
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  should_auto_enroll_ = false;
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  time_start_ = base::Time();  // reset to null.
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (GetCachedDecision()) {
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VLOG(1) << "AutoEnrollmentClient: using cached decision: "
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            << should_auto_enroll_;
178bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  } else if (device_management_service_) {
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (serial_number_hash_.empty()) {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "Failed to get the hash of the serial number, "
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << "will not attempt to auto-enroll.";
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      time_start_ = base::Time::Now();
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SendRequest(power_initial_);
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Don't invoke the callback now.
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Auto-enrollment can't even start, so we're done.
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OnProtocolDone();
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AutoEnrollmentClient::CancelAndDeleteSoon() {
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (time_start_.is_null()) {
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The client isn't running, just delete it.
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delete this;
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Client still running, but our owner isn't interested in the result
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // anymore. Wait until the protocol completes to measure the extra time
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // needed.
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    time_extra_start_ = base::Time::Now();
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    completion_callback_.Reset();
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
207bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdochvoid AutoEnrollmentClient::OnNetworkChanged(
208bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    net::NetworkChangeNotifier::ConnectionType type) {
209bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  if (GetCachedDecision()) {
210bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    // A previous request already obtained a definitive response from the
211bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    // server, so there is no point in retrying; it will get the same decision.
212bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    return;
213bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  }
214bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch
215bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  if (type != net::NetworkChangeNotifier::CONNECTION_NONE &&
216bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      !completion_callback_.is_null() &&
217bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      !request_job_ &&
218bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      device_management_service_ &&
219bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      !serial_number_hash_.empty()) {
220bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    VLOG(1) << "Retrying auto enrollment check after network changed";
221bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    time_start_ = base::Time::Now();
222bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    SendRequest(power_initial_);
223bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  }
224bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch}
225bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool AutoEnrollmentClient::GetCachedDecision() {
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const PrefService::Preference* should_enroll_pref =
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      local_state_->FindPreference(prefs::kShouldAutoEnroll);
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const PrefService::Preference* previous_limit_pref =
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      local_state_->FindPreference(prefs::kAutoEnrollmentPowerLimit);
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool should_auto_enroll = false;
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int previous_limit = -1;
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!should_enroll_pref ||
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      should_enroll_pref->IsDefaultValue() ||
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !should_enroll_pref->GetValue()->GetAsBoolean(&should_auto_enroll) ||
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !previous_limit_pref ||
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      previous_limit_pref->IsDefaultValue() ||
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !previous_limit_pref->GetValue()->GetAsInteger(&previous_limit) ||
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      power_limit_ > previous_limit) {
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  should_auto_enroll_ = should_auto_enroll;
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AutoEnrollmentClient::SendRequest(int power) {
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (power < 0 || power > power_limit_ || serial_number_hash_.empty()) {
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
251bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    OnRequestDone();
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  requests_sent_++;
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only power-of-2 moduli are supported for now. These are computed by taking
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the lower |power| bits of the hash.
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint64 remainder = 0;
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; 8 * i < power; ++i) {
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uint64 byte = serial_number_hash_[31 - i] & 0xff;
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    remainder = remainder | (byte << (8 * i));
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  remainder = remainder & ((GG_UINT64_C(1) << power) - 1);
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_job_.reset(
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      device_management_service_->CreateJob(
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT));
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_job_->SetClientID(device_id_);
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  em::DeviceAutoEnrollmentRequest* request =
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      request_job_->GetRequest()->mutable_auto_enrollment_request();
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request->set_remainder(remainder);
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request->set_modulus(GG_INT64_C(1) << power);
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_job_->Start(base::Bind(&AutoEnrollmentClient::OnRequestCompletion,
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 base::Unretained(this)));
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AutoEnrollmentClient::OnRequestCompletion(
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DeviceManagementStatus status,
280b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    int net_error,
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const em::DeviceManagementResponse& response) {
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (status != DM_STATUS_SUCCESS || !response.has_auto_enrollment_response()) {
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Auto enrollment error: " << status;
284b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    UMA_HISTOGRAM_SPARSE_SLOWLY(kUMARequestStatus, status);
285b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (status == DM_STATUS_REQUEST_FAILED)
286b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      UMA_HISTOGRAM_SPARSE_SLOWLY(kUMANetworkErrorCode, -net_error);
287bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    // The client will retry if a network change is detected.
288bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    OnRequestDone();
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const em::DeviceAutoEnrollmentResponse& enrollment_response =
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response.auto_enrollment_response();
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (enrollment_response.has_expected_modulus()) {
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Server is asking us to retry with a different modulus.
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int64 modulus = enrollment_response.expected_modulus();
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int power = NextPowerOf2(modulus);
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((GG_INT64_C(1) << power) != modulus) {
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(WARNING) << "Auto enrollment: the server didn't ask for a power-of-2 "
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   << "modulus. Using the closest power-of-2 instead "
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   << "(" << modulus << " vs 2^" << power << ")";
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (requests_sent_ >= 2) {
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "Auto enrollment error: already retried with an updated "
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << "modulus but the server asked for a new one again: "
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << power;
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (power > power_limit_) {
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "Auto enrollment error: the server asked for a larger "
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << "modulus than the client accepts (" << power << " vs "
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << power_limit_ << ").";
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Retry at most once with the modulus that the server requested.
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (power <= power_initial_) {
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        LOG(WARNING) << "Auto enrollment: the server asked to use a modulus ("
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     << power << ") that isn't larger than the first used ("
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     << power_initial_ << "). Retrying anyway.";
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
318bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      // Remember this value, so that eventual retries start with the correct
319bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      // modulus.
320bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      power_initial_ = power;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SendRequest(power);
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Server should have sent down a list of hashes to try.
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    should_auto_enroll_ = IsSerialInProtobuf(enrollment_response.hash());
32790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    // Cache the current decision in local_state, so that it is reused in case
32890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    // the device reboots before enrolling.
32990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    local_state_->SetBoolean(prefs::kShouldAutoEnroll, should_auto_enroll_);
33090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    local_state_->SetInteger(prefs::kAutoEnrollmentPowerLimit, power_limit_);
33190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    local_state_->CommitPendingWrite();
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(INFO) << "Auto enrollment complete, should_auto_enroll = "
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              << should_auto_enroll_;
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Auto-enrollment done.
337b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  UMA_HISTOGRAM_SPARSE_SLOWLY(kUMARequestStatus, DM_STATUS_SUCCESS);
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OnProtocolDone();
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool AutoEnrollmentClient::IsSerialInProtobuf(
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const google::protobuf::RepeatedPtrField<std::string>& hashes) {
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < hashes.size(); ++i) {
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (hashes.Get(i) == serial_number_hash_)
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AutoEnrollmentClient::OnProtocolDone() {
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The mininum time can't be 0, must be at least 1.
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const base::TimeDelta kMin = base::TimeDelta::FromMilliseconds(1);
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const base::TimeDelta kMax = base::TimeDelta::FromMinutes(5);
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // However, 0 can still be sampled.
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const base::TimeDelta kZero = base::TimeDelta::FromMilliseconds(0);
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const int kBuckets = 50;
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Time now = base::Time::Now();
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!time_start_.is_null()) {
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::TimeDelta delta = now - time_start_;
361b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    UMA_HISTOGRAM_CUSTOM_TIMES(kUMAProtocolTime, delta, kMin, kMax, kBuckets);
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::TimeDelta delta = kZero;
364bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  if (!time_extra_start_.is_null())
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delta = now - time_extra_start_;
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This samples |kZero| when there was no need for extra time, so that we can
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // measure the ratio of users that succeeded without needing a delay to the
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // total users going through OOBE.
369b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  UMA_HISTOGRAM_CUSTOM_TIMES(kUMAExtraTime, delta, kMin, kMax, kBuckets);
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!completion_callback_.is_null())
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    completion_callback_.Run();
373bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch
374bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  OnRequestDone();
375bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch}
376bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch
377bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdochvoid AutoEnrollmentClient::OnRequestDone() {
378bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  request_job_.reset();
379bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  time_start_ = base::Time();
380bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch
381bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  if (completion_callback_.is_null()) {
382bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    // CancelAndDeleteSoon() was invoked before.
383bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, this);
384bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  }
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace policy
388