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 |