• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "libprocessgroup"
19 
20 #include <fcntl.h>
21 #include <task_profiles.h>
22 #include <string>
23 
24 #include <android-base/file.h>
25 #include <android-base/logging.h>
26 #include <android-base/properties.h>
27 #include <android-base/stringprintf.h>
28 #include <android-base/strings.h>
29 #include <android-base/threads.h>
30 
31 #include <cutils/android_filesystem_config.h>
32 
33 #include <json/reader.h>
34 #include <json/value.h>
35 
36 // To avoid issues in sdk_mac build
37 #if defined(__ANDROID__)
38 #include <sys/prctl.h>
39 #endif
40 
41 using android::base::GetThreadId;
42 using android::base::GetUintProperty;
43 using android::base::StringPrintf;
44 using android::base::StringReplace;
45 using android::base::unique_fd;
46 using android::base::WriteStringToFile;
47 
48 static constexpr const char* TASK_PROFILE_DB_FILE = "/etc/task_profiles.json";
49 static constexpr const char* TASK_PROFILE_DB_VENDOR_FILE = "/vendor/etc/task_profiles.json";
50 
51 static constexpr const char* TEMPLATE_TASK_PROFILE_API_FILE =
52         "/etc/task_profiles/task_profiles_%u.json";
53 
54 class FdCacheHelper {
55   public:
56     enum FdState {
57         FDS_INACCESSIBLE = -1,
58         FDS_APP_DEPENDENT = -2,
59         FDS_NOT_CACHED = -3,
60     };
61 
62     static void Cache(const std::string& path, android::base::unique_fd& fd);
63     static void Drop(android::base::unique_fd& fd);
64     static void Init(const std::string& path, android::base::unique_fd& fd);
IsCached(const android::base::unique_fd & fd)65     static bool IsCached(const android::base::unique_fd& fd) { return fd > FDS_INACCESSIBLE; }
66 
67   private:
68     static bool IsAppDependentPath(const std::string& path);
69 };
70 
Init(const std::string & path,android::base::unique_fd & fd)71 void FdCacheHelper::Init(const std::string& path, android::base::unique_fd& fd) {
72     // file descriptors for app-dependent paths can't be cached
73     if (IsAppDependentPath(path)) {
74         // file descriptor is not cached
75         fd.reset(FDS_APP_DEPENDENT);
76         return;
77     }
78     // file descriptor can be cached later on request
79     fd.reset(FDS_NOT_CACHED);
80 }
81 
Cache(const std::string & path,android::base::unique_fd & fd)82 void FdCacheHelper::Cache(const std::string& path, android::base::unique_fd& fd) {
83     if (fd != FDS_NOT_CACHED) {
84         return;
85     }
86 
87     if (access(path.c_str(), W_OK) != 0) {
88         // file is not accessible
89         fd.reset(FDS_INACCESSIBLE);
90         return;
91     }
92 
93     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));
94     if (tmp_fd < 0) {
95         PLOG(ERROR) << "Failed to cache fd '" << path << "'";
96         fd.reset(FDS_INACCESSIBLE);
97         return;
98     }
99 
100     fd = std::move(tmp_fd);
101 }
102 
Drop(android::base::unique_fd & fd)103 void FdCacheHelper::Drop(android::base::unique_fd& fd) {
104     if (fd == FDS_NOT_CACHED) {
105         return;
106     }
107 
108     fd.reset(FDS_NOT_CACHED);
109 }
110 
IsAppDependentPath(const std::string & path)111 bool FdCacheHelper::IsAppDependentPath(const std::string& path) {
112     return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
113 }
114 
115 IProfileAttribute::~IProfileAttribute() = default;
116 
Reset(const CgroupController & controller,const std::string & file_name)117 void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
118     controller_ = controller;
119     file_name_ = file_name;
120 }
121 
GetPathForTask(int tid,std::string * path) const122 bool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {
123     std::string subgroup;
124     if (!controller()->GetTaskGroup(tid, &subgroup)) {
125         return false;
126     }
127 
128     if (path == nullptr) {
129         return true;
130     }
131 
132     const std::string& file_name =
133             controller()->version() == 2 && !file_v2_name_.empty() ? file_v2_name_ : file_name_;
134     if (subgroup.empty()) {
135         *path = StringPrintf("%s/%s", controller()->path(), file_name.c_str());
136     } else {
137         *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(), file_name.c_str());
138     }
139     return true;
140 }
141 
ExecuteForProcess(uid_t,pid_t) const142 bool SetClampsAction::ExecuteForProcess(uid_t, pid_t) const {
143     // TODO: add support when kernel supports util_clamp
144     LOG(WARNING) << "SetClampsAction::ExecuteForProcess is not supported";
145     return false;
146 }
147 
ExecuteForTask(int) const148 bool SetClampsAction::ExecuteForTask(int) const {
149     // TODO: add support when kernel supports util_clamp
150     LOG(WARNING) << "SetClampsAction::ExecuteForTask is not supported";
151     return false;
152 }
153 
154 // To avoid issues in sdk_mac build
155 #if defined(__ANDROID__)
156 
IsTimerSlackSupported(int tid)157 bool SetTimerSlackAction::IsTimerSlackSupported(int tid) {
158     auto file = StringPrintf("/proc/%d/timerslack_ns", tid);
159 
160     return (access(file.c_str(), W_OK) == 0);
161 }
162 
ExecuteForTask(int tid) const163 bool SetTimerSlackAction::ExecuteForTask(int tid) const {
164     static bool sys_supports_timerslack = IsTimerSlackSupported(tid);
165 
166     // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
167     // TODO: once we've backported this, log if the open(2) fails.
168     if (sys_supports_timerslack) {
169         auto file = StringPrintf("/proc/%d/timerslack_ns", tid);
170         if (!WriteStringToFile(std::to_string(slack_), file)) {
171             if (errno == ENOENT) {
172                 // This happens when process is already dead
173                 return true;
174             }
175             PLOG(ERROR) << "set_timerslack_ns write failed";
176         }
177     }
178 
179     // TODO: Remove when /proc/<tid>/timerslack_ns interface is backported.
180     if (tid == 0 || tid == GetThreadId()) {
181         if (prctl(PR_SET_TIMERSLACK, slack_) == -1) {
182             PLOG(ERROR) << "set_timerslack_ns prctl failed";
183         }
184     }
185 
186     return true;
187 }
188 
189 #else
190 
ExecuteForTask(int) const191 bool SetTimerSlackAction::ExecuteForTask(int) const {
192     return true;
193 };
194 
195 #endif
196 
ExecuteForProcess(uid_t,pid_t pid) const197 bool SetAttributeAction::ExecuteForProcess(uid_t, pid_t pid) const {
198     return ExecuteForTask(pid);
199 }
200 
ExecuteForTask(int tid) const201 bool SetAttributeAction::ExecuteForTask(int tid) const {
202     std::string path;
203 
204     if (!attribute_->GetPathForTask(tid, &path)) {
205         LOG(ERROR) << "Failed to find cgroup for tid " << tid;
206         return false;
207     }
208 
209     if (!WriteStringToFile(value_, path)) {
210         if (access(path.c_str(), F_OK) < 0) {
211             if (optional_) {
212                 return true;
213             } else {
214                 LOG(ERROR) << "No such cgroup attribute: " << path;
215                 return false;
216             }
217         }
218         // The PLOG() statement below uses the error code stored in `errno` by
219         // WriteStringToFile() because access() only overwrites `errno` if it fails
220         // and because this code is only reached if the access() function returns 0.
221         PLOG(ERROR) << "Failed to write '" << value_ << "' to " << path;
222         return false;
223     }
224 
225     return true;
226 }
227 
SetCgroupAction(const CgroupController & c,const std::string & p)228 SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
229     : controller_(c), path_(p) {
230     FdCacheHelper::Init(controller_.GetTasksFilePath(path_), fd_[ProfileAction::RCT_TASK]);
231     // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them
232     FdCacheHelper::Init(controller_.GetProcsFilePath(path_, 0, 0), fd_[ProfileAction::RCT_PROCESS]);
233 }
234 
AddTidToCgroup(int tid,int fd,const char * controller_name)235 bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) {
236     if (tid <= 0) {
237         return true;
238     }
239 
240     std::string value = std::to_string(tid);
241 
242     if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) == value.length()) {
243         return true;
244     }
245 
246     // If the thread is in the process of exiting, don't flag an error
247     if (errno == ESRCH) {
248         return true;
249     }
250 
251     // ENOSPC is returned when cpuset cgroup that we are joining has no online cpus
252     if (errno == ENOSPC && !strcmp(controller_name, "cpuset")) {
253         // This is an abnormal case happening only in testing, so report it only once
254         static bool empty_cpuset_reported = false;
255 
256         if (empty_cpuset_reported) {
257             return true;
258         }
259 
260         LOG(ERROR) << "Failed to add task '" << value
261                    << "' into cpuset because all cpus in that cpuset are offline";
262         empty_cpuset_reported = true;
263     } else {
264         PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
265     }
266 
267     return false;
268 }
269 
UseCachedFd(ResourceCacheType cache_type,int id) const270 ProfileAction::CacheUseResult SetCgroupAction::UseCachedFd(ResourceCacheType cache_type,
271                                                            int id) const {
272     std::lock_guard<std::mutex> lock(fd_mutex_);
273     if (FdCacheHelper::IsCached(fd_[cache_type])) {
274         // fd is cached, reuse it
275         if (!AddTidToCgroup(id, fd_[cache_type], controller()->name())) {
276             LOG(ERROR) << "Failed to add task into cgroup";
277             return ProfileAction::FAIL;
278         }
279         return ProfileAction::SUCCESS;
280     }
281 
282     if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) {
283         // no permissions to access the file, ignore
284         return ProfileAction::SUCCESS;
285     }
286 
287     if (cache_type == ResourceCacheType::RCT_TASK &&
288         fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
289         // application-dependent path can't be used with tid
290         PLOG(ERROR) << "Application profile can't be applied to a thread";
291         return ProfileAction::FAIL;
292     }
293 
294     return ProfileAction::UNUSED;
295 }
296 
ExecuteForProcess(uid_t uid,pid_t pid) const297 bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
298     CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, pid);
299     if (result != ProfileAction::UNUSED) {
300         return result == ProfileAction::SUCCESS;
301     }
302 
303     // fd was not cached or cached fd can't be used
304     std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
305     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
306     if (tmp_fd < 0) {
307         PLOG(WARNING) << "Failed to open " << procs_path;
308         return false;
309     }
310     if (!AddTidToCgroup(pid, tmp_fd, controller()->name())) {
311         LOG(ERROR) << "Failed to add task into cgroup";
312         return false;
313     }
314 
315     return true;
316 }
317 
ExecuteForTask(int tid) const318 bool SetCgroupAction::ExecuteForTask(int tid) const {
319     CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, tid);
320     if (result != ProfileAction::UNUSED) {
321         return result == ProfileAction::SUCCESS;
322     }
323 
324     // fd was not cached or cached fd can't be used
325     std::string tasks_path = controller()->GetTasksFilePath(path_);
326     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
327     if (tmp_fd < 0) {
328         PLOG(WARNING) << "Failed to open " << tasks_path;
329         return false;
330     }
331     if (!AddTidToCgroup(tid, tmp_fd, controller()->name())) {
332         LOG(ERROR) << "Failed to add task into cgroup";
333         return false;
334     }
335 
336     return true;
337 }
338 
EnableResourceCaching(ResourceCacheType cache_type)339 void SetCgroupAction::EnableResourceCaching(ResourceCacheType cache_type) {
340     std::lock_guard<std::mutex> lock(fd_mutex_);
341     // Return early to prevent unnecessary calls to controller_.Get{Tasks|Procs}FilePath() which
342     // include regex evaluations
343     if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) {
344         return;
345     }
346     switch (cache_type) {
347         case (ProfileAction::RCT_TASK):
348             FdCacheHelper::Cache(controller_.GetTasksFilePath(path_), fd_[cache_type]);
349             break;
350         case (ProfileAction::RCT_PROCESS):
351             // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them
352             FdCacheHelper::Cache(controller_.GetProcsFilePath(path_, 0, 0), fd_[cache_type]);
353             break;
354         default:
355             LOG(ERROR) << "Invalid cache type is specified!";
356             break;
357     }
358 }
359 
DropResourceCaching(ResourceCacheType cache_type)360 void SetCgroupAction::DropResourceCaching(ResourceCacheType cache_type) {
361     std::lock_guard<std::mutex> lock(fd_mutex_);
362     FdCacheHelper::Drop(fd_[cache_type]);
363 }
364 
WriteFileAction(const std::string & task_path,const std::string & proc_path,const std::string & value,bool logfailures)365 WriteFileAction::WriteFileAction(const std::string& task_path, const std::string& proc_path,
366                                  const std::string& value, bool logfailures)
367     : task_path_(task_path), proc_path_(proc_path), value_(value), logfailures_(logfailures) {
368     FdCacheHelper::Init(task_path_, fd_[ProfileAction::RCT_TASK]);
369     if (!proc_path_.empty()) FdCacheHelper::Init(proc_path_, fd_[ProfileAction::RCT_PROCESS]);
370 }
371 
WriteValueToFile(const std::string & value_,ResourceCacheType cache_type,int uid,int pid,bool logfailures) const372 bool WriteFileAction::WriteValueToFile(const std::string& value_, ResourceCacheType cache_type,
373                                        int uid, int pid, bool logfailures) const {
374     std::string value(value_);
375 
376     value = StringReplace(value, "<uid>", std::to_string(uid), true);
377     value = StringReplace(value, "<pid>", std::to_string(pid), true);
378 
379     CacheUseResult result = UseCachedFd(cache_type, value);
380 
381     if (result != ProfileAction::UNUSED) {
382         return result == ProfileAction::SUCCESS;
383     }
384 
385     std::string path;
386     if (cache_type == ProfileAction::RCT_TASK || proc_path_.empty()) {
387         path = task_path_;
388     } else {
389         path = proc_path_;
390     }
391 
392     // Use WriteStringToFd instead of WriteStringToFile because the latter will open file with
393     // O_TRUNC which causes kernfs_mutex contention
394     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));
395 
396     if (tmp_fd < 0) {
397         if (logfailures) PLOG(WARNING) << "Failed to open " << path;
398         return false;
399     }
400 
401     if (!WriteStringToFd(value, tmp_fd)) {
402         if (logfailures) PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
403         return false;
404     }
405 
406     return true;
407 }
408 
UseCachedFd(ResourceCacheType cache_type,const std::string & value) const409 ProfileAction::CacheUseResult WriteFileAction::UseCachedFd(ResourceCacheType cache_type,
410                                                            const std::string& value) const {
411     std::lock_guard<std::mutex> lock(fd_mutex_);
412     if (FdCacheHelper::IsCached(fd_[cache_type])) {
413         // fd is cached, reuse it
414         bool ret = WriteStringToFd(value, fd_[cache_type]);
415 
416         if (!ret && logfailures_) {
417             if (cache_type == ProfileAction::RCT_TASK || proc_path_.empty()) {
418                 PLOG(ERROR) << "Failed to write '" << value << "' to " << task_path_;
419             } else {
420                 PLOG(ERROR) << "Failed to write '" << value << "' to " << proc_path_;
421             }
422         }
423         return ret ? ProfileAction::SUCCESS : ProfileAction::FAIL;
424     }
425 
426     if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) {
427         // no permissions to access the file, ignore
428         return ProfileAction::SUCCESS;
429     }
430 
431     if (cache_type == ResourceCacheType::RCT_TASK &&
432         fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
433         // application-dependent path can't be used with tid
434         PLOG(ERROR) << "Application profile can't be applied to a thread";
435         return ProfileAction::FAIL;
436     }
437     return ProfileAction::UNUSED;
438 }
439 
ExecuteForProcess(uid_t uid,pid_t pid) const440 bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
441     if (!proc_path_.empty()) {
442         return WriteValueToFile(value_, ProfileAction::RCT_PROCESS, uid, pid, logfailures_);
443     }
444 
445     DIR* d;
446     struct dirent* de;
447     char proc_path[255];
448     int t_pid;
449 
450     sprintf(proc_path, "/proc/%d/task", pid);
451     if (!(d = opendir(proc_path))) {
452         return false;
453     }
454 
455     while ((de = readdir(d))) {
456         if (de->d_name[0] == '.') {
457             continue;
458         }
459 
460         t_pid = atoi(de->d_name);
461 
462         if (!t_pid) {
463             continue;
464         }
465 
466         WriteValueToFile(value_, ProfileAction::RCT_TASK, uid, t_pid, logfailures_);
467     }
468 
469     closedir(d);
470 
471     return true;
472 }
473 
ExecuteForTask(int tid) const474 bool WriteFileAction::ExecuteForTask(int tid) const {
475     return WriteValueToFile(value_, ProfileAction::RCT_TASK, getuid(), tid, logfailures_);
476 }
477 
EnableResourceCaching(ResourceCacheType cache_type)478 void WriteFileAction::EnableResourceCaching(ResourceCacheType cache_type) {
479     std::lock_guard<std::mutex> lock(fd_mutex_);
480     if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) {
481         return;
482     }
483     switch (cache_type) {
484         case (ProfileAction::RCT_TASK):
485             FdCacheHelper::Cache(task_path_, fd_[cache_type]);
486             break;
487         case (ProfileAction::RCT_PROCESS):
488             if (!proc_path_.empty()) FdCacheHelper::Cache(proc_path_, fd_[cache_type]);
489             break;
490         default:
491             LOG(ERROR) << "Invalid cache type is specified!";
492             break;
493     }
494 }
495 
DropResourceCaching(ResourceCacheType cache_type)496 void WriteFileAction::DropResourceCaching(ResourceCacheType cache_type) {
497     std::lock_guard<std::mutex> lock(fd_mutex_);
498     FdCacheHelper::Drop(fd_[cache_type]);
499 }
500 
ExecuteForProcess(uid_t uid,pid_t pid) const501 bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
502     for (const auto& profile : profiles_) {
503         profile->ExecuteForProcess(uid, pid);
504     }
505     return true;
506 }
507 
ExecuteForTask(int tid) const508 bool ApplyProfileAction::ExecuteForTask(int tid) const {
509     for (const auto& profile : profiles_) {
510         profile->ExecuteForTask(tid);
511     }
512     return true;
513 }
514 
EnableResourceCaching(ResourceCacheType cache_type)515 void ApplyProfileAction::EnableResourceCaching(ResourceCacheType cache_type) {
516     for (const auto& profile : profiles_) {
517         profile->EnableResourceCaching(cache_type);
518     }
519 }
520 
DropResourceCaching(ResourceCacheType cache_type)521 void ApplyProfileAction::DropResourceCaching(ResourceCacheType cache_type) {
522     for (const auto& profile : profiles_) {
523         profile->DropResourceCaching(cache_type);
524     }
525 }
526 
MoveTo(TaskProfile * profile)527 void TaskProfile::MoveTo(TaskProfile* profile) {
528     profile->elements_ = std::move(elements_);
529     profile->res_cached_ = res_cached_;
530 }
531 
ExecuteForProcess(uid_t uid,pid_t pid) const532 bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {
533     for (const auto& element : elements_) {
534         if (!element->ExecuteForProcess(uid, pid)) {
535             LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed";
536             return false;
537         }
538     }
539     return true;
540 }
541 
ExecuteForTask(int tid) const542 bool TaskProfile::ExecuteForTask(int tid) const {
543     if (tid == 0) {
544         tid = GetThreadId();
545     }
546     for (const auto& element : elements_) {
547         if (!element->ExecuteForTask(tid)) {
548             LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed";
549             return false;
550         }
551     }
552     return true;
553 }
554 
EnableResourceCaching(ProfileAction::ResourceCacheType cache_type)555 void TaskProfile::EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) {
556     if (res_cached_) {
557         return;
558     }
559 
560     for (auto& element : elements_) {
561         element->EnableResourceCaching(cache_type);
562     }
563 
564     res_cached_ = true;
565 }
566 
DropResourceCaching(ProfileAction::ResourceCacheType cache_type)567 void TaskProfile::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) {
568     if (!res_cached_) {
569         return;
570     }
571 
572     for (auto& element : elements_) {
573         element->DropResourceCaching(cache_type);
574     }
575 
576     res_cached_ = false;
577 }
578 
DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const579 void TaskProfiles::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const {
580     for (auto& iter : profiles_) {
581         iter.second->DropResourceCaching(cache_type);
582     }
583 }
584 
GetInstance()585 TaskProfiles& TaskProfiles::GetInstance() {
586     // Deliberately leak this object to avoid a race between destruction on
587     // process exit and concurrent access from another thread.
588     static auto* instance = new TaskProfiles;
589     return *instance;
590 }
591 
TaskProfiles()592 TaskProfiles::TaskProfiles() {
593     // load system task profiles
594     if (!Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_FILE)) {
595         LOG(ERROR) << "Loading " << TASK_PROFILE_DB_FILE << " for [" << getpid() << "] failed";
596     }
597 
598     // load API-level specific system task profiles if available
599     unsigned int api_level = GetUintProperty<unsigned int>("ro.product.first_api_level", 0);
600     if (api_level > 0) {
601         std::string api_profiles_path =
602                 android::base::StringPrintf(TEMPLATE_TASK_PROFILE_API_FILE, api_level);
603         if (!access(api_profiles_path.c_str(), F_OK) || errno != ENOENT) {
604             if (!Load(CgroupMap::GetInstance(), api_profiles_path)) {
605                 LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid() << "] failed";
606             }
607         }
608     }
609 
610     // load vendor task profiles if the file exists
611     if (!access(TASK_PROFILE_DB_VENDOR_FILE, F_OK) &&
612         !Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_VENDOR_FILE)) {
613         LOG(ERROR) << "Loading " << TASK_PROFILE_DB_VENDOR_FILE << " for [" << getpid()
614                    << "] failed";
615     }
616 }
617 
Load(const CgroupMap & cg_map,const std::string & file_name)618 bool TaskProfiles::Load(const CgroupMap& cg_map, const std::string& file_name) {
619     std::string json_doc;
620 
621     if (!android::base::ReadFileToString(file_name, &json_doc)) {
622         LOG(ERROR) << "Failed to read task profiles from " << file_name;
623         return false;
624     }
625 
626     Json::CharReaderBuilder builder;
627     std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
628     Json::Value root;
629     std::string errorMessage;
630     if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
631         LOG(ERROR) << "Failed to parse task profiles: " << errorMessage;
632         return false;
633     }
634 
635     const Json::Value& attr = root["Attributes"];
636     for (Json::Value::ArrayIndex i = 0; i < attr.size(); ++i) {
637         std::string name = attr[i]["Name"].asString();
638         std::string controller_name = attr[i]["Controller"].asString();
639         std::string file_attr = attr[i]["File"].asString();
640         std::string file_v2_attr = attr[i]["FileV2"].asString();
641 
642         if (!file_v2_attr.empty() && file_attr.empty()) {
643             LOG(ERROR) << "Attribute " << name << " has FileV2 but no File property";
644             return false;
645         }
646 
647         auto controller = cg_map.FindController(controller_name);
648         if (controller.HasValue()) {
649             auto iter = attributes_.find(name);
650             if (iter == attributes_.end()) {
651                 attributes_[name] =
652                         std::make_unique<ProfileAttribute>(controller, file_attr, file_v2_attr);
653             } else {
654                 iter->second->Reset(controller, file_attr);
655             }
656         } else {
657             LOG(WARNING) << "Controller " << controller_name << " is not found";
658         }
659     }
660 
661     const Json::Value& profiles_val = root["Profiles"];
662     for (Json::Value::ArrayIndex i = 0; i < profiles_val.size(); ++i) {
663         const Json::Value& profile_val = profiles_val[i];
664 
665         std::string profile_name = profile_val["Name"].asString();
666         const Json::Value& actions = profile_val["Actions"];
667         auto profile = std::make_shared<TaskProfile>(profile_name);
668 
669         for (Json::Value::ArrayIndex act_idx = 0; act_idx < actions.size(); ++act_idx) {
670             const Json::Value& action_val = actions[act_idx];
671             std::string action_name = action_val["Name"].asString();
672             const Json::Value& params_val = action_val["Params"];
673             if (action_name == "JoinCgroup") {
674                 std::string controller_name = params_val["Controller"].asString();
675                 std::string path = params_val["Path"].asString();
676 
677                 auto controller = cg_map.FindController(controller_name);
678                 if (controller.HasValue()) {
679                     profile->Add(std::make_unique<SetCgroupAction>(controller, path));
680                 } else {
681                     LOG(WARNING) << "JoinCgroup: controller " << controller_name << " is not found";
682                 }
683             } else if (action_name == "SetTimerSlack") {
684                 std::string slack_value = params_val["Slack"].asString();
685                 char* end;
686                 unsigned long slack;
687 
688                 slack = strtoul(slack_value.c_str(), &end, 10);
689                 if (end > slack_value.c_str()) {
690                     profile->Add(std::make_unique<SetTimerSlackAction>(slack));
691                 } else {
692                     LOG(WARNING) << "SetTimerSlack: invalid parameter: " << slack_value;
693                 }
694             } else if (action_name == "SetAttribute") {
695                 std::string attr_name = params_val["Name"].asString();
696                 std::string attr_value = params_val["Value"].asString();
697                 bool optional = strcmp(params_val["Optional"].asString().c_str(), "true") == 0;
698 
699                 auto iter = attributes_.find(attr_name);
700                 if (iter != attributes_.end()) {
701                     profile->Add(std::make_unique<SetAttributeAction>(iter->second.get(),
702                                                                       attr_value, optional));
703                 } else {
704                     LOG(WARNING) << "SetAttribute: unknown attribute: " << attr_name;
705                 }
706             } else if (action_name == "SetClamps") {
707                 std::string boost_value = params_val["Boost"].asString();
708                 std::string clamp_value = params_val["Clamp"].asString();
709                 char* end;
710                 unsigned long boost;
711 
712                 boost = strtoul(boost_value.c_str(), &end, 10);
713                 if (end > boost_value.c_str()) {
714                     unsigned long clamp = strtoul(clamp_value.c_str(), &end, 10);
715                     if (end > clamp_value.c_str()) {
716                         profile->Add(std::make_unique<SetClampsAction>(boost, clamp));
717                     } else {
718                         LOG(WARNING) << "SetClamps: invalid parameter " << clamp_value;
719                     }
720                 } else {
721                     LOG(WARNING) << "SetClamps: invalid parameter: " << boost_value;
722                 }
723             } else if (action_name == "WriteFile") {
724                 std::string attr_filepath = params_val["FilePath"].asString();
725                 std::string attr_procfilepath = params_val["ProcFilePath"].asString();
726                 std::string attr_value = params_val["Value"].asString();
727                 // FilePath and Value are mandatory
728                 if (!attr_filepath.empty() && !attr_value.empty()) {
729                     std::string attr_logfailures = params_val["LogFailures"].asString();
730                     bool logfailures = attr_logfailures.empty() || attr_logfailures == "true";
731                     profile->Add(std::make_unique<WriteFileAction>(attr_filepath, attr_procfilepath,
732                                                                    attr_value, logfailures));
733                 } else if (attr_filepath.empty()) {
734                     LOG(WARNING) << "WriteFile: invalid parameter: "
735                                  << "empty filepath";
736                 } else if (attr_value.empty()) {
737                     LOG(WARNING) << "WriteFile: invalid parameter: "
738                                  << "empty value";
739                 }
740             } else {
741                 LOG(WARNING) << "Unknown profile action: " << action_name;
742             }
743         }
744         auto iter = profiles_.find(profile_name);
745         if (iter == profiles_.end()) {
746             profiles_[profile_name] = profile;
747         } else {
748             // Move the content rather that replace the profile because old profile might be
749             // referenced from an aggregate profile if vendor overrides task profiles
750             profile->MoveTo(iter->second.get());
751             profile.reset();
752         }
753     }
754 
755     const Json::Value& aggregateprofiles_val = root["AggregateProfiles"];
756     for (Json::Value::ArrayIndex i = 0; i < aggregateprofiles_val.size(); ++i) {
757         const Json::Value& aggregateprofile_val = aggregateprofiles_val[i];
758 
759         std::string aggregateprofile_name = aggregateprofile_val["Name"].asString();
760         const Json::Value& aggregateprofiles = aggregateprofile_val["Profiles"];
761         std::vector<std::shared_ptr<TaskProfile>> profiles;
762         bool ret = true;
763 
764         for (Json::Value::ArrayIndex pf_idx = 0; pf_idx < aggregateprofiles.size(); ++pf_idx) {
765             std::string profile_name = aggregateprofiles[pf_idx].asString();
766 
767             if (profile_name == aggregateprofile_name) {
768                 LOG(WARNING) << "AggregateProfiles: recursive profile name: " << profile_name;
769                 ret = false;
770                 break;
771             } else if (profiles_.find(profile_name) == profiles_.end()) {
772                 LOG(WARNING) << "AggregateProfiles: undefined profile name: " << profile_name;
773                 ret = false;
774                 break;
775             } else {
776                 profiles.push_back(profiles_[profile_name]);
777             }
778         }
779         if (ret) {
780             auto profile = std::make_shared<TaskProfile>(aggregateprofile_name);
781             profile->Add(std::make_unique<ApplyProfileAction>(profiles));
782             profiles_[aggregateprofile_name] = profile;
783         }
784     }
785 
786     return true;
787 }
788 
GetProfile(const std::string & name) const789 TaskProfile* TaskProfiles::GetProfile(const std::string& name) const {
790     auto iter = profiles_.find(name);
791 
792     if (iter != profiles_.end()) {
793         return iter->second.get();
794     }
795     return nullptr;
796 }
797 
GetAttribute(const std::string & name) const798 const IProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
799     auto iter = attributes_.find(name);
800 
801     if (iter != attributes_.end()) {
802         return iter->second.get();
803     }
804     return nullptr;
805 }
806 
SetProcessProfiles(uid_t uid,pid_t pid,const std::vector<std::string> & profiles,bool use_fd_cache)807 bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
808                                       const std::vector<std::string>& profiles, bool use_fd_cache) {
809     bool success = true;
810     for (const auto& name : profiles) {
811         TaskProfile* profile = GetProfile(name);
812         if (profile != nullptr) {
813             if (use_fd_cache) {
814                 profile->EnableResourceCaching(ProfileAction::RCT_PROCESS);
815             }
816             if (!profile->ExecuteForProcess(uid, pid)) {
817                 PLOG(WARNING) << "Failed to apply " << name << " process profile";
818                 success = false;
819             }
820         } else {
821             PLOG(WARNING) << "Failed to find " << name << " process profile";
822             success = false;
823         }
824     }
825     return success;
826 }
827 
SetTaskProfiles(int tid,const std::vector<std::string> & profiles,bool use_fd_cache)828 bool TaskProfiles::SetTaskProfiles(int tid, const std::vector<std::string>& profiles,
829                                    bool use_fd_cache) {
830     bool success = true;
831     for (const auto& name : profiles) {
832         TaskProfile* profile = GetProfile(name);
833         if (profile != nullptr) {
834             if (use_fd_cache) {
835                 profile->EnableResourceCaching(ProfileAction::RCT_TASK);
836             }
837             if (!profile->ExecuteForTask(tid)) {
838                 PLOG(WARNING) << "Failed to apply " << name << " task profile";
839                 success = false;
840             }
841         } else {
842             PLOG(WARNING) << "Failed to find " << name << " task profile";
843             success = false;
844         }
845     }
846     return success;
847 }
848