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

Side by Side Diff: chrome/browser/chromeos/lock_screen_apps/app_manager_impl.cc

Issue 2902293002: Introduce lock screen app manager (Closed)
Patch Set: rebase Created 3 years, 5 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
OLDNEW
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 app should 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.
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698