1// Copyright 2013 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/app_mode/kiosk_app_update_service.h"
6
7#include "base/logging.h"
8#include "chrome/browser/app_mode/app_mode_utils.h"
9#include "chrome/browser/browser_process.h"
10#include "chrome/browser/browser_process_platform_part_chromeos.h"
11#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
12#include "chrome/browser/chromeos/system/automatic_reboot_manager.h"
13#include "chrome/browser/extensions/extension_service.h"
14#include "chrome/browser/lifetime/application_lifetime.h"
15#include "chrome/browser/profiles/profile.h"
16#include "components/keyed_service/content/browser_context_dependency_manager.h"
17#include "extensions/browser/api/runtime/runtime_api.h"
18#include "extensions/browser/extension_system.h"
19#include "extensions/browser/extension_system_provider.h"
20#include "extensions/browser/extensions_browser_client.h"
21#include "extensions/common/extension.h"
22
23namespace chromeos {
24
25namespace {
26
27// How low to wait after an update is available before we force a restart.
28const int kForceRestartWaitTimeMs = 24 * 3600 * 1000;  // 24 hours.
29
30}  // namespace
31
32KioskAppUpdateService::KioskAppUpdateService(
33    Profile* profile,
34    system::AutomaticRebootManager* automatic_reboot_manager)
35    : profile_(profile),
36      automatic_reboot_manager_(automatic_reboot_manager) {
37  ExtensionService* service =
38      extensions::ExtensionSystem::Get(profile_)->extension_service();
39  if (service)
40    service->AddUpdateObserver(this);
41
42  if (automatic_reboot_manager_)
43    automatic_reboot_manager_->AddObserver(this);
44
45  if (KioskAppManager::Get())
46    KioskAppManager::Get()->AddObserver(this);
47}
48
49KioskAppUpdateService::~KioskAppUpdateService() {
50}
51
52void KioskAppUpdateService::StartAppUpdateRestartTimer() {
53  if (restart_timer_.IsRunning())
54    return;
55
56  // Setup timer to force restart once the wait period expires.
57  restart_timer_.Start(
58      FROM_HERE, base::TimeDelta::FromMilliseconds(kForceRestartWaitTimeMs),
59      this, &KioskAppUpdateService::ForceAppUpdateRestart);
60}
61
62void KioskAppUpdateService::ForceAppUpdateRestart() {
63  // Force a chrome restart (not a logout or reboot) by closing all browsers.
64  LOG(WARNING) << "Force closing all browsers to update kiosk app.";
65  chrome::CloseAllBrowsersAndQuit();
66}
67
68void KioskAppUpdateService::Shutdown() {
69  ExtensionService* service =
70      extensions::ExtensionSystem::Get(profile_)->extension_service();
71  if (service)
72    service->RemoveUpdateObserver(this);
73  if (KioskAppManager::Get())
74    KioskAppManager::Get()->RemoveObserver(this);
75}
76
77void KioskAppUpdateService::OnAppUpdateAvailable(
78    const extensions::Extension* extension) {
79  if (extension->id() != app_id_)
80    return;
81
82  // Clears cached app data so that it will be reloaded if update from app
83  // does not finish in this run.
84  KioskAppManager::Get()->ClearAppData(app_id_);
85  KioskAppManager::Get()->UpdateAppDataFromProfile(
86      app_id_, profile_, extension);
87
88  extensions::RuntimeEventRouter::DispatchOnRestartRequiredEvent(
89      profile_,
90      app_id_,
91      extensions::core_api::runtime::OnRestartRequired::REASON_APP_UPDATE);
92
93  StartAppUpdateRestartTimer();
94}
95
96void KioskAppUpdateService::OnRebootScheduled(Reason reason) {
97  extensions::core_api::runtime::OnRestartRequired::Reason restart_reason =
98      extensions::core_api::runtime::OnRestartRequired::REASON_NONE;
99  switch (reason) {
100    case REBOOT_REASON_OS_UPDATE:
101      restart_reason =
102          extensions::core_api::runtime::OnRestartRequired::REASON_OS_UPDATE;
103      break;
104    case REBOOT_REASON_PERIODIC:
105      restart_reason =
106          extensions::core_api::runtime::OnRestartRequired::REASON_PERIODIC;
107      break;
108    default:
109      NOTREACHED() << "Unknown reboot reason=" << reason;
110      return;
111  }
112
113  extensions::RuntimeEventRouter::DispatchOnRestartRequiredEvent(
114      profile_, app_id_, restart_reason);
115}
116
117void KioskAppUpdateService::WillDestroyAutomaticRebootManager() {
118  automatic_reboot_manager_->RemoveObserver(this);
119  automatic_reboot_manager_ = NULL;
120}
121
122void KioskAppUpdateService::OnKioskAppCacheUpdated(const std::string& app_id) {
123  if (app_id != app_id_)
124    return;
125
126  extensions::RuntimeEventRouter::DispatchOnRestartRequiredEvent(
127      profile_,
128      app_id_,
129      extensions::core_api::runtime::OnRestartRequired::REASON_APP_UPDATE);
130
131  StartAppUpdateRestartTimer();
132}
133
134KioskAppUpdateServiceFactory::KioskAppUpdateServiceFactory()
135    : BrowserContextKeyedServiceFactory(
136        "KioskAppUpdateService",
137        BrowserContextDependencyManager::GetInstance()) {
138  DependsOn(
139      extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
140}
141
142KioskAppUpdateServiceFactory::~KioskAppUpdateServiceFactory() {
143}
144
145// static
146KioskAppUpdateService* KioskAppUpdateServiceFactory::GetForProfile(
147    Profile* profile) {
148  // This should never be called unless we are running in forced app mode.
149  DCHECK(chrome::IsRunningInForcedAppMode());
150  if (!chrome::IsRunningInForcedAppMode())
151    return NULL;
152
153  return static_cast<KioskAppUpdateService*>(
154      GetInstance()->GetServiceForBrowserContext(profile, true));
155}
156
157// static
158KioskAppUpdateServiceFactory* KioskAppUpdateServiceFactory::GetInstance() {
159  return Singleton<KioskAppUpdateServiceFactory>::get();
160}
161
162KeyedService* KioskAppUpdateServiceFactory::BuildServiceInstanceFor(
163    content::BrowserContext* context) const {
164  return new KioskAppUpdateService(
165      Profile::FromBrowserContext(context),
166      g_browser_process->platform_part()->automatic_reboot_manager());
167}
168
169}  // namespace chromeos
170