Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(316)

Side by Side Diff: chrome/browser/chromeos/app_mode/startup_app_launcher.cc

Issue 22914008: Refactor kiosk app launch to be part of login screen UI flow. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 "ash/shell.h"
8 #include "base/command_line.h"
9 #include "base/files/file_path.h"
10 #include "base/json/json_file_value_serializer.h"
11 #include "base/path_service.h"
12 #include "base/time/time.h"
13 #include "base/values.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/chromeos/app_mode/app_session_lifetime.h"
16 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
17 #include "chrome/browser/chromeos/login/user_manager.h"
18 #include "chrome/browser/chromeos/ui/app_launch_view.h"
19 #include "chrome/browser/extensions/extension_service.h"
20 #include "chrome/browser/extensions/extension_system.h"
21 #include "chrome/browser/extensions/webstore_startup_installer.h"
22 #include "chrome/browser/lifetime/application_lifetime.h"
23 #include "chrome/browser/signin/profile_oauth2_token_service.h"
24 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
25 #include "chrome/browser/signin/token_service.h"
26 #include "chrome/browser/signin/token_service_factory.h"
27 #include "chrome/browser/ui/extensions/application_launch.h"
28 #include "chrome/common/chrome_paths.h"
29 #include "chrome/common/chrome_switches.h"
30 #include "chrome/common/extensions/extension.h"
31 #include "chrome/common/extensions/manifest_handlers/kiosk_enabled_info.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "content/public/browser/notification_service.h"
34 #include "google_apis/gaia/gaia_auth_consumer.h"
35 #include "google_apis/gaia/gaia_constants.h"
36
37 using content::BrowserThread;
38 using extensions::Extension;
39 using extensions::WebstoreStartupInstaller;
40
41 namespace chromeos {
42
43 namespace {
44
45 const char kOAuthRefreshToken[] = "refresh_token";
46 const char kOAuthClientId[] = "client_id";
47 const char kOAuthClientSecret[] = "client_secret";
48
49 const base::FilePath::CharType kOAuthFileName[] =
50 FILE_PATH_LITERAL("kiosk_auth");
51
52 // Application install splash screen minimum show time in milliseconds.
53 const int kAppInstallSplashScreenMinTimeMS = 3000;
54
55 bool IsAppInstalled(Profile* profile, const std::string& app_id) {
56 return extensions::ExtensionSystem::Get(profile)->extension_service()->
57 GetInstalledExtension(app_id);
58 }
59
60 } // namespace
61
62 StartupAppLauncher::StartupAppLauncher(Profile* profile,
63 const std::string& app_id)
64 : profile_(profile),
65 app_id_(app_id),
66 launch_splash_start_time_(0) {
67 DCHECK(profile_);
68 DCHECK(Extension::IdIsValid(app_id_));
69 DCHECK(ash::Shell::HasInstance());
70 ash::Shell::GetInstance()->AddPreTargetHandler(this);
71 }
72
73 StartupAppLauncher::~StartupAppLauncher() {
74 DCHECK(ash::Shell::HasInstance());
75 ash::Shell::GetInstance()->RemovePreTargetHandler(this);
76 }
77
78 void StartupAppLauncher::Start() {
79 launch_splash_start_time_ = base::TimeTicks::Now().ToInternalValue();
80 DVLOG(1) << "Starting... connection = "
81 << net::NetworkChangeNotifier::GetConnectionType();
82 chromeos::ShowAppLaunchSplashScreen(app_id_);
83 StartLoadingOAuthFile();
84 }
85
86 void StartupAppLauncher::StartLoadingOAuthFile() {
87 KioskOAuthParams* auth_params = new KioskOAuthParams();
88 BrowserThread::PostBlockingPoolTaskAndReply(
89 FROM_HERE,
90 base::Bind(&StartupAppLauncher::LoadOAuthFileOnBlockingPool,
91 auth_params),
92 base::Bind(&StartupAppLauncher::OnOAuthFileLoaded,
93 AsWeakPtr(),
94 base::Owned(auth_params)));
95 }
96
97 // static.
98 void StartupAppLauncher::LoadOAuthFileOnBlockingPool(
99 KioskOAuthParams* auth_params) {
100 int error_code = JSONFileValueSerializer::JSON_NO_ERROR;
101 std::string error_msg;
102 base::FilePath user_data_dir;
103 CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
104 base::FilePath auth_file = user_data_dir.Append(kOAuthFileName);
105 scoped_ptr<JSONFileValueSerializer> serializer(
106 new JSONFileValueSerializer(user_data_dir.Append(kOAuthFileName)));
107 scoped_ptr<base::Value> value(
108 serializer->Deserialize(&error_code, &error_msg));
109 base::DictionaryValue* dict = NULL;
110 if (error_code != JSONFileValueSerializer::JSON_NO_ERROR ||
111 !value.get() || !value->GetAsDictionary(&dict)) {
112 LOG(WARNING) << "Can't find auth file at " << auth_file.value();
113 return;
114 }
115
116 dict->GetString(kOAuthRefreshToken, &auth_params->refresh_token);
117 dict->GetString(kOAuthClientId, &auth_params->client_id);
118 dict->GetString(kOAuthClientSecret, &auth_params->client_secret);
119 }
120
121 void StartupAppLauncher::OnOAuthFileLoaded(KioskOAuthParams* auth_params) {
122 auth_params_ = *auth_params;
123 // Override chrome client_id and secret that will be used for identity
124 // API token minting.
125 if (!auth_params_.client_id.empty() && !auth_params_.client_secret.empty()) {
126 UserManager::Get()->SetAppModeChromeClientOAuthInfo(
127 auth_params_.client_id,
128 auth_params_.client_secret);
129 }
130
131 // If we are restarting chrome (i.e. on crash), we need to initialize
132 // TokenService as well.
133 InitializeTokenService();
134 }
135
136 void StartupAppLauncher::InitializeNetwork() {
137 chromeos::UpdateAppLaunchSplashScreenState(
138 chromeos::APP_LAUNCH_STATE_PREPARING_NETWORK);
139 // Set a maximum allowed wait time for network.
140 const int kMaxNetworkWaitSeconds = 5 * 60;
141 network_wait_timer_.Start(
142 FROM_HERE,
143 base::TimeDelta::FromSeconds(kMaxNetworkWaitSeconds),
144 this, &StartupAppLauncher::OnNetworkWaitTimedout);
145
146 net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
147 OnNetworkChanged(net::NetworkChangeNotifier::GetConnectionType());
148 }
149
150 void StartupAppLauncher::InitializeTokenService() {
151 chromeos::UpdateAppLaunchSplashScreenState(
152 chromeos::APP_LAUNCH_STATE_LOADING_TOKEN_SERVICE);
153 ProfileOAuth2TokenService* profile_token_service =
154 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
155 if (profile_token_service->RefreshTokenIsAvailable()) {
156 InitializeNetwork();
157 return;
158 }
159
160 // At the end of this method, the execution will be put on hold until
161 // ProfileOAuth2TokenService triggers either OnRefreshTokenAvailable or
162 // OnRefreshTokensLoaded. Given that we want to handle exactly one event,
163 // whichever comes first, both handlers call RemoveObserver on PO2TS. Handling
164 // any of the two events is the only way to resume the execution and enable
165 // Cleanup method to be called, self-invoking a destructor. In destructor
166 // StartupAppLauncher is no longer an observer of PO2TS and there is no need
167 // to call RemoveObserver again.
168 profile_token_service->AddObserver(this);
169
170 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
171 token_service->Initialize(GaiaConstants::kChromeSource, profile_);
172
173 // Pass oauth2 refresh token from the auth file.
174 // TODO(zelidrag): We should probably remove this option after M27.
175 // TODO(fgorski): This can go when we have persistence implemented on PO2TS.
176 // Unless the code is no longer needed.
177 if (!auth_params_.refresh_token.empty()) {
178 token_service->UpdateCredentialsWithOAuth2(
179 GaiaAuthConsumer::ClientOAuthResult(
180 auth_params_.refresh_token,
181 std::string(), // access_token
182 0)); // new_expires_in_secs
183 } else {
184 // Load whatever tokens we have stored there last time around.
185 token_service->LoadTokensFromDB();
186 }
187 }
188
189 void StartupAppLauncher::OnRefreshTokenAvailable(
190 const std::string& account_id) {
191 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
192 ->RemoveObserver(this);
193 InitializeNetwork();
194 }
195
196 void StartupAppLauncher::OnRefreshTokensLoaded() {
197 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
198 ->RemoveObserver(this);
199 InitializeNetwork();
200 }
201
202 void StartupAppLauncher::Cleanup() {
203 chromeos::CloseAppLaunchSplashScreen();
204
205 delete this;
206 }
207
208 void StartupAppLauncher::OnLaunchSuccess() {
209 const int64 time_taken_ms = (base::TimeTicks::Now() -
210 base::TimeTicks::FromInternalValue(launch_splash_start_time_)).
211 InMilliseconds();
212
213 // Enforce that we show app install splash screen for some minimum amount
214 // of time.
215 if (time_taken_ms < kAppInstallSplashScreenMinTimeMS) {
216 BrowserThread::PostDelayedTask(
217 BrowserThread::UI,
218 FROM_HERE,
219 base::Bind(&StartupAppLauncher::OnLaunchSuccess, AsWeakPtr()),
220 base::TimeDelta::FromMilliseconds(
221 kAppInstallSplashScreenMinTimeMS - time_taken_ms));
222 return;
223 }
224
225 Cleanup();
226 }
227
228 void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) {
229 DCHECK_NE(KioskAppLaunchError::NONE, error);
230
231 // Saves the error and ends the session to go back to login screen.
232 KioskAppLaunchError::Save(error);
233 chrome::AttemptUserExit();
234
235 Cleanup();
236 }
237
238 void StartupAppLauncher::Launch() {
239 const Extension* extension = extensions::ExtensionSystem::Get(profile_)->
240 extension_service()->GetInstalledExtension(app_id_);
241 CHECK(extension);
242
243 if (!extensions::KioskEnabledInfo::IsKioskEnabled(extension)) {
244 OnLaunchFailure(KioskAppLaunchError::NOT_KIOSK_ENABLED);
245 return;
246 }
247
248 // Always open the app in a window.
249 chrome::OpenApplication(chrome::AppLaunchParams(profile_,
250 extension,
251 extension_misc::LAUNCH_WINDOW,
252 NEW_WINDOW));
253 InitAppSession(profile_, app_id_);
254
255 content::NotificationService::current()->Notify(
256 chrome::NOTIFICATION_KIOSK_APP_LAUNCHED,
257 content::NotificationService::AllSources(),
258 content::NotificationService::NoDetails());
259
260 OnLaunchSuccess();
261 }
262
263 void StartupAppLauncher::BeginInstall() {
264 DVLOG(1) << "BeginInstall... connection = "
265 << net::NetworkChangeNotifier::GetConnectionType();
266
267 chromeos::UpdateAppLaunchSplashScreenState(
268 chromeos::APP_LAUNCH_STATE_INSTALLING_APPLICATION);
269
270 if (IsAppInstalled(profile_, app_id_)) {
271 Launch();
272 return;
273 }
274
275 installer_ = new WebstoreStartupInstaller(
276 app_id_,
277 profile_,
278 false,
279 base::Bind(&StartupAppLauncher::InstallCallback, AsWeakPtr()));
280 installer_->BeginInstall();
281 }
282
283 void StartupAppLauncher::InstallCallback(bool success,
284 const std::string& error) {
285 installer_ = NULL;
286 if (success) {
287 // Schedules Launch() to be called after the callback returns.
288 // So that the app finishes its installation.
289 BrowserThread::PostTask(
290 BrowserThread::UI,
291 FROM_HERE,
292 base::Bind(&StartupAppLauncher::Launch, AsWeakPtr()));
293 return;
294 }
295
296 LOG(ERROR) << "Failed to install app with error: " << error;
297 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
298 }
299
300 void StartupAppLauncher::OnNetworkWaitTimedout() {
301 LOG(WARNING) << "OnNetworkWaitTimedout... connection = "
302 << net::NetworkChangeNotifier::GetConnectionType();
303 // Timeout in waiting for online. Try the install anyway.
304 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
305 BeginInstall();
306 }
307
308 void StartupAppLauncher::OnNetworkChanged(
309 net::NetworkChangeNotifier::ConnectionType type) {
310 DVLOG(1) << "OnNetworkChanged... connection = "
311 << net::NetworkChangeNotifier::GetConnectionType();
312 if (!net::NetworkChangeNotifier::IsOffline()) {
313 DVLOG(1) << "Network up and running!";
314 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
315 network_wait_timer_.Stop();
316
317 BeginInstall();
318 } else {
319 DVLOG(1) << "Network not running yet!";
320 }
321 }
322
323 void StartupAppLauncher::OnKeyEvent(ui::KeyEvent* event) {
324 if (event->type() != ui::ET_KEY_PRESSED)
325 return;
326
327 if (KioskAppManager::Get()->GetDisableBailoutShortcut())
328 return;
329
330 if (event->key_code() != ui::VKEY_S ||
331 !(event->flags() & ui::EF_CONTROL_DOWN) ||
332 !(event->flags() & ui::EF_ALT_DOWN)) {
333 return;
334 }
335
336 OnLaunchFailure(KioskAppLaunchError::USER_CANCEL);
337 }
338
339 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698