| Index: remoting/host/plugin/daemon_controller_win.cc
 | 
| diff --git a/remoting/host/plugin/daemon_controller_win.cc b/remoting/host/plugin/daemon_controller_win.cc
 | 
| index 5325c22b1d7a525d0d7ebdcebbcdb0801d0063a2..f194429d046126fed52364d22628666565a8f275 100644
 | 
| --- a/remoting/host/plugin/daemon_controller_win.cc
 | 
| +++ b/remoting/host/plugin/daemon_controller_win.cc
 | 
| @@ -28,6 +28,7 @@
 | 
|  #include "remoting/base/scoped_sc_handle_win.h"
 | 
|  #include "remoting/host/branding.h"
 | 
|  #include "remoting/host/plugin/daemon_installer_win.h"
 | 
| +#include "remoting/host/usage_stats_consent.h"
 | 
|  
 | 
|  // MIDL-generated declarations and definitions.
 | 
|  #include "remoting/host/elevated_controller.h"
 | 
| @@ -77,12 +78,15 @@ class DaemonControllerWin : public remoting::DaemonController {
 | 
|    virtual void GetConfig(const GetConfigCallback& callback) OVERRIDE;
 | 
|    virtual void SetConfigAndStart(
 | 
|        scoped_ptr<base::DictionaryValue> config,
 | 
| -      const CompletionCallback& done_callback) OVERRIDE;
 | 
| +      bool consent,
 | 
| +      const CompletionCallback& done) OVERRIDE;
 | 
|    virtual void UpdateConfig(scoped_ptr<base::DictionaryValue> config,
 | 
|                              const CompletionCallback& done_callback) OVERRIDE;
 | 
|    virtual void Stop(const CompletionCallback& done_callback) OVERRIDE;
 | 
|    virtual void SetWindow(void* window_handle) OVERRIDE;
 | 
|    virtual void GetVersion(const GetVersionCallback& done_callback) OVERRIDE;
 | 
| +  virtual void GetUsageStatsConsent(
 | 
| +      const GetUsageStatsConsentCallback& done) OVERRIDE;
 | 
|  
 | 
|   private:
 | 
|    // Activates an unprivileged instance of the daemon controller and caches it.
 | 
| @@ -97,7 +101,8 @@ class DaemonControllerWin : public remoting::DaemonController {
 | 
|    // Procedes with the daemon configuration if the installation succeeded,
 | 
|    // otherwise reports the error.
 | 
|    void OnInstallationComplete(scoped_ptr<base::DictionaryValue> config,
 | 
| -                              const CompletionCallback& done_callback,
 | 
| +                              bool consent,
 | 
| +                              const CompletionCallback& done,
 | 
|                                HRESULT result);
 | 
|  
 | 
|    // Opens the Chromoting service returning its handle in |service_out|.
 | 
| @@ -117,18 +122,23 @@ class DaemonControllerWin : public remoting::DaemonController {
 | 
|    // the context of |worker_thread_|;
 | 
|    void DoGetConfig(const GetConfigCallback& callback);
 | 
|    void DoInstallAsNeededAndStart(scoped_ptr<base::DictionaryValue> config,
 | 
| +                                 bool consent,
 | 
|                                   const CompletionCallback& done_callback);
 | 
|    void DoSetConfigAndStart(scoped_ptr<base::DictionaryValue> config,
 | 
| -                           const CompletionCallback& done_callback);
 | 
| +                           bool consent,
 | 
| +                           const CompletionCallback& done);
 | 
|    void DoUpdateConfig(scoped_ptr<base::DictionaryValue> config,
 | 
|                        const CompletionCallback& done_callback);
 | 
|    void DoStop(const CompletionCallback& done_callback);
 | 
|    void DoSetWindow(void* window_handle);
 | 
|    void DoGetVersion(const GetVersionCallback& callback);
 | 
| +  void DoGetUsageStatsConsent(
 | 
| +      const GetUsageStatsConsentCallback& done);
 | 
|  
 | 
| -  // |control_| holds a reference to an instance of the daemon controller
 | 
| -  // to prevent a UAC prompt on every operation.
 | 
| +  // |control_| and |control2_| hold references to an instance of the daemon
 | 
| +  // controller to prevent a UAC prompt on every operation.
 | 
|    ScopedComPtr<IDaemonControl> control_;
 | 
| +  ScopedComPtr<IDaemonControl2> control2_;
 | 
|  
 | 
|    // True if |control_| holds a reference to an elevated instance of the daemon
 | 
|    // controller.
 | 
| @@ -221,11 +231,12 @@ void DaemonControllerWin::GetConfig(const GetConfigCallback& callback) {
 | 
|  
 | 
|  void DaemonControllerWin::SetConfigAndStart(
 | 
|      scoped_ptr<base::DictionaryValue> config,
 | 
| -    const CompletionCallback& done_callback) {
 | 
| +    bool consent,
 | 
| +    const CompletionCallback& done) {
 | 
|    worker_thread_.message_loop_proxy()->PostTask(
 | 
|        FROM_HERE, base::Bind(
 | 
|            &DaemonControllerWin::DoInstallAsNeededAndStart,
 | 
| -          base::Unretained(this), base::Passed(&config), done_callback));
 | 
| +          base::Unretained(this), base::Passed(&config), consent, done));
 | 
|  }
 | 
|  
 | 
|  void DaemonControllerWin::UpdateConfig(
 | 
| @@ -258,6 +269,14 @@ void DaemonControllerWin::GetVersion(const GetVersionCallback& callback) {
 | 
|                   base::Unretained(this), callback));
 | 
|  }
 | 
|  
 | 
| +void DaemonControllerWin::GetUsageStatsConsent(
 | 
| +    const GetUsageStatsConsentCallback& done) {
 | 
| +  worker_thread_.message_loop_proxy()->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&DaemonControllerWin::DoGetUsageStatsConsent,
 | 
| +                 base::Unretained(this), done));
 | 
| +}
 | 
| +
 | 
|  HRESULT DaemonControllerWin::ActivateController() {
 | 
|    DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread());
 | 
|  
 | 
| @@ -273,6 +292,9 @@ HRESULT DaemonControllerWin::ActivateController() {
 | 
|      if (FAILED(hr)) {
 | 
|        return hr;
 | 
|      }
 | 
| +
 | 
| +    // Ignore the error. IID_IDaemonControl2 is optional.
 | 
| +    control_.QueryInterface(IID_IDaemonControl2, control2_.ReceiveVoid());
 | 
|    }
 | 
|  
 | 
|    return S_OK;
 | 
| @@ -302,6 +324,9 @@ HRESULT DaemonControllerWin::ActivateElevatedController() {
 | 
|        return hr;
 | 
|      }
 | 
|  
 | 
| +    // Ignore the error. IID_IDaemonControl2 is optional.
 | 
| +    control_.QueryInterface(IID_IDaemonControl2, control2_.ReceiveVoid());
 | 
| +
 | 
|      // Note that we hold a reference to an elevated instance now.
 | 
|      control_is_elevated_ = true;
 | 
|  
 | 
| @@ -320,22 +345,24 @@ void DaemonControllerWin::ReleaseController() {
 | 
|    DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread());
 | 
|  
 | 
|    control_.Release();
 | 
| +  control2_.Release();
 | 
|    release_timer_.reset();
 | 
|    control_is_elevated_ = false;
 | 
|  }
 | 
|  
 | 
|  void DaemonControllerWin::OnInstallationComplete(
 | 
|      scoped_ptr<base::DictionaryValue> config,
 | 
| -    const CompletionCallback& done_callback,
 | 
| +    bool consent,
 | 
| +    const CompletionCallback& done,
 | 
|      HRESULT result) {
 | 
|    DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread());
 | 
|  
 | 
|    if (SUCCEEDED(result)) {
 | 
| -    DoSetConfigAndStart(config.Pass(), done_callback);
 | 
| +    DoSetConfigAndStart(config.Pass(), consent, done);
 | 
|    } else {
 | 
|      LOG(ERROR) << "Failed to install the Chromoting Host "
 | 
|                 << "(error: 0x" << std::hex << result << std::dec << ").";
 | 
| -    done_callback.Run(HResultToAsyncResult(result));
 | 
| +    done.Run(HResultToAsyncResult(result));
 | 
|    }
 | 
|  
 | 
|    DCHECK(installer_.get() != NULL);
 | 
| @@ -453,13 +480,14 @@ void DaemonControllerWin::DoGetConfig(const GetConfigCallback& callback) {
 | 
|  
 | 
|  void DaemonControllerWin::DoInstallAsNeededAndStart(
 | 
|      scoped_ptr<base::DictionaryValue> config,
 | 
| -    const CompletionCallback& done_callback) {
 | 
| +    bool consent,
 | 
| +    const CompletionCallback& done) {
 | 
|    DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread());
 | 
|  
 | 
|    // Configure and start the Daemon Controller if it is installed already.
 | 
|    HRESULT hr = ActivateElevatedController();
 | 
|    if (SUCCEEDED(hr)) {
 | 
| -    DoSetConfigAndStart(config.Pass(), done_callback);
 | 
| +    DoSetConfigAndStart(config.Pass(), consent, done);
 | 
|      return;
 | 
|    }
 | 
|  
 | 
| @@ -470,7 +498,8 @@ void DaemonControllerWin::DoInstallAsNeededAndStart(
 | 
|          base::Bind(&DaemonControllerWin::OnInstallationComplete,
 | 
|                     base::Unretained(this),
 | 
|                     base::Passed(&config),
 | 
| -                   done_callback));
 | 
| +                   consent,
 | 
| +                   done));
 | 
|      if (installer.get()) {
 | 
|        DCHECK(!installer_.get());
 | 
|        installer_ = installer.Pass();
 | 
| @@ -479,45 +508,55 @@ void DaemonControllerWin::DoInstallAsNeededAndStart(
 | 
|    } else {
 | 
|      LOG(ERROR) << "Failed to initiate the Chromoting Host installation "
 | 
|                 << "(error: 0x" << std::hex << hr << std::dec << ").";
 | 
| -    done_callback.Run(HResultToAsyncResult(hr));
 | 
| +    done.Run(HResultToAsyncResult(hr));
 | 
|    }
 | 
|  }
 | 
|  
 | 
|  void DaemonControllerWin::DoSetConfigAndStart(
 | 
|      scoped_ptr<base::DictionaryValue> config,
 | 
| -    const CompletionCallback& done_callback) {
 | 
| +    bool consent,
 | 
| +    const CompletionCallback& done) {
 | 
|    DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread());
 | 
|  
 | 
|    HRESULT hr = ActivateElevatedController();
 | 
|    if (FAILED(hr)) {
 | 
| -    done_callback.Run(HResultToAsyncResult(hr));
 | 
| +    done.Run(HResultToAsyncResult(hr));
 | 
|      return;
 | 
|    }
 | 
|  
 | 
| +  // Record the user's consent.
 | 
| +  if (control2_.get()) {
 | 
| +    hr = control2_->SetUsageStatsConsent(consent);
 | 
| +    if (FAILED(hr)) {
 | 
| +      done.Run(HResultToAsyncResult(hr));
 | 
| +      return;
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
|    // Set the configuration.
 | 
|    ScopedBstr config_str(NULL);
 | 
|    ConfigToString(*config, &config_str);
 | 
|    if (config_str == NULL) {
 | 
| -    done_callback.Run(HResultToAsyncResult(E_OUTOFMEMORY));
 | 
| +    done.Run(HResultToAsyncResult(E_OUTOFMEMORY));
 | 
|      return;
 | 
|    }
 | 
|  
 | 
|    hr = control_->SetOwnerWindow(
 | 
|        reinterpret_cast<LONG_PTR>(GetTopLevelWindow(window_handle_)));
 | 
|    if (FAILED(hr)) {
 | 
| -    done_callback.Run(HResultToAsyncResult(hr));
 | 
| +    done.Run(HResultToAsyncResult(hr));
 | 
|      return;
 | 
|    }
 | 
|  
 | 
|    hr = control_->SetConfig(config_str);
 | 
|    if (FAILED(hr)) {
 | 
| -    done_callback.Run(HResultToAsyncResult(hr));
 | 
| +    done.Run(HResultToAsyncResult(hr));
 | 
|      return;
 | 
|    }
 | 
|  
 | 
|    // Start daemon.
 | 
|    hr = control_->StartDaemon();
 | 
| -  done_callback.Run(HResultToAsyncResult(hr));
 | 
| +  done.Run(HResultToAsyncResult(hr));
 | 
|  }
 | 
|  
 | 
|  void DaemonControllerWin::DoUpdateConfig(
 | 
| @@ -592,6 +631,29 @@ void DaemonControllerWin::DoGetVersion(const GetVersionCallback& callback) {
 | 
|        string16(static_cast<BSTR>(version), version.Length())));
 | 
|  }
 | 
|  
 | 
| +void DaemonControllerWin::DoGetUsageStatsConsent(
 | 
| +    const GetUsageStatsConsentCallback& done) {
 | 
| +  DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread());
 | 
| +
 | 
| +  // Activate the Daemon Controller and see if it supports |IDaemonControl2|.
 | 
| +  HRESULT hr = ActivateController();
 | 
| +  if (FAILED(hr) || control2_.get() == NULL) {
 | 
| +    done.Run(false, false, false);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  // Get the recorded user's consent.
 | 
| +  BOOL allowed;
 | 
| +  BOOL set_by_policy;
 | 
| +  hr = control2_->GetUsageStatsConsent(&allowed, &set_by_policy);
 | 
| +  if (FAILED(hr)) {
 | 
| +    done.Run(true, false, false);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  done.Run(true, !!allowed, !!set_by_policy);
 | 
| +}
 | 
| +
 | 
|  }  // namespace
 | 
|  
 | 
|  scoped_ptr<DaemonController> remoting::DaemonController::Create() {
 | 
| 
 |