• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/startup_app_launcher.h"
6 
7 #include "base/command_line.h"
8 #include "base/files/file_path.h"
9 #include "base/json/json_file_value_serializer.h"
10 #include "base/path_service.h"
11 #include "base/time/time.h"
12 #include "base/values.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/chromeos/app_mode/app_session_lifetime.h"
15 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
16 #include "chrome/browser/chromeos/login/user_manager.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/extensions/extension_system.h"
19 #include "chrome/browser/extensions/updater/extension_updater.h"
20 #include "chrome/browser/extensions/webstore_startup_installer.h"
21 #include "chrome/browser/lifetime/application_lifetime.h"
22 #include "chrome/browser/signin/profile_oauth2_token_service.h"
23 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
24 #include "chrome/browser/ui/extensions/application_launch.h"
25 #include "chrome/common/chrome_paths.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "chrome/common/chrome_version_info.h"
28 #include "chrome/common/extensions/manifest_url_handler.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/notification_service.h"
31 #include "extensions/common/extension.h"
32 #include "extensions/common/manifest_handlers/kiosk_mode_info.h"
33 #include "google_apis/gaia/gaia_auth_consumer.h"
34 #include "google_apis/gaia/gaia_constants.h"
35 #include "net/base/load_flags.h"
36 #include "net/url_request/url_fetcher.h"
37 #include "net/url_request/url_fetcher_delegate.h"
38 #include "net/url_request/url_request_context_getter.h"
39 #include "net/url_request/url_request_status.h"
40 #include "url/gurl.h"
41 
42 using content::BrowserThread;
43 using extensions::Extension;
44 using extensions::WebstoreStartupInstaller;
45 
46 namespace chromeos {
47 
48 namespace {
49 
50 const char kOAuthRefreshToken[] = "refresh_token";
51 const char kOAuthClientId[] = "client_id";
52 const char kOAuthClientSecret[] = "client_secret";
53 
54 const base::FilePath::CharType kOAuthFileName[] =
55     FILE_PATH_LITERAL("kiosk_auth");
56 
57 }  // namespace
58 
StartupAppLauncher(Profile * profile,const std::string & app_id,StartupAppLauncher::Delegate * delegate)59 StartupAppLauncher::StartupAppLauncher(Profile* profile,
60                                        const std::string& app_id,
61                                        StartupAppLauncher::Delegate* delegate)
62     : profile_(profile),
63       app_id_(app_id),
64       delegate_(delegate),
65       install_attempted_(false),
66       ready_to_launch_(false) {
67   DCHECK(profile_);
68   DCHECK(Extension::IdIsValid(app_id_));
69 }
70 
~StartupAppLauncher()71 StartupAppLauncher::~StartupAppLauncher() {
72   // StartupAppLauncher can be deleted at anytime during the launch process
73   // through a user bailout shortcut.
74   ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
75       ->RemoveObserver(this);
76 }
77 
Initialize()78 void StartupAppLauncher::Initialize() {
79   StartLoadingOAuthFile();
80 }
81 
ContinueWithNetworkReady()82 void StartupAppLauncher::ContinueWithNetworkReady() {
83   // Starts install if it is not started.
84   if (!install_attempted_) {
85     install_attempted_ = true;
86     MaybeInstall();
87   }
88 }
89 
StartLoadingOAuthFile()90 void StartupAppLauncher::StartLoadingOAuthFile() {
91   delegate_->OnLoadingOAuthFile();
92 
93   KioskOAuthParams* auth_params = new KioskOAuthParams();
94   BrowserThread::PostBlockingPoolTaskAndReply(
95       FROM_HERE,
96       base::Bind(&StartupAppLauncher::LoadOAuthFileOnBlockingPool,
97                  auth_params),
98       base::Bind(&StartupAppLauncher::OnOAuthFileLoaded,
99                  AsWeakPtr(),
100                  base::Owned(auth_params)));
101 }
102 
103 // static.
LoadOAuthFileOnBlockingPool(KioskOAuthParams * auth_params)104 void StartupAppLauncher::LoadOAuthFileOnBlockingPool(
105     KioskOAuthParams* auth_params) {
106   int error_code = JSONFileValueSerializer::JSON_NO_ERROR;
107   std::string error_msg;
108   base::FilePath user_data_dir;
109   CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
110   base::FilePath auth_file = user_data_dir.Append(kOAuthFileName);
111   scoped_ptr<JSONFileValueSerializer> serializer(
112       new JSONFileValueSerializer(user_data_dir.Append(kOAuthFileName)));
113   scoped_ptr<base::Value> value(
114       serializer->Deserialize(&error_code, &error_msg));
115   base::DictionaryValue* dict = NULL;
116   if (error_code != JSONFileValueSerializer::JSON_NO_ERROR ||
117       !value.get() || !value->GetAsDictionary(&dict)) {
118     LOG(WARNING) << "Can't find auth file at " << auth_file.value();
119     return;
120   }
121 
122   dict->GetString(kOAuthRefreshToken, &auth_params->refresh_token);
123   dict->GetString(kOAuthClientId, &auth_params->client_id);
124   dict->GetString(kOAuthClientSecret, &auth_params->client_secret);
125 }
126 
OnOAuthFileLoaded(KioskOAuthParams * auth_params)127 void StartupAppLauncher::OnOAuthFileLoaded(KioskOAuthParams* auth_params) {
128   auth_params_ = *auth_params;
129   // Override chrome client_id and secret that will be used for identity
130   // API token minting.
131   if (!auth_params_.client_id.empty() && !auth_params_.client_secret.empty()) {
132     UserManager::Get()->SetAppModeChromeClientOAuthInfo(
133             auth_params_.client_id,
134             auth_params_.client_secret);
135   }
136 
137   // If we are restarting chrome (i.e. on crash), we need to initialize
138   // OAuth2TokenService as well.
139   InitializeTokenService();
140 }
141 
InitializeNetwork()142 void StartupAppLauncher::InitializeNetwork() {
143   delegate_->InitializeNetwork();
144 }
145 
InitializeTokenService()146 void StartupAppLauncher::InitializeTokenService() {
147   delegate_->OnInitializingTokenService();
148 
149   ProfileOAuth2TokenService* profile_token_service =
150       ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
151   if (profile_token_service->RefreshTokenIsAvailable(
152           profile_token_service->GetPrimaryAccountId()) ||
153       auth_params_.refresh_token.empty()) {
154     InitializeNetwork();
155   } else {
156     // Pass oauth2 refresh token from the auth file.
157     // TODO(zelidrag): We should probably remove this option after M27.
158     // TODO(fgorski): This can go when we have persistence implemented on PO2TS.
159     // Unless the code is no longer needed.
160     // TODO(rogerta): Now that this CL implements token persistence in PO2TS, is
161     // this code still needed?  See above two TODOs.
162     //
163     // ProfileOAuth2TokenService triggers either OnRefreshTokenAvailable or
164     // OnRefreshTokensLoaded. Given that we want to handle exactly one event,
165     // whichever comes first, both handlers call RemoveObserver on PO2TS.
166     // Handling any of the two events is the only way to resume the execution
167     // and enable Cleanup method to be called, self-invoking a destructor.
168     profile_token_service->AddObserver(this);
169 
170     profile_token_service->UpdateCredentials(
171         "kiosk_mode@localhost",
172         auth_params_.refresh_token);
173   }
174 }
175 
OnRefreshTokenAvailable(const std::string & account_id)176 void StartupAppLauncher::OnRefreshTokenAvailable(
177     const std::string& account_id) {
178   ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
179       ->RemoveObserver(this);
180   InitializeNetwork();
181 }
182 
OnRefreshTokensLoaded()183 void StartupAppLauncher::OnRefreshTokensLoaded() {
184   ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
185       ->RemoveObserver(this);
186   InitializeNetwork();
187 }
188 
LaunchApp()189 void StartupAppLauncher::LaunchApp() {
190   if (!ready_to_launch_) {
191     NOTREACHED();
192     LOG(ERROR) << "LaunchApp() called but launcher is not initialized.";
193   }
194 
195   const Extension* extension = extensions::ExtensionSystem::Get(profile_)->
196       extension_service()->GetInstalledExtension(app_id_);
197   CHECK(extension);
198 
199   if (!extensions::KioskModeInfo::IsKioskEnabled(extension)) {
200     OnLaunchFailure(KioskAppLaunchError::NOT_KIOSK_ENABLED);
201     return;
202   }
203 
204   // Always open the app in a window.
205   OpenApplication(AppLaunchParams(profile_, extension,
206                                   extensions::LAUNCH_CONTAINER_WINDOW,
207                                   NEW_WINDOW));
208   InitAppSession(profile_, app_id_);
209 
210   UserManager::Get()->SessionStarted();
211 
212   content::NotificationService::current()->Notify(
213       chrome::NOTIFICATION_KIOSK_APP_LAUNCHED,
214       content::NotificationService::AllSources(),
215       content::NotificationService::NoDetails());
216 
217   OnLaunchSuccess();
218 }
219 
OnLaunchSuccess()220 void StartupAppLauncher::OnLaunchSuccess() {
221   delegate_->OnLaunchSucceeded();
222 }
223 
OnLaunchFailure(KioskAppLaunchError::Error error)224 void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) {
225   LOG(ERROR) << "App launch failed, error: " << error;
226   DCHECK_NE(KioskAppLaunchError::NONE, error);
227 
228   delegate_->OnLaunchFailed(error);
229 }
230 
MaybeInstall()231 void StartupAppLauncher::MaybeInstall() {
232   delegate_->OnInstallingApp();
233 
234   ExtensionService* extension_service =
235       extensions::ExtensionSystem::Get(profile_)->extension_service();
236   if (!extension_service->GetInstalledExtension(app_id_)) {
237     BeginInstall();
238     return;
239   }
240 
241   extensions::ExtensionUpdater::CheckParams check_params;
242   check_params.ids.push_back(app_id_);
243   check_params.install_immediately = true;
244   check_params.callback =
245       base::Bind(&StartupAppLauncher::OnUpdateCheckFinished, AsWeakPtr());
246   extension_service->updater()->CheckNow(check_params);
247 }
248 
OnUpdateCheckFinished()249 void StartupAppLauncher::OnUpdateCheckFinished() {
250   OnReadyToLaunch();
251   UpdateAppData();
252 }
253 
BeginInstall()254 void StartupAppLauncher::BeginInstall() {
255   installer_ = new WebstoreStartupInstaller(
256       app_id_,
257       profile_,
258       false,
259       base::Bind(&StartupAppLauncher::InstallCallback, AsWeakPtr()));
260   installer_->BeginInstall();
261 }
262 
InstallCallback(bool success,const std::string & error)263 void StartupAppLauncher::InstallCallback(bool success,
264                                          const std::string& error) {
265   installer_ = NULL;
266   if (success) {
267     // Finish initialization after the callback returns.
268     // So that the app finishes its installation.
269     BrowserThread::PostTask(
270         BrowserThread::UI,
271         FROM_HERE,
272         base::Bind(&StartupAppLauncher::OnReadyToLaunch,
273                    AsWeakPtr()));
274     return;
275   }
276 
277   LOG(ERROR) << "App install failed: " << error;
278   OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
279 }
280 
OnReadyToLaunch()281 void StartupAppLauncher::OnReadyToLaunch() {
282   ready_to_launch_ = true;
283   delegate_->OnReadyToLaunch();
284 }
285 
UpdateAppData()286 void StartupAppLauncher::UpdateAppData() {
287   KioskAppManager::Get()->ClearAppData(app_id_);
288   KioskAppManager::Get()->UpdateAppDataFromProfile(app_id_, profile_, NULL);
289 }
290 
291 }   // namespace chromeos
292