Introduce additional service options for controlling memory cgroups.

The memcg.limit_percent option can be used to limit the cgroup's
max RSS to the given value as a percentage of the device's physical
memory. The memcg.limit_property option specifies the name of a
property that can be used to control the cgroup's max RSS. These
new options correspond to the arguments to the limitProcessMemory
function in frameworks/av/media/libmedia/MediaUtils.cpp; this will
allow us to add these options to the rc files for the programs that
call this function and then remove the callers in a later change.

There is also a change in semantics: the memcg.* options now have
an effect on all devices which support memory cgroups, not just
those with ro.config.low_ram or ro.config.per_app_memcg set to true.
This change also brings the semantics in line with the documentation,
so it looks like the previous semantics were unintentional.

Change-Id: I9495826de6e477b952e23866743b5fa600adcacb
Bug: 118642754
diff --git a/init/service.cpp b/init/service.cpp
index 7f49423..1bda7ec 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -235,9 +235,6 @@
       ioprio_pri_(0),
       priority_(0),
       oom_score_adjust_(-1000),
-      swappiness_(-1),
-      soft_limit_in_bytes_(-1),
-      limit_in_bytes_(-1),
       start_order_(0),
       args_(args) {}
 
@@ -630,6 +627,18 @@
     return Success();
 }
 
+Result<Success> Service::ParseMemcgLimitPercent(std::vector<std::string>&& args) {
+    if (!ParseInt(args[1], &limit_percent_, 0)) {
+        return Error() << "limit_percent value must be equal or greater than 0";
+    }
+    return Success();
+}
+
+Result<Success> Service::ParseMemcgLimitProperty(std::vector<std::string>&& args) {
+    limit_property_ = std::move(args[1]);
+    return Success();
+}
+
 Result<Success> Service::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) {
     if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) {
         return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
@@ -783,6 +792,10 @@
         {"keycodes",    {1,     kMax, &Service::ParseKeycodes}},
         {"memcg.limit_in_bytes",
                         {1,     1,    &Service::ParseMemcgLimitInBytes}},
+        {"memcg.limit_percent",
+                        {1,     1,    &Service::ParseMemcgLimitPercent}},
+        {"memcg.limit_property",
+                        {1,     1,    &Service::ParseMemcgLimitProperty}},
         {"memcg.soft_limit_in_bytes",
                         {1,     1,    &Service::ParseMemcgSoftLimitInBytes}},
         {"memcg.swappiness",
@@ -1001,11 +1014,13 @@
     start_order_ = next_start_order_++;
     process_cgroup_empty_ = false;
 
-    errno = -createProcessGroup(uid_, pid_);
+    bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 ||
+                      limit_percent_ != -1 || !limit_property_.empty();
+    errno = -createProcessGroup(uid_, pid_, use_memcg);
     if (errno != 0) {
         PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
                     << name_ << "'";
-    } else {
+    } else if (use_memcg) {
         if (swappiness_ != -1) {
             if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) {
                 PLOG(ERROR) << "setProcessGroupSwappiness failed";
@@ -1018,8 +1033,29 @@
             }
         }
 
-        if (limit_in_bytes_ != -1) {
-            if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) {
+        size_t computed_limit_in_bytes = limit_in_bytes_;
+        if (limit_percent_ != -1) {
+            long page_size = sysconf(_SC_PAGESIZE);
+            long num_pages = sysconf(_SC_PHYS_PAGES);
+            if (page_size > 0 && num_pages > 0) {
+                size_t max_mem = SIZE_MAX;
+                if (size_t(num_pages) < SIZE_MAX / size_t(page_size)) {
+                    max_mem = size_t(num_pages) * size_t(page_size);
+                }
+                computed_limit_in_bytes =
+                        std::min(computed_limit_in_bytes, max_mem / 100 * limit_percent_);
+            }
+        }
+
+        if (!limit_property_.empty()) {
+            // This ends up overwriting computed_limit_in_bytes but only if the
+            // property is defined.
+            computed_limit_in_bytes = android::base::GetUintProperty(
+                    limit_property_, computed_limit_in_bytes, SIZE_MAX);
+        }
+
+        if (computed_limit_in_bytes != size_t(-1)) {
+            if (!setProcessGroupLimit(uid_, pid_, computed_limit_in_bytes)) {
                 PLOG(ERROR) << "setProcessGroupLimit failed";
             }
         }