| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 // | |
| 5 // The main point of this class is to cache ARC proc nspid<->pid mapping | |
| 6 // globally. Since the calculation is costly, a dedicated worker thread is | |
| 7 // used. All read/write of its internal data structure (i.e., the mapping) | |
| 8 // should be on this thread. | |
| 9 | |
| 10 #include "chrome/browser/chromeos/arc/arc_process_service.h" | |
| 11 | |
| 12 #include <algorithm> | |
| 13 #include <queue> | |
| 14 #include <string> | |
| 15 #include <unordered_map> | |
| 16 #include <unordered_set> | |
| 17 #include <utility> | |
| 18 | |
| 19 #include "base/callback.h" | |
| 20 #include "base/logging.h" | |
| 21 #include "base/process/process.h" | |
| 22 #include "base/process/process_iterator.h" | |
| 23 #include "base/task_runner_util.h" | |
| 24 #include "base/trace_event/trace_event.h" | |
| 25 #include "components/arc/arc_bridge_service.h" | |
| 26 #include "content/public/browser/browser_thread.h" | |
| 27 | |
| 28 namespace arc { | |
| 29 | |
| 30 using base::kNullProcessId; | |
| 31 using base::Process; | |
| 32 using base::ProcessId; | |
| 33 using base::SequencedWorkerPool; | |
| 34 using std::vector; | |
| 35 | |
| 36 namespace { | |
| 37 | |
| 38 // Weak pointer. This class is owned by ArcServiceManager. | |
| 39 ArcProcessService* g_arc_process_service = nullptr; | |
| 40 static constexpr char kInitName[] = "/init"; | |
| 41 static constexpr bool kNotFocused = false; | |
| 42 static constexpr int64_t kNoActivityTimeInfo = 0L; | |
| 43 | |
| 44 // Matches the process name "/init" in the process tree and get the | |
| 45 // corresponding process ID. | |
| 46 base::ProcessId GetArcInitProcessId( | |
| 47 const base::ProcessIterator::ProcessEntries& entry_list) { | |
| 48 for (const base::ProcessEntry& entry : entry_list) { | |
| 49 if (entry.cmd_line_args().empty()) { | |
| 50 continue; | |
| 51 } | |
| 52 // TODO(nya): Add more constraints to avoid mismatches. | |
| 53 const std::string& process_name = entry.cmd_line_args()[0]; | |
| 54 if (process_name == kInitName) { | |
| 55 return entry.pid(); | |
| 56 } | |
| 57 } | |
| 58 return base::kNullProcessId; | |
| 59 } | |
| 60 | |
| 61 std::vector<ArcProcess> GetArcSystemProcessList() { | |
| 62 std::vector<ArcProcess> ret_processes; | |
| 63 const base::ProcessIterator::ProcessEntries& entry_list = | |
| 64 base::ProcessIterator(nullptr).Snapshot(); | |
| 65 const base::ProcessId arc_init_pid = GetArcInitProcessId(entry_list); | |
| 66 | |
| 67 if (arc_init_pid == base::kNullProcessId) { | |
| 68 return ret_processes; | |
| 69 } | |
| 70 | |
| 71 // Enumerate the child processes of ARC init for gathering ARC System | |
| 72 // Processes. | |
| 73 for (const base::ProcessEntry& entry : entry_list) { | |
| 74 if (entry.cmd_line_args().empty()) { | |
| 75 continue; | |
| 76 } | |
| 77 // TODO(hctsai): For now, we only gather direct child process of init, need | |
| 78 // to get the processes below. For example, installd might fork dex2oat and | |
| 79 // it can be executed for minutes. | |
| 80 if (entry.parent_pid() == arc_init_pid) { | |
| 81 const base::ProcessId child_pid = entry.pid(); | |
| 82 const base::ProcessId child_nspid = | |
| 83 base::Process(child_pid).GetPidInNamespace(); | |
| 84 if (child_nspid != base::kNullProcessId) { | |
| 85 const std::string& process_name = entry.cmd_line_args()[0]; | |
| 86 // The is_focused and last_activity_time is not needed thus mocked | |
| 87 ret_processes.emplace_back(child_nspid, child_pid, process_name, | |
| 88 mojom::ProcessState::PERSISTENT, kNotFocused, | |
| 89 kNoActivityTimeInfo); | |
| 90 } | |
| 91 } | |
| 92 } | |
| 93 return ret_processes; | |
| 94 } | |
| 95 | |
| 96 void UpdateNspidToPidMap( | |
| 97 scoped_refptr<ArcProcessService::NSPidToPidMap> pid_map) { | |
| 98 TRACE_EVENT0("browser", "ArcProcessService::UpdateNspidToPidMap"); | |
| 99 | |
| 100 // NB: Despite of its name, ProcessIterator::Snapshot() may return | |
| 101 // inconsistent information because it simply walks procfs. Especially | |
| 102 // we must not assume the parent-child relationships are consistent. | |
| 103 const base::ProcessIterator::ProcessEntries& entry_list = | |
| 104 base::ProcessIterator(nullptr).Snapshot(); | |
| 105 | |
| 106 // Construct the process tree. | |
| 107 // NB: This can contain a loop in case of race conditions. | |
| 108 std::unordered_map<ProcessId, std::vector<ProcessId>> process_tree; | |
| 109 for (const base::ProcessEntry& entry : entry_list) | |
| 110 process_tree[entry.parent_pid()].push_back(entry.pid()); | |
| 111 | |
| 112 ProcessId arc_init_pid = GetArcInitProcessId(entry_list); | |
| 113 | |
| 114 // Enumerate all processes under ARC init and create nspid -> pid map. | |
| 115 if (arc_init_pid != kNullProcessId) { | |
| 116 std::queue<ProcessId> queue; | |
| 117 std::unordered_set<ProcessId> visited; | |
| 118 queue.push(arc_init_pid); | |
| 119 while (!queue.empty()) { | |
| 120 ProcessId pid = queue.front(); | |
| 121 queue.pop(); | |
| 122 // Do not visit the same process twice. Otherwise we may enter an infinite | |
| 123 // loop if |process_tree| contains a loop. | |
| 124 if (!visited.insert(pid).second) | |
| 125 continue; | |
| 126 | |
| 127 const ProcessId nspid = base::Process(pid).GetPidInNamespace(); | |
| 128 | |
| 129 // All ARC processes should be in namespace so nspid is usually non-null, | |
| 130 // but this can happen if the process has already gone. | |
| 131 // Only add processes we're interested in (those appear as keys in | |
| 132 // |pid_map|). | |
| 133 if (nspid != kNullProcessId && pid_map->find(nspid) != pid_map->end()) | |
| 134 (*pid_map)[nspid] = pid; | |
| 135 | |
| 136 for (ProcessId child_pid : process_tree[pid]) | |
| 137 queue.push(child_pid); | |
| 138 } | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 std::vector<ArcProcess> FilterProcessList( | |
| 143 const ArcProcessService::NSPidToPidMap& pid_map, | |
| 144 mojo::Array<mojom::RunningAppProcessInfoPtr> processes) { | |
| 145 std::vector<ArcProcess> ret_processes; | |
| 146 for (const auto& entry : processes) { | |
| 147 const auto it = pid_map.find(entry->pid); | |
| 148 // The nspid could be missing due to race condition. For example, the | |
| 149 // process is still running when we get the process snapshot and ends when | |
| 150 // we update the nspid to pid mapping. | |
| 151 if (it == pid_map.end() || it->second == base::kNullProcessId) { | |
| 152 continue; | |
| 153 } | |
| 154 // Constructs the ArcProcess instance if the mapping is found. | |
| 155 ArcProcess arc_process(entry->pid, pid_map.at(entry->pid), | |
| 156 entry->process_name, entry->process_state, | |
| 157 entry->is_focused, entry->last_activity_time); | |
| 158 // |entry->packages| is provided only when process.mojom's verion is >=4. | |
| 159 if (entry->packages) { | |
| 160 for (const auto& package : entry->packages) { | |
| 161 arc_process.packages().push_back(package.get()); | |
| 162 } | |
| 163 } | |
| 164 ret_processes.push_back(std::move(arc_process)); | |
| 165 } | |
| 166 return ret_processes; | |
| 167 } | |
| 168 | |
| 169 std::vector<ArcProcess> UpdateAndReturnProcessList( | |
| 170 scoped_refptr<ArcProcessService::NSPidToPidMap> nspid_map, | |
| 171 mojo::Array<mojom::RunningAppProcessInfoPtr> processes) { | |
| 172 ArcProcessService::NSPidToPidMap& pid_map = *nspid_map; | |
| 173 // Cleanup dead pids in the cache |pid_map|. | |
| 174 std::unordered_set<ProcessId> nspid_to_remove; | |
| 175 for (const auto& entry : pid_map) { | |
| 176 nspid_to_remove.insert(entry.first); | |
| 177 } | |
| 178 bool unmapped_nspid = false; | |
| 179 for (const auto& entry : processes) { | |
| 180 // erase() returns 0 if coudln't find the key. It means a new process. | |
| 181 if (nspid_to_remove.erase(entry->pid) == 0) { | |
| 182 pid_map[entry->pid] = base::kNullProcessId; | |
| 183 unmapped_nspid = true; | |
| 184 } | |
| 185 } | |
| 186 for (const auto& entry : nspid_to_remove) { | |
| 187 pid_map.erase(entry); | |
| 188 } | |
| 189 | |
| 190 // The operation is costly so avoid calling it when possible. | |
| 191 if (unmapped_nspid) { | |
| 192 UpdateNspidToPidMap(nspid_map); | |
| 193 } | |
| 194 | |
| 195 return FilterProcessList(pid_map, std::move(processes)); | |
| 196 } | |
| 197 | |
| 198 void Reset(scoped_refptr<ArcProcessService::NSPidToPidMap> pid_map) { | |
| 199 if (pid_map.get()) | |
| 200 pid_map->clear(); | |
| 201 } | |
| 202 | |
| 203 } // namespace | |
| 204 | |
| 205 ArcProcessService::ArcProcessService(ArcBridgeService* bridge_service) | |
| 206 : ArcService(bridge_service), | |
| 207 heavy_task_thread_("ArcProcessServiceThread"), | |
| 208 nspid_to_pid_(new NSPidToPidMap()), | |
| 209 weak_ptr_factory_(this) { | |
| 210 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 211 arc_bridge_service()->process()->AddObserver(this); | |
| 212 DCHECK(!g_arc_process_service); | |
| 213 g_arc_process_service = this; | |
| 214 heavy_task_thread_.Start(); | |
| 215 } | |
| 216 | |
| 217 ArcProcessService::~ArcProcessService() { | |
| 218 DCHECK(g_arc_process_service == this); | |
| 219 heavy_task_thread_.Stop(); | |
| 220 g_arc_process_service = nullptr; | |
| 221 arc_bridge_service()->process()->RemoveObserver(this); | |
| 222 } | |
| 223 | |
| 224 // static | |
| 225 ArcProcessService* ArcProcessService::Get() { | |
| 226 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 227 return g_arc_process_service; | |
| 228 } | |
| 229 | |
| 230 void ArcProcessService::OnInstanceReady() { | |
| 231 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 232 GetTaskRunner()->PostTask(FROM_HERE, base::Bind(&Reset, nspid_to_pid_)); | |
| 233 } | |
| 234 | |
| 235 void ArcProcessService::RequestSystemProcessList( | |
| 236 RequestProcessListCallback callback) { | |
| 237 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 238 | |
| 239 base::PostTaskAndReplyWithResult(GetTaskRunner().get(), FROM_HERE, | |
| 240 base::Bind(&GetArcSystemProcessList), | |
| 241 callback); | |
| 242 } | |
| 243 | |
| 244 bool ArcProcessService::RequestAppProcessList( | |
| 245 RequestProcessListCallback callback) { | |
| 246 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 247 | |
| 248 mojom::ProcessInstance* process_instance = | |
| 249 arc_bridge_service()->process()->GetInstanceForMethod( | |
| 250 "RequestProcessList"); | |
| 251 if (!process_instance) { | |
| 252 return false; | |
| 253 } | |
| 254 process_instance->RequestProcessList( | |
| 255 base::Bind(&ArcProcessService::OnReceiveProcessList, | |
| 256 weak_ptr_factory_.GetWeakPtr(), callback)); | |
| 257 return true; | |
| 258 } | |
| 259 | |
| 260 void ArcProcessService::OnReceiveProcessList( | |
| 261 const RequestProcessListCallback& callback, | |
| 262 mojo::Array<mojom::RunningAppProcessInfoPtr> instance_processes) { | |
| 263 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 264 | |
| 265 base::PostTaskAndReplyWithResult( | |
| 266 GetTaskRunner().get(), FROM_HERE, | |
| 267 base::Bind(&UpdateAndReturnProcessList, nspid_to_pid_, | |
| 268 base::Passed(&instance_processes)), | |
| 269 callback); | |
| 270 } | |
| 271 | |
| 272 scoped_refptr<base::SingleThreadTaskRunner> ArcProcessService::GetTaskRunner() { | |
| 273 return heavy_task_thread_.task_runner(); | |
| 274 } | |
| 275 | |
| 276 inline ArcProcessService::NSPidToPidMap::NSPidToPidMap() {} | |
| 277 | |
| 278 inline ArcProcessService::NSPidToPidMap::~NSPidToPidMap() {} | |
| 279 | |
| 280 } // namespace arc | |
| OLD | NEW |