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

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. 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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698