Commit 6bdb00da authored by Nancy Wang's avatar Nancy Wang Committed by Chromium LUCI CQ

Implement app launching function for restoration.

This CL adds the AppLaunchHandler class to call FullRestoreReadHandler
to read the full restore data from the full restore data file on a
background task runner, and restore apps and web pages based on the
user preference or the user's choice.

The apps can be re-launched for the restoration when:
1. There is the restore data for the app.
2. The user preference sets always restore or the user selects 'Restore'
from the notification dialog.
3. The app is ready.

If one of the above 3 conditions is not ready, wait until all of them
are ready.

BUG=1146900

Change-Id: I0ac76e856458d762cbda1606432e26f8bf08447d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2599646Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarNancy Wang <nancylingwang@chromium.org>
Commit-Queue: Nancy Wang <nancylingwang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#841282}
parent 70979cfb
...@@ -1456,6 +1456,8 @@ source_set("chromeos") { ...@@ -1456,6 +1456,8 @@ source_set("chromeos") {
"first_run/drive_first_run_controller.h", "first_run/drive_first_run_controller.h",
"first_run/first_run.cc", "first_run/first_run.cc",
"first_run/first_run.h", "first_run/first_run.h",
"full_restore/app_launch_handler.cc",
"full_restore/app_launch_handler.h",
"full_restore/full_restore_prefs.cc", "full_restore/full_restore_prefs.cc",
"full_restore/full_restore_prefs.h", "full_restore/full_restore_prefs.h",
"full_restore/full_restore_service.cc", "full_restore/full_restore_service.cc",
......
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/full_restore/app_launch_handler.h"
#include <set>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/apps/app_service/browser_app_launcher.h"
#include "chrome/browser/profiles/profile.h"
#include "components/full_restore/full_restore_read_handler.h"
#include "components/full_restore/full_restore_save_handler.h"
#include "components/full_restore/restore_data.h"
#include "components/services/app_service/public/cpp/app_update.h"
#include "components/services/app_service/public/mojom/types.mojom.h"
#include "ui/base/window_open_disposition.h"
namespace chromeos {
namespace full_restore {
AppLaunchHandler::AppLaunchHandler(Profile* profile) : profile_(profile) {
// FullRestoreReadHandler reads the full restore data from the full restore
// data file on a background task runner.
::full_restore::FullRestoreReadHandler::GetInstance()->ReadFromFile(
profile_->GetPath(), base::BindOnce(&AppLaunchHandler::OnGetRestoreData,
weak_ptr_factory_.GetWeakPtr()));
}
AppLaunchHandler::~AppLaunchHandler() = default;
void AppLaunchHandler::OnAppUpdate(const apps::AppUpdate& update) {
// If the restore flag |should_restore_| is false, or the restore data has not
// been read yet, or the app is not ready, don't launch the app for the
// restoration.
if (!should_restore_ || !restore_data_ || !update.ReadinessChanged() ||
update.Readiness() != apps::mojom::Readiness::kReady) {
return;
}
// If there is no restore data or the launch list for the app is empty, don't
// launch the app.
const auto& app_id_to_launch_list = restore_data_->app_id_to_launch_list();
if (app_id_to_launch_list.find(update.AppId()) ==
app_id_to_launch_list.end()) {
return;
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&AppLaunchHandler::LaunchApp,
weak_ptr_factory_.GetWeakPtr(),
update.AppType(), update.AppId()));
}
void AppLaunchHandler::OnAppRegistryCacheWillBeDestroyed(
apps::AppRegistryCache* cache) {
apps::AppRegistryCache::Observer::Observe(nullptr);
}
void AppLaunchHandler::SetShouldRestore() {
should_restore_ = true;
MaybePostRestore();
}
void AppLaunchHandler::OnGetRestoreData(
std::unique_ptr<::full_restore::RestoreData> restore_data) {
restore_data_ = std::move(restore_data);
// After reading the restore data, the restore data can be cleared from the
// restore file to save the new restore data.
::full_restore::FullRestoreSaveHandler::GetInstance()->Flush(
profile_->GetPath());
MaybePostRestore();
}
void AppLaunchHandler::MaybePostRestore() {
// If the restore flag |should_restore_| is not true, or reading the restore
// data hasn't finished, don't restore.
if (!should_restore_ || !restore_data_)
return;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&AppLaunchHandler::MaybeRestore,
weak_ptr_factory_.GetWeakPtr()));
}
void AppLaunchHandler::MaybeRestore() {
// If there is no launch list from the restore data, we don't need to handle
// the restoration.
const auto& launch_list = restore_data_->app_id_to_launch_list();
if (launch_list.empty())
return;
// Observe AppRegistryCache to get the notification when the app is ready.
DCHECK(
apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile_));
auto* cache = &apps::AppServiceProxyFactory::GetForProfile(profile_)
->AppRegistryCache();
Observe(cache);
// Add the app to |app_ids| if there is a launch list from the restore data
// for the app.
std::set<std::string> app_ids;
cache->ForEachApp([&app_ids, &launch_list](const apps::AppUpdate& update) {
if (update.Readiness() == apps::mojom::Readiness::kReady &&
launch_list.find(update.AppId()) != launch_list.end()) {
app_ids.insert(update.AppId());
}
});
for (const auto& app_id : app_ids)
LaunchApp(cache->GetAppType(app_id), app_id);
}
void AppLaunchHandler::LaunchApp(apps::mojom::AppType app_type,
const std::string& app_id) {
DCHECK(restore_data_);
// For the Chrome browser, the browser session restore is used to restore the
// web pages, so we don't need to launch the app.
if (app_id == extension_misc::kChromeAppId) {
restore_data_->RemoveApp(app_id);
return;
}
const auto it = restore_data_->app_id_to_launch_list().find(app_id);
if (it == restore_data_->app_id_to_launch_list().end() ||
it->second.empty()) {
restore_data_->RemoveApp(app_id);
return;
}
switch (app_type) {
case apps::mojom::AppType::kArc:
// TODO(crbug.com/1146900): Handle ARC apps
break;
case apps::mojom::AppType::kExtension:
case apps::mojom::AppType::kWeb:
LaunchWebAppOrExtension(app_id, it->second);
break;
case apps::mojom::AppType::kBuiltIn:
case apps::mojom::AppType::kCrostini:
case apps::mojom::AppType::kPluginVm:
case apps::mojom::AppType::kUnknown:
case apps::mojom::AppType::kMacOs:
case apps::mojom::AppType::kLacros:
case apps::mojom::AppType::kRemote:
case apps::mojom::AppType::kBorealis:
NOTREACHED();
break;
}
restore_data_->RemoveApp(app_id);
}
void AppLaunchHandler::LaunchWebAppOrExtension(
const std::string& app_id,
const ::full_restore::RestoreData::LaunchList& launch_list) {
auto* launcher = apps::AppServiceProxyFactory::GetForProfile(profile_)
->BrowserAppLauncher();
if (!launcher)
return;
for (const auto& it : launch_list) {
DCHECK(it.second->container.has_value());
DCHECK(it.second->disposition.has_value());
DCHECK(it.second->display_id.has_value());
apps::mojom::IntentPtr intent;
apps::AppLaunchParams params(
app_id,
static_cast<apps::mojom::LaunchContainer>(it.second->container.value()),
static_cast<WindowOpenDisposition>(it.second->disposition.value()),
it.second->display_id.value(),
it.second->file_paths.has_value() ? it.second->file_paths.value()
: std::vector<base::FilePath>{},
it.second->intent.has_value() ? it.second->intent.value() : intent);
launcher->LaunchAppWithParams(std::move(params));
}
}
} // namespace full_restore
} // namespace chromeos
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_FULL_RESTORE_APP_LAUNCH_HANDLER_H_
#define CHROME_BROWSER_CHROMEOS_FULL_RESTORE_APP_LAUNCH_HANDLER_H_
#include "base/memory/weak_ptr.h"
#include "components/full_restore/restore_data.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
#include "components/services/app_service/public/mojom/types.mojom.h"
namespace apps {
class AppUpdate;
}
class Profile;
namespace chromeos {
namespace full_restore {
// The AppLaunchHandler class calls FullRestoreReadHandler to read the full
// restore data from the full restore data file on a background task runner, and
// restore apps and web pages based on the user preference or the user's choice.
//
// The apps can be re-launched for the restoration when:
// 1. There is the restore data for the app.
// 2. The user preference sets always restore or the user selects 'Restore' from
// the notification dialog.
// 3. The app is ready.
//
// TODO(crbug.com/1146900): Implement ARC apps launching.
class AppLaunchHandler : public apps::AppRegistryCache::Observer {
public:
explicit AppLaunchHandler(Profile* profile);
AppLaunchHandler(const AppLaunchHandler&) = delete;
AppLaunchHandler& operator=(const AppLaunchHandler&) = delete;
~AppLaunchHandler() override;
// apps::AppRegistryCache::Observer:
void OnAppUpdate(const apps::AppUpdate& update) override;
void OnAppRegistryCacheWillBeDestroyed(
apps::AppRegistryCache* cache) override;
// If the user preference sets always restore or the user selects 'Restore'
// from the notification dialog, sets the restore flag |should_restore_| as
// true to allow the restoration.
void SetShouldRestore();
private:
void OnGetRestoreData(
std::unique_ptr<::full_restore::RestoreData> restore_data);
void MaybePostRestore();
// If there is the restore data, and the restore flag |should_restore_| is
// true, launches apps based on the restore data when apps are ready.
void MaybeRestore();
void LaunchApp(apps::mojom::AppType app_type, const std::string& app_id);
void LaunchWebAppOrExtension(
const std::string& app_id,
const ::full_restore::RestoreData::LaunchList& launch_list);
Profile* profile_ = nullptr;
bool should_restore_ = false;
std::unique_ptr<::full_restore::RestoreData> restore_data_;
base::WeakPtrFactory<AppLaunchHandler> weak_ptr_factory_{this};
};
} // namespace full_restore
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_FULL_RESTORE_APP_LAUNCH_HANDLER_H_
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "ash/public/cpp/notification_utils.h" #include "ash/public/cpp/notification_utils.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "chrome/app/vector_icons/vector_icons.h" #include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/chromeos/full_restore/app_launch_handler.h"
#include "chrome/browser/chromeos/full_restore/full_restore_prefs.h" #include "chrome/browser/chromeos/full_restore/full_restore_prefs.h"
#include "chrome/browser/chromeos/full_restore/full_restore_service_factory.h" #include "chrome/browser/chromeos/full_restore/full_restore_service_factory.h"
#include "chrome/browser/chromeos/full_restore/new_user_restore_pref_handler.h" #include "chrome/browser/chromeos/full_restore/new_user_restore_pref_handler.h"
...@@ -29,7 +30,9 @@ namespace full_restore { ...@@ -29,7 +30,9 @@ namespace full_restore {
const char kRestoreForCrashNotificationId[] = "restore_for_crash_notification"; const char kRestoreForCrashNotificationId[] = "restore_for_crash_notification";
const char kRestoreNotificationId[] = "restore_notification"; const char kRestoreNotificationId[] = "restore_notification";
FullRestoreService::FullRestoreService(Profile* profile) : profile_(profile) { FullRestoreService::FullRestoreService(Profile* profile)
: profile_(profile),
app_launch_handler_(std::make_unique<AppLaunchHandler>(profile_)) {
// If the system crashed before reboot, show the restore notification. // If the system crashed before reboot, show the restore notification.
if (profile->GetLastSessionExitType() == Profile::EXIT_CRASHED) { if (profile->GetLastSessionExitType() == Profile::EXIT_CRASHED) {
ShowRestoreNotification(kRestoreForCrashNotificationId); ShowRestoreNotification(kRestoreForCrashNotificationId);
...@@ -140,8 +143,7 @@ void FullRestoreService::Restore() { ...@@ -140,8 +143,7 @@ void FullRestoreService::Restore() {
user->GetAccountId(), true); user->GetAccountId(), true);
} }
// TODO(crbug.com/909794): Implement the restoration. And move the heavy load app_launch_handler_->SetShouldRestore();
// out of this KeyedService class.
} }
} // namespace full_restore } // namespace full_restore
......
...@@ -17,6 +17,7 @@ class Profile; ...@@ -17,6 +17,7 @@ class Profile;
namespace chromeos { namespace chromeos {
namespace full_restore { namespace full_restore {
class AppLaunchHandler;
class NewUserRestorePrefHandler; class NewUserRestorePrefHandler;
extern const char kRestoreForCrashNotificationId[]; extern const char kRestoreForCrashNotificationId[];
...@@ -65,6 +66,10 @@ class FullRestoreService : public KeyedService { ...@@ -65,6 +66,10 @@ class FullRestoreService : public KeyedService {
std::unique_ptr<NewUserRestorePrefHandler> new_user_pref_handler_; std::unique_ptr<NewUserRestorePrefHandler> new_user_pref_handler_;
// |app_launch_handler_| is responsible for launching apps based on the
// restore data.
std::unique_ptr<AppLaunchHandler> app_launch_handler_;
base::WeakPtrFactory<FullRestoreService> weak_ptr_factory_{this}; base::WeakPtrFactory<FullRestoreService> weak_ptr_factory_{this};
}; };
......
...@@ -41,6 +41,9 @@ void FullRestoreReadHandler::OnGetRestoreData( ...@@ -41,6 +41,9 @@ void FullRestoreReadHandler::OnGetRestoreData(
const base::FilePath& profile_path, const base::FilePath& profile_path,
Callback callback, Callback callback,
std::unique_ptr<RestoreData> restore_data) { std::unique_ptr<RestoreData> restore_data) {
if (restore_data)
profile_path_to_restore_data_[profile_path] = restore_data->Clone();
std::move(callback).Run(std::move(restore_data)); std::move(callback).Run(std::move(restore_data));
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef COMPONENTS_FULL_RESTORE_FULL_RESTORE_READ_HANDLER_H_ #ifndef COMPONENTS_FULL_RESTORE_FULL_RESTORE_READ_HANDLER_H_
#define COMPONENTS_FULL_RESTORE_FULL_RESTORE_READ_HANDLER_H_ #define COMPONENTS_FULL_RESTORE_FULL_RESTORE_READ_HANDLER_H_
#include <map>
#include <memory> #include <memory>
#include "base/callback.h" #include "base/callback.h"
...@@ -49,6 +50,10 @@ class COMPONENT_EXPORT(FULL_RESTORE) FullRestoreReadHandler { ...@@ -49,6 +50,10 @@ class COMPONENT_EXPORT(FULL_RESTORE) FullRestoreReadHandler {
Callback callback, Callback callback,
std::unique_ptr<RestoreData>); std::unique_ptr<RestoreData>);
// The restore data read from the full restore files.
std::map<base::FilePath, std::unique_ptr<RestoreData>>
profile_path_to_restore_data_;
base::WeakPtrFactory<FullRestoreReadHandler> weak_factory_{this}; base::WeakPtrFactory<FullRestoreReadHandler> weak_factory_{this};
}; };
......
...@@ -126,6 +126,22 @@ void FullRestoreSaveHandler::SaveWindowInfo(const WindowInfo& window_info) { ...@@ -126,6 +126,22 @@ void FullRestoreSaveHandler::SaveWindowInfo(const WindowInfo& window_info) {
it->second.second, window_id, window_info); it->second.second, window_id, window_info);
} }
void FullRestoreSaveHandler::Flush(const base::FilePath& profile_path) {
if (save_running_.find(profile_path) != save_running_.end())
return;
save_running_.insert(profile_path);
BackendTaskRunner(profile_path)
->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&FullRestoreFileHandler::WriteToFile,
GetFileHandler(profile_path),
profile_path_to_restore_data_[profile_path].Clone()),
base::BindOnce(&FullRestoreSaveHandler::OnSaveFinished,
weak_factory_.GetWeakPtr(), profile_path));
}
void FullRestoreSaveHandler::MaybeStartSaveTimer() { void FullRestoreSaveHandler::MaybeStartSaveTimer() {
if (!save_timer_.IsRunning() && save_running_.empty()) { if (!save_timer_.IsRunning() && save_running_.empty()) {
save_timer_.Start(FROM_HERE, kSaveDelay, save_timer_.Start(FROM_HERE, kSaveDelay,
...@@ -138,16 +154,9 @@ void FullRestoreSaveHandler::Save() { ...@@ -138,16 +154,9 @@ void FullRestoreSaveHandler::Save() {
if (pending_save_profile_paths_.empty()) if (pending_save_profile_paths_.empty())
return; return;
for (const auto& file_path : pending_save_profile_paths_) { for (const auto& file_path : pending_save_profile_paths_)
save_running_.insert(file_path); Flush(file_path);
BackendTaskRunner(file_path)->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&FullRestoreFileHandler::WriteToFile,
GetFileHandler(file_path),
profile_path_to_restore_data_[file_path].Clone()),
base::BindOnce(&FullRestoreSaveHandler::OnSaveFinished,
weak_factory_.GetWeakPtr(), file_path));
}
pending_save_profile_paths_.clear(); pending_save_profile_paths_.clear();
} }
......
...@@ -61,6 +61,10 @@ class COMPONENT_EXPORT(FULL_RESTORE) FullRestoreSaveHandler ...@@ -61,6 +61,10 @@ class COMPONENT_EXPORT(FULL_RESTORE) FullRestoreSaveHandler
// Save |window_info| to |profile_path_to_restore_data_|. // Save |window_info| to |profile_path_to_restore_data_|.
void SaveWindowInfo(const WindowInfo& window_info); void SaveWindowInfo(const WindowInfo& window_info);
// Flushes the full restore file in |profile_path| with the current restore
// data.
void Flush(const base::FilePath& profile_path);
base::OneShotTimer* GetTimerForTesting() { return &save_timer_; } base::OneShotTimer* GetTimerForTesting() { return &save_timer_; }
private: private:
......
...@@ -117,4 +117,8 @@ void RestoreData::RemoveAppRestoreData(const std::string& app_id, ...@@ -117,4 +117,8 @@ void RestoreData::RemoveAppRestoreData(const std::string& app_id,
app_id_to_launch_list_.erase(app_id); app_id_to_launch_list_.erase(app_id);
} }
void RestoreData::RemoveApp(const std::string& app_id) {
app_id_to_launch_list_.erase(app_id);
}
} // namespace full_restore } // namespace full_restore
...@@ -91,6 +91,9 @@ class COMPONENT_EXPORT(FULL_RESTORE) RestoreData { ...@@ -91,6 +91,9 @@ class COMPONENT_EXPORT(FULL_RESTORE) RestoreData {
// Remove a AppRestoreData with |window_id| for |app_id|. // Remove a AppRestoreData with |window_id| for |app_id|.
void RemoveAppRestoreData(const std::string& app_id, int window_id); void RemoveAppRestoreData(const std::string& app_id, int window_id);
// Remove the launch list for |app_id|.
void RemoveApp(const std::string& app_id);
const AppIdToLaunchList& app_id_to_launch_list() const { const AppIdToLaunchList& app_id_to_launch_list() const {
return app_id_to_launch_list_; return app_id_to_launch_list_;
} }
......
...@@ -271,7 +271,7 @@ TEST_F(RestoreDataTest, RemoveAppRestoreData) { ...@@ -271,7 +271,7 @@ TEST_F(RestoreDataTest, RemoveAppRestoreData) {
ModifyWindowInfos(); ModifyWindowInfos();
VerifyRestoreData(restore_data()); VerifyRestoreData(restore_data());
// Remove kAppId1's kId1. // Remove kAppId1's kWindowId1.
restore_data().RemoveAppRestoreData(kAppId1, kWindowId1); restore_data().RemoveAppRestoreData(kAppId1, kWindowId1);
EXPECT_EQ(2u, app_id_to_launch_list().size()); EXPECT_EQ(2u, app_id_to_launch_list().size());
...@@ -291,7 +291,7 @@ TEST_F(RestoreDataTest, RemoveAppRestoreData) { ...@@ -291,7 +291,7 @@ TEST_F(RestoreDataTest, RemoveAppRestoreData) {
EXPECT_TRUE(base::Contains(launch_list_it2->second, kWindowId3)); EXPECT_TRUE(base::Contains(launch_list_it2->second, kWindowId3));
// Remove kAppId1's kId2. // Remove kAppId1's kWindowId2.
restore_data().RemoveAppRestoreData(kAppId1, kWindowId2); restore_data().RemoveAppRestoreData(kAppId1, kWindowId2);
EXPECT_EQ(1u, app_id_to_launch_list().size()); EXPECT_EQ(1u, app_id_to_launch_list().size());
...@@ -306,12 +306,35 @@ TEST_F(RestoreDataTest, RemoveAppRestoreData) { ...@@ -306,12 +306,35 @@ TEST_F(RestoreDataTest, RemoveAppRestoreData) {
EXPECT_TRUE(base::Contains(launch_list_it2->second, kWindowId3)); EXPECT_TRUE(base::Contains(launch_list_it2->second, kWindowId3));
// Remove kAppId2's kId3. // Remove kAppId2's kWindowId3.
restore_data().RemoveAppRestoreData(kAppId2, kWindowId3); restore_data().RemoveAppRestoreData(kAppId2, kWindowId3);
EXPECT_EQ(0u, app_id_to_launch_list().size()); EXPECT_EQ(0u, app_id_to_launch_list().size());
} }
TEST_F(RestoreDataTest, RemoveApp) {
AddAppLaunchInfos();
ModifyWindowInfos();
VerifyRestoreData(restore_data());
// Remove kAppId1.
restore_data().RemoveApp(kAppId1);
EXPECT_EQ(1u, app_id_to_launch_list().size());
// Verify for |kAppId2|
auto launch_list_it2 = app_id_to_launch_list().find(kAppId2);
EXPECT_TRUE(launch_list_it2 != app_id_to_launch_list().end());
EXPECT_EQ(1u, launch_list_it2->second.size());
EXPECT_TRUE(base::Contains(launch_list_it2->second, kWindowId3));
// Remove kAppId2.
restore_data().RemoveApp(kAppId2);
EXPECT_EQ(0u, app_id_to_launch_list().size());
}
TEST_F(RestoreDataTest, Convert) { TEST_F(RestoreDataTest, Convert) {
AddAppLaunchInfos(); AddAppLaunchInfos();
ModifyWindowInfos(); ModifyWindowInfos();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment