auto_enrollment_client.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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"
17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/prefs/scoped_user_pref_update.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/browser_process.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/browser/chromeos/policy/server_backed_device_state.h"
22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/common/chrome_content_client.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h"
24b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "chromeos/chromeos_switches.h"
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "components/policy/core/browser/browser_policy_connector.h"
26a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "components/policy/core/common/cloud/device_management_service.h"
27a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "components/policy/core/common/cloud/system_policy_request_context.h"
2868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "crypto/sha2.h"
3068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "net/url_request/url_request_context_getter.h"
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "policy/proto/device_management_backend.pb.h"
32a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "url/gurl.h"
3368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
3468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)using content::BrowserThread;
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace em = enterprise_management;
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace policy {
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
42b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// UMA histogram names.
43b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char kUMAProtocolTime[] = "Enterprise.AutoEnrollmentProtocolTime";
44b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char kUMAExtraTime[] = "Enterprise.AutoEnrollmentExtraTime";
45b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char kUMARequestStatus[] = "Enterprise.AutoEnrollmentRequestStatus";
46b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char kUMANetworkErrorCode[] =
47b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    "Enterprise.AutoEnrollmentRequestNetworkErrorCode";
48b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The modulus value is sent in an int64 field in the protobuf, whose maximum
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// value is 2^63-1. So 2^64 and 2^63 can't be represented as moduli and the
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// max is 2^62 (when the moduli are restricted to powers-of-2).
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kMaximumPower = 62;
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the int value of the |switch_name| argument, clamped to the [0, 62]
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// interval. Returns 0 if the argument doesn't exist or isn't an int value.
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int GetSanitizedArg(const std::string& switch_name) {
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CommandLine* command_line = CommandLine::ForCurrentProcess();
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!command_line->HasSwitch(switch_name))
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string value = command_line->GetSwitchValueASCII(switch_name);
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int int_value;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!base::StringToInt(value, &int_value)) {
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Switch \"" << switch_name << "\" is not a valid int. "
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "Defaulting to 0.";
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (int_value < 0) {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Switch \"" << switch_name << "\" can't be negative. "
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "Using 0";
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (int_value > kMaximumPower) {
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Switch \"" << switch_name << "\" can't be greater than "
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << kMaximumPower << ". Using " << kMaximumPower;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return kMaximumPower;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return int_value;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the power of the next power-of-2 starting at |value|.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int NextPowerOf2(int64 value) {
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i <= kMaximumPower; ++i) {
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((GG_INT64_C(1) << i) >= value)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return i;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // No other value can be represented in an int64.
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return kMaximumPower + 1;
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Sets or clears a value in a dictionary.
91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void UpdateDict(base::DictionaryValue* dict,
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                const char* pref_path,
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                bool set_or_clear,
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                base::Value* value) {
95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_ptr<base::Value> scoped_value(value);
96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (set_or_clear)
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    dict->Set(pref_path, scoped_value.release());
98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  else
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    dict->Remove(pref_path, NULL);
100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Converts a restore mode enum value from the DM protocol into the
103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// corresponding prefs string constant.
104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)std::string ConvertRestoreMode(
105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    em::DeviceStateRetrievalResponse::RestoreMode restore_mode) {
106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  switch (restore_mode) {
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case em::DeviceStateRetrievalResponse::RESTORE_MODE_NONE:
108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return std::string();
109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_REQUESTED:
110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return kDeviceStateRestoreModeReEnrollmentRequested;
111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED:
112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return kDeviceStateRestoreModeReEnrollmentEnforced;
113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  NOTREACHED() << "Bad restore mode " << restore_mode;
116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return std::string();
117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
118a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
119a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}  // namespace
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
121a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)AutoEnrollmentClient::AutoEnrollmentClient(
122a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const ProgressCallback& callback,
123a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    DeviceManagementService* service,
124a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    PrefService* local_state,
125a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    scoped_refptr<net::URLRequestContextGetter> system_request_context,
126a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& server_backed_state_key,
127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    bool retrieve_device_state,
128a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    int power_initial,
129a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    int power_limit)
130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    : progress_callback_(callback),
131a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      state_(STATE_PENDING),
132a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      has_server_state_(false),
133a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      device_state_available_(false),
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      device_id_(base::GenerateGUID()),
135a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      server_backed_state_key_(server_backed_state_key),
136a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      retrieve_device_state_(retrieve_device_state),
137a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      current_power_(power_initial),
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      power_limit_(power_limit),
139a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      modulus_updates_received_(0),
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      device_management_service_(service),
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      local_state_(local_state) {
142a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  request_context_ = new SystemPolicyRequestContext(
143a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      system_request_context, GetUserAgent());
144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
145a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK_LE(current_power_, power_limit_);
146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(!progress_callback_.is_null());
147a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!server_backed_state_key_.empty()) {
148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    server_backed_state_key_hash_ =
149a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        crypto::SHA256HashString(server_backed_state_key_);
150a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
151bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
154bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben MurdochAutoEnrollmentClient::~AutoEnrollmentClient() {
155bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
156bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch}
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AutoEnrollmentClient::RegisterPrefs(PrefRegistrySimple* registry) {
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registry->RegisterBooleanPref(prefs::kShouldAutoEnroll, false);
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registry->RegisterIntegerPref(prefs::kAutoEnrollmentPowerLimit, -1);
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool AutoEnrollmentClient::IsDisabled() {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CommandLine* command_line = CommandLine::ForCurrentProcess();
1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Do not communicate auto-enrollment data to the server if
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // 1. we are running integration or perf tests with telemetry.
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // 2. modulus configuration is not present.
170a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return command_line->HasSwitch(chromeos::switches::kOobeSkipPostLogin) ||
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)         (!command_line->HasSwitch(
172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              chromeos::switches::kEnterpriseEnrollmentInitialModulus) &&
173a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          !command_line->HasSwitch(
174a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              chromeos::switches::kEnterpriseEnrollmentModulusLimit));
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)AutoEnrollmentClient* AutoEnrollmentClient::Create(
179a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const ProgressCallback& progress_callback) {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The client won't do anything if |service| is NULL.
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DeviceManagementService* service = NULL;
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsDisabled()) {
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VLOG(1) << "Auto-enrollment is disabled";
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    BrowserPolicyConnector* connector =
1864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        g_browser_process->browser_policy_connector();
1874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    service = connector->device_management_service();
1884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    service->ScheduleInitialization(0);
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int power_initial = GetSanitizedArg(
192b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      chromeos::switches::kEnterpriseEnrollmentInitialModulus);
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int power_limit = GetSanitizedArg(
194b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      chromeos::switches::kEnterpriseEnrollmentModulusLimit);
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (power_initial > power_limit) {
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Initial auto-enrollment modulus is larger than the limit, "
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "clamping to the limit.";
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    power_initial = power_limit;
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bool retrieve_device_state = false;
202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::string device_id;
203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (CommandLine::ForCurrentProcess()->HasSwitch(
204a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          chromeos::switches::kEnterpriseEnableForcedReEnrollment)) {
205a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    retrieve_device_state = true;
206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    device_id = DeviceCloudPolicyManagerChromeOS::GetDeviceStateKey();
207a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } else {
208a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    device_id = DeviceCloudPolicyManagerChromeOS::GetMachineID();
209a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
210a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new AutoEnrollmentClient(
212a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      progress_callback,
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      service,
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      g_browser_process->local_state(),
215a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      g_browser_process->system_request_context(),
216a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      device_id,
217a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      retrieve_device_state,
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      power_initial,
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      power_limit);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AutoEnrollmentClient::CancelAutoEnrollment() {
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrefService* local_state = g_browser_process->local_state();
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local_state->SetBoolean(prefs::kShouldAutoEnroll, false);
226a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  local_state->ClearPref(prefs::kServerBackedDeviceState);
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local_state->CommitPendingWrite();
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AutoEnrollmentClient::Start() {
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Drop the previous job and reset state.
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_job_.reset();
233a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  state_ = STATE_PENDING;
234a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  time_start_ = base::Time::Now();
235a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  modulus_updates_received_ = 0;
236a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  has_server_state_ = false;
237a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  device_state_available_ = false;
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
239a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  NextStep();
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AutoEnrollmentClient::CancelAndDeleteSoon() {
243a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (time_start_.is_null() || !request_job_) {
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The client isn't running, just delete it.
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delete this;
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Client still running, but our owner isn't interested in the result
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // anymore. Wait until the protocol completes to measure the extra time
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // needed.
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    time_extra_start_ = base::Time::Now();
251a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    progress_callback_.Reset();
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdochvoid AutoEnrollmentClient::OnNetworkChanged(
256bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    net::NetworkChangeNotifier::ConnectionType type) {
257bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  if (type != net::NetworkChangeNotifier::CONNECTION_NONE &&
258a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      !progress_callback_.is_null()) {
259a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    RetryStep();
260bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  }
261bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch}
262bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool AutoEnrollmentClient::GetCachedDecision() {
264a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const PrefService::Preference* has_server_state_pref =
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      local_state_->FindPreference(prefs::kShouldAutoEnroll);
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const PrefService::Preference* previous_limit_pref =
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      local_state_->FindPreference(prefs::kAutoEnrollmentPowerLimit);
268a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bool has_server_state = false;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int previous_limit = -1;
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
271a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!has_server_state_pref ||
272a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      has_server_state_pref->IsDefaultValue() ||
273a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      !has_server_state_pref->GetValue()->GetAsBoolean(&has_server_state) ||
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !previous_limit_pref ||
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      previous_limit_pref->IsDefaultValue() ||
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !previous_limit_pref->GetValue()->GetAsInteger(&previous_limit) ||
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      power_limit_ > previous_limit) {
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
281a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  has_server_state_ = has_server_state;
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool AutoEnrollmentClient::RetryStep() {
286a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // If there is a pending request job, let it finish.
287a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (request_job_)
288a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return true;
289a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
290a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (GetCachedDecision()) {
291a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // The bucket download check has completed already. If it came back
292a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // positive, then device state should be (re-)downloaded.
293a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (has_server_state_) {
294a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (retrieve_device_state_ && !device_state_available_ &&
295a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          SendDeviceStateRequest()) {
296a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return true;
297a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
298a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
299a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } else {
300a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Start bucket download.
301a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (SendBucketDownloadRequest())
302a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return true;
303a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
304a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
305a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return false;
306a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
307a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
308a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void AutoEnrollmentClient::ReportProgress(State state) {
309a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  state_ = state;
310a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (progress_callback_.is_null()) {
311a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, this);
312a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } else {
313a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    progress_callback_.Run(state_);
314a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
315a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
316a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
317a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void AutoEnrollmentClient::NextStep() {
318a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!RetryStep()) {
319a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Protocol finished successfully, report result.
320a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    bool trigger_enrollment = false;
321a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (retrieve_device_state_) {
322a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      const base::DictionaryValue* device_state_dict =
323a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          local_state_->GetDictionary(prefs::kServerBackedDeviceState);
324a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      std::string restore_mode;
325a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      device_state_dict->GetString(kDeviceStateRestoreMode, &restore_mode);
326a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      trigger_enrollment =
327a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          (restore_mode == kDeviceStateRestoreModeReEnrollmentRequested ||
328a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)           restore_mode == kDeviceStateRestoreModeReEnrollmentEnforced);
329a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    } else {
330a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      trigger_enrollment = has_server_state_;
331a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
332a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
333a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    ReportProgress(trigger_enrollment ? STATE_TRIGGER_ENROLLMENT
334a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                      : STATE_NO_ENROLLMENT);
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
336a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
338a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool AutoEnrollmentClient::SendBucketDownloadRequest() {
339a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (server_backed_state_key_hash_.empty())
340a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return false;
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only power-of-2 moduli are supported for now. These are computed by taking
343a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // the lower |current_power_| bits of the hash.
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint64 remainder = 0;
345a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  for (int i = 0; 8 * i < current_power_; ++i) {
346a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    uint64 byte = server_backed_state_key_hash_[31 - i] & 0xff;
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    remainder = remainder | (byte << (8 * i));
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
349a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  remainder = remainder & ((GG_UINT64_C(1) << current_power_) - 1);
350a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
351a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ReportProgress(STATE_PENDING);
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_job_.reset(
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      device_management_service_->CreateJob(
355a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT,
356a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          request_context_.get()));
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_job_->SetClientID(device_id_);
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  em::DeviceAutoEnrollmentRequest* request =
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      request_job_->GetRequest()->mutable_auto_enrollment_request();
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request->set_remainder(remainder);
361a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  request->set_modulus(GG_INT64_C(1) << current_power_);
362a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  request_job_->Start(
363a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      base::Bind(&AutoEnrollmentClient::HandleRequestCompletion,
364a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 base::Unretained(this),
365a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 &AutoEnrollmentClient::OnBucketDownloadRequestCompletion));
366a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return true;
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
369a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool AutoEnrollmentClient::SendDeviceStateRequest() {
370a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ReportProgress(STATE_PENDING);
371a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
372a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  request_job_.reset(
373a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      device_management_service_->CreateJob(
374a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          DeviceManagementRequestJob::TYPE_DEVICE_STATE_RETRIEVAL,
375a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          request_context_.get()));
376a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  request_job_->SetClientID(device_id_);
377a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  em::DeviceStateRetrievalRequest* request =
378a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      request_job_->GetRequest()->mutable_device_state_retrieval_request();
379a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  request->set_server_backed_state_key(server_backed_state_key_);
380a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  request_job_->Start(
381a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      base::Bind(&AutoEnrollmentClient::HandleRequestCompletion,
382a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 base::Unretained(this),
383a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 &AutoEnrollmentClient::OnDeviceStateRequestCompletion));
384a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return true;
385a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
386a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
387a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void AutoEnrollmentClient::HandleRequestCompletion(
388a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    RequestCompletionHandler handler,
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DeviceManagementStatus status,
390b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    int net_error,
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const em::DeviceManagementResponse& response) {
392a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  UMA_HISTOGRAM_SPARSE_SLOWLY(kUMARequestStatus, status);
393a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (status != DM_STATUS_SUCCESS) {
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Auto enrollment error: " << status;
395b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (status == DM_STATUS_REQUEST_FAILED)
396b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      UMA_HISTOGRAM_SPARSE_SLOWLY(kUMANetworkErrorCode, -net_error);
397a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    request_job_.reset();
398a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
399a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Abort if CancelAndDeleteSoon has been called meanwhile.
400a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (progress_callback_.is_null()) {
401a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, this);
402a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    } else {
403a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      ReportProgress(status == DM_STATUS_REQUEST_FAILED ? STATE_CONNECTION_ERROR
404a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                                        : STATE_SERVER_ERROR);
405a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
409a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bool progress = (this->*handler)(status, net_error, response);
410a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  request_job_.reset();
411a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (progress)
412a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    NextStep();
413a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  else
414a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    ReportProgress(STATE_SERVER_ERROR);
415a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
416a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
417a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool AutoEnrollmentClient::OnBucketDownloadRequestCompletion(
418a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    DeviceManagementStatus status,
419a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    int net_error,
420a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const em::DeviceManagementResponse& response) {
421a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bool progress = false;
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const em::DeviceAutoEnrollmentResponse& enrollment_response =
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response.auto_enrollment_response();
424a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!response.has_auto_enrollment_response()) {
425a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    LOG(ERROR) << "Server failed to provide auto-enrollment response.";
426a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } else if (enrollment_response.has_expected_modulus()) {
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Server is asking us to retry with a different modulus.
428a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    modulus_updates_received_++;
429a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int64 modulus = enrollment_response.expected_modulus();
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int power = NextPowerOf2(modulus);
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((GG_INT64_C(1) << power) != modulus) {
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(WARNING) << "Auto enrollment: the server didn't ask for a power-of-2 "
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   << "modulus. Using the closest power-of-2 instead "
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   << "(" << modulus << " vs 2^" << power << ")";
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
437a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (modulus_updates_received_ >= 2) {
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "Auto enrollment error: already retried with an updated "
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << "modulus but the server asked for a new one again: "
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << power;
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (power > power_limit_) {
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "Auto enrollment error: the server asked for a larger "
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << "modulus than the client accepts (" << power << " vs "
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << power_limit_ << ").";
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Retry at most once with the modulus that the server requested.
447a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (power <= current_power_) {
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        LOG(WARNING) << "Auto enrollment: the server asked to use a modulus ("
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     << power << ") that isn't larger than the first used ("
450a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                     << current_power_ << "). Retrying anyway.";
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
452bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      // Remember this value, so that eventual retries start with the correct
453bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      // modulus.
454a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      current_power_ = power;
455a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return true;
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Server should have sent down a list of hashes to try.
459a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    has_server_state_ = IsIdHashInProtobuf(enrollment_response.hash());
46090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    // Cache the current decision in local_state, so that it is reused in case
46190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    // the device reboots before enrolling.
462a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    local_state_->SetBoolean(prefs::kShouldAutoEnroll, has_server_state_);
46390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    local_state_->SetInteger(prefs::kAutoEnrollmentPowerLimit, power_limit_);
46490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    local_state_->CommitPendingWrite();
465a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    VLOG(1) << "Auto enrollment check complete, has_server_state_ = "
466a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            << has_server_state_;
467a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    progress = true;
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
470a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Bucket download done, update UMA.
471a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  UpdateBucketDownloadTimingHistograms();
472a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return progress;
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool AutoEnrollmentClient::OnDeviceStateRequestCompletion(
476a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    DeviceManagementStatus status,
477a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    int net_error,
478a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const enterprise_management::DeviceManagementResponse& response) {
479a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bool progress = false;
480a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!response.has_device_state_retrieval_response()) {
481a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    LOG(ERROR) << "Server failed to provide auto-enrollment response.";
482a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } else {
483a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const em::DeviceStateRetrievalResponse& state_response =
484a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        response.device_state_retrieval_response();
485a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    {
486a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      DictionaryPrefUpdate dict(local_state_, prefs::kServerBackedDeviceState);
487a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      UpdateDict(dict.Get(),
488a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 kDeviceStateManagementDomain,
489a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 state_response.has_management_domain(),
490a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 new base::StringValue(state_response.management_domain()));
491a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
492a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      std::string restore_mode =
493a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          ConvertRestoreMode(state_response.restore_mode());
494a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      UpdateDict(dict.Get(),
495a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 kDeviceStateRestoreMode,
496a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 !restore_mode.empty(),
497a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 new base::StringValue(restore_mode));
498a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
499a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    local_state_->CommitPendingWrite();
500a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    device_state_available_ = true;
501a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    progress = true;
502a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
503a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
504a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return progress;
505a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
506a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
507a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool AutoEnrollmentClient::IsIdHashInProtobuf(
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const google::protobuf::RepeatedPtrField<std::string>& hashes) {
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < hashes.size(); ++i) {
510a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (hashes.Get(i) == server_backed_state_key_hash_)
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
516a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void AutoEnrollmentClient::UpdateBucketDownloadTimingHistograms() {
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The mininum time can't be 0, must be at least 1.
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const base::TimeDelta kMin = base::TimeDelta::FromMilliseconds(1);
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const base::TimeDelta kMax = base::TimeDelta::FromMinutes(5);
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // However, 0 can still be sampled.
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const base::TimeDelta kZero = base::TimeDelta::FromMilliseconds(0);
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const int kBuckets = 50;
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Time now = base::Time::Now();
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!time_start_.is_null()) {
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::TimeDelta delta = now - time_start_;
527b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    UMA_HISTOGRAM_CUSTOM_TIMES(kUMAProtocolTime, delta, kMin, kMax, kBuckets);
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::TimeDelta delta = kZero;
530bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  if (!time_extra_start_.is_null())
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delta = now - time_extra_start_;
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This samples |kZero| when there was no need for extra time, so that we can
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // measure the ratio of users that succeeded without needing a delay to the
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // total users going through OOBE.
535b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  UMA_HISTOGRAM_CUSTOM_TIMES(kUMAExtraTime, delta, kMin, kMax, kBuckets);
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace policy
539