| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/task_manager/providers/arc/arc_process_task.h" |
| |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/i18n/rtl.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "chromeos/ash/experiences/arc/arc_util.h" |
| #include "chromeos/ash/experiences/arc/intent_helper/arc_intent_helper_bridge.h" |
| #include "chromeos/ash/experiences/arc/mojom/process.mojom.h" |
| #include "chromeos/ash/experiences/arc/session/arc_bridge_service.h" |
| #include "chromeos/ash/experiences/arc/session/arc_service_manager.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/child_process_host.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/gfx/image/image.h" |
| |
| namespace task_manager { |
| |
| namespace { |
| |
| // |arc_process_.packages()| contains an alphabetically-sorted list of |
| // package names the process has. Since the Task class can hold only one |
| // icon per process, and there is no reliable way to pick the most important |
| // process from the |arc_process_.packages()| list, just use the first item |
| // in the list. In some case, |arc_process_.packages()| is empty, it would |
| // be expected to get default process icon. For example, daemon processes in |
| // android container such like surfaceflinger, debuggerd or installd. Each |
| // of them would be shown on task manager but does not have a package name. |
| std::string FirstPackage(const std::vector<std::string>& packages) { |
| return packages.empty() ? std::string() : packages[0]; |
| } |
| |
| std::u16string MakeTitle(const arc::ArcProcess& arc_process) { |
| int name_template = IDS_TASK_MANAGER_ARC_PREFIX; |
| switch (arc_process.process_state()) { |
| case arc::mojom::ProcessState::PERSISTENT: |
| case arc::mojom::ProcessState::PERSISTENT_UI: |
| name_template = IDS_TASK_MANAGER_ARC_SYSTEM; |
| break; |
| case arc::mojom::ProcessState::FOREGROUND_SERVICE: |
| case arc::mojom::ProcessState::BOUND_FOREGROUND_SERVICE: |
| case arc::mojom::ProcessState::IMPORTANT_FOREGROUND: |
| case arc::mojom::ProcessState::IMPORTANT_BACKGROUND: |
| case arc::mojom::ProcessState::TRANSIENT_BACKGROUND: |
| case arc::mojom::ProcessState::SERVICE: |
| name_template = IDS_TASK_MANAGER_ARC_PREFIX_BACKGROUND_SERVICE; |
| break; |
| case arc::mojom::ProcessState::RECEIVER: |
| name_template = IDS_TASK_MANAGER_ARC_PREFIX_RECEIVER; |
| break; |
| default: |
| break; |
| } |
| std::u16string title = l10n_util::GetStringFUTF16( |
| name_template, base::UTF8ToUTF16(arc_process.process_name())); |
| base::i18n::AdjustStringForLocaleDirection(&title); |
| return title; |
| } |
| |
| // An activity name for retrieving the package's default icon without |
| // specifying an activity name. |
| constexpr char kEmptyActivityName[] = ""; |
| |
| } // namespace |
| |
| ArcProcessTask::ArcProcessTask(arc::ArcProcess arc_process) |
| : Task(MakeTitle(arc_process), |
| nullptr /* icon */, |
| arc_process.pid()), |
| arc_process_(std::move(arc_process)) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| StartIconLoading(); |
| } |
| |
| void ArcProcessTask::StartIconLoading() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| // TaskManager is not tied to BrowserContext. Thus, we just use the |
| // BrowserContext which is tied to ARC. |
| auto* arc_service_manager = arc::ArcServiceManager::Get(); |
| auto* intent_helper_bridge = arc::ArcIntentHelperBridge::GetForBrowserContext( |
| arc_service_manager->browser_context()); |
| arc::ArcIntentHelperBridge::GetResult result = |
| arc::ArcIntentHelperBridge::GetResult::FAILED_ARC_NOT_READY; |
| if (intent_helper_bridge) { |
| std::vector<arc::ArcIntentHelperBridge::ActivityName> activities = { |
| {FirstPackage(arc_process_.packages()), kEmptyActivityName}}; |
| result = intent_helper_bridge->GetActivityIcons( |
| activities, base::BindOnce(&ArcProcessTask::OnIconLoaded, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| if (result == arc::ArcIntentHelperBridge::GetResult::FAILED_ARC_NOT_READY) { |
| // Need to retry loading the icon. |
| arc_service_manager->arc_bridge_service()->intent_helper()->AddObserver( |
| this); |
| } |
| } |
| |
| ArcProcessTask::~ArcProcessTask() { |
| auto* service_manager = arc::ArcServiceManager::Get(); |
| // This destructor can also be called when TaskManagerImpl is destructed. |
| // Since TaskManagerImpl is a LAZY_INSTANCE, arc::ArcServiceManager may have |
| // already been destructed. In that case, arc_bridge_service() has also been |
| // destructed, and it is safe to just return. |
| if (!service_manager) |
| return; |
| service_manager->arc_bridge_service()->intent_helper()->RemoveObserver(this); |
| } |
| |
| Task::Type ArcProcessTask::GetType() const { |
| return Task::ARC; |
| } |
| |
| int ArcProcessTask::GetChildProcessUniqueID() const { |
| // ARC process is not a child process of the browser. |
| return content::ChildProcessHost::kInvalidUniqueID; |
| } |
| |
| bool ArcProcessTask::IsKillable() { |
| // Do not kill persistent processes. |
| return !arc_process_.IsPersistent(); |
| } |
| |
| bool ArcProcessTask::IsRunningInVM() const { |
| return arc::IsArcVmEnabled(); |
| } |
| |
| bool ArcProcessTask::Kill() { |
| auto* process_instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc::ArcServiceManager::Get()->arc_bridge_service()->process(), |
| KillProcess); |
| if (!process_instance) |
| return false; |
| process_instance->KillProcess(arc_process_.nspid(), |
| "Killed manually from Task Manager"); |
| return true; |
| } |
| |
| void ArcProcessTask::OnConnectionReady() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| VLOG(2) << "intent_helper instance is ready. Fetching the icon for " |
| << FirstPackage(arc_process_.packages()); |
| arc::ArcServiceManager::Get() |
| ->arc_bridge_service() |
| ->intent_helper() |
| ->RemoveObserver(this); |
| |
| // Instead of calling into StartIconLoading() directly, return to the main |
| // loop first to make sure other ArcBridgeService observers are notified. |
| // Otherwise, arc::ArcIntentHelperBridge::GetActivityIcon() may fail again. |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce(&ArcProcessTask::StartIconLoading, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void ArcProcessTask::SetProcessState(arc::mojom::ProcessState process_state) { |
| arc_process_.set_process_state(process_state); |
| } |
| |
| void ArcProcessTask::OnIconLoaded( |
| std::unique_ptr<arc::ArcIntentHelperBridge::ActivityToIconsMap> icons) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| for (const auto& kv : *icons) { |
| const gfx::Image& icon = kv.second.icon16; |
| if (icon.IsEmpty()) |
| continue; |
| set_icon(*icon.ToImageSkia()); |
| break; // Since the parent class can hold only one icon, break here. |
| } |
| } |
| |
| } // namespace task_manager |