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. install source location and | |
Devlin
2017/07/05 16:12:05
nit: add punctuation
... the provided |extension_i
tbarzic
2017/07/05 20:16:05
Done.
| |
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(scoped_refptr<const extensions::Extension>()); | |
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, | |
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 std::unique_ptr<base::ScopedTempDir> extension_temp_dir = | |
Devlin
2017/07/05 16:12:05
optional nit: auto extension_temp_dir = base::Make
tbarzic
2017/07/05 20:16:05
Done.
| |
88 base::MakeUnique<base::ScopedTempDir>(); | |
89 if (install_temp_dir.empty() || | |
90 !extension_temp_dir->CreateUniqueTempDirUnderPath(install_temp_dir)) { | |
91 callback.Run(scoped_refptr<const extensions::Extension>()); | |
Devlin
2017/07/05 16:12:05
does Run(nullptr) work?
tbarzic
2017/07/05 20:16:05
it does.
| |
92 return; | |
93 } | |
94 | |
95 // Copy the original extension path to a temp path to prevent | |
96 // ExtensionAssetsManager from deleting it (as InstallExtension renames the | |
97 // source path to a new location under the target install directory). | |
98 base::FilePath temp_copy = | |
99 extension_temp_dir->GetPath().Append(original_path.BaseName()); | |
100 if (!base::CopyDirectory(original_path, temp_copy, true /* recursive */)) { | |
101 callback.Run(scoped_refptr<const extensions::Extension>()); | |
102 return; | |
103 } | |
104 | |
105 // Note: |extension_temp_dir| is passed around to ensure it stays in scope | |
106 // until the app installation is done. | |
107 extensions::ExtensionAssetsManager::GetInstance()->InstallExtension( | |
108 extension.get(), temp_copy, target_install_dir, profile, | |
109 base::Bind(&LoadInstalledExtension, extension->id(), | |
110 extension->location(), extension->creation_flags(), | |
111 base::Passed(std::move(extension_temp_dir)), callback)); | |
112 } | |
113 | |
114 } // namespace | |
115 | |
116 AppManagerImpl::AppManagerImpl() | |
117 : extensions_observer_(this), weak_ptr_factory_(this) {} | |
12 | 118 |
13 AppManagerImpl::~AppManagerImpl() = default; | 119 AppManagerImpl::~AppManagerImpl() = default; |
14 | 120 |
15 void AppManagerImpl::Initialize(Profile* primary_profile, | 121 void AppManagerImpl::Initialize(Profile* primary_profile, |
16 Profile* lock_screen_profile) { | 122 Profile* lock_screen_profile) { |
123 DCHECK_EQ(State::kNotInitialized, state_); | |
17 DCHECK(primary_profile); | 124 DCHECK(primary_profile); |
18 DCHECK(lock_screen_profile); | 125 DCHECK(lock_screen_profile); |
19 DCHECK_NE(primary_profile, lock_screen_profile); | 126 DCHECK_NE(primary_profile, lock_screen_profile); |
20 // Do not use OTR profile for lock screen apps. This is important for | 127 // Do not use OTR profile for lock screen apps. This is important for |
21 // profile usage in |LaunchNoteTaking| - lock screen app background page runs | 128 // 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 | 129 // 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 | 130 // 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 | 131 // difference - the profile is used to get browser context keyed services, all |
25 // of which redirect OTR profile to the original one. | 132 // of which redirect OTR profile to the original one. |
26 DCHECK(!lock_screen_profile->IsOffTheRecord()); | 133 DCHECK(!lock_screen_profile->IsOffTheRecord()); |
27 | 134 |
28 CHECK(!chromeos::ProfileHelper::Get()->GetUserByProfile(lock_screen_profile)) | 135 CHECK(!chromeos::ProfileHelper::Get()->GetUserByProfile(lock_screen_profile)) |
29 << "Lock screen profile should not be associated with any users."; | 136 << "Lock screen profile should not be associated with any users."; |
30 | 137 |
31 primary_profile_ = primary_profile; | 138 primary_profile_ = primary_profile; |
32 lock_screen_profile_ = lock_screen_profile; | 139 lock_screen_profile_ = lock_screen_profile; |
140 state_ = State::kInactive; | |
141 | |
142 pref_change_registrar_.Init(primary_profile->GetPrefs()); | |
143 pref_change_registrar_.Add( | |
144 prefs::kNoteTakingAppId, | |
145 base::Bind(&AppManagerImpl::OnNoteTakingExtensionChanged, | |
146 base::Unretained(this))); | |
147 pref_change_registrar_.Add( | |
148 prefs::kNoteTakingAppEnabledOnLockScreen, | |
149 base::Bind(&AppManagerImpl::OnNoteTakingExtensionChanged, | |
150 base::Unretained(this))); | |
33 } | 151 } |
34 | 152 |
35 void AppManagerImpl::Start(const base::Closure& note_taking_changed_callback) { | 153 void AppManagerImpl::Start(const base::Closure& note_taking_changed_callback) { |
154 DCHECK_NE(State::kNotInitialized, state_); | |
155 | |
36 note_taking_changed_callback_ = note_taking_changed_callback; | 156 note_taking_changed_callback_ = note_taking_changed_callback; |
37 } | 157 extensions_observer_.Add( |
38 | 158 extensions::ExtensionRegistry::Get(primary_profile_)); |
39 void AppManagerImpl::Stop() {} | 159 |
160 if (state_ == State::kActive) | |
161 return; | |
162 | |
163 lock_screen_app_id_.clear(); | |
164 std::string app_id = FindLockScreenNoteTakingApp(); | |
165 if (app_id.empty()) { | |
166 state_ = State::kAppUnavailable; | |
167 return; | |
168 } | |
169 | |
170 state_ = AddAppToLockScreenProfile(app_id); | |
171 if (state_ == State::kActive || state_ == State::kActivating) | |
172 lock_screen_app_id_ = app_id; | |
173 } | |
174 | |
175 void AppManagerImpl::Stop() { | |
176 DCHECK_NE(State::kNotInitialized, state_); | |
177 | |
178 note_taking_changed_callback_.Reset(); | |
179 extensions_observer_.RemoveAll(); | |
180 | |
181 if (state_ == State::kInactive) | |
182 return; | |
183 | |
184 RemoveAppFromLockScreenProfile(lock_screen_app_id_); | |
185 lock_screen_app_id_.clear(); | |
186 state_ = State::kInactive; | |
187 } | |
40 | 188 |
41 bool AppManagerImpl::IsNoteTakingAppAvailable() const { | 189 bool AppManagerImpl::IsNoteTakingAppAvailable() const { |
42 return false; | 190 return state_ == State::kActive && !lock_screen_app_id_.empty(); |
43 } | 191 } |
44 | 192 |
45 std::string AppManagerImpl::GetNoteTakingAppId() const { | 193 std::string AppManagerImpl::GetNoteTakingAppId() const { |
46 return std::string(); | 194 if (!IsNoteTakingAppAvailable()) |
195 return std::string(); | |
196 return lock_screen_app_id_; | |
47 } | 197 } |
48 | 198 |
49 bool AppManagerImpl::LaunchNoteTaking() { | 199 bool AppManagerImpl::LaunchNoteTaking() { |
50 return false; | 200 if (!IsNoteTakingAppAvailable()) |
201 return false; | |
202 | |
203 const extensions::ExtensionRegistry* extension_registry = | |
204 extensions::ExtensionRegistry::Get(lock_screen_profile_); | |
205 const extensions::Extension* app = extension_registry->GetExtensionById( | |
206 lock_screen_app_id_, extensions::ExtensionRegistry::ENABLED); | |
207 if (!app) | |
208 return false; | |
209 | |
210 auto action_data = | |
211 base::MakeUnique<extensions::api::app_runtime::ActionData>(); | |
212 action_data->action_type = | |
213 extensions::api::app_runtime::ActionType::ACTION_TYPE_NEW_NOTE; | |
214 action_data->is_lock_screen_action = base::MakeUnique<bool>(true); | |
215 | |
216 apps::LaunchPlatformAppWithAction(lock_screen_profile_, app, | |
217 std::move(action_data), base::FilePath()); | |
218 return true; | |
219 } | |
220 | |
221 void AppManagerImpl::OnExtensionLoaded(content::BrowserContext* browser_context, | |
222 const extensions::Extension* extension) { | |
223 if (extension->id() == | |
224 primary_profile_->GetPrefs()->GetString(prefs::kNoteTakingAppId)) { | |
225 OnNoteTakingExtensionChanged(); | |
226 } | |
227 } | |
228 | |
229 void AppManagerImpl::OnExtensionUnloaded( | |
230 content::BrowserContext* browser_context, | |
231 const extensions::Extension* extension, | |
232 extensions::UnloadedExtensionReason reason) { | |
233 if (extension->id() == lock_screen_app_id_) | |
234 OnNoteTakingExtensionChanged(); | |
235 } | |
236 | |
237 void AppManagerImpl::OnNoteTakingExtensionChanged() { | |
238 if (state_ == State::kInactive) | |
239 return; | |
240 | |
241 std::string app_id = FindLockScreenNoteTakingApp(); | |
242 if (app_id == lock_screen_app_id_) | |
243 return; | |
244 | |
245 RemoveAppFromLockScreenProfile(lock_screen_app_id_); | |
246 lock_screen_app_id_.clear(); | |
247 | |
248 state_ = AddAppToLockScreenProfile(app_id); | |
249 if (state_ == State::kActive || state_ == State::kActivating) | |
250 lock_screen_app_id_ = app_id; | |
251 | |
252 if (!note_taking_changed_callback_.is_null()) | |
253 note_taking_changed_callback_.Run(); | |
254 } | |
255 | |
256 std::string AppManagerImpl::FindLockScreenNoteTakingApp() const { | |
257 // Note that lock screen does not currently support Android apps, so | |
258 // it's enough to only check the state of the preferred Chrome app. | |
259 std::unique_ptr<chromeos::NoteTakingAppInfo> note_taking_app = | |
260 chromeos::NoteTakingHelper::Get()->GetPreferredChromeAppInfo( | |
261 primary_profile_); | |
262 | |
263 if (!note_taking_app || | |
264 note_taking_app->lock_screen_support != | |
265 chromeos::NoteTakingLockScreenSupport::kSelected) { | |
266 return std::string(); | |
267 } | |
268 | |
269 return note_taking_app->app_id; | |
270 } | |
271 | |
272 AppManagerImpl::State AppManagerImpl::AddAppToLockScreenProfile( | |
273 const std::string& app_id) { | |
274 extensions::ExtensionRegistry* primary_registry = | |
275 extensions::ExtensionRegistry::Get(primary_profile_); | |
276 const extensions::Extension* app = | |
277 primary_registry->enabled_extensions().GetByID(app_id); | |
278 if (!app) | |
279 return State::kAppUnavailable; | |
280 | |
281 // If the app is already installed, remove it. | |
Devlin
2017/07/05 16:12:05
Can you describe when this would happen?
tbarzic
2017/07/05 20:16:05
I can't really think of a scenario as long as the
| |
282 RemoveAppFromLockScreenProfile(app_id); | |
283 | |
284 bool is_unpacked = extensions::Manifest::IsUnpackedLocation(app->location()); | |
285 | |
286 std::string error; | |
287 scoped_refptr<extensions::Extension> lock_profile_app = | |
288 extensions::Extension::Create( | |
289 is_unpacked ? app->path() : base::FilePath(), app->location(), | |
Devlin
2017/07/05 16:12:05
add a comment explaining this
tbarzic
2017/07/05 20:16:05
Done.
| |
290 *app->manifest()->value()->CreateDeepCopy(), app->creation_flags(), | |
291 app->id(), &error); | |
292 | |
293 if (!lock_profile_app) | |
Devlin
2017/07/05 16:12:05
When could this happen?
tbarzic
2017/07/05 20:16:05
In theory, if we attempt to create an extension wi
| |
294 return State::kAppUnavailable; | |
295 | |
296 install_count_++; | |
297 | |
298 if (is_unpacked) { | |
299 InstallAndEnableLockScreenAppInLockScreenProfile(lock_profile_app.get()); | |
300 return State::kActive; | |
301 } | |
302 | |
303 ExtensionService* lock_screen_service = | |
304 extensions::ExtensionSystem::Get(lock_screen_profile_) | |
305 ->extension_service(); | |
306 | |
307 lock_screen_service->GetFileTaskRunner()->PostTask( | |
308 FROM_HERE, | |
309 base::Bind( | |
310 &InstallExtensionCopy, lock_profile_app, app->path(), | |
311 lock_screen_service->install_directory(), lock_screen_profile_, | |
312 base::Bind(&InvokeCallbackOnTaskRunner, | |
313 base::Bind(&AppManagerImpl::CompleteLockScreenAppInstall, | |
314 weak_ptr_factory_.GetWeakPtr(), install_count_), | |
315 base::ThreadTaskRunnerHandle::Get()))); | |
316 return State::kActivating; | |
317 } | |
318 | |
319 void AppManagerImpl::CompleteLockScreenAppInstall( | |
320 int install_id, | |
321 const scoped_refptr<const extensions::Extension>& app) { | |
322 // Bail out if the app manager is no longer waiting for this app's | |
323 // installation. | |
324 if (install_id != install_count_ || state_ != State::kActivating) | |
Devlin
2017/07/05 16:12:05
When do we clean up the resources we copied?
tbarzic
2017/07/05 20:16:05
It will be cleaned up with the profile on Chrome r
| |
325 return; | |
326 | |
327 if (app) { | |
328 DCHECK_EQ(lock_screen_app_id_, app->id()); | |
329 InstallAndEnableLockScreenAppInLockScreenProfile(app.get()); | |
330 state_ = State::kActive; | |
331 } else { | |
332 state_ = State::kAppUnavailable; | |
333 } | |
334 | |
335 if (!note_taking_changed_callback_.is_null()) | |
336 note_taking_changed_callback_.Run(); | |
337 } | |
338 | |
339 void AppManagerImpl::InstallAndEnableLockScreenAppInLockScreenProfile( | |
340 const extensions::Extension* app) { | |
341 ExtensionService* lock_screen_service = | |
342 extensions::ExtensionSystem::Get(lock_screen_profile_) | |
343 ->extension_service(); | |
344 | |
345 lock_screen_service->OnExtensionInstalled( | |
346 app, syncer::StringOrdinal(), extensions::kInstallFlagInstallImmediately); | |
347 lock_screen_service->EnableExtension(app->id()); | |
348 } | |
349 | |
350 void AppManagerImpl::RemoveAppFromLockScreenProfile(const std::string& app_id) { | |
351 if (app_id.empty()) | |
352 return; | |
353 | |
354 extensions::ExtensionRegistry* lock_screen_registry = | |
355 extensions::ExtensionRegistry::Get(lock_screen_profile_); | |
356 if (!lock_screen_registry->GetExtensionById( | |
357 app_id, extensions::ExtensionRegistry::EVERYTHING)) { | |
358 return; | |
359 } | |
360 | |
361 base::string16 error; | |
362 extensions::ExtensionSystem::Get(lock_screen_profile_) | |
363 ->extension_service() | |
364 ->UninstallExtension(app_id, | |
365 extensions::UNINSTALL_REASON_INTERNAL_MANAGEMENT, | |
366 base::Bind(&base::DoNothing), &error); | |
51 } | 367 } |
52 | 368 |
53 } // namespace lock_screen_apps | 369 } // namespace lock_screen_apps |
OLD | NEW |