OLD | NEW |
| (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/kiosk_app_launcher.h" | |
6 | |
7 #include "base/chromeos/chromeos_version.h" | |
8 #include "base/logging.h" | |
9 #include "base/memory/weak_ptr.h" | |
10 #include "base/message_loop/message_loop.h" | |
11 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" | |
12 #include "chrome/browser/chromeos/app_mode/startup_app_launcher.h" | |
13 #include "chrome/browser/chromeos/login/login_display_host_impl.h" | |
14 #include "chrome/browser/chromeos/login/login_utils.h" | |
15 #include "chrome/browser/chromeos/login/user_manager.h" | |
16 #include "chrome/browser/chromeos/settings/cros_settings.h" | |
17 #include "chrome/browser/chromeos/ui/app_launch_view.h" | |
18 #include "chrome/browser/lifetime/application_lifetime.h" | |
19 #include "chromeos/cryptohome/async_method_caller.h" | |
20 #include "chromeos/cryptohome/cryptohome_library.h" | |
21 #include "chromeos/dbus/cryptohome_client.h" | |
22 #include "chromeos/dbus/dbus_thread_manager.h" | |
23 #include "content/public/browser/browser_thread.h" | |
24 | |
25 using content::BrowserThread; | |
26 | |
27 namespace chromeos { | |
28 | |
29 namespace { | |
30 | |
31 void IgnoreResult(bool mount_success, cryptohome::MountError mount_error) {} | |
32 | |
33 } // namespace | |
34 | |
35 // static | |
36 KioskAppLauncher* KioskAppLauncher::running_instance_ = NULL; | |
37 | |
38 //////////////////////////////////////////////////////////////////////////////// | |
39 // KioskAppLauncher::CryptohomedChecker ensures cryptohome daemon is up | |
40 // and running by issuing an IsMounted call. If the call does not go through | |
41 // and chromeos::DBUS_METHOD_CALL_SUCCESS is not returned, it will retry after | |
42 // some time out and at the maximum five times before it gives up. Upon | |
43 // success, it resumes the launch by calling KioskAppLauncher::StartMount. | |
44 | |
45 class KioskAppLauncher::CryptohomedChecker | |
46 : public base::SupportsWeakPtr<CryptohomedChecker> { | |
47 public: | |
48 explicit CryptohomedChecker(KioskAppLauncher* launcher) | |
49 : launcher_(launcher), | |
50 retry_count_(0) { | |
51 } | |
52 ~CryptohomedChecker() {} | |
53 | |
54 void StartCheck() { | |
55 chromeos::DBusThreadManager::Get()->GetCryptohomeClient()->IsMounted( | |
56 base::Bind(&CryptohomedChecker::OnCryptohomeIsMounted, | |
57 AsWeakPtr())); | |
58 } | |
59 | |
60 private: | |
61 void OnCryptohomeIsMounted(chromeos::DBusMethodCallStatus call_status, | |
62 bool is_mounted) { | |
63 if (call_status != chromeos::DBUS_METHOD_CALL_SUCCESS) { | |
64 const int kMaxRetryTimes = 5; | |
65 ++retry_count_; | |
66 if (retry_count_ > kMaxRetryTimes) { | |
67 LOG(ERROR) << "Could not talk to cryptohomed for launching kiosk app."; | |
68 ReportCheckResult(KioskAppLaunchError::CRYPTOHOMED_NOT_RUNNING); | |
69 return; | |
70 } | |
71 | |
72 const int retry_delay_in_milliseconds = 500 * (1 << retry_count_); | |
73 base::MessageLoop::current()->PostDelayedTask( | |
74 FROM_HERE, | |
75 base::Bind(&CryptohomedChecker::StartCheck, AsWeakPtr()), | |
76 base::TimeDelta::FromMilliseconds(retry_delay_in_milliseconds)); | |
77 return; | |
78 } | |
79 | |
80 if (is_mounted) | |
81 LOG(ERROR) << "Cryptohome is mounted before launching kiosk app."; | |
82 | |
83 // Proceed only when cryptohome is not mounded or running on dev box. | |
84 if (!is_mounted || !base::chromeos::IsRunningOnChromeOS()) | |
85 ReportCheckResult(KioskAppLaunchError::NONE); | |
86 else | |
87 ReportCheckResult(KioskAppLaunchError::ALREADY_MOUNTED); | |
88 } | |
89 | |
90 void ReportCheckResult(KioskAppLaunchError::Error error) { | |
91 if (error == KioskAppLaunchError::NONE) | |
92 launcher_->StartMount(); | |
93 else | |
94 launcher_->ReportLaunchResult(error); | |
95 } | |
96 | |
97 KioskAppLauncher* launcher_; | |
98 int retry_count_; | |
99 | |
100 DISALLOW_COPY_AND_ASSIGN(CryptohomedChecker); | |
101 }; | |
102 | |
103 //////////////////////////////////////////////////////////////////////////////// | |
104 // KioskAppLauncher::ProfileLoader creates or loads the app profile. | |
105 | |
106 class KioskAppLauncher::ProfileLoader : public LoginUtils::Delegate { | |
107 public: | |
108 ProfileLoader(KioskAppManager* kiosk_app_manager, | |
109 KioskAppLauncher* kiosk_app_launcher) | |
110 : kiosk_app_launcher_(kiosk_app_launcher), | |
111 user_id_(kiosk_app_launcher->user_id_) { | |
112 CHECK(!user_id_.empty()); | |
113 } | |
114 | |
115 virtual ~ProfileLoader() { | |
116 LoginUtils::Get()->DelegateDeleted(this); | |
117 } | |
118 | |
119 void Start() { | |
120 cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername( | |
121 user_id_, | |
122 base::Bind(&ProfileLoader::OnUsernameHashRetrieved, | |
123 base::Unretained(this))); | |
124 } | |
125 | |
126 private: | |
127 void OnUsernameHashRetrieved(bool success, | |
128 const std::string& username_hash) { | |
129 if (!success) { | |
130 LOG(ERROR) << "Unable to retrieve username hash for user '" << user_id_ | |
131 << "'."; | |
132 kiosk_app_launcher_->ReportLaunchResult( | |
133 KioskAppLaunchError::UNABLE_TO_RETRIEVE_HASH); | |
134 return; | |
135 } | |
136 LoginUtils::Get()->PrepareProfile( | |
137 UserContext(user_id_, | |
138 std::string(), // password | |
139 std::string(), // auth_code | |
140 username_hash), | |
141 std::string(), // display email | |
142 false, // using_oauth | |
143 false, // has_cookies | |
144 false, // has_active_session | |
145 this); | |
146 } | |
147 | |
148 // LoginUtils::Delegate overrides: | |
149 virtual void OnProfilePrepared(Profile* profile) OVERRIDE { | |
150 kiosk_app_launcher_->OnProfilePrepared(profile); | |
151 } | |
152 | |
153 KioskAppLauncher* kiosk_app_launcher_; | |
154 std::string user_id_; | |
155 | |
156 DISALLOW_COPY_AND_ASSIGN(ProfileLoader); | |
157 }; | |
158 | |
159 //////////////////////////////////////////////////////////////////////////////// | |
160 // KioskAppLauncher | |
161 | |
162 KioskAppLauncher::KioskAppLauncher(KioskAppManager* kiosk_app_manager, | |
163 const std::string& app_id) | |
164 : kiosk_app_manager_(kiosk_app_manager), | |
165 app_id_(app_id), | |
166 remove_attempted_(false) { | |
167 KioskAppManager::App app; | |
168 CHECK(kiosk_app_manager_->GetApp(app_id_, &app)); | |
169 user_id_ = app.user_id; | |
170 } | |
171 | |
172 KioskAppLauncher::~KioskAppLauncher() {} | |
173 | |
174 void KioskAppLauncher::Start() { | |
175 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
176 | |
177 if (running_instance_) { | |
178 LOG(WARNING) << "Unable to launch " << app_id_ << "with a pending launch."; | |
179 ReportLaunchResult(KioskAppLaunchError::HAS_PENDING_LAUNCH); | |
180 return; | |
181 } | |
182 | |
183 running_instance_ = this; // Reset in ReportLaunchResult. | |
184 | |
185 // Show app launch splash. The spash is removed either after a successful | |
186 // launch or chrome exit because of launch failure. | |
187 chromeos::ShowAppLaunchSplashScreen(app_id_); | |
188 | |
189 // Check cryptohomed. If all goes good, flow goes to StartMount. Otherwise | |
190 // it goes to ReportLaunchResult with failure. | |
191 crytohomed_checker.reset(new CryptohomedChecker(this)); | |
192 crytohomed_checker->StartCheck(); | |
193 } | |
194 | |
195 void KioskAppLauncher::ReportLaunchResult(KioskAppLaunchError::Error error) { | |
196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
197 | |
198 running_instance_ = NULL; | |
199 | |
200 if (error != KioskAppLaunchError::NONE) { | |
201 // Saves the error and ends the session to go back to login screen. | |
202 KioskAppLaunchError::Save(error); | |
203 chrome::AttemptUserExit(); | |
204 } | |
205 | |
206 delete this; | |
207 } | |
208 | |
209 void KioskAppLauncher::StartMount() { | |
210 // Nuke old home that uses |app_id_| as cryptohome user id. | |
211 // TODO(xiyuan): Remove this after all clients migrated to new home. | |
212 cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove( | |
213 app_id_, | |
214 base::Bind(&IgnoreResult)); | |
215 | |
216 cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountPublic( | |
217 user_id_, | |
218 cryptohome::CREATE_IF_MISSING, | |
219 base::Bind(&KioskAppLauncher::MountCallback, | |
220 base::Unretained(this))); | |
221 } | |
222 | |
223 void KioskAppLauncher::MountCallback(bool mount_success, | |
224 cryptohome::MountError mount_error) { | |
225 if (mount_success) { | |
226 profile_loader_.reset(new ProfileLoader(kiosk_app_manager_, this)); | |
227 profile_loader_->Start(); | |
228 return; | |
229 } | |
230 | |
231 if (!remove_attempted_) { | |
232 LOG(ERROR) << "Attempt to remove app cryptohome because of mount failure" | |
233 << ", mount error=" << mount_error; | |
234 | |
235 remove_attempted_ = true; | |
236 AttemptRemove(); | |
237 return; | |
238 } | |
239 | |
240 LOG(ERROR) << "Failed to mount app cryptohome, error=" << mount_error; | |
241 ReportLaunchResult(KioskAppLaunchError::UNABLE_TO_MOUNT); | |
242 } | |
243 | |
244 void KioskAppLauncher::AttemptRemove() { | |
245 cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove( | |
246 user_id_, | |
247 base::Bind(&KioskAppLauncher::RemoveCallback, | |
248 base::Unretained(this))); | |
249 } | |
250 | |
251 void KioskAppLauncher::RemoveCallback(bool success, | |
252 cryptohome::MountError return_code) { | |
253 if (success) { | |
254 StartMount(); | |
255 return; | |
256 } | |
257 | |
258 LOG(ERROR) << "Failed to remove app cryptohome, erro=" << return_code; | |
259 ReportLaunchResult(KioskAppLaunchError::UNABLE_TO_REMOVE); | |
260 } | |
261 | |
262 void KioskAppLauncher::OnProfilePrepared(Profile* profile) { | |
263 // StartupAppLauncher deletes itself when done. | |
264 (new chromeos::StartupAppLauncher(profile, app_id_))->Start(); | |
265 | |
266 if (LoginDisplayHostImpl::default_host()) | |
267 LoginDisplayHostImpl::default_host()->Finalize(); | |
268 UserManager::Get()->SessionStarted(); | |
269 | |
270 ReportLaunchResult(KioskAppLaunchError::NONE); | |
271 } | |
272 | |
273 } // namespace chromeos | |
OLD | NEW |