| Index: chrome/browser/chromeos/policy/active_directory_policy_manager.cc
 | 
| diff --git a/chrome/browser/chromeos/policy/active_directory_policy_manager.cc b/chrome/browser/chromeos/policy/active_directory_policy_manager.cc
 | 
| index 195140d9e972a2b2efc033267aa4707a4a941935..fc1fcbf558dfe5e375a272a604cbf079351dc2f2 100644
 | 
| --- a/chrome/browser/chromeos/policy/active_directory_policy_manager.cc
 | 
| +++ b/chrome/browser/chromeos/policy/active_directory_policy_manager.cc
 | 
| @@ -31,17 +31,23 @@ ActiveDirectoryPolicyManager::~ActiveDirectoryPolicyManager() {}
 | 
|  std::unique_ptr<ActiveDirectoryPolicyManager>
 | 
|  ActiveDirectoryPolicyManager::CreateForDevicePolicy(
 | 
|      std::unique_ptr<CloudPolicyStore> store) {
 | 
| +  // Can't use MakeUnique<> because the constructor is private.
 | 
|    return base::WrapUnique(
 | 
| -      new ActiveDirectoryPolicyManager(EmptyAccountId(), std::move(store)));
 | 
| +      new ActiveDirectoryPolicyManager(EmptyAccountId(), base::TimeDelta(),
 | 
| +                                       base::OnceClosure(), std::move(store)));
 | 
|  }
 | 
|  
 | 
|  // static
 | 
|  std::unique_ptr<ActiveDirectoryPolicyManager>
 | 
|  ActiveDirectoryPolicyManager::CreateForUserPolicy(
 | 
|      const AccountId& account_id,
 | 
| +    base::TimeDelta initial_policy_fetch_timeout,
 | 
| +    base::OnceClosure exit_session,
 | 
|      std::unique_ptr<CloudPolicyStore> store) {
 | 
| -  return base::WrapUnique(
 | 
| -      new ActiveDirectoryPolicyManager(account_id, std::move(store)));
 | 
| +  // Can't use MakeUnique<> because the constructor is private.
 | 
| +  return base::WrapUnique(new ActiveDirectoryPolicyManager(
 | 
| +      account_id, initial_policy_fetch_timeout, std::move(exit_session),
 | 
| +      std::move(store)));
 | 
|  }
 | 
|  
 | 
|  void ActiveDirectoryPolicyManager::Init(SchemaRegistry* registry) {
 | 
| @@ -56,7 +62,7 @@ void ActiveDirectoryPolicyManager::Init(SchemaRegistry* registry) {
 | 
|    PublishPolicy();
 | 
|  
 | 
|    scheduler_ = base::MakeUnique<PolicyScheduler>(
 | 
| -      base::BindRepeating(&ActiveDirectoryPolicyManager::DoFetch,
 | 
| +      base::BindRepeating(&ActiveDirectoryPolicyManager::DoPolicyFetch,
 | 
|                            weak_ptr_factory_.GetWeakPtr()),
 | 
|        base::BindRepeating(&ActiveDirectoryPolicyManager::OnPolicyFetched,
 | 
|                            weak_ptr_factory_.GetWeakPtr()),
 | 
| @@ -70,6 +76,9 @@ void ActiveDirectoryPolicyManager::Shutdown() {
 | 
|  
 | 
|  bool ActiveDirectoryPolicyManager::IsInitializationComplete(
 | 
|      PolicyDomain domain) const {
 | 
| +  if (waiting_for_initial_policy_fetch_) {
 | 
| +    return false;
 | 
| +  }
 | 
|    if (domain == POLICY_DOMAIN_CHROME) {
 | 
|      return store_->is_initialized();
 | 
|    }
 | 
| @@ -84,6 +93,12 @@ void ActiveDirectoryPolicyManager::OnStoreLoaded(
 | 
|      CloudPolicyStore* cloud_policy_store) {
 | 
|    DCHECK_EQ(store_.get(), cloud_policy_store);
 | 
|    PublishPolicy();
 | 
| +  if (fetch_ever_completed_) {
 | 
| +    // Policy is guaranteed to be up to date with the previous fetch result
 | 
| +    // because OnPolicyFetched() cancels any potentially running Load()
 | 
| +    // operations.
 | 
| +    CancelWaitForInitialPolicy(fetch_ever_succeeded_ /* success */);
 | 
| +  }
 | 
|  }
 | 
|  
 | 
|  void ActiveDirectoryPolicyManager::OnStoreError(
 | 
| @@ -93,12 +108,39 @@ void ActiveDirectoryPolicyManager::OnStoreError(
 | 
|    // complete on the ConfigurationPolicyProvider interface. Technically, this is
 | 
|    // only required on the first load, but doesn't hurt in any case.
 | 
|    PublishPolicy();
 | 
| +  if (fetch_ever_completed_) {
 | 
| +    CancelWaitForInitialPolicy(false /* success */);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void ActiveDirectoryPolicyManager::ForceTimeoutForTest() {
 | 
| +  DCHECK(initial_policy_timeout_.IsRunning());
 | 
| +  // Stop the timer to mimic what happens when a real timer fires, then invoke
 | 
| +  // the timer callback directly.
 | 
| +  initial_policy_timeout_.Stop();
 | 
| +  OnBlockingFetchTimeout();
 | 
|  }
 | 
|  
 | 
|  ActiveDirectoryPolicyManager::ActiveDirectoryPolicyManager(
 | 
|      const AccountId& account_id,
 | 
| +    base::TimeDelta initial_policy_fetch_timeout,
 | 
| +    base::OnceClosure exit_session,
 | 
|      std::unique_ptr<CloudPolicyStore> store)
 | 
| -    : account_id_(account_id), store_(std::move(store)) {}
 | 
| +    : account_id_(account_id),
 | 
| +      waiting_for_initial_policy_fetch_(
 | 
| +          !initial_policy_fetch_timeout.is_zero()),
 | 
| +      initial_policy_fetch_may_fail_(!initial_policy_fetch_timeout.is_max()),
 | 
| +      exit_session_(std::move(exit_session)),
 | 
| +      store_(std::move(store)) {
 | 
| +  // Delaying initialization complete is intended for user policy only.
 | 
| +  DCHECK(account_id != EmptyAccountId() || !waiting_for_initial_policy_fetch_);
 | 
| +  if (waiting_for_initial_policy_fetch_ && initial_policy_fetch_may_fail_) {
 | 
| +    initial_policy_timeout_.Start(
 | 
| +        FROM_HERE, initial_policy_fetch_timeout,
 | 
| +        base::Bind(&ActiveDirectoryPolicyManager::OnBlockingFetchTimeout,
 | 
| +                   weak_ptr_factory_.GetWeakPtr()));
 | 
| +  }
 | 
| +}
 | 
|  
 | 
|  void ActiveDirectoryPolicyManager::PublishPolicy() {
 | 
|    if (!store_->is_initialized()) {
 | 
| @@ -117,7 +159,7 @@ void ActiveDirectoryPolicyManager::PublishPolicy() {
 | 
|    UpdatePolicy(std::move(bundle));
 | 
|  }
 | 
|  
 | 
| -void ActiveDirectoryPolicyManager::DoFetch(
 | 
| +void ActiveDirectoryPolicyManager::DoPolicyFetch(
 | 
|      base::OnceCallback<void(bool success)> callback) {
 | 
|    chromeos::DBusThreadManager* thread_manager =
 | 
|        chromeos::DBusThreadManager::Get();
 | 
| @@ -133,12 +175,65 @@ void ActiveDirectoryPolicyManager::DoFetch(
 | 
|  }
 | 
|  
 | 
|  void ActiveDirectoryPolicyManager::OnPolicyFetched(bool success) {
 | 
| -  if (!success) {
 | 
| +  fetch_ever_completed_ = true;
 | 
| +  if (success) {
 | 
| +    fetch_ever_succeeded_ = true;
 | 
| +  } else {
 | 
|      LOG(ERROR) << "Active Directory policy fetch failed.";
 | 
| +    if (store_->is_initialized()) {
 | 
| +      CancelWaitForInitialPolicy(false /* success */);
 | 
| +    }
 | 
|    }
 | 
| -  // Load independently of success or failure to keep up to date with whatever
 | 
| -  // has happened on the authpolicyd / session manager side.
 | 
| +  // Load independently of success or failure to keep in sync with the state in
 | 
| +  // session manager. This cancels any potentially running Load() operations
 | 
| +  // thus it is guaranteed that at the next OnStoreLoaded() invocation the
 | 
| +  // policy is up-to-date with what was fetched.
 | 
|    store_->Load();
 | 
|  }
 | 
|  
 | 
| +void ActiveDirectoryPolicyManager::OnBlockingFetchTimeout() {
 | 
| +  DCHECK(waiting_for_initial_policy_fetch_);
 | 
| +  LOG(WARNING) << "Timed out while waiting for the policy fetch. "
 | 
| +               << "The session will start with the cached policy.";
 | 
| +  CancelWaitForInitialPolicy(false);
 | 
| +}
 | 
| +
 | 
| +void ActiveDirectoryPolicyManager::CancelWaitForInitialPolicy(bool success) {
 | 
| +  if (!waiting_for_initial_policy_fetch_)
 | 
| +    return;
 | 
| +
 | 
| +  initial_policy_timeout_.Stop();
 | 
| +
 | 
| +  // If the conditions to continue profile initialization are not met, the user
 | 
| +  // session is exited and initialization is not set as completed.
 | 
| +  // TODO(tnagel): Maybe add code to retry policy fetch?
 | 
| +  if (!store_->has_policy()) {
 | 
| +    // If there's no policy at all (not even cached) the user session must not
 | 
| +    // continue.
 | 
| +    LOG(ERROR) << "Policy could not be obtained. "
 | 
| +               << "Aborting profile initialization";
 | 
| +    // Prevent duplicate exit session calls.
 | 
| +    if (exit_session_) {
 | 
| +      std::move(exit_session_).Run();
 | 
| +    }
 | 
| +    return;
 | 
| +  }
 | 
| +  if (!success && !initial_policy_fetch_may_fail_) {
 | 
| +    LOG(ERROR) << "Policy fetch failed for the user. "
 | 
| +               << "Aborting profile initialization";
 | 
| +    // Prevent duplicate exit session calls.
 | 
| +    if (exit_session_) {
 | 
| +      std::move(exit_session_).Run();
 | 
| +    }
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  // Set initialization complete.
 | 
| +  waiting_for_initial_policy_fetch_ = false;
 | 
| +
 | 
| +  // Publish policy (even though it hasn't changed) in order to signal load
 | 
| +  // complete on the ConfigurationPolicyProvider interface.
 | 
| +  PublishPolicy();
 | 
| +}
 | 
| +
 | 
|  }  // namespace policy
 | 
| 
 |