limit mediaserver memory

Limit mediaserver using rlimit, to prevent it from bringing down the system
via the low memory killer.
Default max is 65% of total RAM, but can be customized via system property.

Bug: 28471206
Bug: 28615448
Change-Id: Ic84137435d1ef0a6883e9789a4b4f399e4283f05
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index a3c3d3c..9f836f0 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -44,6 +44,7 @@
     IResourceManagerService.cpp \
     IStreamSource.cpp \
     MediaCodecInfo.cpp \
+    MediaUtils.cpp \
     Metadata.cpp \
     mediarecorder.cpp \
     IMediaMetadataRetriever.cpp \
diff --git a/media/libmedia/MediaUtils.cpp b/media/libmedia/MediaUtils.cpp
new file mode 100644
index 0000000..a02ca65
--- /dev/null
+++ b/media/libmedia/MediaUtils.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MediaUtils"
+#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <cutils/properties.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include "MediaUtils.h"
+
+namespace android {
+
+void limitProcessMemory(
+    const char *property,
+    size_t numberOfBytes,
+    size_t percentageOfTotalMem) {
+
+    long pageSize = sysconf(_SC_PAGESIZE);
+    long numPages = sysconf(_SC_PHYS_PAGES);
+    size_t maxMem = SIZE_MAX;
+
+    if (pageSize > 0 && numPages > 0) {
+        if (size_t(numPages) < SIZE_MAX / size_t(pageSize)) {
+            maxMem = size_t(numPages) * size_t(pageSize);
+        }
+        ALOGV("physMem: %zu", maxMem);
+        if (percentageOfTotalMem > 100) {
+            ALOGW("requested %zu%% of total memory, using 100%%", percentageOfTotalMem);
+            percentageOfTotalMem = 100;
+        }
+        maxMem = maxMem / 100 * percentageOfTotalMem;
+        if (numberOfBytes < maxMem) {
+            maxMem = numberOfBytes;
+        }
+        ALOGV("requested limit: %zu", maxMem);
+    } else {
+        ALOGW("couldn't determine total RAM");
+    }
+
+    int64_t propVal = property_get_int64(property, maxMem);
+    if (propVal > 0 && uint64_t(propVal) <= SIZE_MAX) {
+        maxMem = propVal;
+    }
+    ALOGV("actual limit: %zu", maxMem);
+
+    struct rlimit limit;
+    getrlimit(RLIMIT_AS, &limit);
+    ALOGV("original limits: %lld/%lld", (long long)limit.rlim_cur, (long long)limit.rlim_max);
+    limit.rlim_cur = maxMem;
+    setrlimit(RLIMIT_AS, &limit);
+    limit.rlim_cur = -1;
+    limit.rlim_max = -1;
+    getrlimit(RLIMIT_AS, &limit);
+    ALOGV("new limits: %lld/%lld", (long long)limit.rlim_cur, (long long)limit.rlim_max);
+
+}
+
+} // namespace android
diff --git a/media/libmedia/MediaUtils.h b/media/libmedia/MediaUtils.h
new file mode 100644
index 0000000..f80dd30
--- /dev/null
+++ b/media/libmedia/MediaUtils.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MEDIA_UTILS_H
+#define _MEDIA_UTILS_H
+
+namespace android {
+
+/**
+   Limit the amount of memory a process can allocate using setrlimit(RLIMIT_AS).
+   The value to use will be read from the specified system property, or if the
+   property doesn't exist it will use the specified number of bytes or the
+   specified percentage of total memory, whichever is smaller.
+*/
+void limitProcessMemory(
+    const char *property,
+    size_t numberOfBytes,
+    size_t percentageOfTotalMem);
+
+}   // namespace android
+
+#endif  // _MEDIA_UTILS_H
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index b6de0d9..7e017b9 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -37,6 +37,7 @@
 
 LOCAL_C_INCLUDES := \
     frameworks/av/media/libmediaplayerservice \
+    frameworks/av/media/libmedia \
     frameworks/av/services/medialog \
     frameworks/av/services/audioflinger \
     frameworks/av/services/audiopolicy \
diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp
index 4a485ed..8cc9508 100644
--- a/media/mediaserver/main_mediaserver.cpp
+++ b/media/mediaserver/main_mediaserver.cpp
@@ -36,6 +36,7 @@
 #include "MediaPlayerService.h"
 #include "ResourceManagerService.h"
 #include "service/AudioPolicyService.h"
+#include "MediaUtils.h"
 #include "SoundTriggerHwService.h"
 #include "RadioService.h"
 
@@ -43,6 +44,11 @@
 
 int main(int argc __unused, char** argv)
 {
+    limitProcessMemory(
+        "ro.media.maxmem", /* property that defines limit */
+        SIZE_MAX, /* upper limit in bytes */
+        65 /* upper limit as percentage of physical RAM */);
+
     signal(SIGPIPE, SIG_IGN);
     char value[PROPERTY_VALUE_MAX];
     bool doLog = (property_get("ro.test_harness", value, "0") > 0) && (atoi(value) == 1);