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