Merge "Introduce property types"
This commit is contained in:
commit
a0ffad60ef
6 changed files with 327 additions and 62 deletions
|
|
@ -75,6 +75,7 @@ cc_library_static {
|
||||||
"persistent_properties.cpp",
|
"persistent_properties.cpp",
|
||||||
"persistent_properties.proto",
|
"persistent_properties.proto",
|
||||||
"property_service.cpp",
|
"property_service.cpp",
|
||||||
|
"property_type.cpp",
|
||||||
"security.cpp",
|
"security.cpp",
|
||||||
"selinux.cpp",
|
"selinux.cpp",
|
||||||
"service.cpp",
|
"service.cpp",
|
||||||
|
|
@ -178,6 +179,7 @@ cc_test {
|
||||||
"init_test.cpp",
|
"init_test.cpp",
|
||||||
"persistent_properties_test.cpp",
|
"persistent_properties_test.cpp",
|
||||||
"property_service_test.cpp",
|
"property_service_test.cpp",
|
||||||
|
"property_type_test.cpp",
|
||||||
"result_test.cpp",
|
"result_test.cpp",
|
||||||
"rlimit_parser_test.cpp",
|
"rlimit_parser_test.cpp",
|
||||||
"service_test.cpp",
|
"service_test.cpp",
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@
|
||||||
|
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "persistent_properties.h"
|
#include "persistent_properties.h"
|
||||||
|
#include "property_type.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
using android::base::ReadFileToString;
|
using android::base::ReadFileToString;
|
||||||
|
|
@ -95,14 +96,9 @@ void property_init() {
|
||||||
LOG(FATAL) << "Failed to load serialized property info file";
|
LOG(FATAL) << "Failed to load serialized property info file";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* cr) {
|
static bool CheckMacPerms(const std::string& name, const char* target_context,
|
||||||
if (!sctx) {
|
const char* source_context, struct ucred* cr) {
|
||||||
return false;
|
if (!target_context || !source_context) {
|
||||||
}
|
|
||||||
|
|
||||||
const char* target_context = nullptr;
|
|
||||||
property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);
|
|
||||||
if (target_context == nullptr) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,29 +107,12 @@ static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* c
|
||||||
audit_data.name = name.c_str();
|
audit_data.name = name.c_str();
|
||||||
audit_data.cr = cr;
|
audit_data.cr = cr;
|
||||||
|
|
||||||
bool has_access =
|
bool has_access = (selinux_check_access(source_context, target_context, "property_service",
|
||||||
(selinux_check_access(sctx, target_context, "property_service", "set", &audit_data) == 0);
|
"set", &audit_data) == 0);
|
||||||
|
|
||||||
return has_access;
|
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) {
|
bool is_legal_property_name(const std::string& name) {
|
||||||
size_t namelen = name.size();
|
size_t namelen = name.size();
|
||||||
|
|
||||||
|
|
@ -422,52 +401,70 @@ static void handle_property_set(SocketConnection& socket,
|
||||||
struct ucred cr = socket.cred();
|
struct ucred cr = socket.cred();
|
||||||
char* source_ctx = nullptr;
|
char* source_ctx = nullptr;
|
||||||
getpeercon(socket.socket(), &source_ctx);
|
getpeercon(socket.socket(), &source_ctx);
|
||||||
|
std::string source_context = source_ctx;
|
||||||
|
freecon(source_ctx);
|
||||||
|
|
||||||
if (StartsWith(name, "ctl.")) {
|
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());
|
handle_control_message(name.c_str() + 4, value.c_str());
|
||||||
if (!legacy_protocol) {
|
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 {
|
} 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
|
// 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.
|
// any process that sets this property to be able to accurately blame the cause of a shutdown.
|
||||||
if (name == "sys.powerctl") {
|
if (name == "sys.powerctl") {
|
||||||
std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
|
std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
|
||||||
std::string process_cmdline;
|
std::string process_cmdline;
|
||||||
std::string process_log_string;
|
std::string process_log_string;
|
||||||
if (ReadFileToString(cmdline_path, &process_cmdline)) {
|
if (ReadFileToString(cmdline_path, &process_cmdline)) {
|
||||||
// Since cmdline is null deliminated, .c_str() conveniently gives us just the process path.
|
// Since cmdline is null deliminated, .c_str() conveniently gives us just the process path.
|
||||||
process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
|
process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
|
||||||
}
|
}
|
||||||
LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
|
LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
|
||||||
<< process_log_string;
|
<< process_log_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t result = property_set(name, value);
|
uint32_t result = property_set(name, value);
|
||||||
if (!legacy_protocol) {
|
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() {
|
static void handle_property_set_fd() {
|
||||||
|
|
@ -764,9 +761,10 @@ void CreateSerializedPropertyInfo() {
|
||||||
}
|
}
|
||||||
LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
|
LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto serialized_contexts = std::string();
|
auto serialized_contexts = std::string();
|
||||||
auto error = 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)) {
|
&error)) {
|
||||||
LOG(ERROR) << "Unable to serialize property contexts: " << error;
|
LOG(ERROR) << "Unable to serialize property contexts: " << error;
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
81
init/property_type.cpp
Normal file
81
init/property_type.cpp
Normal file
|
|
@ -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
|
||||||
30
init/property_type.h
Normal file
30
init/property_type.h
Normal file
|
|
@ -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
|
||||||
95
init/property_type_test.cpp
Normal file
95
init/property_type_test.cpp
Normal file
|
|
@ -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
|
||||||
|
|
@ -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 <property_info_serializer/property_info_serializer.h>
|
||||||
|
|
||||||
#include <android-base/strings.h>
|
#include <android-base/strings.h>
|
||||||
|
|
||||||
#include "space_tokenizer.h"
|
#include "space_tokenizer.h"
|
||||||
|
|
||||||
|
using android::base::Join;
|
||||||
using android::base::Split;
|
using android::base::Split;
|
||||||
using android::base::StartsWith;
|
using android::base::StartsWith;
|
||||||
using android::base::Trim;
|
using android::base::Trim;
|
||||||
|
|
@ -11,6 +28,34 @@ using android::base::Trim;
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace properties {
|
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) {
|
bool ParsePropertyInfoLine(const std::string& line, PropertyInfoEntry* out, std::string* error) {
|
||||||
auto tokenizer = SpaceTokenizer(line);
|
auto tokenizer = SpaceTokenizer(line);
|
||||||
|
|
||||||
|
|
@ -26,14 +71,28 @@ bool ParsePropertyInfoLine(const std::string& line, PropertyInfoEntry* out, std:
|
||||||
return false;
|
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 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void ParsePropertyInfoFile(const std::string& file_contents,
|
void ParsePropertyInfoFile(const std::string& file_contents,
|
||||||
std::vector<PropertyInfoEntry>* property_infos,
|
std::vector<PropertyInfoEntry>* property_infos,
|
||||||
std::vector<std::string>* errors) {
|
std::vector<std::string>* errors) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue