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 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 |
OLD | NEW |