1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "content/browser/idle/idle_manager_impl.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "content/public/browser/browser_context.h"
12 #include "content/public/browser/idle_manager.h"
13 #include "content/public/browser/permission_controller.h"
14 #include "content/public/browser/permission_type.h"
15 #include "ui/base/idle/idle.h"
16 #include "url/gurl.h"
17 #include "url/origin.h"
18 
19 namespace content {
20 
21 namespace {
22 
23 using blink::mojom::IdleManagerError;
24 using blink::mojom::IdleState;
25 using blink::mojom::PermissionStatus;
26 
27 constexpr base::TimeDelta kPollInterval = base::TimeDelta::FromSeconds(1);
28 
29 constexpr base::TimeDelta kMinimumThreshold = base::TimeDelta::FromSeconds(60);
30 
31 // Default provider implementation. Everything is delegated to
32 // ui::CalculateIdleTime and ui::CheckIdleStateIsLocked.
33 class DefaultIdleProvider : public IdleManager::IdleTimeProvider {
34  public:
35   DefaultIdleProvider() = default;
36   ~DefaultIdleProvider() override = default;
37 
CalculateIdleTime()38   base::TimeDelta CalculateIdleTime() override {
39     return base::TimeDelta::FromSeconds(ui::CalculateIdleTime());
40   }
41 
CheckIdleStateIsLocked()42   bool CheckIdleStateIsLocked() override {
43     return ui::CheckIdleStateIsLocked();
44   }
45 };
46 
IdleTimeToIdleState(bool locked,base::TimeDelta idle_time,base::TimeDelta idle_threshold)47 blink::mojom::IdleStatePtr IdleTimeToIdleState(bool locked,
48                                                base::TimeDelta idle_time,
49                                                base::TimeDelta idle_threshold) {
50   blink::mojom::UserIdleState user;
51   if (idle_time >= idle_threshold)
52     user = blink::mojom::UserIdleState::kIdle;
53   else
54     user = blink::mojom::UserIdleState::kActive;
55 
56   blink::mojom::ScreenIdleState screen;
57   if (locked)
58     screen = blink::mojom::ScreenIdleState::kLocked;
59   else
60     screen = blink::mojom::ScreenIdleState::kUnlocked;
61 
62   return IdleState::New(user, screen);
63 }
64 
65 }  // namespace
66 
IdleManagerImpl(BrowserContext * browser_context)67 IdleManagerImpl::IdleManagerImpl(BrowserContext* browser_context)
68     : idle_time_provider_(new DefaultIdleProvider()),
69       browser_context_(browser_context) {}
70 
~IdleManagerImpl()71 IdleManagerImpl::~IdleManagerImpl() {
72   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
73 
74   while (!monitors_.empty()) {
75     IdleMonitor* monitor = monitors_.head()->value();
76     monitor->RemoveFromList();
77     delete monitor;
78   }
79 }
80 
CreateService(mojo::PendingReceiver<blink::mojom::IdleManager> receiver,const url::Origin & origin)81 void IdleManagerImpl::CreateService(
82     mojo::PendingReceiver<blink::mojom::IdleManager> receiver,
83     const url::Origin& origin) {
84   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
85 
86   receivers_.Add(this, std::move(receiver), origin);
87 }
88 
SetIdleOverride(blink::mojom::UserIdleState user_state,blink::mojom::ScreenIdleState screen_state)89 void IdleManagerImpl::SetIdleOverride(
90     blink::mojom::UserIdleState user_state,
91     blink::mojom::ScreenIdleState screen_state) {
92   state_override_ = IdleState::New(user_state, screen_state);
93   UpdateIdleState();
94 }
95 
ClearIdleOverride()96 void IdleManagerImpl::ClearIdleOverride() {
97   state_override_ = nullptr;
98   UpdateIdleState();
99 }
100 
AddMonitor(base::TimeDelta threshold,mojo::PendingRemote<blink::mojom::IdleMonitor> monitor_remote,AddMonitorCallback callback)101 void IdleManagerImpl::AddMonitor(
102     base::TimeDelta threshold,
103     mojo::PendingRemote<blink::mojom::IdleMonitor> monitor_remote,
104     AddMonitorCallback callback) {
105   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
106   if (threshold < kMinimumThreshold) {
107     receivers_.ReportBadMessage("Minimum threshold is 1 minute.");
108     return;
109   }
110 
111   const url::Origin& origin = receivers_.current_context();
112   if (!HasPermission(origin)) {
113     std::move(callback).Run(IdleManagerError::kPermissionDisabled, nullptr);
114     return;
115   }
116 
117   blink::mojom::IdleStatePtr current_state = CheckIdleState(threshold);
118   auto response_state = current_state->Clone();
119   auto monitor = std::make_unique<IdleMonitor>(
120       std::move(monitor_remote), std::move(current_state), threshold);
121 
122   // This unretained reference is safe because IdleManagerImpl owns all
123   // IdleMonitor instances.
124   monitor->SetErrorHandler(
125       base::BindOnce(&IdleManagerImpl::RemoveMonitor, base::Unretained(this)));
126 
127   monitors_.Append(monitor.release());
128 
129   StartPolling();
130 
131   std::move(callback).Run(IdleManagerError::kSuccess,
132                           std::move(response_state));
133 }
134 
HasPermission(const url::Origin & origin)135 bool IdleManagerImpl::HasPermission(const url::Origin& origin) {
136   PermissionController* permission_controller =
137       BrowserContext::GetPermissionController(browser_context_);
138   DCHECK(permission_controller);
139   PermissionStatus status = permission_controller->GetPermissionStatus(
140       PermissionType::IDLE_DETECTION, origin.GetURL(), origin.GetURL());
141   return status == PermissionStatus::GRANTED;
142 }
143 
RemoveMonitor(IdleMonitor * monitor)144 void IdleManagerImpl::RemoveMonitor(IdleMonitor* monitor) {
145   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
146 
147   monitor->RemoveFromList();
148   delete monitor;
149 
150   if (monitors_.empty()) {
151     StopPolling();
152   }
153 }
154 
SetIdleTimeProviderForTest(std::unique_ptr<IdleTimeProvider> idle_time_provider)155 void IdleManagerImpl::SetIdleTimeProviderForTest(
156     std::unique_ptr<IdleTimeProvider> idle_time_provider) {
157   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
158   idle_time_provider_ = std::move(idle_time_provider);
159 }
160 
IsPollingForTest()161 bool IdleManagerImpl::IsPollingForTest() {
162   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
163   return poll_timer_.IsRunning();
164 }
165 
StartPolling()166 void IdleManagerImpl::StartPolling() {
167   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
168   if (!poll_timer_.IsRunning()) {
169     poll_timer_.Start(FROM_HERE, kPollInterval,
170                       base::BindRepeating(&IdleManagerImpl::UpdateIdleState,
171                                           base::Unretained(this)));
172   }
173 }
174 
StopPolling()175 void IdleManagerImpl::StopPolling() {
176   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
177   poll_timer_.Stop();
178 }
179 
CheckIdleState(base::TimeDelta threshold)180 blink::mojom::IdleStatePtr IdleManagerImpl::CheckIdleState(
181     base::TimeDelta threshold) {
182   if (state_override_) {
183     return state_override_->Clone();
184   }
185   base::TimeDelta idle_time = idle_time_provider_->CalculateIdleTime();
186   bool locked = idle_time_provider_->CheckIdleStateIsLocked();
187 
188   return IdleTimeToIdleState(locked, idle_time, threshold);
189 }
190 
UpdateIdleState()191 void IdleManagerImpl::UpdateIdleState() {
192   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
193 
194   for (auto* node = monitors_.head(); node != monitors_.end();
195        node = node->next()) {
196     IdleMonitor* monitor = node->value();
197     monitor->SetLastState(CheckIdleState(monitor->threshold()));
198   }
199 }
200 
201 }  // namespace content
202