libprocessgroup: add support to set aggregate profiles

To support setting multiple profiles with one call. The json format
is as below example.

  "AggregateProfiles": [
    ...
    {
      "Name": "SCHED_SP_BACKGROUND",
      "Profiles": [ "HighEnergySaving", "LowIoPriority", "TimerSlackHigh" ]
    },
    ...
  }

Bug: 139521784
Test: SetProfile works as expected
Change-Id: Ibe14ed57d5169cafcbcbbdb054df3ed171a2f6a2
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 7c191be..7b6dde2 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -117,43 +117,11 @@
 
 bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
                         bool use_fd_cache) {
-    const TaskProfiles& tp = TaskProfiles::GetInstance();
-
-    for (const auto& name : profiles) {
-        TaskProfile* profile = tp.GetProfile(name);
-        if (profile != nullptr) {
-            if (use_fd_cache) {
-                profile->EnableResourceCaching();
-            }
-            if (!profile->ExecuteForProcess(uid, pid)) {
-                PLOG(WARNING) << "Failed to apply " << name << " process profile";
-            }
-        } else {
-            PLOG(WARNING) << "Failed to find " << name << "process profile";
-        }
-    }
-
-    return true;
+    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, use_fd_cache);
 }
 
 bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
-    const TaskProfiles& tp = TaskProfiles::GetInstance();
-
-    for (const auto& name : profiles) {
-        TaskProfile* profile = tp.GetProfile(name);
-        if (profile != nullptr) {
-            if (use_fd_cache) {
-                profile->EnableResourceCaching();
-            }
-            if (!profile->ExecuteForTask(tid)) {
-                PLOG(WARNING) << "Failed to apply " << name << " task profile";
-            }
-        } else {
-            PLOG(WARNING) << "Failed to find " << name << "task profile";
-        }
-    }
-
-    return true;
+    return TaskProfiles::GetInstance().SetTaskProfiles(tid, profiles, use_fd_cache);
 }
 
 static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 74a39cd..608f007 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -15,7 +15,6 @@
       "Controller": "cpuset",
       "File": "top-app/cpus"
     },
-
     {
       "Name": "MemLimit",
       "Controller": "memory",
@@ -494,5 +493,52 @@
         }
       ]
     }
+  ],
+
+  "AggregateProfiles": [
+    {
+      "Name": "SCHED_SP_DEFAULT",
+      "Profiles": [ "TimerSlackNormal" ]
+    },
+    {
+      "Name": "SCHED_SP_BACKGROUND",
+      "Profiles": [ "HighEnergySaving", "LowIoPriority", "TimerSlackHigh" ]
+    },
+    {
+      "Name": "SCHED_SP_FOREGROUND",
+      "Profiles": [ "HighPerformance", "HighIoPriority", "TimerSlackNormal" ]
+    },
+    {
+      "Name": "SCHED_SP_TOP_APP",
+      "Profiles": [ "MaxPerformance", "MaxIoPriority", "TimerSlackNormal" ]
+    },
+    {
+      "Name": "SCHED_SP_RT_APP",
+      "Profiles": [ "RealtimePerformance", "MaxIoPriority", "TimerSlackNormal" ]
+    },
+    {
+      "Name": "CPUSET_SP_DEFAULT",
+      "Profiles": [ "TimerSlackNormal" ]
+    },
+    {
+      "Name": "CPUSET_SP_BACKGROUND",
+      "Profiles": [ "HighEnergySaving", "ProcessCapacityLow", "LowIoPriority", "TimerSlackHigh" ]
+    },
+    {
+      "Name": "CPUSET_SP_FOREGROUND",
+      "Profiles": [ "HighPerformance", "ProcessCapacityHigh", "HighIoPriority", "TimerSlackNormal" ]
+    },
+    {
+      "Name": "CPUSET_SP_TOP_APP",
+      "Profiles": [ "MaxPerformance", "ProcessCapacityMax", "MaxIoPriority", "TimerSlackNormal" ]
+    },
+    {
+      "Name": "CPUSET_SP_SYSTEM",
+      "Profiles": [ "ServiceCapacityLow", "TimerSlackNormal" ]
+    },
+    {
+      "Name": "CPUSET_SP_RESTRICTED",
+      "Profiles": [ "ServiceCapacityRestricted", "TimerSlackNormal" ]
+    }
   ]
 }
diff --git a/libprocessgroup/profiles/task_profiles.proto b/libprocessgroup/profiles/task_profiles.proto
index 578f0d3..1de4395 100644
--- a/libprocessgroup/profiles/task_profiles.proto
+++ b/libprocessgroup/profiles/task_profiles.proto
@@ -18,10 +18,11 @@
 
 package android.profiles;
 
-// Next: 3
+// Next: 4
 message TaskProfiles {
     repeated Attribute attributes = 1 [json_name = "Attributes"];
     repeated Profile profiles = 2 [json_name = "Profiles"];
+    repeated AggregateProfiles aggregateprofiles = 3 [json_name = "AggregateProfiles"];
 }
 
 // Next: 4
@@ -42,3 +43,9 @@
     string name = 1 [json_name = "Name"];
     map<string, string> params = 2 [json_name = "Params"];
 }
+
+// Next: 3
+message AggregateProfiles {
+    string name = 1 [json_name = "Name"];
+    repeated string profiles = 2 [json_name = "Profiles"];
+}
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index c83df1a..6b0ab87 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -46,34 +46,17 @@
 
     switch (policy) {
         case SP_BACKGROUND:
-            return SetTaskProfiles(tid,
-                                   {"HighEnergySaving", "ProcessCapacityLow", "LowIoPriority",
-                                    "TimerSlackHigh"},
-                                   true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"CPUSET_SP_BACKGROUND"}, true) ? 0 : -1;
         case SP_FOREGROUND:
         case SP_AUDIO_APP:
         case SP_AUDIO_SYS:
-            return SetTaskProfiles(tid,
-                                   {"HighPerformance", "ProcessCapacityHigh", "HighIoPriority",
-                                    "TimerSlackNormal"},
-                                   true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"CPUSET_SP_FOREGROUND"}, true) ? 0 : -1;
         case SP_TOP_APP:
-            return SetTaskProfiles(tid,
-                                   {"MaxPerformance", "ProcessCapacityMax", "MaxIoPriority",
-                                    "TimerSlackNormal"},
-                                   true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"CPUSET_SP_TOP_APP"}, true) ? 0 : -1;
         case SP_SYSTEM:
-            return SetTaskProfiles(tid, {"ServiceCapacityLow", "TimerSlackNormal"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid, {"CPUSET_SP_SYSTEM"}, true) ? 0 : -1;
         case SP_RESTRICTED:
-            return SetTaskProfiles(tid, {"ServiceCapacityRestricted", "TimerSlackNormal"}, true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"CPUSET_SP_RESTRICTED"}, true) ? 0 : -1;
         default:
             break;
     }
@@ -134,29 +117,17 @@
 
     switch (policy) {
         case SP_BACKGROUND:
-            return SetTaskProfiles(tid, {"HighEnergySaving", "LowIoPriority", "TimerSlackHigh"},
-                                   true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1;
         case SP_FOREGROUND:
         case SP_AUDIO_APP:
         case SP_AUDIO_SYS:
-            return SetTaskProfiles(tid, {"HighPerformance", "HighIoPriority", "TimerSlackNormal"},
-                                   true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND"}, true) ? 0 : -1;
         case SP_TOP_APP:
-            return SetTaskProfiles(tid, {"MaxPerformance", "MaxIoPriority", "TimerSlackNormal"},
-                                   true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"SCHED_SP_TOP_APP"}, true) ? 0 : -1;
         case SP_RT_APP:
-            return SetTaskProfiles(
-                           tid, {"RealtimePerformance", "MaxIoPriority", "TimerSlackNormal"}, true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"SCHED_SP_RT_APP"}, true) ? 0 : -1;
         default:
-            return SetTaskProfiles(tid, {"TimerSlackNormal"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid, {"SCHED_SP_DEFAULT"}, true) ? 0 : -1;
     }
 
     return 0;
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index aee5f0c..9447f86 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -268,6 +268,26 @@
     return true;
 }
 
+bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+    for (const auto& profile : profiles_) {
+        profile->EnableResourceCaching();
+        if (!profile->ExecuteForProcess(uid, pid)) {
+            PLOG(WARNING) << "ExecuteForProcess failed for aggregate profile";
+        }
+    }
+    return true;
+}
+
+bool ApplyProfileAction::ExecuteForTask(int tid) const {
+    for (const auto& profile : profiles_) {
+        profile->EnableResourceCaching();
+        if (!profile->ExecuteForTask(tid)) {
+            PLOG(WARNING) << "ExecuteForTask failed for aggregate profile";
+        }
+    }
+    return true;
+}
+
 bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {
     for (const auto& element : elements_) {
         if (!element->ExecuteForProcess(uid, pid)) {
@@ -373,15 +393,13 @@
         }
     }
 
-    std::map<std::string, std::string> params;
-
     const Json::Value& profiles_val = root["Profiles"];
     for (Json::Value::ArrayIndex i = 0; i < profiles_val.size(); ++i) {
         const Json::Value& profile_val = profiles_val[i];
 
         std::string profile_name = profile_val["Name"].asString();
         const Json::Value& actions = profile_val["Actions"];
-        auto profile = std::make_unique<TaskProfile>();
+        auto profile = std::make_shared<TaskProfile>();
 
         for (Json::Value::ArrayIndex act_idx = 0; act_idx < actions.size(); ++act_idx) {
             const Json::Value& action_val = actions[act_idx];
@@ -440,7 +458,38 @@
                 LOG(WARNING) << "Unknown profile action: " << action_name;
             }
         }
-        profiles_[profile_name] = std::move(profile);
+        profiles_[profile_name] = profile;
+    }
+
+    const Json::Value& aggregateprofiles_val = root["AggregateProfiles"];
+    for (Json::Value::ArrayIndex i = 0; i < aggregateprofiles_val.size(); ++i) {
+        const Json::Value& aggregateprofile_val = aggregateprofiles_val[i];
+
+        std::string aggregateprofile_name = aggregateprofile_val["Name"].asString();
+        const Json::Value& aggregateprofiles = aggregateprofile_val["Profiles"];
+        std::vector<std::shared_ptr<TaskProfile>> profiles;
+        bool ret = true;
+
+        for (Json::Value::ArrayIndex pf_idx = 0; pf_idx < aggregateprofiles.size(); ++pf_idx) {
+            std::string profile_name = aggregateprofiles[pf_idx].asString();
+
+            if (profile_name == aggregateprofile_name) {
+                LOG(WARNING) << "AggregateProfiles: recursive profile name: " << profile_name;
+                ret = false;
+                break;
+            } else if (profiles_.find(profile_name) == profiles_.end()) {
+                LOG(WARNING) << "AggregateProfiles: undefined profile name: " << profile_name;
+                ret = false;
+                break;
+            } else {
+                profiles.push_back(profiles_[profile_name]);
+            }
+        }
+        if (ret) {
+            auto profile = std::make_shared<TaskProfile>();
+            profile->Add(std::make_unique<ApplyProfileAction>(profiles));
+            profiles_[aggregateprofile_name] = profile;
+        }
     }
 
     return true;
@@ -463,3 +512,39 @@
     }
     return nullptr;
 }
+
+bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
+                                      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();
+            }
+            if (!profile->ExecuteForProcess(uid, pid)) {
+                PLOG(WARNING) << "Failed to apply " << name << " process profile";
+            }
+        } else {
+            PLOG(WARNING) << "Failed to find " << name << "process profile";
+        }
+    }
+    return true;
+}
+
+bool TaskProfiles::SetTaskProfiles(int tid, 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();
+            }
+            if (!profile->ExecuteForTask(tid)) {
+                PLOG(WARNING) << "Failed to apply " << name << " task profile";
+            }
+        } else {
+            PLOG(WARNING) << "Failed to find " << name << "task profile";
+        }
+    }
+    return true;
+}
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 891d5b5..9f2308c 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -154,6 +154,19 @@
     std::vector<std::unique_ptr<ProfileAction>> elements_;
 };
 
+// Set aggregate profile element
+class ApplyProfileAction : public ProfileAction {
+  public:
+    ApplyProfileAction(const std::vector<std::shared_ptr<TaskProfile>>& profiles)
+        : profiles_(profiles) {}
+
+    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+    virtual bool ExecuteForTask(int tid) const;
+
+  private:
+    std::vector<std::shared_ptr<TaskProfile>> profiles_;
+};
+
 class TaskProfiles {
   public:
     // Should be used by all users
@@ -162,9 +175,12 @@
     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,
+                            bool use_fd_cache);
+    bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache);
 
   private:
-    std::map<std::string, std::unique_ptr<TaskProfile>> profiles_;
+    std::map<std::string, std::shared_ptr<TaskProfile>> profiles_;
     std::map<std::string, std::unique_ptr<ProfileAttribute>> attributes_;
 
     TaskProfiles();