resetprop: replace nanopb with quick-protobuf for persist

This commit is contained in:
LoveSy 2023-06-23 20:37:06 +08:00 committed by John Wu
parent 7826d7527f
commit 8d81bd0e33
17 changed files with 330 additions and 305 deletions

3
.gitmodules vendored
View File

@ -16,9 +16,6 @@
[submodule "xz"]
path = native/src/external/xz
url = https://github.com/xz-mirror/xz.git
[submodule "nanopb"]
path = native/src/external/nanopb
url = https://github.com/nanopb/nanopb.git
[submodule "pcre"]
path = native/src/external/pcre
url = https://android.googlesource.com/platform/external/pcre

View File

@ -10,7 +10,6 @@ include $(CLEAR_VARS)
LOCAL_MODULE := magisk
LOCAL_STATIC_LIBRARIES := \
libbase \
libnanopb \
libsystemproperties \
libphmap \
liblsplt \
@ -28,7 +27,6 @@ LOCAL_SRC_FILES := \
core/restorecon.cpp \
core/module.cpp \
core/thread.cpp \
core/resetprop/persist.cpp \
core/resetprop/resetprop.cpp \
core/core-rs.cpp \
core/su/su.cpp \
@ -139,14 +137,13 @@ include $(CLEAR_VARS)
LOCAL_MODULE := resetprop
LOCAL_STATIC_LIBRARIES := \
libbase \
libnanopb \
libsystemproperties \
libmagisk-rs
LOCAL_SRC_FILES := \
core/applet_stub.cpp \
core/resetprop/resetprop.cpp \
core/resetprop/persist.cpp
core/core-rs.cpp
LOCAL_CFLAGS := -DAPPLET_STUB_MAIN=resetprop_main
include $(BUILD_EXECUTABLE)

2
native/src/Cargo.lock generated
View File

@ -424,6 +424,8 @@ dependencies = [
"cxx-gen",
"num-derive",
"num-traits",
"pb-rs",
"quick-protobuf",
]
[[package]]

View File

@ -345,6 +345,27 @@ impl Directory {
})?;
Ok(())
}
pub fn for_all_file<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
&mut self,
mut f: F,
) -> io::Result<WalkResult> {
use WalkResult::*;
loop {
match self.read()? {
None => return Ok(Continue),
Some(ref e) => {
if e.is_dir() {
return Ok(Continue);
}
match f(e)? {
Abort | Skip => return Ok(Continue),
Continue => {}
}
}
}
}
}
}
impl Directory {

View File

@ -83,14 +83,14 @@ macro_rules! bfmt_cstr {
#[macro_export]
macro_rules! cstr {
($str:literal) => {{
($($str:tt)*) => {{
assert!(
!$str.bytes().any(|b| b == b'\0'),
!($($str)*).bytes().any(|b| b == b'\0'),
"cstr argument contains embedded NUL bytes",
);
#[allow(unused_unsafe)]
unsafe {
$crate::Utf8CStr::from_bytes_unchecked(concat!($str, "\0").as_bytes())
$crate::Utf8CStr::from_bytes_unchecked(concat!($($str)*, "\0").as_bytes())
}
}};
}

View File

@ -171,7 +171,7 @@ fn do_extract_boot_from_payload(
if !ffi::decompress(data, out_file.as_raw_fd()) {
return Err(bad_payload!("decompression failed"));
}
}
},
_ => return Err(bad_payload!("unsupported operation type")),
};
}

View File

@ -9,9 +9,11 @@ path = "lib.rs"
[build-dependencies]
cxx-gen = { workspace = true }
pb-rs = { workspace = true }
[dependencies]
base = { path = "../base" }
cxx = { workspace = true }
num-traits = { workspace = true }
num-derive = { workspace = true }
quick-protobuf = { workspace = true }

View File

@ -1,8 +1,27 @@
use pb_rs::{types::FileDescriptor, ConfigBuilder};
use crate::gen::gen_cxx_binding;
#[path = "../include/gen.rs"]
mod gen;
fn main() {
println!("cargo:rerun-if-changed=resetprop/proto/persistent_properties.proto");
gen_cxx_binding("core-rs");
let cb = ConfigBuilder::new(
&["resetprop/proto/persistent_properties.proto"],
None,
Some(&"resetprop/proto"),
&["."],
)
.unwrap();
FileDescriptor::run(
&cb.single_module(true)
.dont_use_cow(true)
.generate_getters(true)
.build(),
)
.unwrap();
}

View File

@ -5,18 +5,26 @@ use base::Utf8CStr;
use cert::read_certificate;
use daemon::{daemon_entry, find_apk_path, get_magiskd, zygisk_entry, MagiskD};
use logging::{android_logging, magisk_logging, zygisk_logging};
use resetprop::persist::{
persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop,
};
mod cert;
#[path = "../include/consts.rs"]
mod consts;
mod daemon;
mod logging;
mod resetprop;
#[cxx::bridge]
pub mod ffi {
extern "C++" {
pub type prop_cb;
include!("resetprop/resetprop.hpp");
unsafe fn get_prop_rs(name: *const c_char, persist: bool) -> String;
include!("../base/files.hpp");
pub unsafe fn get_prop_rs(name: *const c_char, persist: bool) -> String;
pub unsafe fn prop_cb_exec(cb: *mut prop_cb, name: *const c_char, value: *const c_char);
pub unsafe fn clone_attr(src: *const c_char, dst: *const c_char);
}
extern "Rust" {
@ -26,6 +34,10 @@ pub mod ffi {
fn zygisk_logging();
fn find_apk_path(pkg: &[u8], data: &mut [u8]) -> usize;
fn read_certificate(fd: i32, version: i32) -> Vec<u8>;
unsafe fn persist_get_prop(name: *const c_char, prop_cb: *mut prop_cb);
unsafe fn persist_get_props(prop_cb: *mut prop_cb);
unsafe fn persist_delete_prop(name: *const c_char) -> bool;
unsafe fn persist_set_prop(name: *const c_char, value: *const c_char) -> bool;
}
#[namespace = "rust"]

2
native/src/core/resetprop/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
proto/mod.rs
proto/persistent_properties.rs

View File

@ -0,0 +1,2 @@
pub mod persist;
pub mod proto;

View File

@ -1,277 +0,0 @@
#include <pb.h>
#include <pb_decode.h>
#include <pb_encode.h>
#include <base.hpp>
#include "resetprop.hpp"
using namespace std;
/* ***********************************************************************
* Auto generated header and field definitions compiled from
* https://android.googlesource.com/platform/system/core/+/master/init/persistent_properties.proto
* Generated with Nanopb: https://github.com/nanopb/nanopb
* ***********************************************************************/
/* Automatically generated nanopb header */
/* Generated by nanopb-0.4.3 */
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
/* Struct definitions */
struct PersistentProperties {
pb_callback_t properties;
};
struct PersistentProperties_PersistentPropertyRecord {
pb_callback_t name;
bool has_value;
char value[92];
};
/* Initializer values for message structs */
#define PersistentProperties_init_default {{{NULL}, NULL}}
#define PersistentProperties_PersistentPropertyRecord_init_default {{{NULL}, NULL}, false, ""}
#define PersistentProperties_init_zero {{{NULL}, NULL}}
#define PersistentProperties_PersistentPropertyRecord_init_zero {{{NULL}, NULL}, false, ""}
/* Field tags (for use in manual encoding/decoding) */
#define PersistentProperties_properties_tag 1
#define PersistentProperties_PersistentPropertyRecord_name_tag 1
#define PersistentProperties_PersistentPropertyRecord_value_tag 2
/* Struct field encoding specification for nanopb */
#define PersistentProperties_FIELDLIST(X, a) \
X(a, CALLBACK, REPEATED, MESSAGE, properties, 1)
#define PersistentProperties_CALLBACK pb_default_field_callback
#define PersistentProperties_DEFAULT NULL
#define PersistentProperties_properties_MSGTYPE PersistentProperties_PersistentPropertyRecord
#define PersistentProperties_PersistentPropertyRecord_FIELDLIST(X, a) \
X(a, CALLBACK, OPTIONAL, STRING, name, 1) \
X(a, STATIC, OPTIONAL, STRING, value, 2)
#define PersistentProperties_PersistentPropertyRecord_CALLBACK pb_default_field_callback
#define PersistentProperties_PersistentPropertyRecord_DEFAULT NULL
extern const pb_msgdesc_t PersistentProperties_msg;
extern const pb_msgdesc_t PersistentProperties_PersistentPropertyRecord_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define PersistentProperties_fields &PersistentProperties_msg
#define PersistentProperties_PersistentPropertyRecord_fields &PersistentProperties_PersistentPropertyRecord_msg
/* Maximum encoded size of messages (where known) */
/* PersistentProperties_size depends on runtime parameters */
/* PersistentProperties_PersistentPropertyRecord_size depends on runtime parameters */
PB_BIND(PersistentProperties, PersistentProperties, AUTO)
PB_BIND(PersistentProperties_PersistentPropertyRecord, PersistentProperties_PersistentPropertyRecord, AUTO)
/* ***************************
* End of auto generated code
* ***************************/
#define PERSIST_PROP_DIR "/data/property"
#define PERSIST_PROP PERSIST_PROP_DIR "/persistent_properties"
static bool name_decode(pb_istream_t *stream, const pb_field_t *, void **arg) {
string &name = *static_cast<string *>(*arg);
name.resize(stream->bytes_left);
return pb_read(stream, (pb_byte_t *)(name.data()), stream->bytes_left);
}
static bool name_encode(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
return pb_encode_tag_for_field(stream, field) &&
pb_encode_string(stream, (const pb_byte_t *) *arg, strlen((const char *) *arg));
}
static bool prop_decode(pb_istream_t *stream, const pb_field_t *, void **arg) {
PersistentProperties_PersistentPropertyRecord prop{};
string name;
prop.name.funcs.decode = name_decode;
prop.name.arg = &name;
if (!pb_decode(stream, &PersistentProperties_PersistentPropertyRecord_msg, &prop))
return false;
auto cb = static_cast<prop_cb*>(*arg);
cb->exec(name.data(), prop.value);
return true;
}
static bool prop_encode(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
PersistentProperties_PersistentPropertyRecord prop{};
prop.name.funcs.encode = name_encode;
prop.has_value = true;
prop_list &list = *static_cast<prop_list *>(*arg);
for (auto &p : list) {
if (!pb_encode_tag_for_field(stream, field))
return false;
prop.name.arg = (void *) p.first.data();
strscpy(prop.value, p.second.data(), sizeof(prop.value));
if (!pb_encode_submessage(stream, &PersistentProperties_PersistentPropertyRecord_msg, &prop))
return false;
}
return true;
}
static bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count) {
int fd = (intptr_t)stream->state;
return xwrite(fd, buf, count) == count;
}
static pb_ostream_t create_ostream(int fd) {
pb_ostream_t o = {
.callback = write_callback,
.state = (void*)(intptr_t)fd,
.max_size = SIZE_MAX,
.bytes_written = 0,
};
return o;
}
static void pb_get_prop(prop_cb *prop_cb) {
LOGD("resetprop: decode with protobuf [" PERSIST_PROP "]\n");
PersistentProperties props{};
props.properties.funcs.decode = prop_decode;
props.properties.arg = prop_cb;
mmap_data m(PERSIST_PROP);
pb_istream_t stream = pb_istream_from_buffer(m.buf(), m.sz());
pb_decode(&stream, &PersistentProperties_msg, &props);
}
static bool pb_write_props(prop_list &list) {
char tmp[4096];
strscpy(tmp, PERSIST_PROP ".XXXXXX", sizeof(tmp));
int fd = mkostemp(tmp, O_CLOEXEC);
if (fd < 0)
return false;
pb_ostream_t ostream = create_ostream(fd);
PersistentProperties props{};
props.properties.funcs.encode = prop_encode;
props.properties.arg = &list;
LOGD("resetprop: encode with protobuf [%s]\n", tmp);
bool ret = pb_encode(&ostream, &PersistentProperties_msg, &props);
close(fd);
if (!ret)
return false;
clone_attr(PERSIST_PROP, tmp);
return rename(tmp, PERSIST_PROP) == 0;
}
static bool file_get_prop(const char *name, char *value) {
char path[4096];
ssprintf(path, sizeof(path), PERSIST_PROP_DIR "/%s", name);
int fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd < 0)
return false;
LOGD("resetprop: read prop from [%s]\n", path);
value[read(fd, value, PROP_VALUE_MAX - 1)] = '\0'; // Null terminate the read value
close(fd);
return value[0] != '\0';
}
static bool file_set_prop(const char *name, const char *value) {
char tmp[4096];
strscpy(tmp, PERSIST_PROP_DIR "/prop.XXXXXX", sizeof(tmp));
int fd = mkostemp(tmp, O_CLOEXEC);
if (fd < 0)
return false;
auto len = strlen(value);
LOGD("resetprop: write prop to [%s]\n", tmp);
bool ret = write(fd, value, len) == len;
close(fd);
if (!ret)
return false;
char path[4096];
ssprintf(path, sizeof(path), PERSIST_PROP_DIR "/%s", name);
return rename(tmp, path) == 0;
}
static bool check_pb() {
static bool use_pb = access(PERSIST_PROP, R_OK) == 0;
return use_pb;
}
void persist_get_props(prop_cb *prop_cb) {
if (check_pb()) {
pb_get_prop(prop_cb);
} else {
auto dir = open_dir(PERSIST_PROP_DIR);
if (!dir) return;
char value[PROP_VALUE_MAX];
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (file_get_prop(entry->d_name, value))
prop_cb->exec(entry->d_name, value);
}
}
}
struct match_prop_name : prop_cb {
explicit match_prop_name(const char *name) : _name(name) { value[0] = '\0'; }
void exec(const char *name, const char *val) override {
if (value[0] == '\0' && _name == name)
strscpy(value, val, sizeof(value));
}
char value[PROP_VALUE_MAX];
private:
string_view _name;
};
void persist_get_prop(const char *name, prop_cb *prop_cb) {
if (check_pb()) {
match_prop_name cb(name);
pb_get_prop(&cb);
if (cb.value[0]) {
LOGD("resetprop: get prop (persist) [%s]: [%s]\n", name, cb.value);
prop_cb->exec(name, cb.value);
}
} else {
// Try to read from file
char value[PROP_VALUE_MAX];
if (file_get_prop(name, value)) {
LOGD("resetprop: get prop (persist) [%s]: [%s]\n", name, value);
prop_cb->exec(name, value);
}
}
}
bool persist_delete_prop(const char *name) {
if (check_pb()) {
prop_list list;
prop_collector collector(list);
pb_get_prop(&collector);
auto it = list.find(name);
if (it != list.end()) {
list.erase(it);
return pb_write_props(list);
}
return false;
} else {
char path[4096];
ssprintf(path, sizeof(path), PERSIST_PROP_DIR "/%s", name);
if (unlink(path) == 0) {
LOGD("resetprop: unlink [%s]\n", path);
return true;
}
}
return false;
}
bool persist_set_prop(const char *name, const char *value) {
if (check_pb()) {
prop_list list;
prop_collector collector(list);
pb_get_prop(&collector);
list[name] = value;
return pb_write_props(list);
} else {
return file_set_prop(name, value);
}
}

View File

@ -0,0 +1,232 @@
use crate::ffi::{clone_attr, prop_cb, prop_cb_exec};
use crate::resetprop::proto::persistent_properties::{
mod_PersistentProperties::PersistentPropertyRecord, PersistentProperties,
};
use base::{cstr, debug, libc::mkstemp, Directory, LoggedResult, MappedFile, Utf8CStr, WalkResult};
use core::ffi::c_char;
use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer};
use std::{
fs::{read_to_string, remove_file, rename, File},
io::{BufWriter, Write},
os::fd::FromRawFd,
path::{Path, PathBuf},
};
macro_rules! PERSIST_PROP_DIR {
() => {
"/data/property"
};
}
macro_rules! PERSIST_PROP {
() => {
concat!(PERSIST_PROP_DIR!(), "/persistent_properties")
};
}
struct PropCb {
cb: *mut prop_cb,
}
struct MatchNameCb<'a> {
cb: PropCb,
name: &'a Utf8CStr,
}
struct PropCollectCb<'a> {
props: &'a mut PersistentProperties,
replace_name: Option<&'a Utf8CStr>,
replace_value: Option<&'a Utf8CStr>,
}
trait PropCbExec {
fn exec(&mut self, name: &Utf8CStr, value: &Utf8CStr);
}
impl PropCbExec for PropCb {
fn exec(&mut self, name: &Utf8CStr, value: &Utf8CStr) {
if !self.cb.is_null() {
unsafe { prop_cb_exec(self.cb, name.as_ptr(), value.as_ptr()) }
}
}
}
impl PropCbExec for MatchNameCb<'_> {
fn exec(&mut self, name: &Utf8CStr, value: &Utf8CStr) {
if name.as_bytes() == self.name.as_bytes() {
self.cb.exec(name, value);
debug!("resetprop: found prop [{}] = [{}]", name, value);
}
}
}
impl PropCbExec for PropCollectCb<'_> {
fn exec(&mut self, name: &Utf8CStr, value: &Utf8CStr) {
let replace_value = self.replace_value.unwrap_or(value);
let value = self.replace_name.map_or(value, |replace_name| {
if name.as_bytes() == replace_name.as_bytes() {
replace_value
} else {
value
}
});
self.props.properties.push(PersistentPropertyRecord {
name: Some(name.to_string()),
value: Some(value.to_string()),
})
}
}
fn check_pb() -> bool {
Path::new(PERSIST_PROP!()).exists()
}
fn pb_get_prop(cb: &mut dyn PropCbExec) -> LoggedResult<()> {
debug!("resetprop: decode with protobuf [{}]", PERSIST_PROP!());
let m = MappedFile::open(cstr!(PERSIST_PROP!()))?;
let m = m.as_ref();
let mut r = BytesReader::from_bytes(m);
let mut pp = PersistentProperties::from_reader(&mut r, m)?;
pp.properties.iter_mut().for_each(|p| {
if let PersistentPropertyRecord {
name: Some(ref mut n),
value: Some(ref mut v),
} = p
{
cb.exec(Utf8CStr::from_string(n), Utf8CStr::from_string(v));
}
});
Ok(())
}
fn file_get_prop(name: &Utf8CStr) -> LoggedResult<String> {
let path = PathBuf::new().join(PERSIST_PROP_DIR!()).join(name);
let path = path.as_path();
debug!("resetprop: read prop from [{}]\n", path.display());
Ok(read_to_string(path)?)
}
fn pb_write_props(props: &PersistentProperties) -> LoggedResult<()> {
let mut tmp = String::from(concat!(PERSIST_PROP!(), ".XXXXXX"));
let tmp = Utf8CStr::from_string(&mut tmp);
{
let f = unsafe {
let fd = mkstemp(tmp.as_ptr() as *mut c_char);
if fd < 0 {
return Err(Default::default());
}
File::from_raw_fd(fd)
};
debug!("resetprop: encode with protobuf [{}]", tmp);
props.write_message(&mut Writer::new(BufWriter::new(f)))?;
}
unsafe {
clone_attr(cstr!(PERSIST_PROP!()).as_ptr(), tmp.as_ptr());
}
rename(tmp, PERSIST_PROP!())?;
Ok(())
}
fn file_set_prop(name: &Utf8CStr, value: Option<&Utf8CStr>) -> LoggedResult<()> {
let path = PathBuf::new().join(PERSIST_PROP_DIR!()).join(name);
let path = path.as_path();
if let Some(value) = value {
let mut tmp = String::from(concat!(PERSIST_PROP_DIR!(), ".prop.XXXXXX"));
{
let mut f = unsafe {
let fd = mkstemp(tmp.as_mut_ptr() as *mut c_char);
if fd < 0 {
return Err(Default::default());
}
File::from_raw_fd(fd)
};
f.write_all(value.as_bytes())?;
}
debug!("resetprop: write prop to [{}]\n", tmp);
rename(tmp, path)?;
} else {
debug!("resetprop: unlink [{}]\n", path.display());
remove_file(path)?;
}
Ok(())
}
fn do_persist_get_prop(name: &Utf8CStr, mut prop_cb: PropCb) -> LoggedResult<()> {
if check_pb() {
pb_get_prop(&mut MatchNameCb { cb: prop_cb, name })
} else {
let mut value = file_get_prop(name)?;
prop_cb.exec(name, Utf8CStr::from_string(&mut value));
debug!("resetprop: found prop [{}] = [{}]", name, value);
Ok(())
}
}
fn do_persist_get_props(mut prop_cb: PropCb) -> LoggedResult<()> {
if check_pb() {
pb_get_prop(&mut prop_cb)
} else {
let mut dir = Directory::open(cstr!(PERSIST_PROP_DIR!()))?;
dir.for_all_file(|f| {
if let Ok(name) = Utf8CStr::from_bytes(f.d_name().to_bytes()) {
if let Ok(mut value) = file_get_prop(name) {
prop_cb.exec(name, Utf8CStr::from_string(&mut value));
}
}
Ok(WalkResult::Continue)
})?;
Ok(())
}
}
fn do_persist_delete_prop(name: &Utf8CStr) -> LoggedResult<()> {
if check_pb() {
let mut pp = PersistentProperties { properties: vec![] };
pb_get_prop(&mut PropCollectCb {
props: &mut pp,
replace_name: Some(name),
replace_value: None,
})?;
pb_write_props(&pp)
} else {
file_set_prop(name, None)
}
}
fn do_persist_set_prop(name: &Utf8CStr, value: &Utf8CStr) -> LoggedResult<()> {
if check_pb() {
let mut pp = PersistentProperties { properties: vec![] };
pb_get_prop(&mut PropCollectCb {
props: &mut pp,
replace_name: Some(name),
replace_value: Some(value),
})?;
pb_write_props(&pp)
} else {
file_set_prop(name, Some(value))
}
}
pub unsafe fn persist_get_prop(name: *const c_char, prop_cb: *mut prop_cb) {
unsafe fn inner(name: *const c_char, prop_cb: *mut prop_cb) -> LoggedResult<()> {
do_persist_get_prop(Utf8CStr::from_ptr(name)?, PropCb { cb: prop_cb })
}
inner(name, prop_cb).ok();
}
pub unsafe fn persist_get_props(prop_cb: *mut prop_cb) {
do_persist_get_props(PropCb { cb: prop_cb }).ok();
}
pub unsafe fn persist_delete_prop(name: *const c_char) -> bool {
unsafe fn inner(name: *const c_char) -> LoggedResult<()> {
do_persist_delete_prop(Utf8CStr::from_ptr(name)?)
}
inner(name).is_ok()
}
pub unsafe fn persist_set_prop(name: *const c_char, value: *const c_char) -> bool {
unsafe fn inner(name: *const c_char, value: *const c_char) -> LoggedResult<()> {
do_persist_set_prop(Utf8CStr::from_ptr(name)?, Utf8CStr::from_ptr(value)?)
}
inner(name, value).is_ok()
}

View File

@ -0,0 +1,24 @@
/*
* 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.
*/
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
message PersistentProperties {
message PersistentPropertyRecord {
optional string name = 1;
optional string value = 2;
}
repeated PersistentPropertyRecord properties = 1;
}

View File

@ -29,7 +29,11 @@ int delete_prop(const char *name, bool persist = false);
int set_prop(const char *name, const char *value, bool skip_svc = false);
void load_prop_file(const char *filename, bool skip_svc = false);
void persist_get_prop(const char *name, prop_cb *prop_cb);
void persist_get_props(prop_cb *prop_cb);
bool persist_delete_prop(const char *name);
bool persist_set_prop(const char *name, const char *value);
inline void prop_cb_exec(prop_cb *cb, const char *name, const char *value) {
cb->exec(name, value);
}
void persist_get_prop(const char *name, prop_cb *prop_cb) noexcept;
void persist_get_props(prop_cb *prop_cb) noexcept;
bool persist_delete_prop(const char *name) noexcept;
bool persist_set_prop(const char *name, const char *value) noexcept;

View File

@ -17,17 +17,6 @@ LOCAL_SRC_FILES := \
xz-embedded/xz_dec_stream.c
include $(BUILD_STATIC_LIBRARY)
# libnanopb.a
include $(CLEAR_VARS)
LOCAL_MODULE:= libnanopb
LOCAL_C_INCLUDES := $(LOCAL_PATH)/nanopb
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_SRC_FILES := \
nanopb/pb_common.c \
nanopb/pb_decode.c \
nanopb/pb_encode.c
include $(BUILD_STATIC_LIBRARY)
# libfdt.a
include $(CLEAR_VARS)
LOCAL_MODULE:= libfdt

@ -1 +0,0 @@
Subproject commit c9124132a604047d0ef97a09c0e99cd9bed2c818