| // 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 "components/app_restore/arc_save_handler.h" |
| |
| #include "base/containers/contains.h" |
| #include "components/app_restore/app_launch_info.h" |
| #include "components/app_restore/full_restore_info.h" |
| #include "components/app_restore/full_restore_save_handler.h" |
| #include "components/app_restore/full_restore_utils.h" |
| #include "components/app_restore/window_info.h" |
| #include "components/app_restore/window_properties.h" |
| #include "ui/aura/window.h" |
| |
| namespace full_restore { |
| |
| namespace { |
| |
| // Repeat timer interval between each checking that whether a task is created |
| // for each app launching. |
| constexpr base::TimeDelta kCheckCycleInterval = |
| base::TimeDelta::FromSeconds(600); |
| |
| } // namespace |
| |
| ArcSaveHandler::ArcSaveHandler(const base::FilePath& profile_path) |
| : profile_path_(profile_path) {} |
| |
| ArcSaveHandler::~ArcSaveHandler() = default; |
| |
| void ArcSaveHandler::SaveAppLaunchInfo(AppLaunchInfoPtr app_launch_info) { |
| DCHECK(app_launch_info->arc_session_id.has_value()); |
| |
| // Save |app_launch_info| to |session_id_to_app_launch_info_|, and wait for |
| // the ARC task to be created. |
| int32_t session_id = app_launch_info->arc_session_id.value(); |
| |
| // If the ghost window has been created for `session_id`, and the launch info |
| // hasn't been added yet, add `app_launch_info` to the restore data. |
| if (base::Contains(ghost_window_session_id_to_app_id_, session_id)) { |
| if (!FullRestoreSaveHandler::GetInstance()->HasAppRestoreData( |
| profile_path_, app_launch_info->app_id, session_id)) { |
| app_launch_info->window_id = session_id; |
| FullRestoreSaveHandler::GetInstance()->AddAppLaunchInfo( |
| profile_path_, std::move(app_launch_info)); |
| |
| // Go through `arc_window_candidates_`. If the window for `session_id` has |
| // been created, call OnAppLaunched to save the window info. |
| auto window_it = std::find_if( |
| arc_window_candidates_.begin(), arc_window_candidates_.end(), |
| [session_id](aura::Window* window) { |
| return window->GetProperty(app_restore::kGhostWindowSessionIdKey) == |
| session_id; |
| }); |
| if (window_it != arc_window_candidates_.end()) { |
| FullRestoreInfo::GetInstance()->OnAppLaunched(*window_it); |
| arc_window_candidates_.erase(*window_it); |
| } |
| } |
| return; |
| } |
| |
| session_id_to_app_launch_info_[session_id] = |
| std::make_pair(std::move(app_launch_info), base::TimeTicks::Now()); |
| |
| MaybeStartCheckTimer(); |
| } |
| |
| void ArcSaveHandler::ModifyWindowInfo( |
| const app_restore::WindowInfo& window_info) { |
| aura::Window* window = window_info.window; |
| |
| int32_t task_id = window->GetProperty(app_restore::kWindowIdKey); |
| auto task_it = task_id_to_app_id_.find(task_id); |
| if (task_it != task_id_to_app_id_.end()) { |
| FullRestoreSaveHandler::GetInstance()->ModifyWindowInfo( |
| profile_path_, task_it->second, task_id, window_info); |
| return; |
| } |
| |
| // For the ghost window, modify the window info with `session_id` as the |
| // window id. |
| int32_t session_id = |
| window->GetProperty(app_restore::kGhostWindowSessionIdKey); |
| auto it = ghost_window_session_id_to_app_id_.find(session_id); |
| if (it != ghost_window_session_id_to_app_id_.end()) { |
| FullRestoreSaveHandler::GetInstance()->ModifyWindowInfo( |
| profile_path_, it->second, session_id, window_info); |
| } |
| } |
| |
| void ArcSaveHandler::OnWindowInitialized(aura::Window* window) { |
| int32_t task_id = window->GetProperty(app_restore::kWindowIdKey); |
| if (!base::Contains(task_id_to_app_id_, task_id)) { |
| // Check `session_id` to see whether this is a ghost window. |
| int32_t session_id = |
| window->GetProperty(app_restore::kGhostWindowSessionIdKey); |
| if (session_id < kArcSessionIdOffsetForRestoredLaunching) { |
| // If the task hasn't been created, and this is not a ghost window, add |
| // `window` to `arc_window_candidates_` to wait for the task to be |
| // created. |
| arc_window_candidates_.insert(window); |
| return; |
| } |
| |
| // Save `session_id` for the ghost window, to wait for the task created to |
| // replace the window id with the task id in the restore data. |
| const std::string* app_id = window->GetProperty(app_restore::kAppIdKey); |
| DCHECK(app_id); |
| ghost_window_session_id_to_app_id_[session_id] = *app_id; |
| |
| auto it = session_id_to_app_launch_info_.find(session_id); |
| if (it == session_id_to_app_launch_info_.end()) { |
| arc_window_candidates_.insert(window); |
| return; |
| } |
| |
| // If there is `app_launch_info`, add it to the restore data using |
| // `session_id` as `window_id`. |
| auto app_launch_info = std::move(it->second.first); |
| session_id_to_app_launch_info_.erase(it); |
| if (session_id_to_app_launch_info_.empty()) |
| check_timer_.Stop(); |
| |
| app_launch_info->window_id = session_id; |
| FullRestoreSaveHandler::GetInstance()->AddAppLaunchInfo( |
| profile_path_, std::move(app_launch_info)); |
| return; |
| } |
| |
| // If the task has been created, call OnAppLaunched to save the window |
| // information. |
| FullRestoreInfo::GetInstance()->OnAppLaunched(window); |
| } |
| |
| void ArcSaveHandler::OnWindowDestroyed(aura::Window* window) { |
| arc_window_candidates_.erase(window); |
| |
| int32_t task_id = window->GetProperty(app_restore::kWindowIdKey); |
| |
| auto task_it = task_id_to_app_id_.find(task_id); |
| if (task_it != task_id_to_app_id_.end()) { |
| // During the system shutdown phase, the ARC instance connection is lost, |
| // and the ARC windows are destroyed, but the ARC tasks are not destroyed |
| // yet. This might cause window bounds lost, and ghost window can't be |
| // created after reboot due to no window bounds. So if the ARC instance |
| // connection is lost, don't remove the window info, but wait for the task |
| // destroyed to keep the window bounds info. Remove the window info only |
| // when the ARC instance is connected. |
| if (is_connection_ready_) { |
| FullRestoreSaveHandler::GetInstance()->RemoveWindowInfo( |
| profile_path_, task_it->second, task_id); |
| } |
| return; |
| } |
| |
| // If the ghost window has been created for `session_id`, remove |
| // `app_launch_info` from the restore data with `session_id` as the window id. |
| int32_t session_id = |
| window->GetProperty(app_restore::kGhostWindowSessionIdKey); |
| auto it = ghost_window_session_id_to_app_id_.find(session_id); |
| if (it != ghost_window_session_id_to_app_id_.end()) { |
| // For ghost windows, we don't need to wait for OnTaskDestroyed, so remove |
| // AppRestoreData for `session_id`. |
| FullRestoreSaveHandler::GetInstance()->RemoveAppRestoreData( |
| profile_path_, it->second, session_id); |
| ghost_window_session_id_to_app_id_.erase(it); |
| } |
| } |
| |
| void ArcSaveHandler::OnTaskCreated(const std::string& app_id, |
| int32_t task_id, |
| int32_t session_id) { |
| auto it = session_id_to_app_launch_info_.find(session_id); |
| if (it == session_id_to_app_launch_info_.end()) { |
| auto session_it = ghost_window_session_id_to_app_id_.find(session_id); |
| if (session_it == ghost_window_session_id_to_app_id_.end()) |
| return; |
| |
| // For the ghost window, modify the window id from `session_id` to |
| // `task_id`. |
| FullRestoreSaveHandler::GetInstance()->ModifyWindowId( |
| profile_path_, session_it->second, session_id, task_id); |
| ghost_window_session_id_to_app_id_.erase(session_it); |
| task_id_to_app_id_[task_id] = app_id; |
| return; |
| } |
| |
| task_id_to_app_id_[task_id] = app_id; |
| |
| auto app_launch_info = std::move(it->second.first); |
| session_id_to_app_launch_info_.erase(it); |
| if (session_id_to_app_launch_info_.empty()) |
| check_timer_.Stop(); |
| |
| app_launch_info->window_id = task_id; |
| FullRestoreSaveHandler::GetInstance()->AddAppLaunchInfo( |
| profile_path_, std::move(app_launch_info)); |
| |
| // Go through |arc_window_candidates_|. If the window for |task_id| has been |
| // created, call OnAppLaunched to save the window info. |
| auto window_it = std::find_if( |
| arc_window_candidates_.begin(), arc_window_candidates_.end(), |
| [task_id](aura::Window* window) { |
| return window->GetProperty(app_restore::kWindowIdKey) == task_id; |
| }); |
| if (window_it != arc_window_candidates_.end()) { |
| FullRestoreInfo::GetInstance()->OnAppLaunched(*window_it); |
| arc_window_candidates_.erase(*window_it); |
| } |
| } |
| |
| void ArcSaveHandler::OnTaskDestroyed(int32_t task_id) { |
| auto it = task_id_to_app_id_.find(task_id); |
| if (it == task_id_to_app_id_.end()) |
| return; |
| |
| FullRestoreSaveHandler::GetInstance()->RemoveAppRestoreData( |
| profile_path_, it->second, task_id); |
| |
| task_id_to_app_id_.erase(task_id); |
| } |
| |
| void ArcSaveHandler::OnTaskThemeColorUpdated(int32_t task_id, |
| uint32_t primary_color, |
| uint32_t status_bar_color) { |
| auto it = task_id_to_app_id_.find(task_id); |
| if (it == task_id_to_app_id_.end()) |
| return; |
| |
| FullRestoreSaveHandler::GetInstance()->ModifyThemeColor( |
| profile_path_, it->second, task_id, primary_color, status_bar_color); |
| } |
| |
| int32_t ArcSaveHandler::GetArcSessionId() { |
| if (session_id_ >= kArcSessionIdOffsetForRestoredLaunching) { |
| LOG(WARNING) << "ARC session id is too large: " << session_id_; |
| session_id_ = 0; |
| } |
| |
| return ++session_id_; |
| } |
| |
| std::string ArcSaveHandler::GetAppId(aura::Window* window) { |
| // First check |task_id_to_app_id_| to see if we can find app id there. |
| const int32_t task_id = window->GetProperty(app_restore::kWindowIdKey); |
| auto task_iter = task_id_to_app_id_.find(task_id); |
| if (task_iter != task_id_to_app_id_.end()) |
| return task_iter->second; |
| |
| // If not, try to search in |ghost_window_session_id_to_app_id_|. |
| const int32_t session_id = |
| window->GetProperty(app_restore::kGhostWindowSessionIdKey); |
| auto ghost_iter = ghost_window_session_id_to_app_id_.find(session_id); |
| return ghost_iter != ghost_window_session_id_to_app_id_.end() |
| ? ghost_iter->second |
| : std::string(); |
| } |
| |
| void ArcSaveHandler::MaybeStartCheckTimer() { |
| if (!check_timer_.IsRunning()) { |
| check_timer_.Start( |
| FROM_HERE, kCheckCycleInterval, |
| base::BindRepeating(&ArcSaveHandler::CheckTasksForAppLaunching, |
| weak_factory_.GetWeakPtr())); |
| } |
| } |
| |
| void ArcSaveHandler::CheckTasksForAppLaunching() { |
| std::set<int32_t> session_ids; |
| for (const auto& it : session_id_to_app_launch_info_) { |
| base::TimeDelta time_delta = base::TimeTicks::Now() - it.second.second; |
| if (time_delta > kCheckCycleInterval) |
| session_ids.insert(it.first); |
| } |
| |
| for (auto id : session_ids) |
| session_id_to_app_launch_info_.erase(id); |
| |
| if (session_id_to_app_launch_info_.empty()) |
| check_timer_.Stop(); |
| } |
| |
| } // namespace full_restore |