libprocessgroup: Add fd caching support for SetProcessProfiles

Process profiles operating on paths that do not depend on pid or uid of
the process can cache the fd of the file they are operating on. Add
support for fd caching similar to how SetTaskProfiles caches the fd
of the file it needs to write to.

Bug: 215557553
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
Change-Id: Ie73ebcbbf1919d90409f40c1f6b08743f4edf97c
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index be34f95..c5badc9 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -35,6 +35,8 @@
 
 #ifndef __ANDROID_VNDK__
 
+bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
+
 static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc";
 
 bool UsePerAppMemcg();
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 0320b02..cb2fe0a 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -126,11 +126,16 @@
 }
 
 void DropTaskProfilesResourceCaching() {
-    TaskProfiles::GetInstance().DropResourceCaching();
+    TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_TASK);
+    TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_PROCESS);
 }
 
 bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
-    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles);
+    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, false);
+}
+
+bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
+    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, true);
 }
 
 bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index a61700c..74ba7f6 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -207,7 +207,9 @@
 
 SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
     : controller_(c), path_(p) {
-    FdCacheHelper::Init(controller_.GetTasksFilePath(path_), fd_);
+    FdCacheHelper::Init(controller_.GetTasksFilePath(path_), fd_[ProfileAction::RCT_TASK]);
+    // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them
+    FdCacheHelper::Init(controller_.GetProcsFilePath(path_, 0, 0), fd_[ProfileAction::RCT_PROCESS]);
 }
 
 bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) {
@@ -245,7 +247,40 @@
     return false;
 }
 
+ProfileAction::CacheUseResult SetCgroupAction::UseCachedFd(ResourceCacheType cache_type,
+                                                           int id) const {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    if (FdCacheHelper::IsCached(fd_[cache_type])) {
+        // fd is cached, reuse it
+        if (!AddTidToCgroup(id, fd_[cache_type], controller()->name())) {
+            LOG(ERROR) << "Failed to add task into cgroup";
+            return ProfileAction::FAIL;
+        }
+        return ProfileAction::SUCCESS;
+    }
+
+    if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) {
+        // no permissions to access the file, ignore
+        return ProfileAction::SUCCESS;
+    }
+
+    if (cache_type == ResourceCacheType::RCT_TASK &&
+        fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
+        // application-dependent path can't be used with tid
+        PLOG(ERROR) << "Application profile can't be applied to a thread";
+        return ProfileAction::FAIL;
+    }
+
+    return ProfileAction::UNUSED;
+}
+
 bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+    CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, pid);
+    if (result != ProfileAction::UNUSED) {
+        return result == ProfileAction::SUCCESS;
+    }
+
+    // fd was not cached or cached fd can't be used
     std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
@@ -261,28 +296,12 @@
 }
 
 bool SetCgroupAction::ExecuteForTask(int tid) const {
-    std::lock_guard<std::mutex> lock(fd_mutex_);
-    if (FdCacheHelper::IsCached(fd_)) {
-        // fd is cached, reuse it
-        if (!AddTidToCgroup(tid, fd_, controller()->name())) {
-            LOG(ERROR) << "Failed to add task into cgroup";
-            return false;
-        }
-        return true;
+    CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, tid);
+    if (result != ProfileAction::UNUSED) {
+        return result == ProfileAction::SUCCESS;
     }
 
-    if (fd_ == FdCacheHelper::FDS_INACCESSIBLE) {
-        // no permissions to access the file, ignore
-        return true;
-    }
-
-    if (fd_ == FdCacheHelper::FDS_APP_DEPENDENT) {
-        // application-dependent path can't be used with tid
-        PLOG(ERROR) << "Application profile can't be applied to a thread";
-        return false;
-    }
-
-    // fd was not cached because cached fd can't be used
+    // fd was not cached or cached fd can't be used
     std::string tasks_path = controller()->GetTasksFilePath(path_);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
@@ -297,14 +316,30 @@
     return true;
 }
 
-void SetCgroupAction::EnableResourceCaching() {
+void SetCgroupAction::EnableResourceCaching(ResourceCacheType cache_type) {
     std::lock_guard<std::mutex> lock(fd_mutex_);
-    FdCacheHelper::Cache(controller_.GetTasksFilePath(path_), fd_);
+    // Return early to prevent unnecessary calls to controller_.Get{Tasks|Procs}FilePath() which
+    // include regex evaluations
+    if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) {
+        return;
+    }
+    switch (cache_type) {
+        case (ProfileAction::RCT_TASK):
+            FdCacheHelper::Cache(controller_.GetTasksFilePath(path_), fd_[cache_type]);
+            break;
+        case (ProfileAction::RCT_PROCESS):
+            // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them
+            FdCacheHelper::Cache(controller_.GetProcsFilePath(path_, 0, 0), fd_[cache_type]);
+            break;
+        default:
+            LOG(ERROR) << "Invalid cache type is specified!";
+            break;
+    }
 }
 
-void SetCgroupAction::DropResourceCaching() {
+void SetCgroupAction::DropResourceCaching(ResourceCacheType cache_type) {
     std::lock_guard<std::mutex> lock(fd_mutex_);
-    FdCacheHelper::Drop(fd_);
+    FdCacheHelper::Drop(fd_[cache_type]);
 }
 
 WriteFileAction::WriteFileAction(const std::string& path, const std::string& value,
@@ -332,13 +367,43 @@
     return true;
 }
 
-bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+ProfileAction::CacheUseResult WriteFileAction::UseCachedFd(ResourceCacheType cache_type,
+                                                           const std::string& value) const {
     std::lock_guard<std::mutex> lock(fd_mutex_);
+    if (FdCacheHelper::IsCached(fd_)) {
+        // fd is cached, reuse it
+        if (!WriteStringToFd(value, fd_)) {
+            if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << path_;
+            return ProfileAction::FAIL;
+        }
+        return ProfileAction::SUCCESS;
+    }
+
+    if (fd_ == FdCacheHelper::FDS_INACCESSIBLE) {
+        // no permissions to access the file, ignore
+        return ProfileAction::SUCCESS;
+    }
+
+    if (cache_type == ResourceCacheType::RCT_TASK && fd_ == FdCacheHelper::FDS_APP_DEPENDENT) {
+        // application-dependent path can't be used with tid
+        PLOG(ERROR) << "Application profile can't be applied to a thread";
+        return ProfileAction::FAIL;
+    }
+    return ProfileAction::UNUSED;
+}
+
+bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
     std::string value(value_);
-    std::string path(path_);
 
     value = StringReplace(value, "<uid>", std::to_string(uid), true);
     value = StringReplace(value, "<pid>", std::to_string(pid), true);
+
+    CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, value);
+    if (result != ProfileAction::UNUSED) {
+        return result == ProfileAction::SUCCESS;
+    }
+
+    std::string path(path_);
     path = StringReplace(path, "<uid>", std::to_string(uid), true);
     path = StringReplace(path, "<pid>", std::to_string(pid), true);
 
@@ -346,51 +411,33 @@
 }
 
 bool WriteFileAction::ExecuteForTask(int tid) const {
-    std::lock_guard<std::mutex> lock(fd_mutex_);
     std::string value(value_);
     int uid = getuid();
 
     value = StringReplace(value, "<uid>", std::to_string(uid), true);
     value = StringReplace(value, "<pid>", std::to_string(tid), true);
 
-    if (FdCacheHelper::IsCached(fd_)) {
-        // fd is cached, reuse it
-        if (!WriteStringToFd(value, fd_)) {
-            if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << path_;
-            return false;
-        }
-        return true;
-    }
-
-    if (fd_ == FdCacheHelper::FDS_INACCESSIBLE) {
-        // no permissions to access the file, ignore
-        return true;
-    }
-
-    if (fd_ == FdCacheHelper::FDS_APP_DEPENDENT) {
-        // application-dependent path can't be used with tid
-        PLOG(ERROR) << "Application profile can't be applied to a thread";
-        return false;
+    CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, value);
+    if (result != ProfileAction::UNUSED) {
+        return result == ProfileAction::SUCCESS;
     }
 
     return WriteValueToFile(value, path_, logfailures_);
 }
 
-void WriteFileAction::EnableResourceCaching() {
+void WriteFileAction::EnableResourceCaching(ResourceCacheType) {
     std::lock_guard<std::mutex> lock(fd_mutex_);
     FdCacheHelper::Cache(path_, fd_);
 }
 
-void WriteFileAction::DropResourceCaching() {
+void WriteFileAction::DropResourceCaching(ResourceCacheType) {
     std::lock_guard<std::mutex> lock(fd_mutex_);
     FdCacheHelper::Drop(fd_);
 }
 
 bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
     for (const auto& profile : profiles_) {
-        if (!profile->ExecuteForProcess(uid, pid)) {
-            PLOG(WARNING) << "ExecuteForProcess failed for aggregate profile";
-        }
+        profile->ExecuteForProcess(uid, pid);
     }
     return true;
 }
@@ -402,15 +449,15 @@
     return true;
 }
 
-void ApplyProfileAction::EnableResourceCaching() {
+void ApplyProfileAction::EnableResourceCaching(ResourceCacheType cache_type) {
     for (const auto& profile : profiles_) {
-        profile->EnableResourceCaching();
+        profile->EnableResourceCaching(cache_type);
     }
 }
 
-void ApplyProfileAction::DropResourceCaching() {
+void ApplyProfileAction::DropResourceCaching(ResourceCacheType cache_type) {
     for (const auto& profile : profiles_) {
-        profile->DropResourceCaching();
+        profile->DropResourceCaching(cache_type);
     }
 }
 
@@ -440,33 +487,33 @@
     return true;
 }
 
-void TaskProfile::EnableResourceCaching() {
+void TaskProfile::EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) {
     if (res_cached_) {
         return;
     }
 
     for (auto& element : elements_) {
-        element->EnableResourceCaching();
+        element->EnableResourceCaching(cache_type);
     }
 
     res_cached_ = true;
 }
 
-void TaskProfile::DropResourceCaching() {
+void TaskProfile::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) {
     if (!res_cached_) {
         return;
     }
 
     for (auto& element : elements_) {
-        element->DropResourceCaching();
+        element->DropResourceCaching(cache_type);
     }
 
     res_cached_ = false;
 }
 
-void TaskProfiles::DropResourceCaching() const {
+void TaskProfiles::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const {
     for (auto& iter : profiles_) {
-        iter.second->DropResourceCaching();
+        iter.second->DropResourceCaching(cache_type);
     }
 }
 
@@ -683,10 +730,13 @@
 }
 
 bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
-                                      const std::vector<std::string>& profiles) {
+                                      const std::vector<std::string>& profiles, bool use_fd_cache) {
     for (const auto& name : profiles) {
         TaskProfile* profile = GetProfile(name);
         if (profile != nullptr) {
+            if (use_fd_cache) {
+                profile->EnableResourceCaching(ProfileAction::RCT_PROCESS);
+            }
             if (!profile->ExecuteForProcess(uid, pid)) {
                 PLOG(WARNING) << "Failed to apply " << name << " process profile";
             }
@@ -703,7 +753,7 @@
         TaskProfile* profile = GetProfile(name);
         if (profile != nullptr) {
             if (use_fd_cache) {
-                profile->EnableResourceCaching();
+                profile->EnableResourceCaching(ProfileAction::RCT_TASK);
             }
             if (!profile->ExecuteForTask(tid)) {
                 PLOG(WARNING) << "Failed to apply " << name << " task profile";
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 11f20a2..1aaa196 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -45,14 +45,19 @@
 // Abstract profile element
 class ProfileAction {
   public:
+    enum ResourceCacheType { RCT_TASK = 0, RCT_PROCESS, RCT_COUNT };
+
     virtual ~ProfileAction() {}
 
     // Default implementations will fail
     virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; };
     virtual bool ExecuteForTask(int) const { return false; };
 
-    virtual void EnableResourceCaching() {}
-    virtual void DropResourceCaching() {}
+    virtual void EnableResourceCaching(ResourceCacheType) {}
+    virtual void DropResourceCaching(ResourceCacheType) {}
+
+  protected:
+    enum CacheUseResult { SUCCESS, FAIL, UNUSED };
 };
 
 // Profile actions
@@ -115,18 +120,19 @@
 
     virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     virtual bool ExecuteForTask(int tid) const;
-    virtual void EnableResourceCaching();
-    virtual void DropResourceCaching();
+    virtual void EnableResourceCaching(ResourceCacheType cache_type);
+    virtual void DropResourceCaching(ResourceCacheType cache_type);
 
     const CgroupController* controller() const { return &controller_; }
 
   private:
     CgroupController controller_;
     std::string path_;
-    android::base::unique_fd fd_;
+    android::base::unique_fd fd_[ProfileAction::RCT_COUNT];
     mutable std::mutex fd_mutex_;
 
     static bool AddTidToCgroup(int tid, int fd, const char* controller_name);
+    CacheUseResult UseCachedFd(ResourceCacheType cache_type, int id) const;
 };
 
 // Write to file action
@@ -136,8 +142,8 @@
 
     virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     virtual bool ExecuteForTask(int tid) const;
-    virtual void EnableResourceCaching();
-    virtual void DropResourceCaching();
+    virtual void EnableResourceCaching(ResourceCacheType cache_type);
+    virtual void DropResourceCaching(ResourceCacheType cache_type);
 
   private:
     std::string path_, value_;
@@ -147,6 +153,7 @@
 
     static bool WriteValueToFile(const std::string& value, const std::string& path,
                                  bool logfailures);
+    CacheUseResult UseCachedFd(ResourceCacheType cache_type, const std::string& value) const;
 };
 
 class TaskProfile {
@@ -158,8 +165,8 @@
 
     bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     bool ExecuteForTask(int tid) const;
-    void EnableResourceCaching();
-    void DropResourceCaching();
+    void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type);
+    void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);
 
   private:
     bool res_cached_;
@@ -174,8 +181,8 @@
 
     virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     virtual bool ExecuteForTask(int tid) const;
-    virtual void EnableResourceCaching();
-    virtual void DropResourceCaching();
+    virtual void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type);
+    virtual void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);
 
   private:
     std::vector<std::shared_ptr<TaskProfile>> profiles_;
@@ -188,8 +195,9 @@
 
     TaskProfile* GetProfile(const std::string& name) const;
     const ProfileAttribute* GetAttribute(const std::string& name) const;
-    void DropResourceCaching() const;
-    bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
+    void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const;
+    bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
+                            bool use_fd_cache);
     bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache);
 
   private: