Merge changes from topic "adb_bp"
* changes:
adb: remove meaningless const.
adb: convert Connection to a nonblocking interface.
adb: use soong version stamping.
adb: switch over to Android.bp.
diff --git a/OWNERS b/OWNERS
index 682a067..1d319af 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1 +1,5 @@
enh@google.com
+per-file libsysutils/src/Netlink* = ek@google.com
+per-file libsysutils/src/Netlink* = lorenzo@google.com
+per-file libsysutils/include/sysutils/Netlink* = ek@google.com
+per-file libsysutils/include/sysutils/Netlink* = lorenzo@google.com
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index e5666bc..31cb853 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -77,6 +77,7 @@
static void intentionally_leak() {
void* p = ::operator new(1);
+ // The analyzer is upset about this leaking. NOLINTNEXTLINE
LOG(INFO) << "leaking pointer " << p;
}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index dfcf090..f5bcc26 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -39,13 +39,15 @@
LOCAL_MODULE_TAGS := debug
LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
-LOCAL_REQUIRED_MODULES := mke2fs e2fsdroid mke2fs.conf make_f2fs sload_f2fs
+LOCAL_REQUIRED_MODULES := mke2fs make_f2fs
LOCAL_SRC_FILES_linux := usb_linux.cpp
LOCAL_STATIC_LIBRARIES_linux := libselinux
+LOCAL_REQUIRED_MODULES_linux := e2fsdroid mke2fs.conf sload_f2fs
LOCAL_SRC_FILES_darwin := usb_osx.cpp
LOCAL_STATIC_LIBRARIES_darwin := libselinux
+LOCAL_REQUIRED_MODULES_darwin := e2fsdroid mke2fs.conf sload_f2fs
LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
LOCAL_CFLAGS_darwin := -Wno-unused-parameter
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 08b8b26..fa79d0b 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -143,7 +143,7 @@
BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
std::string buf;
- BatteryMonitor::PowerSupplyType ret;
+ int ret;
struct sysfsStringEnumMap supplyTypeMap[] = {
{ "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN },
{ "Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY },
@@ -164,13 +164,13 @@
if (readFromFile(path, &buf) <= 0)
return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
- ret = (BatteryMonitor::PowerSupplyType)mapSysfsString(buf.c_str(), supplyTypeMap);
+ ret = mapSysfsString(buf.c_str(), supplyTypeMap);
if (ret < 0) {
KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
}
- return ret;
+ return static_cast<BatteryMonitor::PowerSupplyType>(ret);
}
bool BatteryMonitor::getBooleanField(const String8& path) {
diff --git a/init/Android.bp b/init/Android.bp
index 69b4ee4..31c8efb 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -194,4 +194,67 @@
static_libs: ["libinit"],
}
+// Host Verifier
+// ------------------------------------------------------------------------------
+
+genrule {
+ name: "generated_stub_builtin_function_map",
+ out: ["generated_stub_builtin_function_map.h"],
+ srcs: ["builtins.cpp"],
+ cmd: "sed -n '/Builtin-function-map start/{:a;n;/Builtin-function-map end/q;p;ba}' $(in) | sed -e 's/do_[^}]*/do_stub/g' > $(out)",
+}
+
+cc_binary {
+ name: "host_init_verifier",
+ host_supported: true,
+ cpp_std: "experimental",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Wno-unused-parameter",
+ "-Werror",
+ ],
+ static_libs: [
+ "libbase",
+ "libselinux",
+ ],
+ whole_static_libs: ["libcap"],
+ shared_libs: [
+ "libprotobuf-cpp-lite",
+ "libhidl-gen-utils",
+ "libprocessgroup",
+ "liblog",
+ "libcutils",
+ ],
+ srcs: [
+ "action.cpp",
+ "action_manager.cpp",
+ "action_parser.cpp",
+ "capabilities.cpp",
+ "descriptors.cpp",
+ "import_parser.cpp",
+ "host_init_parser.cpp",
+ "host_init_stubs.cpp",
+ "parser.cpp",
+ "rlimit_parser.cpp",
+ "tokenizer.cpp",
+ "service.cpp",
+ "subcontext.cpp",
+ "subcontext.proto",
+ "util.cpp",
+ ],
+ proto: {
+ type: "lite",
+ },
+ generated_headers: ["generated_stub_builtin_function_map"],
+ target: {
+ android: {
+ enabled: false,
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
subdirs = ["*"]
diff --git a/init/action.cpp b/init/action.cpp
index 11335ca..f782b51 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -18,11 +18,16 @@
#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
-#include <android-base/properties.h>
#include <android-base/strings.h>
#include "util.h"
+#if defined(__ANDROID__)
+#include <android-base/properties.h>
+#else
+#include "host_init_stubs.h"
+#endif
+
using android::base::Join;
namespace android {
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index 8a4b518..a2c9671 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -16,11 +16,16 @@
#include "action_parser.h"
-#include <android-base/properties.h>
#include <android-base/strings.h>
#include "stable_properties.h"
+#if defined(__ANDROID__)
+#include <android-base/properties.h>
+#else
+#include "host_init_stubs.h"
+#endif
+
using android::base::GetBoolProperty;
using android::base::StartsWith;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 1040b47..fc74dda 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -285,8 +285,11 @@
if (e4crypt_is_native()) {
if (e4crypt_set_directory_policy(args[1].c_str())) {
- reboot_into_recovery(
- {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
+ const std::vector<std::string> options = {
+ "--prompt_and_wipe_data",
+ "--reason=set_policy_failed:"s + args[1]};
+ reboot_into_recovery(options);
+ return Success();
}
}
return Success();
@@ -968,8 +971,8 @@
const char* value = args[2].c_str();
size_t value_len = strlen(value);
- if (!is_legal_property_name(name)) {
- return Error() << "is_legal_property_name(" << name << ") failed";
+ if (!IsLegalPropertyName(name)) {
+ return Error() << "IsLegalPropertyName(" << name << ") failed";
}
if (value_len >= PROP_VALUE_MAX) {
return Error() << "value too long";
@@ -984,24 +987,6 @@
return android::base::GetProperty("ro.crypto.type", "") == "file";
}
-static Result<Success> ExecWithRebootOnFailure(const std::string& reboot_reason,
- const std::vector<std::string>& args) {
- auto service = Service::MakeTemporaryOneshotService(args);
- if (!service) {
- return Error() << "Could not create exec service";
- }
- service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
- if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
- reboot_into_recovery({"--prompt_and_wipe_data", "--reason="s + reboot_reason});
- }
- });
- if (auto result = service->ExecStart(); !result) {
- return Error() << "Could not start exec service: " << result.error();
- }
- ServiceList::GetInstance().AddService(std::move(service));
- return Success();
-}
-
static Result<Success> do_installkey(const BuiltinArguments& args) {
if (!is_file_crypto()) return Success();
@@ -1009,15 +994,18 @@
if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
return ErrnoError() << "Failed to create " << unencrypted_dir;
}
- return ExecWithRebootOnFailure("enablefilecrypto_failed", {"exec", "/system/bin/vdc", "--wait",
- "cryptfs", "enablefilecrypto"});
+ std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
+ "enablefilecrypto"};
+ return do_exec({std::move(exec_args), args.context});
}
static Result<Success> do_init_user0(const BuiltinArguments& args) {
- return ExecWithRebootOnFailure("init_user0_failed",
- {"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"});
+ std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
+ "init_user0"};
+ return do_exec({std::move(exec_args), args.context});
}
+// Builtin-function-map start
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
@@ -1075,6 +1063,7 @@
// clang-format on
return builtin_functions;
}
+// Builtin-function-map end
} // namespace init
} // namespace android
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index 50987db..a91cd1d 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -14,7 +14,6 @@
#include "capabilities.h"
-#include <sys/capability.h>
#include <sys/prctl.h>
#include <map>
@@ -72,10 +71,15 @@
static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ");
static bool ComputeCapAmbientSupported() {
+#if defined(__ANDROID__)
return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >= 0;
+#else
+ return true;
+#endif
}
static unsigned int ComputeLastValidCap() {
+#if defined(__ANDROID__)
// Android does not support kernels < 3.8. 'CAP_WAKE_ALARM' has been present since 3.0, see
// http://lxr.free-electrons.com/source/include/linux/capability.h?v=3.0#L360.
unsigned int last_valid_cap = CAP_WAKE_ALARM;
@@ -83,6 +87,9 @@
// |last_valid_cap| will be the first failing value.
return last_valid_cap - 1;
+#else
+ return CAP_LAST_CAP;
+#endif
}
static bool DropBoundingSet(const CapSet& to_keep) {
@@ -139,6 +146,7 @@
}
static bool SetAmbientCaps(const CapSet& to_raise) {
+#if defined(__ANDROID__)
for (size_t cap = 0; cap < to_raise.size(); ++cap) {
if (to_raise.test(cap)) {
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) != 0) {
@@ -147,6 +155,7 @@
}
}
}
+#endif
return true;
}
diff --git a/init/capabilities.h b/init/capabilities.h
index fc80c98..891e0ac 100644
--- a/init/capabilities.h
+++ b/init/capabilities.h
@@ -21,6 +21,17 @@
#include <string>
#include <type_traits>
+#if !defined(__ANDROID__)
+#ifndef CAP_BLOCK_SUSPEND
+#define CAP_BLOCK_SUSPEND 36
+#endif
+#ifndef CAP_AUDIT_READ
+#define CAP_AUDIT_READ 37
+#endif
+#undef CAP_LAST_CAP
+#define CAP_LAST_CAP CAP_AUDIT_READ
+#endif
+
namespace android {
namespace init {
diff --git a/init/host_init_parser.cpp b/init/host_init_parser.cpp
new file mode 100644
index 0000000..5232b7e
--- /dev/null
+++ b/init/host_init_parser.cpp
@@ -0,0 +1,82 @@
+//
+// Copyright (C) 2018 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.
+//
+
+#include <pwd.h>
+
+#include <android-base/logging.h>
+
+#include "action.h"
+#include "action_manager.h"
+#include "action_parser.h"
+#include "parser.h"
+#include "result.h"
+#include "service.h"
+
+// The host passwd file won't have the Android entries, so we fake success here.
+passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
+ char dummy_buf[] = "dummy";
+ static passwd dummy_passwd = {
+ .pw_name = dummy_buf,
+ .pw_dir = dummy_buf,
+ .pw_shell = dummy_buf,
+ .pw_uid = 123,
+ .pw_gid = 123,
+ };
+ return &dummy_passwd;
+}
+
+namespace android {
+namespace init {
+
+static Result<Success> do_stub(const BuiltinArguments& args) {
+ return Success();
+}
+
+#include "generated_stub_builtin_function_map.h"
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::StderrLogger);
+ if (argc != 2) {
+ LOG(ERROR) << "Usage: " << argv[0] << " <init file to parse>";
+ return -1;
+ }
+ const BuiltinFunctionMap function_map;
+ Action::set_function_map(&function_map);
+ ActionManager& am = ActionManager::GetInstance();
+ ServiceList& sl = ServiceList::GetInstance();
+ Parser parser;
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+
+ size_t num_errors = 0;
+ if (!parser.ParseConfig(argv[1], &num_errors)) {
+ LOG(ERROR) << "Failed to find script";
+ return -1;
+ }
+ if (num_errors > 0) {
+ LOG(ERROR) << "Parse failed with " << num_errors << " errors";
+ return -1;
+ }
+ LOG(INFO) << "Parse success!";
+ return 0;
+}
+
+} // namespace init
+} // namespace android
+
+int main(int argc, char** argv) {
+ android::init::main(argc, argv);
+}
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
new file mode 100644
index 0000000..e6cc08a
--- /dev/null
+++ b/init/host_init_stubs.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "host_init_stubs.h"
+
+// unistd.h
+int setgroups(size_t __size, const gid_t* __list) {
+ return 0;
+}
+
+namespace android {
+namespace base {
+
+std::string GetProperty(const std::string&, const std::string& default_value) {
+ return default_value;
+}
+
+bool GetBoolProperty(const std::string&, bool default_value) {
+ return default_value;
+}
+
+} // namespace base
+} // namespace android
+
+namespace android {
+namespace init {
+
+// init.h
+std::string default_console = "/dev/console";
+
+// property_service.h
+uint32_t (*property_set)(const std::string& name, const std::string& value) = nullptr;
+uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&, const ucred&,
+ std::string*) {
+ return 0;
+}
+
+// selinux.h
+void SelabelInitialize() {}
+
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
+ return false;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
new file mode 100644
index 0000000..f31ece6
--- /dev/null
+++ b/init/host_init_stubs.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 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 _INIT_HOST_INIT_STUBS_H
+#define _INIT_HOST_INIT_STUBS_H
+
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <string>
+
+// sys/system_properties.h
+#define PROP_VALUE_MAX 92
+
+// unistd.h
+int setgroups(size_t __size, const gid_t* __list);
+
+// android-base/properties.h
+namespace android {
+namespace base {
+
+std::string GetProperty(const std::string& key, const std::string& default_value);
+bool GetBoolProperty(const std::string& key, bool default_value);
+
+} // namespace base
+} // namespace android
+
+namespace android {
+namespace init {
+
+// init.h
+extern std::string default_console;
+
+// property_service.h
+extern uint32_t (*property_set)(const std::string& name, const std::string& value);
+uint32_t HandlePropertySet(const std::string& name, const std::string& value,
+ const std::string& source_context, const ucred& cr, std::string* error);
+
+// selinux.h
+void SelabelInitialize();
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/parser.cpp b/init/parser.cpp
index 4c69bac..4453aaa 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -39,7 +39,7 @@
line_callbacks_.emplace_back(prefix, callback);
}
-void Parser::ParseData(const std::string& filename, const std::string& data) {
+void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
// TODO: Use a parser with const input and remove this copy
std::vector<char> data_copy(data.begin(), data.end());
data_copy.push_back('\0');
@@ -57,6 +57,7 @@
if (section_parser == nullptr) return;
if (auto result = section_parser->EndSection(); !result) {
+ (*parse_errors)++;
LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
}
@@ -80,6 +81,7 @@
end_section();
if (auto result = callback(std::move(args)); !result) {
+ (*parse_errors)++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
break;
@@ -92,12 +94,14 @@
if (auto result =
section_parser->ParseSection(std::move(args), filename, state.line);
!result) {
+ (*parse_errors)++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
section_parser = nullptr;
}
} else if (section_parser) {
if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
!result) {
+ (*parse_errors)++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
}
@@ -110,7 +114,7 @@
}
}
-bool Parser::ParseConfigFile(const std::string& path) {
+bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
auto config_contents = ReadFile(path);
@@ -120,7 +124,7 @@
}
config_contents->push_back('\n'); // TODO: fix parse_config.
- ParseData(path, *config_contents);
+ ParseData(path, *config_contents, parse_errors);
for (const auto& [section_name, section_parser] : section_parsers_) {
section_parser->EndFile();
}
@@ -129,7 +133,7 @@
return true;
}
-bool Parser::ParseConfigDir(const std::string& path) {
+bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
LOG(INFO) << "Parsing directory " << path << "...";
std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
if (!config_dir) {
@@ -149,7 +153,7 @@
// Sort first so we load files in a consistent order (bug 31996208)
std::sort(files.begin(), files.end());
for (const auto& file : files) {
- if (!ParseConfigFile(file)) {
+ if (!ParseConfigFile(file, parse_errors)) {
LOG(ERROR) << "could not import file '" << file << "'";
}
}
@@ -157,10 +161,16 @@
}
bool Parser::ParseConfig(const std::string& path) {
+ size_t parse_errors;
+ return ParseConfig(path, &parse_errors);
+}
+
+bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
+ *parse_errors = 0;
if (is_dir(path.c_str())) {
- return ParseConfigDir(path);
+ return ParseConfigDir(path, parse_errors);
}
- return ParseConfigFile(path);
+ return ParseConfigFile(path, parse_errors);
}
} // namespace init
diff --git a/init/parser.h b/init/parser.h
index 110a468..f6e237f 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -72,13 +72,14 @@
Parser();
bool ParseConfig(const std::string& path);
+ bool ParseConfig(const std::string& path, size_t* parse_errors);
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
private:
- void ParseData(const std::string& filename, const std::string& data);
- bool ParseConfigFile(const std::string& path);
- bool ParseConfigDir(const std::string& path);
+ void ParseData(const std::string& filename, const std::string& data, size_t* parse_errors);
+ bool ParseConfigFile(const std::string& path, size_t* parse_errors);
+ bool ParseConfigDir(const std::string& path, size_t* parse_errors);
std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 0cf6184..95ef35c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -59,8 +59,11 @@
#include "init.h"
#include "persistent_properties.h"
#include "property_type.h"
+#include "subcontext.h"
#include "util.h"
+using namespace std::literals;
+
using android::base::ReadFileToString;
using android::base::Split;
using android::base::StartsWith;
@@ -117,48 +120,21 @@
return has_access;
}
-bool is_legal_property_name(const std::string& name) {
- size_t namelen = name.size();
-
- if (namelen < 1) return false;
- if (name[0] == '.') return false;
- if (name[namelen - 1] == '.') return false;
-
- /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
- /* Don't allow ".." to appear in a property name */
- for (size_t i = 0; i < namelen; i++) {
- if (name[i] == '.') {
- // i=0 is guaranteed to never have a dot. See above.
- if (name[i-1] == '.') return false;
- continue;
- }
- if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
- if (name[i] >= 'a' && name[i] <= 'z') continue;
- if (name[i] >= 'A' && name[i] <= 'Z') continue;
- if (name[i] >= '0' && name[i] <= '9') continue;
- return false;
- }
-
- return true;
-}
-
-static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
+static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
size_t valuelen = value.size();
- if (!is_legal_property_name(name)) {
- LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
+ if (!IsLegalPropertyName(name)) {
+ *error = "Illegal property name";
return PROP_ERROR_INVALID_NAME;
}
if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
- LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
- << "value too long";
+ *error = "Property value too long";
return PROP_ERROR_INVALID_VALUE;
}
if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
- LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
- << "value not a UTF8 encoded string";
+ *error = "Value is not a UTF8 encoded string";
return PROP_ERROR_INVALID_VALUE;
}
@@ -166,8 +142,7 @@
if (pi != nullptr) {
// ro.* properties are actually "write-once".
if (StartsWith(name, "ro.")) {
- LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
- << "property already set";
+ *error = "Read-only property was already set";
return PROP_ERROR_READ_ONLY_PROPERTY;
}
@@ -175,8 +150,7 @@
} else {
int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
if (rc < 0) {
- LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
- << "__system_property_add failed";
+ *error = "__system_property_add failed";
return PROP_ERROR_SET_FAILED;
}
}
@@ -230,8 +204,10 @@
if (info.pid != pid) {
return false;
}
- if (PropertySetImpl(info.name, info.value) != PROP_SUCCESS) {
- LOG(ERROR) << "Failed to set async property " << info.name;
+ std::string error;
+ if (PropertySet(info.name, info.value, &error) != PROP_SUCCESS) {
+ LOG(ERROR) << "Failed to set async property " << info.name << " to " << info.value << ": "
+ << error;
}
property_children.pop();
if (!property_children.empty()) {
@@ -241,9 +217,9 @@
}
static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
- PropertyAsyncFunc func) {
+ PropertyAsyncFunc func, std::string* error) {
if (value.empty()) {
- return PropertySetImpl(name, value);
+ return PropertySet(name, value, error);
}
PropertyChildInfo info;
@@ -261,30 +237,27 @@
return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
}
-uint32_t PropertySet(const std::string& name, const std::string& value) {
- if (name == "selinux.restorecon_recursive") {
- return PropertySetAsync(name, value, RestoreconRecursiveAsync);
- }
-
- return PropertySetImpl(name, value);
-}
-
uint32_t InitPropertySet(const std::string& name, const std::string& value) {
if (StartsWith(name, "ctl.")) {
- LOG(ERROR) << "Do not set ctl. properties from init; call the Service functions directly";
+ LOG(ERROR) << "InitPropertySet: Do not set ctl. properties from init; call the Service "
+ "functions directly";
+ return PROP_ERROR_INVALID_NAME;
+ }
+ if (name == "selinux.restorecon_recursive") {
+ LOG(ERROR) << "InitPropertySet: Do not set selinux.restorecon_recursive from init; use the "
+ "restorecon builtin directly";
return PROP_ERROR_INVALID_NAME;
}
- const char* type = nullptr;
- property_info_area->GetPropertyInfo(name.c_str(), nullptr, &type);
-
- if (type == nullptr || !CheckType(type, value)) {
- LOG(ERROR) << "property_set: name: '" << name << "' type check failed, type: '"
- << (type ?: "(null)") << "' value: '" << value << "'";
- return PROP_ERROR_INVALID_VALUE;
+ uint32_t result = 0;
+ ucred cr = {.pid = 1, .uid = 0, .gid = 0};
+ std::string error;
+ result = HandlePropertySet(name, value, kInitContext.c_str(), cr, &error);
+ if (result != PROP_SUCCESS) {
+ LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
}
- return PropertySet(name, value);
+ return result;
}
class SocketConnection {
@@ -415,9 +388,9 @@
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
- const std::string& source_context, const ucred& cr) {
- if (!is_legal_property_name(name)) {
- LOG(ERROR) << "PropertySet: illegal property name \"" << name << "\"";
+ const std::string& source_context, const ucred& cr, std::string* error) {
+ if (!IsLegalPropertyName(name)) {
+ *error = "Illegal property name";
return PROP_ERROR_INVALID_NAME;
}
@@ -430,9 +403,7 @@
const char* type = nullptr;
property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type);
if (!CheckMacPerms(control_string, target_context, source_context.c_str(), cr)) {
- LOG(ERROR) << "PropertySet: Unable to " << (name.c_str() + 4) << " service ctl ["
- << value << "]"
- << " uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid;
+ *error = StringPrintf("Unable to '%s' service %s", name.c_str() + 4, value.c_str());
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
@@ -445,13 +416,13 @@
property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);
if (!CheckMacPerms(name, target_context, source_context.c_str(), cr)) {
- LOG(ERROR) << "PropertySet: permission denied uid:" << cr.uid << " name:" << name;
+ *error = "SELinux permission check failed";
return PROP_ERROR_PERMISSION_DENIED;
}
if (type == nullptr || !CheckType(type, value)) {
- LOG(ERROR) << "PropertySet: name: '" << name << "' type check failed, type: '"
- << (type ?: "(null)") << "' value: '" << value << "'";
+ *error = StringPrintf("Property type check failed, value doesn't match expected type '%s'",
+ (type ?: "(null)"));
return PROP_ERROR_INVALID_VALUE;
}
@@ -470,7 +441,11 @@
<< process_log_string;
}
- return PropertySet(name, value);
+ if (name == "selinux.restorecon_recursive") {
+ return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
+ }
+
+ return PropertySet(name, value, error);
}
static void handle_property_set_fd() {
@@ -513,7 +488,16 @@
prop_name[PROP_NAME_MAX-1] = 0;
prop_value[PROP_VALUE_MAX-1] = 0;
- HandlePropertySet(prop_value, prop_value, socket.source_context(), socket.cred());
+ const auto& cr = socket.cred();
+ std::string error;
+ uint32_t result =
+ HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
+ if (result != PROP_SUCCESS) {
+ LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
+ << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
+ << error;
+ }
+
break;
}
@@ -527,7 +511,14 @@
return;
}
- auto result = HandlePropertySet(name, value, socket.source_context(), socket.cred());
+ const auto& cr = socket.cred();
+ std::string error;
+ uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
+ if (result != PROP_SUCCESS) {
+ LOG(ERROR) << "Unable to set property '" << name << "' to '" << value
+ << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
+ << error;
+ }
socket.SendUint32(result);
break;
}
@@ -545,11 +536,17 @@
* Filter is used to decide which properties to load: NULL loads all keys,
* "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
*/
-static void load_properties(char *data, const char *filter)
-{
+static void LoadProperties(char* data, const char* filter, const char* filename) {
char *key, *value, *eol, *sol, *tmp, *fn;
size_t flen = 0;
+ const char* context = kInitContext.c_str();
+ for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
+ if (StartsWith(filename, path_prefix)) {
+ context = secontext;
+ }
+ }
+
if (filter) {
flen = strlen(filter);
}
@@ -596,7 +593,21 @@
}
}
- property_set(key, value);
+ if (StartsWith(key, "ctl.") || key == "sys.powerctl"s ||
+ key == "selinux.restorecon_recursive"s) {
+ LOG(ERROR) << "Ignoring disallowed property '" << key
+ << "' with special meaning in prop file '" << filename << "'";
+ continue;
+ }
+
+ uint32_t result = 0;
+ ucred cr = {.pid = 1, .uid = 0, .gid = 0};
+ std::string error;
+ result = HandlePropertySet(key, value, context, cr, &error);
+ if (result != PROP_SUCCESS) {
+ LOG(ERROR) << "Unable to set property '" << key << "' to '" << value
+ << "' in property file '" << filename << "': " << error;
+ }
}
}
}
@@ -612,7 +623,8 @@
return false;
}
file_contents->push_back('\n');
- load_properties(file_contents->data(), filter);
+
+ LoadProperties(file_contents->data(), filter, filename);
LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
return true;
}
diff --git a/init/property_service.h b/init/property_service.h
index 8161b40..29eaaa9 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -32,7 +32,7 @@
extern uint32_t (*property_set)(const std::string& name, const std::string& value);
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
- const std::string& source_context, const ucred& cr);
+ const std::string& source_context, const ucred& cr, std::string* error);
extern bool PropertyChildReap(pid_t pid);
@@ -41,7 +41,6 @@
void load_persist_props(void);
void load_system_props(void);
void start_property_service(void);
-bool is_legal_property_name(const std::string& name);
} // namespace init
} // namespace android
diff --git a/init/service.cpp b/init/service.cpp
index 35dd319..8130e73 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -24,7 +24,6 @@
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/stat.h>
-#include <sys/system_properties.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <termios.h>
@@ -33,8 +32,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
-#include <android-base/properties.h>
-#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <hidl-util/FQName.h>
@@ -42,15 +39,23 @@
#include <selinux/selinux.h>
#include <system/thread_defs.h>
-#include "init.h"
-#include "property_service.h"
#include "rlimit_parser.h"
#include "util.h"
+#if defined(__ANDROID__)
+#include <sys/system_properties.h>
+
+#include <android-base/properties.h>
+
+#include "init.h"
+#include "property_service.h"
+#else
+#include "host_init_stubs.h"
+#endif
+
using android::base::boot_clock;
using android::base::GetProperty;
using android::base::Join;
-using android::base::make_scope_guard;
using android::base::ParseInt;
using android::base::StartsWith;
using android::base::StringPrintf;
@@ -298,7 +303,7 @@
}
}
-void Service::Reap(const siginfo_t& siginfo) {
+void Service::Reap() {
if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
KillProcessGroup(SIGKILL);
}
@@ -307,10 +312,6 @@
std::for_each(descriptors_.begin(), descriptors_.end(),
std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
- for (const auto& f : reap_callbacks_) {
- f(siginfo);
- }
-
if (flags_ & SVC_EXEC) UnSetExec();
if (flags_ & SVC_TEMPORARY) return;
@@ -1168,7 +1169,7 @@
// Property values can contain any characters, but may only be a certain length.
// (The latter restriction is needed because `start` and `stop` work by writing
// the service name to the "ctl.start" and "ctl.stop" properties.)
- return is_legal_property_name("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
+ return IsLegalPropertyName("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
}
} // namespace init
diff --git a/init/service.h b/init/service.h
index bcf1943..d46a413 100644
--- a/init/service.h
+++ b/init/service.h
@@ -17,7 +17,6 @@
#ifndef _INIT_SERVICE_H
#define _INIT_SERVICE_H
-#include <signal.h>
#include <sys/resource.h>
#include <sys/types.h>
@@ -82,7 +81,7 @@
void Stop();
void Terminate();
void Restart();
- void Reap(const siginfo_t& siginfo);
+ void Reap();
void DumpState() const;
void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
@@ -90,9 +89,6 @@
is_exec_service_running_ = false;
flags_ &= ~SVC_EXEC;
}
- void AddReapCallback(std::function<void(const siginfo_t& siginfo)> callback) {
- reap_callbacks_.emplace_back(std::move(callback));
- }
static bool is_exec_service_running() { return is_exec_service_running_; }
@@ -214,8 +210,6 @@
std::vector<std::pair<int, rlimit>> rlimits_;
std::vector<std::string> args_;
-
- std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
};
class ServiceList {
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index badacaf..072a0fb 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -84,15 +84,16 @@
}
}
- if (siginfo.si_code == CLD_EXITED) {
- LOG(INFO) << name << " exited with status " << siginfo.si_status << wait_string;
- } else {
- LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
+ auto status = siginfo.si_status;
+ if (WIFEXITED(status)) {
+ LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;
+ } else if (WIFSIGNALED(status)) {
+ LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;
}
if (!service) return true;
- service->Reap(siginfo);
+ service->Reap();
if (service->flags() & SVC_TEMPORARY) {
ServiceList::GetInstance().RemoveService(*service);
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index f3b643a..c1846f7 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -27,12 +27,14 @@
#include <selinux/android.h>
#include "action.h"
-#include "property_service.h"
-#include "selinux.h"
#include "util.h"
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
+#if defined(__ANDROID__)
+#include "property_service.h"
+#include "selinux.h"
+#else
+#include "host_init_stubs.h"
+#endif
using android::base::GetExecutablePath;
using android::base::Join;
@@ -47,6 +49,11 @@
const std::string kInitContext = "u:r:init:s0";
const std::string kVendorContext = "u:r:vendor_init:s0";
+const char* const paths_and_secontexts[2][2] = {
+ {"/vendor", kVendorContext.c_str()},
+ {"/odm", kVendorContext.c_str()},
+};
+
namespace {
constexpr size_t kBufferSize = 4096;
@@ -83,7 +90,7 @@
uint32_t SubcontextPropertySet(const std::string& name, const std::string& value) {
properties_to_set.emplace_back(name, value);
- return PROP_SUCCESS;
+ return 0;
}
class SubcontextProcess {
@@ -295,7 +302,11 @@
for (const auto& property : subcontext_reply->properties_to_set()) {
ucred cr = {.pid = pid_, .uid = 0, .gid = 0};
- HandlePropertySet(property.name(), property.value(), context_, cr);
+ std::string error;
+ if (HandlePropertySet(property.name(), property.value(), context_, cr, &error) != 0) {
+ LOG(ERROR) << "Subcontext init could not set '" << property.name() << "' to '"
+ << property.value() << "': " << error;
+ }
}
if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {
@@ -343,9 +354,6 @@
static std::vector<Subcontext> subcontexts;
std::vector<Subcontext>* InitializeSubcontexts() {
- static const char* const paths_and_secontexts[][2] = {
- {"/vendor", kVendorContext.c_str()},
- };
for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
subcontexts.emplace_back(path_prefix, secontext);
}
diff --git a/init/subcontext.h b/init/subcontext.h
index 5601b80..22d7d43 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -33,6 +33,7 @@
extern const std::string kInitContext;
extern const std::string kVendorContext;
+extern const char* const paths_and_secontexts[2][2];
class Subcontext {
public:
diff --git a/init/util.cpp b/init/util.cpp
index d80cb1e..4455b2e 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -33,7 +33,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -42,7 +41,14 @@
#include <selinux/android.h>
#include "reboot.h"
+
+#if defined(__ANDROID__)
+#include <android-base/properties.h>
+
#include "selinux.h"
+#else
+#include "host_init_stubs.h"
+#endif
#ifdef _INIT_INIT_H
#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
@@ -409,5 +415,30 @@
return false;
}
+bool IsLegalPropertyName(const std::string& name) {
+ size_t namelen = name.size();
+
+ if (namelen < 1) return false;
+ if (name[0] == '.') return false;
+ if (name[namelen - 1] == '.') return false;
+
+ /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
+ /* Don't allow ".." to appear in a property name */
+ for (size_t i = 0; i < namelen; i++) {
+ if (name[i] == '.') {
+ // i=0 is guaranteed to never have a dot. See above.
+ if (name[i - 1] == '.') return false;
+ continue;
+ }
+ if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
+ if (name[i] >= 'a' && name[i] <= 'z') continue;
+ if (name[i] >= 'A' && name[i] <= 'Z') continue;
+ if (name[i] >= '0' && name[i] <= '9') continue;
+ return false;
+ }
+
+ return true;
+}
+
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index 2cfcf6c..07e4864 100644
--- a/init/util.h
+++ b/init/util.h
@@ -62,6 +62,8 @@
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
+bool IsLegalPropertyName(const std::string& name);
+
} // namespace init
} // namespace android
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index f9f8c73..5e5e7af 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -224,7 +224,9 @@
return kInvalidOffset;
}
if (eocd->num_records == 0) {
+#if defined(__ANDROID__)
ALOGW("Zip: empty archive?");
+#endif
return kEmptyArchive;
}
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 3f8a503..76d308a 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -4,10 +4,17 @@
srcs: ["lmkd.c"],
shared_libs: [
"liblog",
- "libprocessgroup",
"libcutils",
],
cflags: ["-Werror"],
init_rc: ["lmkd.rc"],
+
+ product_variables: {
+ debuggable: {
+ cflags: [
+ "-DLMKD_TRACE_KILLS"
+ ],
+ },
+ },
}
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 15471e0..338e5fa 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -29,13 +29,31 @@
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
-#include <time.h>
+#include <sys/sysinfo.h>
#include <unistd.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
#include <log/log.h>
-#include <processgroup/processgroup.h>
+
+/*
+ * Define LMKD_TRACE_KILLS to record lmkd kills in kernel traces
+ * to profile and correlate with OOM kills
+ */
+#ifdef LMKD_TRACE_KILLS
+
+#define ATRACE_TAG ATRACE_TAG_ALWAYS
+#include <cutils/trace.h>
+
+#define TRACE_KILL_START(pid) ATRACE_INT(__FUNCTION__, pid);
+#define TRACE_KILL_END() ATRACE_INT(__FUNCTION__, 0);
+
+#else /* LMKD_TRACE_KILLS */
+
+#define TRACE_KILL_START(pid)
+#define TRACE_KILL_END()
+
+#endif /* LMKD_TRACE_KILLS */
#ifndef __unused
#define __unused __attribute__((__unused__))
@@ -44,8 +62,6 @@
#define MEMCG_SYSFS_PATH "/dev/memcg/"
#define MEMCG_MEMORY_USAGE "/dev/memcg/memory.usage_in_bytes"
#define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes"
-#define MEMPRESSURE_WATCH_MEDIUM_LEVEL "medium"
-#define MEMPRESSURE_WATCH_CRITICAL_LEVEL "critical"
#define ZONEINFO_PATH "/proc/zoneinfo"
#define LINE_MAX 128
@@ -72,26 +88,47 @@
static int use_inkernel_interface = 1;
static bool has_inkernel_module;
-/* memory pressure level medium event */
-static int mpevfd[2];
-#define CRITICAL_INDEX 1
-#define MEDIUM_INDEX 0
+/* memory pressure levels */
+enum vmpressure_level {
+ VMPRESS_LEVEL_LOW = 0,
+ VMPRESS_LEVEL_MEDIUM,
+ VMPRESS_LEVEL_CRITICAL,
+ VMPRESS_LEVEL_COUNT
+};
-static int medium_oomadj;
-static int critical_oomadj;
+static const char *level_name[] = {
+ "low",
+ "medium",
+ "critical"
+};
+
+struct mem_size {
+ int free_mem;
+ int free_swap;
+};
+
+struct {
+ int min_free; /* recorded but not used yet */
+ int max_free;
+} low_pressure_mem = { -1, -1 };
+
+static int level_oomadj[VMPRESS_LEVEL_COUNT];
+static int mpevfd[VMPRESS_LEVEL_COUNT] = { -1, -1, -1 };
static bool debug_process_killing;
static bool enable_pressure_upgrade;
static int64_t upgrade_pressure;
static int64_t downgrade_pressure;
static bool is_go_device;
+static bool kill_heaviest_task;
+static unsigned long kill_timeout_ms;
/* control socket listen and data */
static int ctrl_lfd;
static int ctrl_dfd = -1;
static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */
-/* 2 memory pressure levels, 1 ctrl listen socket, 1 ctrl data socket */
-#define MAX_EPOLL_EVENTS 4
+/* 3 memory pressure levels, 1 ctrl listen socket, 1 ctrl data socket */
+#define MAX_EPOLL_EVENTS 5
static int epollfd;
static int maxevents;
@@ -226,7 +263,7 @@
return 0;
}
-static void writefilestring(char *path, char *s) {
+static void writefilestring(const char *path, char *s) {
int fd = open(path, O_WRONLY | O_CLOEXEC);
int len = strlen(s);
int ret;
@@ -534,6 +571,18 @@
return 0;
}
+static int get_free_memory(struct mem_size *ms) {
+ struct sysinfo si;
+
+ if (sysinfo(&si) < 0)
+ return -1;
+
+ ms->free_mem = (int)(si.freeram * si.mem_unit / PAGE_SIZE);
+ ms->free_swap = (int)(si.freeswap * si.mem_unit / PAGE_SIZE);
+
+ return 0;
+}
+
static int proc_get_size(int pid) {
char path[PATH_MAX];
char line[LINE_MAX];
@@ -586,8 +635,32 @@
return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
}
+static struct proc *proc_get_heaviest(int oomadj) {
+ struct adjslot_list *head = &procadjslot_list[ADJTOSLOT(oomadj)];
+ struct adjslot_list *curr = head->next;
+ struct proc *maxprocp = NULL;
+ int maxsize = 0;
+ while (curr != head) {
+ int pid = ((struct proc *)curr)->pid;
+ int tasksize = proc_get_size(pid);
+ if (tasksize <= 0) {
+ struct adjslot_list *next = curr->next;
+ pid_remove(pid);
+ curr = next;
+ } else {
+ if (tasksize > maxsize) {
+ maxsize = tasksize;
+ maxprocp = (struct proc *)curr;
+ }
+ curr = curr->next;
+ }
+ }
+ return maxprocp;
+}
+
/* Kill one process specified by procp. Returns the size of the process killed */
-static int kill_one_process(struct proc* procp, int min_score_adj, bool is_critical) {
+static int kill_one_process(struct proc* procp, int min_score_adj,
+ enum vmpressure_level level) {
int pid = procp->pid;
uid_t uid = procp->uid;
char *taskname;
@@ -606,14 +679,18 @@
return -1;
}
+ TRACE_KILL_START(pid);
+
+ r = kill(pid, SIGKILL);
ALOGI(
"Killing '%s' (%d), uid %d, adj %d\n"
" to free %ldkB because system is under %s memory pressure oom_adj %d\n",
- taskname, pid, uid, procp->oomadj, tasksize * page_k, is_critical ? "critical" : "medium",
- min_score_adj);
- r = kill(pid, SIGKILL);
+ taskname, pid, uid, procp->oomadj, tasksize * page_k,
+ level_name[level], min_score_adj);
pid_remove(pid);
+ TRACE_KILL_END();
+
if (r) {
ALOGE("kill(%d): errno=%d", pid, errno);
return -1;
@@ -623,31 +700,40 @@
}
/*
- * Find a process to kill based on the current (possibly estimated) free memory
- * and cached memory sizes. Returns the size of the killed processes.
+ * Find processes to kill to free required number of pages.
+ * If pages_to_free is set to 0 only one process will be killed.
+ * Returns the size of the killed processes.
*/
-static int find_and_kill_process(bool is_critical) {
+static int find_and_kill_processes(enum vmpressure_level level,
+ int pages_to_free) {
int i;
- int killed_size = 0;
- int min_score_adj = is_critical ? critical_oomadj : medium_oomadj;
+ int killed_size;
+ int pages_freed = 0;
+ int min_score_adj = level_oomadj[level];
for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
struct proc *procp;
-retry:
- procp = proc_adj_lru(i);
+ while (true) {
+ if (is_go_device)
+ procp = proc_adj_lru(i);
+ else
+ procp = proc_get_heaviest(i);
- if (procp) {
- killed_size = kill_one_process(procp, min_score_adj, is_critical);
- if (killed_size < 0) {
- goto retry;
- } else {
- return killed_size;
+ if (!procp)
+ break;
+
+ killed_size = kill_one_process(procp, min_score_adj, level);
+ if (killed_size >= 0) {
+ pages_freed += killed_size;
+ if (pages_freed >= pages_to_free) {
+ return pages_freed;
+ }
}
}
}
- return 0;
+ return pages_freed;
}
static int64_t get_memory_usage(const char* path) {
@@ -674,33 +760,118 @@
return mem_usage;
}
-static void mp_event_common(bool is_critical) {
+void record_low_pressure_levels(struct mem_size *free_mem) {
+ if (low_pressure_mem.min_free == -1 ||
+ low_pressure_mem.min_free > free_mem->free_mem) {
+ if (debug_process_killing) {
+ ALOGI("Low pressure min memory update from %d to %d",
+ low_pressure_mem.min_free, free_mem->free_mem);
+ }
+ low_pressure_mem.min_free = free_mem->free_mem;
+ }
+ /*
+ * Free memory at low vmpressure events occasionally gets spikes,
+ * possibly a stale low vmpressure event with memory already
+ * freed up (no memory pressure should have been reported).
+ * Ignore large jumps in max_free that would mess up our stats.
+ */
+ if (low_pressure_mem.max_free == -1 ||
+ (low_pressure_mem.max_free < free_mem->free_mem &&
+ free_mem->free_mem - low_pressure_mem.max_free < low_pressure_mem.max_free * 0.1)) {
+ if (debug_process_killing) {
+ ALOGI("Low pressure max memory update from %d to %d",
+ low_pressure_mem.max_free, free_mem->free_mem);
+ }
+ low_pressure_mem.max_free = free_mem->free_mem;
+ }
+}
+
+enum vmpressure_level upgrade_level(enum vmpressure_level level) {
+ return (enum vmpressure_level)((level < VMPRESS_LEVEL_CRITICAL) ?
+ level + 1 : level);
+}
+
+enum vmpressure_level downgrade_level(enum vmpressure_level level) {
+ return (enum vmpressure_level)((level > VMPRESS_LEVEL_LOW) ?
+ level - 1 : level);
+}
+
+static inline unsigned long get_time_diff_ms(struct timeval *from,
+ struct timeval *to) {
+ return (to->tv_sec - from->tv_sec) * 1000 +
+ (to->tv_usec - from->tv_usec) / 1000;
+}
+
+static void mp_event_common(enum vmpressure_level level) {
int ret;
unsigned long long evcount;
- int index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX;
int64_t mem_usage, memsw_usage;
int64_t mem_pressure;
+ enum vmpressure_level lvl;
+ struct mem_size free_mem;
+ static struct timeval last_report_tm;
+ static unsigned long skip_count = 0;
- ret = read(mpevfd[index], &evcount, sizeof(evcount));
- if (ret < 0)
- ALOGE("Error reading memory pressure event fd; errno=%d",
- errno);
+ /*
+ * Check all event counters from low to critical
+ * and upgrade to the highest priority one. By reading
+ * eventfd we also reset the event counters.
+ */
+ for (lvl = VMPRESS_LEVEL_LOW; lvl < VMPRESS_LEVEL_COUNT; lvl++) {
+ if (mpevfd[lvl] != -1 &&
+ read(mpevfd[lvl], &evcount, sizeof(evcount)) > 0 &&
+ evcount > 0 && lvl > level) {
+ level = lvl;
+ }
+ }
+
+ if (kill_timeout_ms) {
+ struct timeval curr_tm;
+ gettimeofday(&curr_tm, NULL);
+ if (get_time_diff_ms(&last_report_tm, &curr_tm) < kill_timeout_ms) {
+ skip_count++;
+ return;
+ }
+ }
+
+ if (skip_count > 0) {
+ if (debug_process_killing) {
+ ALOGI("%lu memory pressure events were skipped after a kill!",
+ skip_count);
+ }
+ skip_count = 0;
+ }
+
+ if (get_free_memory(&free_mem) == 0) {
+ if (level == VMPRESS_LEVEL_LOW) {
+ record_low_pressure_levels(&free_mem);
+ }
+ } else {
+ ALOGE("Failed to get free memory!");
+ return;
+ }
+
+ if (level_oomadj[level] > OOM_SCORE_ADJ_MAX) {
+ /* Do not monitor this pressure level */
+ return;
+ }
mem_usage = get_memory_usage(MEMCG_MEMORY_USAGE);
memsw_usage = get_memory_usage(MEMCG_MEMORYSW_USAGE);
if (memsw_usage < 0 || mem_usage < 0) {
- find_and_kill_process(is_critical);
- return;
+ goto do_kill;
}
// Calculate percent for swappinness.
mem_pressure = (mem_usage * 100) / memsw_usage;
- if (enable_pressure_upgrade && !is_critical) {
+ if (enable_pressure_upgrade && level != VMPRESS_LEVEL_CRITICAL) {
// We are swapping too much.
if (mem_pressure < upgrade_pressure) {
- ALOGI("Event upgraded to critical.");
- is_critical = true;
+ level = upgrade_level(level);
+ if (debug_process_killing) {
+ ALOGI("Event upgraded to %s", level_name[level]);
+ }
}
}
@@ -708,41 +879,74 @@
// kill any process, since enough memory is available.
if (mem_pressure > downgrade_pressure) {
if (debug_process_killing) {
- ALOGI("Ignore %s memory pressure", is_critical ? "critical" : "medium");
+ ALOGI("Ignore %s memory pressure", level_name[level]);
}
return;
- } else if (is_critical && mem_pressure > upgrade_pressure) {
+ } else if (level == VMPRESS_LEVEL_CRITICAL &&
+ mem_pressure > upgrade_pressure) {
if (debug_process_killing) {
ALOGI("Downgrade critical memory pressure");
}
- // Downgrade event to medium, since enough memory available.
- is_critical = false;
+ // Downgrade event, since enough memory available.
+ level = downgrade_level(level);
}
- if (find_and_kill_process(is_critical) == 0) {
- if (debug_process_killing) {
- ALOGI("Nothing to kill");
+do_kill:
+ if (is_go_device) {
+ /* For Go devices kill only one task */
+ if (find_and_kill_processes(level, 0) == 0) {
+ if (debug_process_killing) {
+ ALOGI("Nothing to kill");
+ }
+ }
+ } else {
+ /* If pressure level is less than critical and enough free swap then ignore */
+ if (level < VMPRESS_LEVEL_CRITICAL && free_mem.free_swap > low_pressure_mem.max_free) {
+ if (debug_process_killing) {
+ ALOGI("Ignoring pressure since %d swap pages are available ", free_mem.free_swap);
+ }
+ return;
+ }
+
+ /* Free up enough memory to downgrate the memory pressure to low level */
+ if (free_mem.free_mem < low_pressure_mem.max_free) {
+ int pages_to_free = low_pressure_mem.max_free - free_mem.free_mem;
+ if (debug_process_killing) {
+ ALOGI("Trying to free %d pages", pages_to_free);
+ }
+ int pages_freed = find_and_kill_processes(level, pages_to_free);
+ if (pages_freed < pages_to_free) {
+ if (debug_process_killing) {
+ ALOGI("Unable to free enough memory (pages freed=%d)",
+ pages_freed);
+ }
+ } else {
+ gettimeofday(&last_report_tm, NULL);
+ }
}
}
}
-static void mp_event(uint32_t events __unused) {
- mp_event_common(false);
+static void mp_event_low(uint32_t events __unused) {
+ mp_event_common(VMPRESS_LEVEL_LOW);
+}
+
+static void mp_event_medium(uint32_t events __unused) {
+ mp_event_common(VMPRESS_LEVEL_MEDIUM);
}
static void mp_event_critical(uint32_t events __unused) {
- mp_event_common(true);
+ mp_event_common(VMPRESS_LEVEL_CRITICAL);
}
-static int init_mp_common(char *levelstr, void *event_handler, bool is_critical)
-{
+static bool init_mp_common(void *event_handler, enum vmpressure_level level) {
int mpfd;
int evfd;
int evctlfd;
char buf[256];
struct epoll_event epev;
int ret;
- int mpevfd_index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX;
+ const char *levelstr = level_name[level];
mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
if (mpfd < 0) {
@@ -783,8 +987,9 @@
goto err;
}
maxevents++;
- mpevfd[mpevfd_index] = evfd;
- return 0;
+ mpevfd[level] = evfd;
+ close(evctlfd);
+ return true;
err:
close(evfd);
@@ -793,17 +998,7 @@
err_open_evctlfd:
close(mpfd);
err_open_mpfd:
- return -1;
-}
-
-static int init_mp_medium()
-{
- return init_mp_common(MEMPRESSURE_WATCH_MEDIUM_LEVEL, (void *)&mp_event, false);
-}
-
-static int init_mp_critical()
-{
- return init_mp_common(MEMPRESSURE_WATCH_CRITICAL_LEVEL, (void *)&mp_event_critical, true);
+ return false;
}
static int init(void) {
@@ -843,15 +1038,18 @@
maxevents++;
has_inkernel_module = !access(INKERNEL_MINFREE_PATH, W_OK);
- use_inkernel_interface = has_inkernel_module && !is_go_device;
+ use_inkernel_interface = has_inkernel_module;
if (use_inkernel_interface) {
ALOGI("Using in-kernel low memory killer interface");
} else {
- ret = init_mp_medium();
- ret |= init_mp_critical();
- if (ret)
+ if (!init_mp_common((void *)&mp_event_low, VMPRESS_LEVEL_LOW) ||
+ !init_mp_common((void *)&mp_event_medium, VMPRESS_LEVEL_MEDIUM) ||
+ !init_mp_common((void *)&mp_event_critical,
+ VMPRESS_LEVEL_CRITICAL)) {
ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
+ return -1;
+ }
}
for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
@@ -892,13 +1090,27 @@
.sched_priority = 1,
};
- medium_oomadj = property_get_int32("ro.lmk.medium", 800);
- critical_oomadj = property_get_int32("ro.lmk.critical", 0);
+ /* By default disable low level vmpressure events */
+ level_oomadj[VMPRESS_LEVEL_LOW] =
+ property_get_int32("ro.lmk.low", OOM_SCORE_ADJ_MAX + 1);
+ level_oomadj[VMPRESS_LEVEL_MEDIUM] =
+ property_get_int32("ro.lmk.medium", 800);
+ level_oomadj[VMPRESS_LEVEL_CRITICAL] =
+ property_get_int32("ro.lmk.critical", 0);
debug_process_killing = property_get_bool("ro.lmk.debug", false);
- enable_pressure_upgrade = property_get_bool("ro.lmk.critical_upgrade", false);
- upgrade_pressure = (int64_t)property_get_int32("ro.lmk.upgrade_pressure", 50);
- downgrade_pressure = (int64_t)property_get_int32("ro.lmk.downgrade_pressure", 60);
+
+ /* By default disable upgrade/downgrade logic */
+ enable_pressure_upgrade =
+ property_get_bool("ro.lmk.critical_upgrade", false);
+ upgrade_pressure =
+ (int64_t)property_get_int32("ro.lmk.upgrade_pressure", 100);
+ downgrade_pressure =
+ (int64_t)property_get_int32("ro.lmk.downgrade_pressure", 100);
+ kill_heaviest_task =
+ property_get_bool("ro.lmk.kill_heaviest_task", true);
is_go_device = property_get_bool("ro.config.low_ram", false);
+ kill_timeout_ms =
+ (unsigned long)property_get_int32("ro.lmk.kill_timeout_ms", 0);
// MCL_ONFAULT pins pages as they fault instead of loading
// everything immediately all at once. (Which would be bad,
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 560f490..b8af2f0 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -198,7 +198,7 @@
int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
pid_t tid, const char* msg, unsigned short len) {
- if ((log_id >= LOG_ID_MAX) || (log_id < 0)) {
+ if (log_id >= LOG_ID_MAX) {
return -EINVAL;
}