Merge "lmkd: Close cgroup.event_control file when done writing"
diff --git a/init/Android.bp b/init/Android.bp
index 2fea359..0db7824 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -75,6 +75,7 @@
"persistent_properties.cpp",
"persistent_properties.proto",
"property_service.cpp",
+ "property_type.cpp",
"security.cpp",
"selinux.cpp",
"service.cpp",
@@ -178,6 +179,7 @@
"init_test.cpp",
"persistent_properties_test.cpp",
"property_service_test.cpp",
+ "property_type_test.cpp",
"result_test.cpp",
"rlimit_parser_test.cpp",
"service_test.cpp",
diff --git a/init/action.cpp b/init/action.cpp
index 16ecdcd..ba03e66 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -21,6 +21,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
+#include "stable_properties.h"
#include "util.h"
using android::base::Join;
@@ -134,6 +135,25 @@
}
}
+static bool IsActionableProperty(Subcontext* subcontext, const std::string& prop_name) {
+ static bool enabled =
+ android::base::GetBoolProperty("ro.actionable_compatible_property.enabled", false);
+
+ if (subcontext == nullptr || !enabled) {
+ return true;
+ }
+
+ if (kExportedActionableProperties.count(prop_name) == 1) {
+ return true;
+ }
+ for (const auto& prefix : kPartnerPrefixes) {
+ if (android::base::StartsWith(prop_name, prefix)) {
+ return true;
+ }
+ }
+ return false;
+}
+
Result<Success> Action::ParsePropertyTrigger(const std::string& trigger) {
const static std::string prop_str("property:");
std::string prop_name(trigger.substr(prop_str.length()));
@@ -145,6 +165,10 @@
std::string prop_value(prop_name.substr(equal_pos + 1));
prop_name.erase(equal_pos);
+ if (!IsActionableProperty(subcontext_, prop_name)) {
+ return Error() << "unexported property tigger found: " << prop_name;
+ }
+
if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
return Error() << "multiple property triggers found for same property";
}
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 7aa94b0..79f7f25 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -58,6 +58,7 @@
#include "init.h"
#include "persistent_properties.h"
+#include "property_type.h"
#include "util.h"
using android::base::ReadFileToString;
@@ -95,14 +96,9 @@
LOG(FATAL) << "Failed to load serialized property info file";
}
}
-static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* cr) {
- if (!sctx) {
- return false;
- }
-
- const char* target_context = nullptr;
- property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);
- if (target_context == nullptr) {
+static bool CheckMacPerms(const std::string& name, const char* target_context,
+ const char* source_context, struct ucred* cr) {
+ if (!target_context || !source_context) {
return false;
}
@@ -111,29 +107,12 @@
audit_data.name = name.c_str();
audit_data.cr = cr;
- bool has_access =
- (selinux_check_access(sctx, target_context, "property_service", "set", &audit_data) == 0);
+ bool has_access = (selinux_check_access(source_context, target_context, "property_service",
+ "set", &audit_data) == 0);
return has_access;
}
-static int check_control_mac_perms(const char *name, char *sctx, struct ucred *cr)
-{
- /*
- * Create a name prefix out of ctl.<service name>
- * The new prefix allows the use of the existing
- * property service backend labeling while avoiding
- * mislabels based on true property prefixes.
- */
- char ctl_name[PROP_VALUE_MAX+4];
- int ret = snprintf(ctl_name, sizeof(ctl_name), "ctl.%s", name);
-
- if (ret < 0 || (size_t) ret >= sizeof(ctl_name))
- return 0;
-
- return check_mac_perms(ctl_name, sctx, cr);
-}
-
bool is_legal_property_name(const std::string& name) {
size_t namelen = name.size();
@@ -422,52 +401,70 @@
struct ucred cr = socket.cred();
char* source_ctx = nullptr;
getpeercon(socket.socket(), &source_ctx);
+ std::string source_context = source_ctx;
+ freecon(source_ctx);
if (StartsWith(name, "ctl.")) {
- if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
+ // ctl. properties have their name ctl.<action> and their value is the name of the service to
+ // apply that action to. Permissions for these actions are based on the service, so we must
+ // create a fake name of ctl.<service> to check permissions.
+ auto control_string = "ctl." + value;
+ const char* target_context = nullptr;
+ 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) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
+ << " service ctl [" << value << "]"
+ << " uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid;
+ if (!legacy_protocol) {
+ socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
+ }
+ return;
+ }
+
handle_control_message(name.c_str() + 4, value.c_str());
if (!legacy_protocol) {
- socket.SendUint32(PROP_SUCCESS);
+ socket.SendUint32(PROP_SUCCESS);
}
- } else {
- LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
- << " service ctl [" << value << "]"
- << " uid:" << cr.uid
- << " gid:" << cr.gid
- << " pid:" << cr.pid;
- if (!legacy_protocol) {
- socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
- }
- }
} else {
- if (check_mac_perms(name, source_ctx, &cr)) {
+ const char* target_context = nullptr;
+ const char* type = nullptr;
+ property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);
+ if (!CheckMacPerms(name, target_context, source_context.c_str(), &cr)) {
+ LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid
+ << " name:" << name;
+ if (!legacy_protocol) {
+ socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
+ }
+ return;
+ }
+ if (type == nullptr || !CheckType(type, value)) {
+ LOG(ERROR) << "sys_prop(" << cmd_name << "): type check failed, type: '"
+ << (type ?: "(null)") << "' value: '" << value << "'";
+ if (!legacy_protocol) {
+ socket.SendUint32(PROP_ERROR_INVALID_VALUE);
+ }
+ return;
+ }
// sys.powerctl is a special property that is used to make the device reboot. We want to log
// any process that sets this property to be able to accurately blame the cause of a shutdown.
if (name == "sys.powerctl") {
- std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
- std::string process_cmdline;
- std::string process_log_string;
- if (ReadFileToString(cmdline_path, &process_cmdline)) {
- // Since cmdline is null deliminated, .c_str() conveniently gives us just the process path.
- process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
- }
- LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
- << process_log_string;
+ std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
+ std::string process_cmdline;
+ std::string process_log_string;
+ if (ReadFileToString(cmdline_path, &process_cmdline)) {
+ // Since cmdline is null deliminated, .c_str() conveniently gives us just the process path.
+ process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
+ }
+ LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
+ << process_log_string;
}
uint32_t result = property_set(name, value);
if (!legacy_protocol) {
- socket.SendUint32(result);
+ socket.SendUint32(result);
}
- } else {
- LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;
- if (!legacy_protocol) {
- socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
- }
- }
}
-
- freecon(source_ctx);
}
static void handle_property_set_fd() {
@@ -764,9 +761,10 @@
}
LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
}
+
auto serialized_contexts = std::string();
auto error = std::string();
- if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "\\s*", &serialized_contexts,
+ if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "string", &serialized_contexts,
&error)) {
LOG(ERROR) << "Unable to serialize property contexts: " << error;
return;
diff --git a/init/property_type.cpp b/init/property_type.cpp
new file mode 100644
index 0000000..249b12b
--- /dev/null
+++ b/init/property_type.cpp
@@ -0,0 +1,81 @@
+//
+// Copyright (C) 2017 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 "property_type.h"
+
+#include <android-base/parsedouble.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+using android::base::ParseDouble;
+using android::base::ParseInt;
+using android::base::ParseUint;
+using android::base::Split;
+
+namespace android {
+namespace init {
+
+bool CheckType(const std::string& type_string, const std::string& value) {
+ auto type_strings = Split(type_string, " ");
+ if (type_strings.empty()) {
+ return false;
+ }
+ auto type = type_strings[0];
+
+ if (type == "string") {
+ return true;
+ }
+ if (type == "bool") {
+ return value == "true" || value == "false" || value == "1" || value == "0";
+ }
+ if (type == "int") {
+ int64_t parsed;
+ return ParseInt(value, &parsed);
+ }
+ if (type == "uint") {
+ uint64_t parsed;
+ if (value.empty() || value.front() == '-') {
+ return false;
+ }
+ return ParseUint(value, &parsed);
+ }
+ if (type == "double") {
+ double parsed;
+ return ParseDouble(value.c_str(), &parsed);
+ }
+ if (type == "size") {
+ auto it = value.begin();
+ while (it != value.end() && isdigit(*it)) {
+ it++;
+ }
+ if (it == value.begin() || it == value.end() || (*it != 'g' && *it != 'k' && *it != 'm')) {
+ return false;
+ }
+ it++;
+ return it == value.end();
+ }
+ if (type == "enum") {
+ for (auto it = std::next(type_strings.begin()); it != type_strings.end(); ++it) {
+ if (*it == value) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/property_type.h b/init/property_type.h
new file mode 100644
index 0000000..c889e16
--- /dev/null
+++ b/init/property_type.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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_PROPERTY_TYPE_H
+#define _INIT_PROPERTY_TYPE_H
+
+#include <string>
+
+namespace android {
+namespace init {
+
+bool CheckType(const std::string& type_string, const std::string& value);
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/property_type_test.cpp b/init/property_type_test.cpp
new file mode 100644
index 0000000..068bccc
--- /dev/null
+++ b/init/property_type_test.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 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 "property_type.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace init {
+
+TEST(property_type, CheckType_string) {
+ EXPECT_TRUE(CheckType("string", ""));
+ EXPECT_TRUE(CheckType("string", "-234"));
+ EXPECT_TRUE(CheckType("string", "234"));
+ EXPECT_TRUE(CheckType("string", "true"));
+ EXPECT_TRUE(CheckType("string", "false"));
+ EXPECT_TRUE(CheckType("string", "45645634563456345634563456"));
+ EXPECT_TRUE(CheckType("string", "some other string"));
+}
+
+TEST(property_type, CheckType_int) {
+ EXPECT_FALSE(CheckType("int", ""));
+ EXPECT_FALSE(CheckType("int", "abc"));
+ EXPECT_FALSE(CheckType("int", "-abc"));
+ EXPECT_TRUE(CheckType("int", "0"));
+ EXPECT_TRUE(CheckType("int", std::to_string(std::numeric_limits<int64_t>::min())));
+ EXPECT_TRUE(CheckType("int", std::to_string(std::numeric_limits<int64_t>::max())));
+ EXPECT_TRUE(CheckType("int", "123"));
+ EXPECT_TRUE(CheckType("int", "-123"));
+}
+
+TEST(property_type, CheckType_uint) {
+ EXPECT_FALSE(CheckType("uint", ""));
+ EXPECT_FALSE(CheckType("uint", "abc"));
+ EXPECT_FALSE(CheckType("uint", "-abc"));
+ EXPECT_TRUE(CheckType("uint", "0"));
+ EXPECT_TRUE(CheckType("uint", std::to_string(std::numeric_limits<uint64_t>::max())));
+ EXPECT_TRUE(CheckType("uint", "123"));
+ EXPECT_FALSE(CheckType("uint", "-123"));
+}
+
+TEST(property_type, CheckType_double) {
+ EXPECT_FALSE(CheckType("double", ""));
+ EXPECT_FALSE(CheckType("double", "abc"));
+ EXPECT_FALSE(CheckType("double", "-abc"));
+ EXPECT_TRUE(CheckType("double", "0.0"));
+ EXPECT_TRUE(CheckType("double", std::to_string(std::numeric_limits<double>::min())));
+ EXPECT_TRUE(CheckType("double", std::to_string(std::numeric_limits<double>::max())));
+ EXPECT_TRUE(CheckType("double", "123.1"));
+ EXPECT_TRUE(CheckType("double", "-123.1"));
+}
+
+TEST(property_type, CheckType_size) {
+ EXPECT_FALSE(CheckType("size", ""));
+ EXPECT_FALSE(CheckType("size", "ab"));
+ EXPECT_FALSE(CheckType("size", "abcd"));
+ EXPECT_FALSE(CheckType("size", "0"));
+
+ EXPECT_TRUE(CheckType("size", "512g"));
+ EXPECT_TRUE(CheckType("size", "512k"));
+ EXPECT_TRUE(CheckType("size", "512m"));
+
+ EXPECT_FALSE(CheckType("size", "512gggg"));
+ EXPECT_FALSE(CheckType("size", "512mgk"));
+ EXPECT_FALSE(CheckType("size", "g"));
+ EXPECT_FALSE(CheckType("size", "m"));
+}
+
+TEST(property_type, CheckType_enum) {
+ EXPECT_FALSE(CheckType("enum abc", ""));
+ EXPECT_FALSE(CheckType("enum abc", "ab"));
+ EXPECT_FALSE(CheckType("enum abc", "abcd"));
+ EXPECT_FALSE(CheckType("enum 123 456 789", "0"));
+
+ EXPECT_TRUE(CheckType("enum abc", "abc"));
+ EXPECT_TRUE(CheckType("enum 123 456 789", "123"));
+ EXPECT_TRUE(CheckType("enum 123 456 789", "456"));
+ EXPECT_TRUE(CheckType("enum 123 456 789", "789"));
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/stable_properties.h b/init/stable_properties.h
new file mode 100644
index 0000000..8219838
--- /dev/null
+++ b/init/stable_properties.h
@@ -0,0 +1,46 @@
+/*
+ * 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_STABLE_PROPERTIES_H
+#define _INIT_STABLE_PROPERTIES_H
+
+#include <set>
+#include <string>
+
+namespace android {
+namespace init {
+
+static constexpr const char* kPartnerPrefixes[] = {
+ "init.svc.vendor.", "ro.vendor.", "persist.vendor.", "vendor.",
+ "init.svc.odm.", "ro.odm.", "persist.odm.", "odm.",
+};
+
+static const std::set<std::string> kExportedActionableProperties = {
+ "init.svc.zygote", "persist.bluetooth.btsnoopenable",
+ "persist.sys.crash_rcu", "persist.sys.zram_enabled",
+ "ro.boot.revision", "ro.bootmode",
+ "ro.build.type", "sys.boot_completed",
+ "sys.retaildemo.enabled", "sys.shutdown.requested",
+ "sys.usb.config", "sys.usb.configfs",
+ "sys.usb.ffs.mtp.ready", "sys.usb.ffs.ready",
+ "sys.user.0.ce_available", "sys.vdso",
+ "vts.native_server.on",
+};
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/property_service/libpropertyinfoserializer/property_info_file.cpp b/property_service/libpropertyinfoserializer/property_info_file.cpp
index bf96d88..2cdc62d 100644
--- a/property_service/libpropertyinfoserializer/property_info_file.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_file.cpp
@@ -1,9 +1,26 @@
+//
+// Copyright (C) 2017 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 <property_info_serializer/property_info_serializer.h>
#include <android-base/strings.h>
#include "space_tokenizer.h"
+using android::base::Join;
using android::base::Split;
using android::base::StartsWith;
using android::base::Trim;
@@ -11,6 +28,34 @@
namespace android {
namespace properties {
+namespace {
+
+bool IsTypeValid(const std::vector<std::string>& type_strings) {
+ if (type_strings.empty()) {
+ return false;
+ }
+
+ // There must be at least one string following 'enum'
+ if (type_strings[0] == "enum") {
+ return type_strings.size() > 1;
+ }
+
+ // There should not be any string following any other types.
+ if (type_strings.size() != 1) {
+ return false;
+ }
+
+ // Check that the type matches one of remaining valid types.
+ static const char* const no_parameter_types[] = {"string", "bool", "int",
+ "uint", "double", "size"};
+ for (const auto& type : no_parameter_types) {
+ if (type_strings[0] == type) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool ParsePropertyInfoLine(const std::string& line, PropertyInfoEntry* out, std::string* error) {
auto tokenizer = SpaceTokenizer(line);
@@ -26,14 +71,28 @@
return false;
}
- // It is not an error to not find these, as older files will not contain them.
+ // It is not an error to not find exact_match or a type, as older files will not contain them.
auto exact_match = tokenizer.GetNext();
- auto type = tokenizer.GetRemaining();
+ // We reformat type to be space deliminated regardless of the input whitespace for easier storage
+ // and subsequent parsing.
+ auto type_strings = std::vector<std::string>{};
+ auto type = tokenizer.GetNext();
+ while (!type.empty()) {
+ type_strings.emplace_back(type);
+ type = tokenizer.GetNext();
+ }
- *out = {property, context, type, exact_match == "exact"};
+ if (!type_strings.empty() && !IsTypeValid(type_strings)) {
+ *error = "Type '" + Join(type_strings, " ") + "' is not valid";
+ return false;
+ }
+
+ *out = {property, context, Join(type_strings, " "), exact_match == "exact"};
return true;
}
+} // namespace
+
void ParsePropertyInfoFile(const std::string& file_contents,
std::vector<PropertyInfoEntry>* property_infos,
std::vector<std::string>* errors) {
diff --git a/storaged/binder/android/os/IStoraged.aidl b/storaged/binder/android/os/IStoraged.aidl
index f81e904..0bcc70c 100644
--- a/storaged/binder/android/os/IStoraged.aidl
+++ b/storaged/binder/android/os/IStoraged.aidl
@@ -20,4 +20,5 @@
interface IStoraged {
void onUserStarted(int userId);
void onUserStopped(int userId);
+ int getRecentPerf();
}
\ No newline at end of file
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index c5cac27..7b27988 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -119,6 +119,8 @@
return storage_info->get_perf_history();
}
+ uint32_t get_recent_perf(void) { return storage_info->get_recent_perf(); }
+
map<uint64_t, struct uid_records> get_uid_records(
double hours, uint64_t threshold, bool force_report) {
return mUidm.dump(hours, threshold, force_report);
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h
index 3a6a0d4..88a53de 100644
--- a/storaged/include/storaged_info.h
+++ b/storaged/include/storaged_info.h
@@ -78,6 +78,7 @@
void update_perf_history(uint32_t bw,
const time_point<system_clock>& tp);
vector<int> get_perf_history();
+ uint32_t get_recent_perf();
};
class emmc_info_t : public storage_info_t {
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
index 05c3b94..7ec6864 100644
--- a/storaged/include/storaged_service.h
+++ b/storaged/include/storaged_service.h
@@ -39,6 +39,7 @@
binder::Status onUserStarted(int32_t userId);
binder::Status onUserStopped(int32_t userId);
+ binder::Status getRecentPerf(int32_t* _aidl_return);
};
class StoragedPrivateService : public BinderService<StoragedPrivateService>, public BnStoragedPrivate {
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index b6dd164..055f375 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -219,6 +219,13 @@
return ret;
}
+uint32_t storage_info_t::get_recent_perf() {
+ Mutex::Autolock _l(si_mutex);
+ if (recent_perf.size() == 0) return 0;
+ return accumulate(recent_perf.begin(), recent_perf.end(), recent_perf.size() / 2) /
+ recent_perf.size();
+}
+
void emmc_info_t::report()
{
if (!report_sysfs() && !report_debugfs())
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index 3c790e6..17ea25b 100644
--- a/storaged/storaged_service.cpp
+++ b/storaged/storaged_service.cpp
@@ -174,6 +174,16 @@
return binder::Status::ok();
}
+binder::Status StoragedService::getRecentPerf(int32_t* _aidl_return) {
+ uint32_t recent_perf = storaged_sp->get_recent_perf();
+ if (recent_perf > INT32_MAX) {
+ *_aidl_return = INT32_MAX;
+ } else {
+ *_aidl_return = static_cast<int32_t>(recent_perf);
+ }
+ return binder::Status::ok();
+}
+
status_t StoragedPrivateService::start() {
return BinderService<StoragedPrivateService>::publish();
}