OLD | NEW |
---|---|
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/chromeos/lock_screen_apps/app_manager_impl.h" | 5 #include "chrome/browser/chromeos/lock_screen_apps/app_manager_impl.h" |
6 | 6 |
7 #include <memory> | |
8 #include <utility> | |
9 | |
10 #include "apps/launcher.h" | |
11 #include "base/bind.h" | |
12 #include "base/bind_helpers.h" | |
13 #include "base/files/file_path.h" | |
14 #include "base/files/file_util.h" | |
15 #include "base/files/scoped_temp_dir.h" | |
16 #include "base/memory/ptr_util.h" | |
17 #include "base/memory/ref_counted.h" | |
18 #include "base/sequenced_task_runner.h" | |
19 #include "base/strings/string16.h" | |
20 #include "base/threading/thread_task_runner_handle.h" | |
21 #include "chrome/browser/chromeos/note_taking_helper.h" | |
7 #include "chrome/browser/chromeos/profiles/profile_helper.h" | 22 #include "chrome/browser/chromeos/profiles/profile_helper.h" |
23 #include "chrome/browser/extensions/extension_assets_manager.h" | |
24 #include "chrome/browser/extensions/extension_service.h" | |
25 #include "chrome/common/pref_names.h" | |
26 #include "extensions/browser/extension_registry.h" | |
27 #include "extensions/browser/extension_system.h" | |
28 #include "extensions/browser/install_flag.h" | |
29 #include "extensions/common/api/app_runtime.h" | |
30 #include "extensions/common/extension.h" | |
31 #include "extensions/common/extension_set.h" | |
32 #include "extensions/common/file_util.h" | |
33 #include "extensions/common/manifest.h" | |
8 | 34 |
9 namespace lock_screen_apps { | 35 namespace lock_screen_apps { |
10 | 36 |
11 AppManagerImpl::AppManagerImpl() = default; | 37 namespace { |
38 | |
39 using ExtensionCallback = base::Callback<void( | |
40 const scoped_refptr<const extensions::Extension>& extension)>; | |
41 | |
42 void InvokeCallbackOnTaskRunner( | |
43 const ExtensionCallback& callback, | |
44 const scoped_refptr<base::SequencedTaskRunner>& task_runner, | |
45 const scoped_refptr<const extensions::Extension>& extension) { | |
46 task_runner->PostTask(FROM_HERE, base::Bind(callback, extension)); | |
47 } | |
48 | |
49 // Loads extension with the provided |extension_id|, |location|, and | |
50 // |creation_flags| from the |version_dir| directory - directory to which the | |
51 // extension has been installed. | |
52 // |temp_copy| - scoped dir that contains the path from which extension | |
53 // resources have been installed. not used in this method, but passed around | |
54 // to keep the directory in scope while the app is being installed. | |
55 // |callback| - callback to which the loaded ap pshould be passed. | |
56 void LoadInstalledExtension(const std::string& extension_id, | |
57 extensions::Manifest::Location install_source, | |
58 int creation_flags, | |
59 std::unique_ptr<base::ScopedTempDir> temp_copy, | |
60 const ExtensionCallback& callback, | |
61 const base::FilePath& version_dir) { | |
62 if (version_dir.empty()) { | |
63 callback.Run(nullptr); | |
64 return; | |
65 } | |
66 | |
67 std::string error; | |
68 scoped_refptr<const extensions::Extension> extension = | |
69 extensions::file_util::LoadExtension( | |
70 version_dir, extension_id, install_source, creation_flags, &error); | |
71 callback.Run(extension); | |
72 } | |
73 | |
74 // Installs |extension| as a copy of an extension unpacked at |original_path| | |
75 // into |target_install_dir|. | |
76 // |profile| is the profile to which the extension is being installed. | |
77 // |callback| - called with the app loaded from the final installation path, | |
Devlin
2017/07/06 15:14:14
nit: s/,/.
tbarzic
2017/07/06 16:36:15
Done.
| |
78 void InstallExtensionCopy( | |
79 const scoped_refptr<const extensions::Extension>& extension, | |
80 const base::FilePath& original_path, | |
81 const base::FilePath& target_install_dir, | |
82 Profile* profile, | |
83 const ExtensionCallback& callback) { | |
84 base::FilePath target_dir = target_install_dir.Append(extension->id()); | |
85 base::FilePath install_temp_dir = | |
86 extensions::file_util::GetInstallTempDir(target_dir); | |
87 auto extension_temp_dir = base::MakeUnique<base::ScopedTempDir>(); | |
88 if (install_temp_dir.empty() || | |
89 !extension_temp_dir->CreateUniqueTempDirUnderPath(install_temp_dir)) { | |
90 callback.Run(nullptr); | |
91 return; | |
92 } | |
93 | |
94 // Copy the original extension path to a temp path to prevent | |
95 // ExtensionAssetsManager from deleting it (as InstallExtension renames the | |
96 // source path to a new location under the target install directory). | |
97 base::FilePath temp_copy = | |
98 extension_temp_dir->GetPath().Append(original_path.BaseName()); | |
99 if (!base::CopyDirectory(original_path, temp_copy, true /* recursive */)) { | |
100 callback.Run(nullptr); | |
101 return; | |
102 } | |
103 | |
104 // Note: |extension_temp_dir| is passed around to ensure it stays in scope | |
105 // until the app installation is done. | |
106 extensions::ExtensionAssetsManager::GetInstance()->InstallExtension( | |
107 extension.get(), temp_copy, target_install_dir, profile, | |
108 base::Bind(&LoadInstalledExtension, extension->id(), | |
109 extension->location(), extension->creation_flags(), | |
110 base::Passed(std::move(extension_temp_dir)), callback)); | |
111 } | |
112 | |
113 } // namespace | |
114 | |
115 AppManagerImpl::AppManagerImpl() | |
116 : extensions_observer_(this), weak_ptr_factory_(this) {} | |
12 | 117 |
13 AppManagerImpl::~AppManagerImpl() = default; | 118 AppManagerImpl::~AppManagerImpl() = default; |
14 | 119 |
15 void AppManagerImpl::Initialize(Profile* primary_profile, | 120 void AppManagerImpl::Initialize(Profile* primary_profile, |
16 Profile* lock_screen_profile) { | 121 Profile* lock_screen_profile) { |
122 DCHECK_EQ(State::kNotInitialized, state_); | |
17 DCHECK(primary_profile); | 123 DCHECK(primary_profile); |
18 DCHECK(lock_screen_profile); | 124 DCHECK(lock_screen_profile); |
19 DCHECK_NE(primary_profile, lock_screen_profile); | 125 DCHECK_NE(primary_profile, lock_screen_profile); |
20 // Do not use OTR profile for lock screen apps. This is important for | 126 // Do not use OTR profile for lock screen apps. This is important for |
21 // profile usage in |LaunchNoteTaking| - lock screen app background page runs | 127 // profile usage in |LaunchNoteTaking| - lock screen app background page runs |
22 // in original, non off the record profile, so the launch event has to be | 128 // in original, non off the record profile, so the launch event has to be |
23 // dispatched to that profile. For other |lock_screen_profile_|, it makes no | 129 // dispatched to that profile. For other |lock_screen_profile_|, it makes no |
24 // difference - the profile is used to get browser context keyed services, all | 130 // difference - the profile is used to get browser context keyed services, all |
25 // of which redirect OTR profile to the original one. | 131 // of which redirect OTR profile to the original one. |
26 DCHECK(!lock_screen_profile->IsOffTheRecord()); | 132 DCHECK(!lock_screen_profile->IsOffTheRecord()); |
27 | 133 |
28 CHECK(!chromeos::ProfileHelper::Get()->GetUserByProfile(lock_screen_profile)) | 134 CHECK(!chromeos::ProfileHelper::Get()->GetUserByProfile(lock_screen_profile)) |
29 << "Lock screen profile should not be associated with any users."; | 135 << "Lock screen profile should not be associated with any users."; |
30 | 136 |
31 primary_profile_ = primary_profile; | 137 primary_profile_ = primary_profile; |
32 lock_screen_profile_ = lock_screen_profile; | 138 lock_screen_profile_ = lock_screen_profile; |
139 state_ = State::kInactive; | |
140 | |
141 pref_change_registrar_.Init(primary_profile->GetPrefs()); | |
142 pref_change_registrar_.Add( | |
143 prefs::kNoteTakingAppId, | |
144 base::Bind(&AppManagerImpl::OnNoteTakingExtensionChanged, | |
145 base::Unretained(this))); | |
146 pref_change_registrar_.Add( | |
147 prefs::kNoteTakingAppEnabledOnLockScreen, | |
148 base::Bind(&AppManagerImpl::OnNoteTakingExtensionChanged, | |
149 base::Unretained(this))); | |
33 } | 150 } |
34 | 151 |
35 void AppManagerImpl::Start(const base::Closure& note_taking_changed_callback) { | 152 void AppManagerImpl::Start(const base::Closure& note_taking_changed_callback) { |
153 DCHECK_NE(State::kNotInitialized, state_); | |
154 | |
36 note_taking_changed_callback_ = note_taking_changed_callback; | 155 note_taking_changed_callback_ = note_taking_changed_callback; |
37 } | 156 extensions_observer_.Add( |
38 | 157 extensions::ExtensionRegistry::Get(primary_profile_)); |
39 void AppManagerImpl::Stop() {} | 158 |
159 if (state_ == State::kActive) | |
160 return; | |
161 | |
162 lock_screen_app_id_.clear(); | |
163 std::string app_id = FindLockScreenNoteTakingApp(); | |
164 if (app_id.empty()) { | |
165 state_ = State::kAppUnavailable; | |
166 return; | |
167 } | |
168 | |
169 state_ = AddAppToLockScreenProfile(app_id); | |
170 if (state_ == State::kActive || state_ == State::kActivating) | |
171 lock_screen_app_id_ = app_id; | |
172 } | |
173 | |
174 void AppManagerImpl::Stop() { | |
175 DCHECK_NE(State::kNotInitialized, state_); | |
176 | |
177 note_taking_changed_callback_.Reset(); | |
178 extensions_observer_.RemoveAll(); | |
179 | |
180 if (state_ == State::kInactive) | |
181 return; | |
182 | |
183 RemoveAppFromLockScreenProfile(lock_screen_app_id_); | |
184 lock_screen_app_id_.clear(); | |
185 state_ = State::kInactive; | |
186 } | |
40 | 187 |
41 bool AppManagerImpl::IsNoteTakingAppAvailable() const { | 188 bool AppManagerImpl::IsNoteTakingAppAvailable() const { |
42 return false; | 189 return state_ == State::kActive && !lock_screen_app_id_.empty(); |
43 } | 190 } |
44 | 191 |
45 std::string AppManagerImpl::GetNoteTakingAppId() const { | 192 std::string AppManagerImpl::GetNoteTakingAppId() const { |
46 return std::string(); | 193 if (!IsNoteTakingAppAvailable()) |
194 return std::string(); | |
195 return lock_screen_app_id_; | |
47 } | 196 } |
48 | 197 |
49 bool AppManagerImpl::LaunchNoteTaking() { | 198 bool AppManagerImpl::LaunchNoteTaking() { |
50 return false; | 199 if (!IsNoteTakingAppAvailable()) |
200 return false; | |
201 | |
202 const extensions::ExtensionRegistry* extension_registry = | |
203 extensions::ExtensionRegistry::Get(lock_screen_profile_); | |
204 const extensions::Extension* app = extension_registry->GetExtensionById( | |
205 lock_screen_app_id_, extensions::ExtensionRegistry::ENABLED); | |
206 if (!app) | |
207 return false; | |
208 | |
209 auto action_data = | |
210 base::MakeUnique<extensions::api::app_runtime::ActionData>(); | |
211 action_data->action_type = | |
212 extensions::api::app_runtime::ActionType::ACTION_TYPE_NEW_NOTE; | |
213 action_data->is_lock_screen_action = base::MakeUnique<bool>(true); | |
214 | |
215 apps::LaunchPlatformAppWithAction(lock_screen_profile_, app, | |
216 std::move(action_data), base::FilePath()); | |
217 return true; | |
218 } | |
219 | |
220 void AppManagerImpl::OnExtensionLoaded(content::BrowserContext* browser_context, | |
221 const extensions::Extension* extension) { | |
222 if (extension->id() == | |
223 primary_profile_->GetPrefs()->GetString(prefs::kNoteTakingAppId)) { | |
224 OnNoteTakingExtensionChanged(); | |
225 } | |
226 } | |
227 | |
228 void AppManagerImpl::OnExtensionUnloaded( | |
229 content::BrowserContext* browser_context, | |
230 const extensions::Extension* extension, | |
231 extensions::UnloadedExtensionReason reason) { | |
232 if (extension->id() == lock_screen_app_id_) | |
233 OnNoteTakingExtensionChanged(); | |
234 } | |
235 | |
236 void AppManagerImpl::OnNoteTakingExtensionChanged() { | |
237 if (state_ == State::kInactive) | |
238 return; | |
239 | |
240 std::string app_id = FindLockScreenNoteTakingApp(); | |
241 if (app_id == lock_screen_app_id_) | |
242 return; | |
243 | |
244 RemoveAppFromLockScreenProfile(lock_screen_app_id_); | |
245 lock_screen_app_id_.clear(); | |
246 | |
247 state_ = AddAppToLockScreenProfile(app_id); | |
248 if (state_ == State::kActive || state_ == State::kActivating) | |
249 lock_screen_app_id_ = app_id; | |
250 | |
251 if (!note_taking_changed_callback_.is_null()) | |
252 note_taking_changed_callback_.Run(); | |
253 } | |
254 | |
255 std::string AppManagerImpl::FindLockScreenNoteTakingApp() const { | |
256 // Note that lock screen does not currently support Android apps, so | |
257 // it's enough to only check the state of the preferred Chrome app. | |
258 std::unique_ptr<chromeos::NoteTakingAppInfo> note_taking_app = | |
259 chromeos::NoteTakingHelper::Get()->GetPreferredChromeAppInfo( | |
260 primary_profile_); | |
261 | |
262 if (!note_taking_app || | |
263 note_taking_app->lock_screen_support != | |
264 chromeos::NoteTakingLockScreenSupport::kSelected) { | |
265 return std::string(); | |
266 } | |
267 | |
268 return note_taking_app->app_id; | |
269 } | |
270 | |
271 AppManagerImpl::State AppManagerImpl::AddAppToLockScreenProfile( | |
272 const std::string& app_id) { | |
273 extensions::ExtensionRegistry* primary_registry = | |
274 extensions::ExtensionRegistry::Get(primary_profile_); | |
275 const extensions::Extension* app = | |
276 primary_registry->enabled_extensions().GetByID(app_id); | |
277 if (!app) | |
278 return State::kAppUnavailable; | |
279 | |
280 bool is_unpacked = extensions::Manifest::IsUnpackedLocation(app->location()); | |
281 | |
282 // Unpacked apps in lock screen profile will be loaded from their original | |
283 // file path, so their path will be the same as the primary profile app's. | |
284 // For the rest, the app will be copied to a location in the lock screen | |
285 // profile's extension install directory (using |InstallExtensionCopy|) - the | |
286 // exact final path is not known at this point, and will be set as part of | |
287 // |InstallExtensionCopy|. | |
288 base::FilePath lock_profile_app_path = | |
289 is_unpacked ? app->path() : base::FilePath(); | |
290 | |
291 std::string error; | |
292 scoped_refptr<extensions::Extension> lock_profile_app = | |
293 extensions::Extension::Create(lock_profile_app_path, app->location(), | |
294 *app->manifest()->value()->CreateDeepCopy(), | |
295 app->creation_flags(), app->id(), &error); | |
296 | |
297 // While extension creation can fail in general, in this case the lock screen | |
298 // profile extension creation arguments come from an app already installed in | |
299 // a user profile. If the extension parameters were invalid, the app would not | |
300 // exist in a user profile, and thus |app| would be nullptr, which is not the | |
301 // case at this point. | |
302 DCHECK(lock_profile_app); | |
303 | |
304 install_count_++; | |
305 | |
306 if (is_unpacked) { | |
307 InstallAndEnableLockScreenAppInLockScreenProfile(lock_profile_app.get()); | |
308 return State::kActive; | |
309 } | |
310 | |
311 ExtensionService* lock_screen_service = | |
312 extensions::ExtensionSystem::Get(lock_screen_profile_) | |
313 ->extension_service(); | |
314 | |
315 lock_screen_service->GetFileTaskRunner()->PostTask( | |
316 FROM_HERE, | |
317 base::Bind( | |
318 &InstallExtensionCopy, lock_profile_app, app->path(), | |
319 lock_screen_service->install_directory(), lock_screen_profile_, | |
320 base::Bind(&InvokeCallbackOnTaskRunner, | |
321 base::Bind(&AppManagerImpl::CompleteLockScreenAppInstall, | |
322 weak_ptr_factory_.GetWeakPtr(), install_count_), | |
323 base::ThreadTaskRunnerHandle::Get()))); | |
324 return State::kActivating; | |
325 } | |
326 | |
327 void AppManagerImpl::CompleteLockScreenAppInstall( | |
328 int install_id, | |
329 const scoped_refptr<const extensions::Extension>& app) { | |
330 // Bail out if the app manager is no longer waiting for this app's | |
331 // installation - the copied resources will be cleaned up when the (ephemeral) | |
332 // lock screen profile is destroyed. | |
333 if (install_id != install_count_ || state_ != State::kActivating) | |
334 return; | |
335 | |
336 if (app) { | |
337 DCHECK_EQ(lock_screen_app_id_, app->id()); | |
338 InstallAndEnableLockScreenAppInLockScreenProfile(app.get()); | |
339 state_ = State::kActive; | |
340 } else { | |
341 state_ = State::kAppUnavailable; | |
342 } | |
343 | |
344 if (!note_taking_changed_callback_.is_null()) | |
345 note_taking_changed_callback_.Run(); | |
346 } | |
347 | |
348 void AppManagerImpl::InstallAndEnableLockScreenAppInLockScreenProfile( | |
349 const extensions::Extension* app) { | |
350 ExtensionService* lock_screen_service = | |
351 extensions::ExtensionSystem::Get(lock_screen_profile_) | |
352 ->extension_service(); | |
353 | |
354 lock_screen_service->OnExtensionInstalled( | |
355 app, syncer::StringOrdinal(), extensions::kInstallFlagInstallImmediately); | |
356 lock_screen_service->EnableExtension(app->id()); | |
357 } | |
358 | |
359 void AppManagerImpl::RemoveAppFromLockScreenProfile(const std::string& app_id) { | |
360 if (app_id.empty()) | |
361 return; | |
362 | |
363 extensions::ExtensionRegistry* lock_screen_registry = | |
364 extensions::ExtensionRegistry::Get(lock_screen_profile_); | |
365 if (!lock_screen_registry->GetExtensionById( | |
366 app_id, extensions::ExtensionRegistry::EVERYTHING)) { | |
367 return; | |
368 } | |
369 | |
370 base::string16 error; | |
371 extensions::ExtensionSystem::Get(lock_screen_profile_) | |
372 ->extension_service() | |
373 ->UninstallExtension(app_id, | |
374 extensions::UNINSTALL_REASON_INTERNAL_MANAGEMENT, | |
375 base::Bind(&base::DoNothing), &error); | |
51 } | 376 } |
52 | 377 |
53 } // namespace lock_screen_apps | 378 } // namespace lock_screen_apps |
OLD | NEW |