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_activity_registry.h"
6 
7 #include <algorithm>
8 
9 #include "base/logging.h"
10 #include "base/stl_util.h"
11 #include "base/time/default_tick_clock.h"
12 #include "base/values.h"
13 #include "chrome/browser/chromeos/child_accounts/time_limits/app_time_limit_utils.h"
14 #include "chrome/browser/chromeos/child_accounts/time_limits/app_time_limits_allowlist_policy_wrapper.h"
15 #include "chrome/browser/chromeos/child_accounts/time_limits/app_time_notification_delegate.h"
16 #include "chrome/browser/chromeos/child_accounts/time_limits/app_time_policy_helpers.h"
17 #include "chrome/browser/chromeos/child_accounts/time_limits/persisted_app_info.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/browser_list.h"
20 #include "chrome/browser/ui/browser_window.h"
21 #include "chrome/common/pref_names.h"
22 #include "components/policy/proto/device_management_backend.pb.h"
23 #include "components/prefs/pref_registry_simple.h"
24 #include "components/prefs/pref_service.h"
25 #include "components/prefs/scoped_user_pref_update.h"
26 #include "components/services/app_service/public/mojom/types.mojom.h"
27 
28 namespace chromeos {
29 namespace app_time {
30 
31 namespace {
32 
33 constexpr base::TimeDelta kFiveMinutes = base::TimeDelta::FromMinutes(5);
34 constexpr base::TimeDelta kOneMinute = base::TimeDelta::FromMinutes(1);
35 constexpr base::TimeDelta kZeroMinutes = base::TimeDelta::FromMinutes(0);
36 
AppStateForReporting(AppState state)37 enterprise_management::AppActivity::AppState AppStateForReporting(
38     AppState state) {
39   switch (state) {
40     case AppState::kAvailable:
41       return enterprise_management::AppActivity::DEFAULT;
42     case AppState::kAlwaysAvailable:
43       return enterprise_management::AppActivity::ALWAYS_AVAILABLE;
44     case AppState::kBlocked:
45       return enterprise_management::AppActivity::BLOCKED;
46     case AppState::kLimitReached:
47       return enterprise_management::AppActivity::LIMIT_REACHED;
48     case AppState::kUninstalled:
49       return enterprise_management::AppActivity::UNINSTALLED;
50     default:
51       return enterprise_management::AppActivity::UNKNOWN;
52   }
53 }
54 
55 }  // namespace
56 
TestApi(AppActivityRegistry * registry)57 AppActivityRegistry::TestApi::TestApi(AppActivityRegistry* registry)
58     : registry_(registry) {}
59 
60 AppActivityRegistry::TestApi::~TestApi() = default;
61 
GetAppLimit(const AppId & app_id) const62 const base::Optional<AppLimit>& AppActivityRegistry::TestApi::GetAppLimit(
63     const AppId& app_id) const {
64   DCHECK(base::Contains(registry_->activity_registry_, app_id));
65   return registry_->activity_registry_.at(app_id).limit;
66 }
67 
GetTimeLeft(const AppId & app_id) const68 base::Optional<base::TimeDelta> AppActivityRegistry::TestApi::GetTimeLeft(
69     const AppId& app_id) const {
70   return registry_->GetTimeLeftForApp(app_id);
71 }
72 
SaveAppActivity()73 void AppActivityRegistry::TestApi::SaveAppActivity() {
74   registry_->SaveAppActivity();
75 }
76 
SystemNotification(base::Optional<base::TimeDelta> app_time_limit,AppNotification app_notification)77 AppActivityRegistry::SystemNotification::SystemNotification(
78     base::Optional<base::TimeDelta> app_time_limit,
79     AppNotification app_notification)
80     : time_limit(app_time_limit), notification(app_notification) {}
81 
82 AppActivityRegistry::SystemNotification::SystemNotification(
83     const SystemNotification&) = default;
84 
85 AppActivityRegistry::SystemNotification&
86 AppActivityRegistry::SystemNotification::operator=(const SystemNotification&) =
87     default;
88 
89 AppActivityRegistry::AppDetails::AppDetails() = default;
90 
AppDetails(const AppActivity & activity)91 AppActivityRegistry::AppDetails::AppDetails(const AppActivity& activity)
92     : activity(activity) {}
93 
94 AppActivityRegistry::AppDetails::~AppDetails() = default;
95 
ResetTimeCheck()96 void AppActivityRegistry::AppDetails::ResetTimeCheck() {
97   activity.set_last_notification(AppNotification::kUnknown);
98   if (app_limit_timer)
99     app_limit_timer->AbandonAndStop();
100 }
101 
IsLimitReached() const102 bool AppActivityRegistry::AppDetails::IsLimitReached() const {
103   if (!limit.has_value())
104     return false;
105 
106   if (limit->restriction() != AppRestriction::kTimeLimit)
107     return false;
108 
109   DCHECK(limit->daily_limit());
110   if (limit->daily_limit() > activity.RunningActiveTime())
111     return false;
112 
113   return true;
114 }
115 
IsLimitEqual(const base::Optional<AppLimit> & another_limit) const116 bool AppActivityRegistry::AppDetails::IsLimitEqual(
117     const base::Optional<AppLimit>& another_limit) const {
118   if (limit.has_value() != another_limit.has_value())
119     return false;
120 
121   if (!limit.has_value())
122     return true;
123 
124   if (limit->restriction() == another_limit->restriction() &&
125       limit->daily_limit() == another_limit->daily_limit()) {
126     return true;
127   }
128 
129   return false;
130 }
131 
132 // static
RegisterProfilePrefs(PrefRegistrySimple * registry)133 void AppActivityRegistry::RegisterProfilePrefs(PrefRegistrySimple* registry) {
134   registry->RegisterListPref(prefs::kPerAppTimeLimitsAppActivities);
135   registry->RegisterInt64Pref(prefs::kPerAppTimeLimitsLastSuccessfulReportTime,
136                               0);
137   registry->RegisterInt64Pref(prefs::kPerAppTimeLimitsLatestLimitUpdateTime, 0);
138 }
139 
AppActivityRegistry(AppServiceWrapper * app_service_wrapper,AppTimeNotificationDelegate * notification_delegate,PrefService * pref_service)140 AppActivityRegistry::AppActivityRegistry(
141     AppServiceWrapper* app_service_wrapper,
142     AppTimeNotificationDelegate* notification_delegate,
143     PrefService* pref_service)
144     : pref_service_(pref_service),
145       app_service_wrapper_(app_service_wrapper),
146       notification_delegate_(notification_delegate),
147       save_data_to_pref_service_(base::DefaultTickClock::GetInstance()) {
148   DCHECK(app_service_wrapper_);
149   DCHECK(notification_delegate_);
150   DCHECK(pref_service_);
151 
152   if (ShouldCleanUpStoredPref())
153     CleanRegistry(base::Time::Now() - base::TimeDelta::FromDays(30));
154 
155   InitializeRegistryFromPref();
156 
157   save_data_to_pref_service_.Start(FROM_HERE, base::TimeDelta::FromMinutes(5),
158                                    this, &AppActivityRegistry::SaveAppActivity);
159 
160   app_service_wrapper_->AddObserver(this);
161 }
162 
~AppActivityRegistry()163 AppActivityRegistry::~AppActivityRegistry() {
164   app_service_wrapper_->RemoveObserver(this);
165 }
166 
OnAppInstalled(const AppId & app_id)167 void AppActivityRegistry::OnAppInstalled(const AppId& app_id) {
168   // App might be already present in registry, because we preserve info between
169   // sessions and app service does not. Make sure not to override cached state.
170   if (!base::Contains(activity_registry_, app_id)) {
171     Add(app_id);
172   } else {
173     activity_registry_.at(app_id).received_app_installed_ = true;
174 
175     // First send the system notifications for the application.
176     SendSystemNotificationsForApp(app_id);
177 
178     if (GetAppState(app_id) == AppState::kLimitReached) {
179       NotifyLimitReached(app_id, /* was_active */ false);
180     } else if (GetAppState(app_id) == AppState::kUninstalled) {
181       OnAppReinstalled(app_id);
182     }
183   }
184 }
185 
OnAppUninstalled(const AppId & app_id)186 void AppActivityRegistry::OnAppUninstalled(const AppId& app_id) {
187   // TODO(agawronska): Consider DCHECK instead of it. Not sure if there are
188   // legit cases when we might go out of sync with AppService.
189   if (base::Contains(activity_registry_, app_id))
190     SetAppState(app_id, AppState::kUninstalled);
191 }
192 
OnAppAvailable(const AppId & app_id)193 void AppActivityRegistry::OnAppAvailable(const AppId& app_id) {
194   if (!base::Contains(activity_registry_, app_id))
195     return;
196 
197   AppState prev_state = GetAppState(app_id);
198 
199   if (prev_state == AppState::kLimitReached)
200     return;
201 
202   // This may happen in the scenario where the application is uninstalled and
203   // reinstalled in the same session.
204   if (prev_state == AppState::kUninstalled) {
205     OnAppReinstalled(app_id);
206   }
207 
208   if (IsWebAppOrExtension(app_id) && app_id != GetChromeAppId() &&
209       base::Contains(activity_registry_, GetChromeAppId()) &&
210       GetAppState(app_id) == AppState::kBlocked) {
211     SetAppState(app_id, GetAppState(GetChromeAppId()));
212     return;
213   }
214 
215   SetAppState(app_id, AppState::kAvailable);
216 }
217 
OnAppBlocked(const AppId & app_id)218 void AppActivityRegistry::OnAppBlocked(const AppId& app_id) {
219   if (!base::Contains(activity_registry_, app_id))
220     return;
221 
222   if (GetAppState(app_id) == AppState::kBlocked)
223     return;
224 
225   SetAppState(app_id, AppState::kBlocked);
226 }
227 
OnAppActive(const AppId & app_id,aura::Window * window,base::Time timestamp)228 void AppActivityRegistry::OnAppActive(const AppId& app_id,
229                                       aura::Window* window,
230                                       base::Time timestamp) {
231   if (!base::Contains(activity_registry_, app_id))
232     return;
233 
234   if (app_id == GetChromeAppId())
235     return;
236 
237   AppDetails& app_details = activity_registry_[app_id];
238 
239   // We are notified that a paused app is active. Notify observers to pause it.
240   if (GetAppState(app_id) == AppState::kLimitReached) {
241     // If the window is in |app_details.paused_windows| then AppActivityRegistry
242     // has already notified its observers to pause it. Return.
243     if (base::Contains(app_details.paused_windows, window))
244       return;
245 
246     app_details.paused_windows.insert(window);
247     NotifyLimitReached(app_id, /* was_active */ true);
248     return;
249   }
250 
251   if (!IsAppAvailable(app_id))
252     return;
253 
254   std::set<aura::Window*>& active_windows = app_details.active_windows;
255 
256   if (base::Contains(active_windows, window))
257     return;
258 
259   active_windows.insert(window);
260 
261   // No need to set app as active if there were already active windows for the
262   // app
263   if (active_windows.size() > 1)
264     return;
265 
266   SetAppActive(app_id, timestamp);
267 }
268 
OnAppInactive(const AppId & app_id,aura::Window * window,base::Time timestamp)269 void AppActivityRegistry::OnAppInactive(const AppId& app_id,
270                                         aura::Window* window,
271                                         base::Time timestamp) {
272   if (!base::Contains(activity_registry_, app_id))
273     return;
274 
275   if (app_id == GetChromeAppId())
276     return;
277 
278   std::set<aura::Window*>& active_windows =
279       activity_registry_[app_id].active_windows;
280 
281   if (!base::Contains(active_windows, window))
282     return;
283 
284   active_windows.erase(window);
285   if (active_windows.size() > 0)
286     return;
287 
288   SetAppInactive(app_id, timestamp);
289 }
290 
OnAppDestroyed(const AppId & app_id,aura::Window * window,base::Time timestamp)291 void AppActivityRegistry::OnAppDestroyed(const AppId& app_id,
292                                          aura::Window* window,
293                                          base::Time timestamp) {
294   if (!base::Contains(activity_registry_, app_id))
295     return;
296 
297   if (app_id == GetChromeAppId())
298     return;
299 
300   AppDetails& app_details = activity_registry_.at(app_id);
301   if (base::Contains(app_details.paused_windows, window))
302     app_details.paused_windows.erase(window);
303 }
304 
IsAppInstalled(const AppId & app_id) const305 bool AppActivityRegistry::IsAppInstalled(const AppId& app_id) const {
306   if (base::Contains(activity_registry_, app_id))
307     return GetAppState(app_id) != AppState::kUninstalled;
308   return false;
309 }
310 
IsAppAvailable(const AppId & app_id) const311 bool AppActivityRegistry::IsAppAvailable(const AppId& app_id) const {
312   DCHECK(base::Contains(activity_registry_, app_id));
313   auto state = GetAppState(app_id);
314   return state == AppState::kAvailable || state == AppState::kAlwaysAvailable;
315 }
316 
IsAppBlocked(const AppId & app_id) const317 bool AppActivityRegistry::IsAppBlocked(const AppId& app_id) const {
318   DCHECK(base::Contains(activity_registry_, app_id));
319   return GetAppState(app_id) == AppState::kBlocked;
320 }
321 
IsAppTimeLimitReached(const AppId & app_id) const322 bool AppActivityRegistry::IsAppTimeLimitReached(const AppId& app_id) const {
323   DCHECK(base::Contains(activity_registry_, app_id));
324   return GetAppState(app_id) == AppState::kLimitReached;
325 }
326 
IsAppActive(const AppId & app_id) const327 bool AppActivityRegistry::IsAppActive(const AppId& app_id) const {
328   DCHECK(base::Contains(activity_registry_, app_id));
329   return activity_registry_.at(app_id).activity.is_active();
330 }
331 
IsAllowlistedApp(const AppId & app_id) const332 bool AppActivityRegistry::IsAllowlistedApp(const AppId& app_id) const {
333   DCHECK(base::Contains(activity_registry_, app_id));
334   return GetAppState(app_id) == AppState::kAlwaysAvailable;
335 }
336 
AddAppStateObserver(AppActivityRegistry::AppStateObserver * observer)337 void AppActivityRegistry::AddAppStateObserver(
338     AppActivityRegistry::AppStateObserver* observer) {
339   app_state_observers_.AddObserver(observer);
340 }
341 
RemoveAppStateObserver(AppActivityRegistry::AppStateObserver * observer)342 void AppActivityRegistry::RemoveAppStateObserver(
343     AppActivityRegistry::AppStateObserver* observer) {
344   app_state_observers_.RemoveObserver(observer);
345 }
346 
SetInstalledApps(const std::vector<AppId> & installed_apps)347 void AppActivityRegistry::SetInstalledApps(
348     const std::vector<AppId>& installed_apps) {
349   for (const auto& app : installed_apps)
350     OnAppInstalled(app);
351 }
352 
GetActiveTime(const AppId & app_id) const353 base::TimeDelta AppActivityRegistry::GetActiveTime(const AppId& app_id) const {
354   DCHECK(base::Contains(activity_registry_, app_id));
355   return activity_registry_.at(app_id).activity.RunningActiveTime();
356 }
357 
GetWebTimeLimit() const358 const base::Optional<AppLimit>& AppActivityRegistry::GetWebTimeLimit() const {
359   DCHECK(base::Contains(activity_registry_, GetChromeAppId()));
360   return activity_registry_.at(GetChromeAppId()).limit;
361 }
362 
GetAppState(const AppId & app_id) const363 AppState AppActivityRegistry::GetAppState(const AppId& app_id) const {
364   DCHECK(base::Contains(activity_registry_, app_id));
365   return activity_registry_.at(app_id).activity.app_state();
366 }
367 
GetTimeLimit(const AppId & app_id) const368 base::Optional<base::TimeDelta> AppActivityRegistry::GetTimeLimit(
369     const AppId& app_id) const {
370   if (!base::Contains(activity_registry_, app_id))
371     return base::nullopt;
372 
373   const base::Optional<AppLimit>& limit = activity_registry_.at(app_id).limit;
374   if (!limit || limit->restriction() != AppRestriction::kTimeLimit)
375     return base::nullopt;
376 
377   DCHECK(limit->daily_limit());
378   return limit->daily_limit();
379 }
380 
SetReportingEnabled(base::Optional<bool> value)381 void AppActivityRegistry::SetReportingEnabled(base::Optional<bool> value) {
382   if (value.has_value())
383     activity_reporting_enabled_ = value.value();
384 }
385 
GenerateHiddenApps(enterprise_management::ChildStatusReportRequest * report)386 void AppActivityRegistry::GenerateHiddenApps(
387     enterprise_management::ChildStatusReportRequest* report) {
388   const std::vector<AppId> hidden_arc_apps =
389       app_service_wrapper_->GetHiddenArcApps();
390   for (const auto& app_id : hidden_arc_apps) {
391     enterprise_management::App* app_info = report->add_hidden_app();
392     app_info->set_app_id(app_id.app_id());
393     app_info->set_app_type(AppTypeForReporting(app_id.app_type()));
394     if (app_id.app_type() == apps::mojom::AppType::kArc) {
395       app_info->add_additional_app_id(
396           app_service_wrapper_->GetAppServiceId(app_id));
397     }
398   }
399 }
400 
401 AppActivityReportInterface::ReportParams
GenerateAppActivityReport(enterprise_management::ChildStatusReportRequest * report)402 AppActivityRegistry::GenerateAppActivityReport(
403     enterprise_management::ChildStatusReportRequest* report) {
404   // Calling SaveAppActivity is beneficial even if this method is returning
405   // early due to reporting not being enabled. This is because it helps move the
406   // ActiveTimes information from AppActivityRegistry to the stored pref data
407   // which will then be cleaned in the direct CleanRegistry() call below.
408   SaveAppActivity();
409 
410   // If app activity reporting is not enabled, simply return.
411   if (!activity_reporting_enabled_) {
412     base::Time timestamp = base::Time::Now();
413     CleanRegistry(timestamp);
414     return AppActivityReportInterface::ReportParams{timestamp, false};
415   }
416 
417   const base::Value* value =
418       pref_service_->GetList(prefs::kPerAppTimeLimitsAppActivities);
419   DCHECK(value);
420 
421   const std::vector<PersistedAppInfo> applications_info =
422       PersistedAppInfo::PersistedAppInfosFromList(
423           value,
424           /* include_app_activity_array */ true);
425 
426   const base::Time timestamp = base::Time::Now();
427   bool anything_reported = false;
428 
429   for (const auto& entry : applications_info) {
430     const AppId& app_id = entry.app_id();
431     const std::vector<AppActivity::ActiveTime>& active_times =
432         entry.active_times();
433 
434     // Do not report if there is no activity.
435     if (active_times.empty())
436       continue;
437 
438     enterprise_management::AppActivity* app_activity =
439         report->add_app_activity();
440     enterprise_management::App* app_info = app_activity->mutable_app_info();
441     app_info->set_app_id(app_id.app_id());
442     app_info->set_app_type(AppTypeForReporting(app_id.app_type()));
443     // AppService is is only different for ARC++ apps.
444     if (app_id.app_type() == apps::mojom::AppType::kArc) {
445       app_info->add_additional_app_id(
446           app_service_wrapper_->GetAppServiceId(app_id));
447     }
448     app_activity->set_app_state(AppStateForReporting(entry.app_state()));
449     app_activity->set_populated_at(timestamp.ToJavaTime());
450 
451     for (const auto& active_time : active_times) {
452       enterprise_management::TimePeriod* time_period =
453           app_activity->add_active_time_periods();
454       time_period->set_start_timestamp(active_time.active_from().ToJavaTime());
455       time_period->set_end_timestamp(active_time.active_to().ToJavaTime());
456     }
457     anything_reported = true;
458   }
459 
460   return AppActivityReportInterface::ReportParams{timestamp, anything_reported};
461 }
462 
OnSuccessfullyReported(base::Time timestamp)463 void AppActivityRegistry::OnSuccessfullyReported(base::Time timestamp) {
464   CleanRegistry(timestamp);
465 
466   // Update last successful report time.
467   pref_service_->SetInt64(
468       prefs::kPerAppTimeLimitsLastSuccessfulReportTime,
469       timestamp.ToDeltaSinceWindowsEpoch().InMicroseconds());
470 }
471 
UpdateAppLimits(const std::map<AppId,AppLimit> & app_limits)472 bool AppActivityRegistry::UpdateAppLimits(
473     const std::map<AppId, AppLimit>& app_limits) {
474   base::Time latest_update = latest_app_limit_update_;
475   bool policy_updated = false;
476   for (auto& entry : activity_registry_) {
477     const AppId& app_id = entry.first;
478 
479     // Web time limits are updated when chrome's time limit is updated.
480     if (app_id != GetChromeAppId() && IsWebAppOrExtension(app_id))
481       continue;
482 
483     base::Optional<AppLimit> new_limit = base::nullopt;
484     if (base::Contains(app_limits, app_id))
485       new_limit = app_limits.at(app_id);
486 
487     policy_updated |= SetAppLimit(app_id, new_limit);
488 
489     if (new_limit && new_limit->last_updated() > latest_update)
490       latest_update = new_limit->last_updated();
491   }
492 
493   latest_app_limit_update_ = latest_update;
494 
495   // Update the latest app limit update.
496   pref_service_->SetInt64(
497       prefs::kPerAppTimeLimitsLatestLimitUpdateTime,
498       latest_app_limit_update_.ToDeltaSinceWindowsEpoch().InMicroseconds());
499 
500   return policy_updated;
501 }
502 
SetAppLimit(const AppId & app_id,const base::Optional<AppLimit> & app_limit)503 bool AppActivityRegistry::SetAppLimit(
504     const AppId& app_id,
505     const base::Optional<AppLimit>& app_limit) {
506   DCHECK(base::Contains(activity_registry_, app_id));
507 
508   // If an application is not installed but present in the registry return
509   // early.
510   if (!IsAppInstalled(app_id))
511     return false;
512 
513   // Chrome and web apps should not be blocked.
514   if (app_limit && app_limit->restriction() == AppRestriction::kBlocked &&
515       IsWebAppOrExtension(app_id)) {
516     return false;
517   }
518 
519   AppDetails& details = activity_registry_.at(app_id);
520   // Limit 'data' are considered equal if only the |last_updated_| is different.
521   // Update the limit to store new |last_updated_| value.
522   bool did_change = !details.IsLimitEqual(app_limit);
523   bool updated =
524       ShowLimitUpdatedNotificationIfNeeded(app_id, details.limit, app_limit);
525   details.limit = app_limit;
526 
527   // If |did_change| is false, handle the following corner case before
528   // returning. The default value for app limit during construction at the
529   // beginning of the session is base::nullopt. If the application was paused in
530   // the previous session, and its limit was removed or feature is disabled in
531   // the current session, the |app_limit| provided will be base::nullopt. Since
532   // both values(the default app limit and the |app_limit| provided as an
533   // argument for this method) are the same base::nullopt, |did_change| will be
534   // false. But we still need to update the state to available as the new app
535   // limit is base::nullopt.
536   if (!did_change && (IsAppAvailable(app_id) || app_limit.has_value()))
537     return updated;
538 
539   if (IsAllowlistedApp(app_id)) {
540     if (app_limit.has_value()) {
541       VLOG(1) << "Tried to set time limit for " << app_id
542               << " which is allowlisted.";
543     }
544 
545     details.limit = base::nullopt;
546     return false;
547   }
548 
549   if (!IsWebAppOrExtension(app_id)) {
550     AppLimitUpdated(app_id);
551     return updated;
552   }
553 
554   for (auto& entry : activity_registry_) {
555     const AppId& app_id = entry.first;
556     AppDetails& details = entry.second;
557     if (ContributesToWebTimeLimit(app_id, GetAppState(app_id)))
558       details.limit = app_limit;
559   }
560 
561   for (auto& entry : activity_registry_) {
562     const AppId& app_id = entry.first;
563     if (ContributesToWebTimeLimit(app_id, GetAppState(app_id)))
564       AppLimitUpdated(app_id);
565   }
566 
567   return updated;
568 }
569 
SetAppAllowlisted(const AppId & app_id)570 void AppActivityRegistry::SetAppAllowlisted(const AppId& app_id) {
571   if (!base::Contains(activity_registry_, app_id))
572     return;
573   SetAppState(app_id, AppState::kAlwaysAvailable);
574 }
575 
OnChromeAppActivityChanged(ChromeAppActivityState state,base::Time timestamp)576 void AppActivityRegistry::OnChromeAppActivityChanged(
577     ChromeAppActivityState state,
578     base::Time timestamp) {
579   AppId chrome_app_id = GetChromeAppId();
580   if (!base::Contains(activity_registry_, chrome_app_id))
581     return;
582 
583   AppDetails& details = activity_registry_[chrome_app_id];
584   bool was_active = details.activity.is_active();
585 
586   bool is_active = (state == ChromeAppActivityState::kActive);
587 
588   // No need to notify observers that limit has reached. They will be notified
589   // in AppActivityRegistry::OnAppActive.
590   if (GetAppState(chrome_app_id) == AppState::kLimitReached && is_active)
591     return;
592 
593   // No change in state.
594   if (was_active == is_active)
595     return;
596 
597   if (is_active) {
598     SetAppActive(chrome_app_id, timestamp);
599     return;
600   }
601 
602   SetAppInactive(chrome_app_id, timestamp);
603 }
604 
OnTimeLimitAllowlistChanged(const AppTimeLimitsAllowlistPolicyWrapper & wrapper)605 void AppActivityRegistry::OnTimeLimitAllowlistChanged(
606     const AppTimeLimitsAllowlistPolicyWrapper& wrapper) {
607   std::vector<AppId> allowlisted_apps = wrapper.GetAllowlistAppList();
608   for (const AppId& app : allowlisted_apps) {
609     if (!base::Contains(activity_registry_, app))
610       continue;
611 
612     if (GetAppState(app) == AppState::kAlwaysAvailable)
613       continue;
614 
615     base::Optional<AppLimit>& limit = activity_registry_.at(app).limit;
616     if (limit.has_value())
617       limit = base::nullopt;
618 
619     SetAppState(app, AppState::kAlwaysAvailable);
620   }
621 }
622 
SaveAppActivity()623 void AppActivityRegistry::SaveAppActivity() {
624   {
625     ListPrefUpdate update(pref_service_, prefs::kPerAppTimeLimitsAppActivities);
626     base::ListValue* list_value = update.Get();
627 
628     const base::Time now = base::Time::Now();
629 
630     base::Value::ListView list_view = list_value->GetList();
631     for (base::Value& entry : list_view) {
632       base::Optional<AppId> app_id = policy::AppIdFromAppInfoDict(entry);
633       DCHECK(app_id.has_value());
634 
635       if (!base::Contains(activity_registry_, app_id.value())) {
636         base::Optional<AppState> state =
637             PersistedAppInfo::GetAppStateFromDict(&entry);
638         DCHECK(state.has_value() && state.value() == AppState::kUninstalled);
639         continue;
640       }
641 
642       const PersistedAppInfo info =
643           GetPersistedAppInfoForApp(app_id.value(), now);
644       info.UpdateAppActivityPreference(&entry, /* replace */ false);
645     }
646 
647     for (const AppId& app_id : newly_installed_apps_) {
648       const PersistedAppInfo info = GetPersistedAppInfoForApp(app_id, now);
649       base::Value value(base::Value::Type::DICTIONARY);
650       info.UpdateAppActivityPreference(&value, /* replace */ false);
651       list_value->Append(std::move(value));
652     }
653     newly_installed_apps_.clear();
654   }
655 
656   // Ensure that the app activity is persisted.
657   pref_service_->CommitPendingWrite();
658 }
659 
GetAppsWithAppRestriction(AppRestriction restriction) const660 std::vector<AppId> AppActivityRegistry::GetAppsWithAppRestriction(
661     AppRestriction restriction) const {
662   std::vector<AppId> apps_with_limit;
663   for (const auto& entry : activity_registry_) {
664     const AppId& app = entry.first;
665     const AppDetails& details = entry.second;
666     if (details.limit && details.limit->restriction() == restriction) {
667       apps_with_limit.push_back(app);
668     }
669   }
670   return apps_with_limit;
671 }
672 
OnResetTimeReached(base::Time timestamp)673 void AppActivityRegistry::OnResetTimeReached(base::Time timestamp) {
674   for (std::pair<const AppId, AppDetails>& info : activity_registry_) {
675     const AppId& app = info.first;
676     AppDetails& details = info.second;
677 
678     // Reset running active time.
679     details.activity.ResetRunningActiveTime(timestamp);
680 
681     // If timer is running, stop timer. Abandon all tasks set.
682     details.ResetTimeCheck();
683 
684     // If the time limit has been reached, mark the app as available.
685     if (details.activity.app_state() == AppState::kLimitReached)
686       SetAppState(app, AppState::kAvailable);
687 
688     // If the application is currently active, schedule a time limit
689     // check.
690     if (details.activity.is_active())
691       ScheduleTimeLimitCheckForApp(app);
692   }
693 }
694 
CleanRegistry(base::Time timestamp)695 void AppActivityRegistry::CleanRegistry(base::Time timestamp) {
696   ListPrefUpdate update(pref_service_, prefs::kPerAppTimeLimitsAppActivities);
697 
698   base::ListValue* list_value = update.Get();
699 
700   // base::Value::ListStorage is an alias for std::vector<base::Value>.
701   base::Value::ListStorage list_storage = list_value->TakeList();
702 
703   for (size_t index = 0; index < list_storage.size();) {
704     base::Value& entry = list_storage[index];
705     base::Optional<PersistedAppInfo> info =
706         PersistedAppInfo::PersistedAppInfoFromDict(&entry, true);
707     DCHECK(info.has_value());
708     info->RemoveActiveTimeEarlierThan(timestamp);
709     info->UpdateAppActivityPreference(&entry, /* replace */ true);
710 
711     if (info->ShouldRemoveApp()) {
712       // Remove entry in |activity_registry_| if it is present.
713       activity_registry_.erase(info->app_id());
714 
715       // To efficiently remove the entry, swap it with the last element and pop
716       // back.
717       if (index < list_storage.size() - 1)
718         std::swap(list_storage[index], list_storage[list_storage.size() - 1]);
719       list_storage.pop_back();
720     } else {
721       ++index;
722     }
723   }
724 
725   *list_value = base::ListValue(std::move(list_storage));
726 }
727 
OnAppReinstalled(const AppId & app_id)728 void AppActivityRegistry::OnAppReinstalled(const AppId& app_id) {
729   DCHECK(base::Contains(activity_registry_, app_id));
730   AppDetails& details = activity_registry_.at(app_id);
731   if (details.IsLimitReached()) {
732     SetAppState(app_id, AppState::kLimitReached);
733   } else {
734     SetAppState(app_id, AppState::kAvailable);
735   }
736 
737   // Notify observers.
738   for (auto& observer : app_state_observers_)
739     observer.OnAppInstalled(app_id);
740 }
741 
Add(const AppId & app_id)742 void AppActivityRegistry::Add(const AppId& app_id) {
743   activity_registry_[app_id].activity = AppActivity(AppState::kAvailable);
744   activity_registry_[app_id].received_app_installed_ = true;
745 
746   bool is_app_chrome = app_id == GetChromeAppId();
747   bool is_web = IsWebAppOrExtension(app_id);
748   bool is_chrome_installed =
749       base::Contains(activity_registry_, GetChromeAppId());
750   if (!is_app_chrome && is_web && is_chrome_installed) {
751     activity_registry_[app_id].limit = GetWebTimeLimit();
752     activity_registry_[app_id].activity.SetAppState(
753         GetAppState(GetChromeAppId()));
754   }
755 
756   newly_installed_apps_.push_back(app_id);
757   for (auto& observer : app_state_observers_)
758     observer.OnAppInstalled(app_id);
759 }
760 
SetAppState(const AppId & app_id,AppState app_state)761 void AppActivityRegistry::SetAppState(const AppId& app_id, AppState app_state) {
762   DCHECK(base::Contains(activity_registry_, app_id));
763   AppDetails& app_details = activity_registry_.at(app_id);
764   AppActivity& app_activity = app_details.activity;
765   AppState previous_state = app_activity.app_state();
766 
767   // There was no change in state, return.
768   if (previous_state == app_state)
769     return;
770 
771   app_activity.SetAppState(app_state);
772 
773   if (app_activity.app_state() == AppState::kLimitReached) {
774     bool was_active = false;
775     if (app_activity.is_active()) {
776       was_active = true;
777       app_details.paused_windows = std::move(app_details.active_windows);
778       SetAppInactive(app_id, base::Time::Now());
779     }
780 
781     NotifyLimitReached(app_id, was_active);
782     return;
783   }
784 
785   if (previous_state == AppState::kLimitReached &&
786       app_activity.app_state() != AppState::kLimitReached) {
787     for (auto& observer : app_state_observers_)
788       observer.OnAppLimitRemoved(app_id);
789     return;
790   }
791 }
792 
NotifyLimitReached(const AppId & app_id,bool was_active)793 void AppActivityRegistry::NotifyLimitReached(const AppId& app_id,
794                                              bool was_active) {
795   DCHECK(base::Contains(activity_registry_, app_id));
796   DCHECK_EQ(GetAppState(app_id), AppState::kLimitReached);
797 
798   const base::Optional<AppLimit>& limit = activity_registry_.at(app_id).limit;
799   DCHECK(limit->daily_limit());
800   for (auto& observer : app_state_observers_) {
801     observer.OnAppLimitReached(app_id, limit->daily_limit().value(),
802                                was_active);
803   }
804 }
805 
SetAppActive(const AppId & app_id,base::Time timestamp)806 void AppActivityRegistry::SetAppActive(const AppId& app_id,
807                                        base::Time timestamp) {
808   DCHECK(base::Contains(activity_registry_, app_id));
809   AppDetails& app_details = activity_registry_[app_id];
810   DCHECK(!app_details.activity.is_active());
811   if (ContributesToWebTimeLimit(app_id, GetAppState(app_id)))
812     app_details.activity.set_running_active_time(GetWebActiveRunningTime());
813 
814   app_details.activity.SetAppActive(timestamp);
815 
816   ScheduleTimeLimitCheckForApp(app_id);
817 }
818 
SetAppInactive(const AppId & app_id,base::Time timestamp)819 void AppActivityRegistry::SetAppInactive(const AppId& app_id,
820                                          base::Time timestamp) {
821   DCHECK(base::Contains(activity_registry_, app_id));
822   auto& details = activity_registry_.at(app_id);
823 
824   details.activity.SetAppInactive(timestamp);
825   details.ResetTimeCheck();
826 
827   // If the application is a web app, synchronize its running active time with
828   // those of other inactive web apps.
829   if (ContributesToWebTimeLimit(app_id, GetAppState(app_id))) {
830     base::TimeDelta active_time = details.activity.RunningActiveTime();
831     for (auto& app_info : activity_registry_) {
832       const AppId& app_id = app_info.first;
833       if (!ContributesToWebTimeLimit(app_id, GetAppState(app_id))) {
834         continue;
835       }
836 
837       AppDetails& details = app_info.second;
838       if (!details.activity.is_active())
839         details.activity.set_running_active_time(active_time);
840     }
841   }
842 }
843 
ScheduleTimeLimitCheckForApp(const AppId & app_id)844 void AppActivityRegistry::ScheduleTimeLimitCheckForApp(const AppId& app_id) {
845   DCHECK(base::Contains(activity_registry_, app_id));
846   AppDetails& app_details = activity_registry_[app_id];
847 
848   // If there is no time limit information, don't set the timer.
849   if (!app_details.limit.has_value())
850     return;
851 
852   const AppLimit& limit = app_details.limit.value();
853   if (limit.restriction() != AppRestriction::kTimeLimit)
854     return;
855 
856   if (!app_details.app_limit_timer) {
857     app_details.app_limit_timer = std::make_unique<base::OneShotTimer>(
858         base::DefaultTickClock::GetInstance());
859   }
860 
861   DCHECK(!app_details.app_limit_timer->IsRunning());
862 
863   // Check that the timer instance has been created.
864   base::Optional<base::TimeDelta> time_limit = GetTimeLeftForApp(app_id);
865   DCHECK(time_limit.has_value());
866 
867   if (time_limit > kFiveMinutes) {
868     time_limit = time_limit.value() - kFiveMinutes;
869   } else if (time_limit > kOneMinute) {
870     time_limit = time_limit.value() - kOneMinute;
871   } else if (time_limit == kZeroMinutes) {
872     // Zero minutes case could be handled by using the timer below, but we call
873     // it explicitly to simplify tests.
874     CheckTimeLimitForApp(app_id);
875     return;
876   }
877 
878   VLOG(1) << "Schedule app time limit check for " << app_id << " for "
879           << time_limit.value();
880 
881   app_details.app_limit_timer->Start(
882       FROM_HERE, time_limit.value(),
883       base::BindOnce(&AppActivityRegistry::CheckTimeLimitForApp,
884                      base::Unretained(this), app_id));
885 }
886 
GetTimeLeftForApp(const AppId & app_id) const887 base::Optional<base::TimeDelta> AppActivityRegistry::GetTimeLeftForApp(
888     const AppId& app_id) const {
889   DCHECK(base::Contains(activity_registry_, app_id));
890   const AppDetails& app_details = activity_registry_.at(app_id);
891 
892   // If |app_details.limit| doesn't have value, the app has no restriction.
893   if (!app_details.limit.has_value())
894     return base::nullopt;
895 
896   const AppLimit& limit = app_details.limit.value();
897 
898   if (limit.restriction() != AppRestriction::kTimeLimit)
899     return base::nullopt;
900 
901   // If the app has kTimeLimit restriction, DCHECK that daily limit has value.
902   DCHECK(limit.daily_limit().has_value());
903 
904   AppState state = app_details.activity.app_state();
905   if (state == AppState::kAlwaysAvailable || state == AppState::kBlocked)
906     return base::nullopt;
907 
908   if (state == AppState::kLimitReached)
909     return kZeroMinutes;
910 
911   DCHECK(state == AppState::kAvailable);
912 
913   base::TimeDelta time_limit = limit.daily_limit().value();
914 
915   base::TimeDelta active_time;
916   if (ContributesToWebTimeLimit(app_id, GetAppState(app_id))) {
917     active_time = GetWebActiveRunningTime();
918   } else {
919     active_time = app_details.activity.RunningActiveTime();
920   }
921 
922   if (active_time >= time_limit)
923     return kZeroMinutes;
924 
925   return time_limit - active_time;
926 }
927 
CheckTimeLimitForApp(const AppId & app_id)928 void AppActivityRegistry::CheckTimeLimitForApp(const AppId& app_id) {
929   AppDetails& details = activity_registry_[app_id];
930 
931   base::Optional<base::TimeDelta> time_left = GetTimeLeftForApp(app_id);
932   AppNotification last_notification = details.activity.last_notification();
933 
934   if (!time_left.has_value())
935     return;
936 
937   DCHECK(details.limit.has_value());
938   DCHECK(details.limit->daily_limit().has_value());
939   const base::TimeDelta time_limit = details.limit->daily_limit().value();
940 
941   if (time_left <= kFiveMinutes && time_left > kOneMinute &&
942       last_notification != AppNotification::kFiveMinutes) {
943     MaybeShowSystemNotification(
944         app_id, SystemNotification(time_limit, AppNotification::kFiveMinutes));
945     ScheduleTimeLimitCheckForApp(app_id);
946     return;
947   }
948 
949   if (time_left <= kOneMinute && time_left > kZeroMinutes &&
950       last_notification != AppNotification::kOneMinute) {
951     MaybeShowSystemNotification(
952         app_id, SystemNotification(time_limit, AppNotification::kOneMinute));
953     ScheduleTimeLimitCheckForApp(app_id);
954     return;
955   }
956 
957   if (time_left == kZeroMinutes &&
958       last_notification != AppNotification::kTimeLimitReached) {
959     MaybeShowSystemNotification(
960         app_id,
961         SystemNotification(time_limit, AppNotification::kTimeLimitReached));
962 
963     if (ContributesToWebTimeLimit(app_id, GetAppState(app_id))) {
964       WebTimeLimitReached(base::Time::Now());
965     } else {
966       SetAppState(app_id, AppState::kLimitReached);
967     }
968   }
969 }
970 
ShowLimitUpdatedNotificationIfNeeded(const AppId & app_id,const base::Optional<AppLimit> & old_limit,const base::Optional<AppLimit> & new_limit)971 bool AppActivityRegistry::ShowLimitUpdatedNotificationIfNeeded(
972     const AppId& app_id,
973     const base::Optional<AppLimit>& old_limit,
974     const base::Optional<AppLimit>& new_limit) {
975   // Web app limit changes are covered by Chrome notification.
976   if (app_id != GetChromeAppId() && IsWebAppOrExtension(app_id))
977     return false;
978 
979   // Don't show notification if the time limit's update was older than the
980   // latest update.
981   if (new_limit && new_limit->last_updated() <= latest_app_limit_update_)
982     return false;
983 
984   const bool was_blocked =
985       old_limit && old_limit->restriction() == AppRestriction::kBlocked;
986   const bool is_blocked =
987       new_limit && new_limit->restriction() == AppRestriction::kBlocked;
988 
989   if (!was_blocked && is_blocked) {
990     MaybeShowSystemNotification(
991         app_id, SystemNotification(base::nullopt, AppNotification::kBlocked));
992     return true;
993   }
994 
995   const bool had_time_limit =
996       old_limit && old_limit->restriction() == AppRestriction::kTimeLimit;
997   const bool has_time_limit =
998       new_limit && new_limit->restriction() == AppRestriction::kTimeLimit;
999 
1000   if (was_blocked && !is_blocked && !has_time_limit) {
1001     MaybeShowSystemNotification(
1002         app_id, SystemNotification(base::nullopt, AppNotification::kAvailable));
1003     return true;
1004   }
1005 
1006   // Time limit was removed.
1007   if (!has_time_limit && had_time_limit) {
1008     MaybeShowSystemNotification(
1009         app_id,
1010         SystemNotification(base::nullopt, AppNotification::kTimeLimitChanged));
1011     return true;
1012   }
1013 
1014   // Time limit was set or value changed.
1015   if (has_time_limit && (!had_time_limit || old_limit->daily_limit() !=
1016                                                 new_limit->daily_limit())) {
1017     MaybeShowSystemNotification(
1018         app_id, SystemNotification(new_limit->daily_limit(),
1019                                    AppNotification::kTimeLimitChanged));
1020     return true;
1021   }
1022 
1023   return false;
1024 }
1025 
GetWebActiveRunningTime() const1026 base::TimeDelta AppActivityRegistry::GetWebActiveRunningTime() const {
1027   base::TimeDelta active_running_time = base::TimeDelta::FromSeconds(0);
1028   for (const auto& app_info : activity_registry_) {
1029     const AppId& app_id = app_info.first;
1030     const AppDetails& details = app_info.second;
1031     if (!ContributesToWebTimeLimit(app_id, GetAppState(app_id))) {
1032       continue;
1033     }
1034 
1035     active_running_time = details.activity.RunningActiveTime();
1036 
1037     // If the app is active, then it has the most up to date active running
1038     // time.
1039     if (details.activity.is_active())
1040       return active_running_time;
1041   }
1042 
1043   return active_running_time;
1044 }
1045 
WebTimeLimitReached(base::Time timestamp)1046 void AppActivityRegistry::WebTimeLimitReached(base::Time timestamp) {
1047   for (auto& app_info : activity_registry_) {
1048     const AppId& app_id = app_info.first;
1049     if (!ContributesToWebTimeLimit(app_id, GetAppState(app_id)))
1050       continue;
1051 
1052     SetAppState(app_id, AppState::kLimitReached);
1053   }
1054 }
1055 
InitializeRegistryFromPref()1056 void AppActivityRegistry::InitializeRegistryFromPref() {
1057   DCHECK(pref_service_);
1058 
1059   int64_t last_limits_updates =
1060       pref_service_->GetInt64(prefs::kPerAppTimeLimitsLatestLimitUpdateTime);
1061 
1062   latest_app_limit_update_ = base::Time::FromDeltaSinceWindowsEpoch(
1063       base::TimeDelta::FromMicroseconds(last_limits_updates));
1064 
1065   InitializeAppActivities();
1066 }
1067 
InitializeAppActivities()1068 void AppActivityRegistry::InitializeAppActivities() {
1069   const base::Value* value =
1070       pref_service_->GetList(prefs::kPerAppTimeLimitsAppActivities);
1071   DCHECK(value);
1072 
1073   const std::vector<PersistedAppInfo> applications_info =
1074       PersistedAppInfo::PersistedAppInfosFromList(
1075           value,
1076           /* include_app_activity_array */ false);
1077 
1078   for (const auto& app_info : applications_info) {
1079     DCHECK(!base::Contains(activity_registry_, app_info.app_id()));
1080 
1081     // Don't restore uninstalled application's if its running active time is
1082     // zero.
1083     if (!app_info.ShouldRestoreApp())
1084       continue;
1085 
1086     activity_registry_[app_info.app_id()].activity =
1087         AppActivity(app_info.app_state(), app_info.active_running_time());
1088   }
1089 }
1090 
GetPersistedAppInfoForApp(const AppId & app_id,base::Time timestamp)1091 PersistedAppInfo AppActivityRegistry::GetPersistedAppInfoForApp(
1092     const AppId& app_id,
1093     base::Time timestamp) {
1094   DCHECK(base::Contains(activity_registry_, app_id));
1095 
1096   AppDetails& details = activity_registry_.at(app_id);
1097 
1098   base::TimeDelta running_active_time = details.activity.RunningActiveTime();
1099   if (ContributesToWebTimeLimit(app_id, GetAppState(app_id)))
1100     running_active_time = GetWebActiveRunningTime();
1101 
1102   // Updates |AppActivity::active_times_| to include the current activity up to
1103   // |timestamp|.
1104   details.activity.CaptureOngoingActivity(timestamp);
1105 
1106   std::vector<AppActivity::ActiveTime> activity =
1107       details.activity.TakeActiveTimes();
1108 
1109   // If reporting is not enabled, don't save unnecessary data.
1110   if (!activity_reporting_enabled_)
1111     activity.clear();
1112 
1113   return PersistedAppInfo(app_id, details.activity.app_state(),
1114                           running_active_time, std::move(activity));
1115 }
1116 
ShouldCleanUpStoredPref()1117 bool AppActivityRegistry::ShouldCleanUpStoredPref() {
1118   int64_t last_time =
1119       pref_service_->GetInt64(prefs::kPerAppTimeLimitsLastSuccessfulReportTime);
1120 
1121   if (last_time == 0)
1122     return false;
1123 
1124   base::Time time = base::Time::FromDeltaSinceWindowsEpoch(
1125       base::TimeDelta::FromMicroseconds(last_time));
1126 
1127   return time < base::Time::Now() - base::TimeDelta::FromDays(30);
1128 }
1129 
SendSystemNotificationsForApp(const AppId & app_id)1130 void AppActivityRegistry::SendSystemNotificationsForApp(const AppId& app_id) {
1131   DCHECK(base::Contains(activity_registry_, app_id));
1132 
1133   AppDetails& app_details = activity_registry_.at(app_id);
1134   DCHECK(app_details.received_app_installed_);
1135 
1136   // TODO(yilkal): Filter out the notifications to show. For example don't show
1137   // 5 min and 1 min left notifications at the same time here. However, time
1138   // limit changed and 1 min left notifications can be shown at the same time.
1139   for (const auto& elem : app_details.pending_notifications_) {
1140     notification_delegate_->ShowAppTimeLimitNotification(
1141         app_id, elem.time_limit, elem.notification);
1142   }
1143   app_details.pending_notifications_.clear();
1144 }
1145 
MaybeShowSystemNotification(const AppId & app_id,const SystemNotification & notification)1146 void AppActivityRegistry::MaybeShowSystemNotification(
1147     const AppId& app_id,
1148     const SystemNotification& notification) {
1149   DCHECK(base::Contains(activity_registry_, app_id));
1150 
1151   AppDetails& app_details = activity_registry_.at(app_id);
1152   app_details.activity.set_last_notification(notification.notification);
1153 
1154   // AppActivityRegistry has not yet received OnAppInstalled call from
1155   // AppService. Add notification to |AppDetails::pending_notifications_|.
1156   if (!app_details.received_app_installed_) {
1157     app_details.pending_notifications_.push_back(notification);
1158     return;
1159   }
1160 
1161   // Otherwise, just show the notification.
1162   notification_delegate_->ShowAppTimeLimitNotification(
1163       app_id, notification.time_limit, notification.notification);
1164 }
1165 
AppLimitUpdated(const AppId & app_id)1166 void AppActivityRegistry::AppLimitUpdated(const AppId& app_id) {
1167   DCHECK(base::Contains(activity_registry_, app_id));
1168   AppDetails& details = activity_registry_.at(app_id);
1169 
1170   // Limit for the active app changed - adjust the timers.
1171   // Handling of active app is different, because ongoing activity needs to be
1172   // taken into account.
1173   if (IsAppActive(app_id)) {
1174     details.ResetTimeCheck();
1175     ScheduleTimeLimitCheckForApp(app_id);
1176     return;
1177   }
1178 
1179   // Inactive available app reached the limit - update the state.
1180   // If app is in any other state than |kAvailable| the limit does not take
1181   // effect.
1182   if (IsAppAvailable(app_id) && details.IsLimitReached()) {
1183     SetAppInactive(app_id, base::Time::Now());
1184     SetAppState(app_id, AppState::kLimitReached);
1185     return;
1186   }
1187 
1188   // Paused inactive app is below the limit again - update the state.
1189   // This can happen if the limit was removed or new limit is greater the the
1190   // previous one. We know that the state should be available, because app can
1191   // only reach the limit if it is available.
1192   if (IsAppTimeLimitReached(app_id) && !details.IsLimitReached())
1193     SetAppState(app_id, AppState::kAvailable);
1194 }
1195 
1196 }  // namespace app_time
1197 }  // namespace chromeos
1198