1 // Copyright 2019 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 "chrome/browser/chromeos/child_accounts/time_limits/app_types.h"
6 
7 #include <algorithm>
8 
9 #include "base/check_op.h"
10 #include "base/notreached.h"
11 
12 namespace chromeos {
13 
14 namespace app_time {
15 
16 namespace {
17 
AppTypeToString(apps::mojom::AppType app_type)18 std::string AppTypeToString(apps::mojom::AppType app_type) {
19   switch (app_type) {
20     case apps::mojom::AppType::kUnknown:
21       return "Unknown";
22     case apps::mojom::AppType::kArc:
23       return "Arc";
24     case apps::mojom::AppType::kWeb:
25       return "Web";
26     case apps::mojom::AppType::kExtension:
27       return "Extension";
28     case apps::mojom::AppType::kBuiltIn:
29       return "Built in";
30     case apps::mojom::AppType::kCrostini:
31       return "Crostini";
32     case apps::mojom::AppType::kMacOs:
33       return "Mac OS";
34     case apps::mojom::AppType::kPluginVm:
35       return "Plugin VM";
36     case apps::mojom::AppType::kLacros:
37       return "LaCrOS";
38     case apps::mojom::AppType::kRemote:
39       return "Remote";
40     case apps::mojom::AppType::kBorealis:
41       return "Borealis";
42   }
43   NOTREACHED();
44 }
45 
46 // static
CanMerge(const AppActivity::ActiveTime & t1,const AppActivity::ActiveTime & t2)47 bool CanMerge(const AppActivity::ActiveTime& t1,
48               const AppActivity::ActiveTime& t2) {
49   if (t1.active_from() <= t2.active_from() &&
50       t1.active_to() >=
51           t2.active_from() -
52               AppActivity::ActiveTime::kActiveTimeMergePrecision) {
53     return true;
54   }
55 
56   if (t2.active_from() <= t1.active_from() &&
57       t2.active_to() >=
58           t1.active_from() -
59               AppActivity::ActiveTime::kActiveTimeMergePrecision) {
60     return true;
61   }
62   return false;
63 }
64 
65 }  // namespace
66 
AppId(apps::mojom::AppType app_type,const std::string & app_id)67 AppId::AppId(apps::mojom::AppType app_type, const std::string& app_id)
68     : app_type_(app_type), app_id_(app_id) {
69   DCHECK(!app_id.empty());
70 }
71 
72 AppId::AppId(const AppId&) = default;
73 
74 AppId& AppId::operator=(const AppId&) = default;
75 
76 AppId::AppId(AppId&&) = default;
77 
78 AppId& AppId::operator=(AppId&&) = default;
79 
80 AppId::~AppId() = default;
81 
operator ==(const AppId & rhs) const82 bool AppId::operator==(const AppId& rhs) const {
83   return app_type_ == rhs.app_type() && app_id_ == rhs.app_id();
84 }
85 
operator !=(const AppId & rhs) const86 bool AppId::operator!=(const AppId& rhs) const {
87   return !(*this == rhs);
88 }
89 
operator <(const AppId & rhs) const90 bool AppId::operator<(const AppId& rhs) const {
91   return app_id_ < rhs.app_id();
92 }
93 
operator <<(std::ostream & out,const AppId & id)94 std::ostream& operator<<(std::ostream& out, const AppId& id) {
95   return out << " [" << AppTypeToString(id.app_type()) << " : " << id.app_id()
96              << "]";
97 }
98 
PauseAppInfo(const AppId & app,base::TimeDelta limit,bool show_dialog)99 PauseAppInfo::PauseAppInfo(const AppId& app,
100                            base::TimeDelta limit,
101                            bool show_dialog)
102     : app_id(app), daily_limit(limit), show_pause_dialog(show_dialog) {}
103 
AppLimit(AppRestriction restriction,base::Optional<base::TimeDelta> daily_limit,base::Time last_updated)104 AppLimit::AppLimit(AppRestriction restriction,
105                    base::Optional<base::TimeDelta> daily_limit,
106                    base::Time last_updated)
107     : restriction_(restriction),
108       daily_limit_(daily_limit),
109       last_updated_(last_updated) {
110   DCHECK_EQ(restriction_ == AppRestriction::kBlocked,
111             daily_limit_ == base::nullopt);
112   DCHECK(daily_limit_ == base::nullopt ||
113          daily_limit >= base::TimeDelta::FromHours(0));
114   DCHECK(daily_limit_ == base::nullopt ||
115          daily_limit <= base::TimeDelta::FromHours(24));
116 }
117 
118 AppLimit::AppLimit(const AppLimit&) = default;
119 
120 AppLimit& AppLimit::operator=(const AppLimit&) = default;
121 
122 AppLimit::AppLimit(AppLimit&&) = default;
123 
124 AppLimit& AppLimit::operator=(AppLimit&&) = default;
125 
126 AppLimit::~AppLimit() = default;
127 
128 // static
Merge(const ActiveTime & t1,const ActiveTime & t2)129 base::Optional<AppActivity::ActiveTime> AppActivity::ActiveTime::Merge(
130     const ActiveTime& t1,
131     const ActiveTime& t2) {
132   if (!CanMerge(t1, t2))
133     return base::nullopt;
134 
135   base::Time active_from = std::min(t1.active_from(), t2.active_from());
136   base::Time active_to = std::max(t1.active_to(), t2.active_to());
137   return AppActivity::ActiveTime(active_from, active_to);
138 }
139 
140 // static
141 const base::TimeDelta AppActivity::ActiveTime::kActiveTimeMergePrecision =
142     base::TimeDelta::FromSeconds(1);
143 
ActiveTime(base::Time start,base::Time end)144 AppActivity::ActiveTime::ActiveTime(base::Time start, base::Time end)
145     : active_from_(start), active_to_(end) {
146   DCHECK_GT(active_to_, active_from_);
147 }
148 
149 AppActivity::ActiveTime::ActiveTime(const AppActivity::ActiveTime& rhs) =
150     default;
151 
152 AppActivity::ActiveTime& AppActivity::ActiveTime::operator=(
153     const AppActivity::ActiveTime& rhs) = default;
154 
operator ==(const ActiveTime & rhs) const155 bool AppActivity::ActiveTime::operator==(const ActiveTime& rhs) const {
156   return active_from_ == rhs.active_from() && active_to_ == rhs.active_to();
157 }
158 
operator !=(const ActiveTime & rhs) const159 bool AppActivity::ActiveTime::operator!=(const ActiveTime& rhs) const {
160   return !(*this == rhs);
161 }
162 
Contains(base::Time timestamp) const163 bool AppActivity::ActiveTime::Contains(base::Time timestamp) const {
164   return active_from_ < timestamp && active_to_ > timestamp;
165 }
166 
IsEarlierThan(base::Time timestamp) const167 bool AppActivity::ActiveTime::IsEarlierThan(base::Time timestamp) const {
168   return active_to_ <= timestamp;
169 }
170 
IsLaterThan(base::Time timestamp) const171 bool AppActivity::ActiveTime::IsLaterThan(base::Time timestamp) const {
172   return active_from_ >= timestamp;
173 }
174 
set_active_from(base::Time active_from)175 void AppActivity::ActiveTime::set_active_from(base::Time active_from) {
176   DCHECK_GT(active_to_, active_from);
177   active_from_ = active_from;
178 }
179 
set_active_to(base::Time active_to)180 void AppActivity::ActiveTime::set_active_to(base::Time active_to) {
181   DCHECK_GT(active_to, active_from_);
182   active_to_ = active_to;
183 }
184 
AppActivity(AppState app_state)185 AppActivity::AppActivity(AppState app_state)
186     : app_state_(app_state),
187       running_active_time_(base::TimeDelta::FromSeconds(0)),
188       last_updated_time_ticks_(base::TimeTicks::Now()) {}
AppActivity(AppState app_state,base::TimeDelta running_active_time)189 AppActivity::AppActivity(AppState app_state,
190                          base::TimeDelta running_active_time)
191     : app_state_(app_state),
192       running_active_time_(running_active_time),
193       last_updated_time_ticks_(base::TimeTicks::Now()) {}
194 AppActivity::AppActivity(const AppActivity&) = default;
195 AppActivity& AppActivity::operator=(const AppActivity&) = default;
196 AppActivity::AppActivity(AppActivity&&) = default;
197 AppActivity& AppActivity::operator=(AppActivity&&) = default;
198 AppActivity::~AppActivity() = default;
199 
SetAppState(AppState app_state)200 void AppActivity::SetAppState(AppState app_state) {
201   app_state_ = app_state;
202   CaptureOngoingActivity(base::Time::Now());
203   if (!is_active_)
204     last_updated_time_ticks_ = base::TimeTicks::Now();
205 }
206 
SetAppActive(base::Time timestamp)207 void AppActivity::SetAppActive(base::Time timestamp) {
208   DCHECK(!is_active_);
209   DCHECK(app_state_ == AppState::kAvailable ||
210          app_state_ == AppState::kAlwaysAvailable);
211   is_active_ = true;
212   last_updated_time_ticks_ = base::TimeTicks::Now();
213 }
214 
SetAppInactive(base::Time timestamp)215 void AppActivity::SetAppInactive(base::Time timestamp) {
216   if (!is_active_)
217     return;
218   CaptureOngoingActivity(timestamp);
219   is_active_ = false;
220 }
221 
ResetRunningActiveTime(base::Time timestamp)222 void AppActivity::ResetRunningActiveTime(base::Time timestamp) {
223   CaptureOngoingActivity(timestamp);
224   running_active_time_ = base::TimeDelta::FromMinutes(0);
225 }
226 
RunningActiveTime() const227 base::TimeDelta AppActivity::RunningActiveTime() const {
228   if (!is_active_)
229     return running_active_time_;
230 
231   return running_active_time_ +
232          (base::TimeTicks::Now() - last_updated_time_ticks_);
233 }
234 
CaptureOngoingActivity(base::Time timestamp)235 void AppActivity::CaptureOngoingActivity(base::Time timestamp) {
236   if (!is_active_)
237     return;
238 
239   // Log the active time before the until the reset.
240   base::TimeTicks now = base::TimeTicks::Now();
241   base::TimeDelta active_time = now - last_updated_time_ticks_;
242 
243   // Update |running_active_time_|.
244   running_active_time_ += active_time;
245 
246   base::Time start_time = timestamp - active_time;
247 
248   // Timestamps can be equal if SetAppInactive() is called directly after
249   // SetAppState(). Happens in tests.
250   DCHECK_GE(timestamp, start_time);
251   if (timestamp > start_time)
252     active_times_.push_back(ActiveTime(start_time, timestamp));
253 
254   last_updated_time_ticks_ = now;
255 }
256 
TakeActiveTimes()257 std::vector<AppActivity::ActiveTime> AppActivity::TakeActiveTimes() {
258   return std::move(active_times_);
259 }
260 
261 }  // namespace app_time
262 }  // namespace chromeos
263