Merge "Fix various adb error reporting bugs."
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 4ddcc52..e3d4f87 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -128,18 +128,20 @@
}
}
+static bool should_force_check(int fs_stat) {
+ return fs_stat & (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
+ FS_STAT_TUNE2FS_FAILED | FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED |
+ FS_STAT_FULL_MOUNT_FAILED | FS_STAT_E2FSCK_FAILED);
+}
+
static void check_fs(const char *blk_device, char *fs_type, char *target, int *fs_stat)
{
int status;
int ret;
long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
char tmpmnt_opts[64] = "errors=remount-ro";
- const char *e2fsck_argv[] = {
- E2FSCK_BIN,
- "-f",
- "-y",
- blk_device
- };
+ const char* e2fsck_argv[] = {E2FSCK_BIN, "-y", blk_device};
+ const char* e2fsck_forced_argv[] = {E2FSCK_BIN, "-f", "-y", blk_device};
/* Check for the types of filesystems we know how to check */
if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
@@ -159,32 +161,35 @@
* filesytsem due to an error, e2fsck is still run to do a full check
* fix the filesystem.
*/
- errno = 0;
- if (!strcmp(fs_type, "ext4")) {
- // This option is only valid with ext4
- strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
- }
- ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
- PINFO << __FUNCTION__ << "(): mount(" << blk_device << "," << target
- << "," << fs_type << ")=" << ret;
- if (!ret) {
- int i;
- for (i = 0; i < 5; i++) {
- // Try to umount 5 times before continuing on.
- // Should we try rebooting if all attempts fail?
- int result = umount(target);
- if (result == 0) {
- LINFO << __FUNCTION__ << "(): unmount(" << target
- << ") succeeded";
- break;
- }
- *fs_stat |= FS_STAT_RO_UNMOUNT_FAILED;
- PERROR << __FUNCTION__ << "(): umount(" << target << ")="
- << result;
- sleep(1);
+ if (!(*fs_stat & FS_STAT_FULL_MOUNT_FAILED)) { // already tried if full mount failed
+ errno = 0;
+ if (!strcmp(fs_type, "ext4")) {
+ // This option is only valid with ext4
+ strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
}
- } else {
- *fs_stat |= FS_STAT_RO_MOUNT_FAILED;
+ ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
+ PINFO << __FUNCTION__ << "(): mount(" << blk_device << "," << target << "," << fs_type
+ << ")=" << ret;
+ if (!ret) {
+ bool umounted = false;
+ int retry_count = 5;
+ while (retry_count-- > 0) {
+ umounted = umount(target) == 0;
+ if (umounted) {
+ LINFO << __FUNCTION__ << "(): unmount(" << target << ") succeeded";
+ break;
+ }
+ PERROR << __FUNCTION__ << "(): umount(" << target << ") failed";
+ if (retry_count) sleep(1);
+ }
+ if (!umounted) {
+ // boot may fail but continue and leave it to later stage for now.
+ PERROR << __FUNCTION__ << "(): umount(" << target << ") timed out";
+ *fs_stat |= FS_STAT_RO_UNMOUNT_FAILED;
+ }
+ } else {
+ *fs_stat |= FS_STAT_RO_MOUNT_FAILED;
+ }
}
/*
@@ -196,14 +201,15 @@
<< " (executable not in system image)";
} else {
LINFO << "Running " << E2FSCK_BIN << " on " << blk_device;
-
- *fs_stat |= FS_STAT_E2FSCK_F_ALWAYS;
- ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv),
- const_cast<char **>(e2fsck_argv),
- &status, true, LOG_KLOG | LOG_FILE,
- true,
- const_cast<char *>(FSCK_LOG_FILE),
- NULL, 0);
+ if (should_force_check(*fs_stat)) {
+ ret = android_fork_execvp_ext(
+ ARRAY_SIZE(e2fsck_forced_argv), const_cast<char**>(e2fsck_forced_argv), &status,
+ true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), NULL, 0);
+ } else {
+ ret = android_fork_execvp_ext(
+ ARRAY_SIZE(e2fsck_argv), const_cast<char**>(e2fsck_argv), &status, true,
+ LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), NULL, 0);
+ }
if (ret < 0) {
/* No need to check for error in fork, we can't really handle it now */
@@ -574,21 +580,31 @@
&fstab->recs[i], &fs_stat);
}
- if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, &fstab->recs[i])) {
- *attempted_idx = i;
- mounted = 1;
- if (i != start_idx) {
- LERROR << __FUNCTION__ << "(): Mounted "
- << fstab->recs[i].blk_device << " on "
- << fstab->recs[i].mount_point << " with fs_type="
- << fstab->recs[i].fs_type << " instead of "
- << fstab->recs[start_idx].fs_type;
- }
- } else {
- fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
- /* back up the first errno for crypto decisions */
- if (mount_errno == 0) {
- mount_errno = errno;
+ int retry_count = 2;
+ while (retry_count-- > 0) {
+ if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point,
+ &fstab->recs[i])) {
+ *attempted_idx = i;
+ mounted = 1;
+ if (i != start_idx) {
+ LERROR << __FUNCTION__ << "(): Mounted " << fstab->recs[i].blk_device
+ << " on " << fstab->recs[i].mount_point
+ << " with fs_type=" << fstab->recs[i].fs_type << " instead of "
+ << fstab->recs[start_idx].fs_type;
+ }
+ fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
+ mount_errno = 0;
+ break;
+ } else {
+ if (retry_count <= 0) break; // run check_fs only once
+ fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
+ /* back up the first errno for crypto decisions */
+ if (mount_errno == 0) {
+ mount_errno = errno;
+ }
+ // retry after fsck
+ check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+ fstab->recs[i].mount_point, &fs_stat);
}
}
log_fs_stat(fstab->recs[i].blk_device, fs_stat);
@@ -1074,17 +1090,22 @@
} else {
m = fstab->recs[i].mount_point;
}
- if (__mount(n_blk_device, m, &fstab->recs[i])) {
- if (!first_mount_errno) first_mount_errno = errno;
- mount_errors++;
- fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
- log_fs_stat(fstab->recs[i].blk_device, fs_stat);
- continue;
- } else {
- ret = 0;
- log_fs_stat(fstab->recs[i].blk_device, fs_stat);
- goto out;
+ int retry_count = 2;
+ while (retry_count-- > 0) {
+ if (!__mount(n_blk_device, m, &fstab->recs[i])) {
+ ret = 0;
+ fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
+ goto out;
+ } else {
+ if (retry_count <= 0) break; // run check_fs only once
+ if (!first_mount_errno) first_mount_errno = errno;
+ mount_errors++;
+ fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
+ // try again after fsck
+ check_fs(n_blk_device, fstab->recs[i].fs_type, fstab->recs[i].mount_point, &fs_stat);
+ }
}
+ log_fs_stat(fstab->recs[i].blk_device, fs_stat);
}
if (mount_errors) {
PERROR << "Cannot mount filesystem on " << n_blk_device
diff --git a/init/README.md b/init/README.md
index 8cb1e52..1eb42e0 100644
--- a/init/README.md
+++ b/init/README.md
@@ -16,9 +16,7 @@
or options belong to the section most recently declared. Commands
or options before the first section are ignored.
-Actions and Services have unique names. If a second Action is defined
-with the same name as an existing one, its commands are appended to
-the commands of the existing action. If a second Service is defined
+Services have unique names. If a second Service is defined
with the same name as an existing one, it is ignored and an error
message is logged.
@@ -31,13 +29,21 @@
/init.rc is the primary .rc file and is loaded by the init executable
at the beginning of its execution. It is responsible for the initial
-set up of the system. It imports /init.${ro.hardware}.rc which is the
-primary vendor supplied .rc file.
+set up of the system.
-During the mount\_all command, the init executable loads all of the
-files contained within the /{system,vendor,odm}/etc/init/ directories.
-These directories are intended for all Actions and Services used after
-file system mounting.
+Devices that mount /system, /vendor through the early mount mechanism
+load all of the files contained within the
+/{system,vendor,odm}/etc/init/ directories immediately after loading
+the primary /init.rc. This is explained in more details in the
+Imports section of this file.
+
+Legacy devices without the early mount mechanism do the following:
+1. /init.rc imports /init.${ro.hardware}.rc which is the primary
+ vendor supplied .rc file.
+2. During the mount\_all command, the init executable loads all of the
+ files contained within the /{system,vendor,odm}/etc/init/ directories.
+ These directories are intended for all Actions and Services used after
+ file system mounting.
One may specify paths in the mount\_all command line to have it import
.rc files at the specified paths instead of the default ones listed above.
@@ -89,7 +95,7 @@
Actions
-------
Actions are named sequences of commands. Actions have a trigger which
-is used to determine when the action should occur. When an event
+is used to determine when the action is executed. When an event
occurs which matches an action's trigger, that action is added to
the tail of a to-be-executed queue (unless it is already on the
queue).
@@ -106,6 +112,34 @@
<command>
<command>
+Actions are added to the queue and executed based on the order that
+the file that contains them was parsed (see the Imports section), then
+sequentially within an individual file.
+
+For example if a file contains:
+
+ on boot
+ setprop a 1
+ setprop b 2
+
+ on boot && property:true=true
+ setprop c 1
+ setprop d 2
+
+ on boot
+ setprop e 1
+ setprop f 2
+
+Then when the `boot` trigger occurs and assuming the property `true`
+equals `true`, then the order of the commands executed will be:
+
+ setprop a 1
+ setprop b 2
+ setprop c 1
+ setprop d 2
+ setprop e 1
+ setprop f 2
+
Services
--------
@@ -276,7 +310,8 @@
`class_start <serviceclass>`
> Start all services of the specified class if they are
- not already running.
+ not already running. See the start entry for more information on
+ starting services.
`class_stop <serviceclass>`
> Stop and disable all services of the specified class if they are
@@ -401,6 +436,16 @@
`start <service>`
> Start a service running if it is not already running.
+ Note that this is _not_ synchronous, and even if it were, there is
+ no guarantee that the operating system's scheduler will execute the
+ service sufficiently to guarantee anything about the service's status.
+
+> This creates an important consequence that if the service offers
+ functionality to other services, such as providing a
+ communication channel, simply starting this service before those
+ services is _not_ sufficient to guarantee that the channel has
+ been set up before those services ask for it. There must be a
+ separate mechanism to make any such guarantees.
`stop <service>`
> Stop a service from running if it is currently running.
@@ -447,21 +492,54 @@
Imports
-------
-The import keyword is not a command, but rather its own section and is
-handled immediately after the .rc file that contains it has finished
-being parsed. It takes the below form:
-
`import <path>`
> Parse an init config file, extending the current configuration.
If _path_ is a directory, each file in the directory is parsed as
a config file. It is not recursive, nested directories will
not be parsed.
-There are only two times where the init executable imports .rc files:
+The import keyword is not a command, but rather its own section,
+meaning that it does not happen as part of an Action, but rather,
+imports are handled as a file is being parsed and follow the below logic.
- 1. When it imports /init.rc during initial boot
- 2. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
- paths during mount_all
+There are only three times where the init executable imports .rc files:
+
+ 1. When it imports /init.rc or the script indicated by the property
+ `ro.boot.init_rc` during initial boot.
+ 2. When it imports /{system,vendor,odm}/etc/init/ for early mount
+ devices immediately after importing /init.rc.
+ 3. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
+ paths during mount_all.
+
+The order that files are imported is a bit complex for legacy reasons
+and to keep backwards compatibility. It is not strictly guaranteed.
+
+The only correct way to guarantee that a command has been run before a
+different command is to either 1) place it in an Action with an
+earlier executed trigger, or 2) place it in an Action with the same
+trigger within the same file at an earlier line.
+
+Nonetheless, the defacto order for early mount devices is:
+1. /init.rc is parsed then recursively each of its imports are
+ parsed.
+2. The contents of /system/etc/init/ are alphabetized and parsed
+ sequentially, with imports happening recursively after each file is
+ parsed.
+3. Step 2 is repeated for /vendor/etc/init then /odm/etc/init
+
+The below pseudocode may explain this more clearly:
+
+ fn Import(file)
+ Parse(file)
+ for (import : file.imports)
+ Import(import)
+
+ Import(/init.rc)
+ Directories = [/system/etc/init, /vendor/etc/init, /odm/etc/init]
+ for (directory : Directories)
+ files = <Alphabetical order of directory's contents>
+ for (file : files)
+ Import(file)
Properties
diff --git a/init/action.cpp b/init/action.cpp
index 347edb0..c128968 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -26,10 +26,8 @@
using android::base::Join;
using android::base::StringPrintf;
-Command::Command(BuiltinFunction f, const std::vector<std::string>& args,
- const std::string& filename, int line)
- : func_(f), args_(args), filename_(filename), line_(line) {
-}
+Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
+ : func_(f), args_(args), line_(line) {}
int Command::InvokeFunc() const {
std::vector<std::string> expanded_args;
@@ -49,21 +47,12 @@
return Join(args_, ' ');
}
-std::string Command::BuildSourceString() const {
- if (!filename_.empty()) {
- return StringPrintf(" (%s:%d)", filename_.c_str(), line_);
- } else {
- return std::string();
- }
-}
-
-Action::Action(bool oneshot) : oneshot_(oneshot) {
-}
+Action::Action(bool oneshot, const std::string& filename, int line)
+ : oneshot_(oneshot), filename_(filename), line_(line) {}
const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
-bool Action::AddCommand(const std::vector<std::string>& args,
- const std::string& filename, int line, std::string* err) {
+bool Action::AddCommand(const std::vector<std::string>& args, int line, std::string* err) {
if (!function_map_) {
*err = "no function map available";
return false;
@@ -79,20 +68,12 @@
return false;
}
- AddCommand(function, args, filename, line);
+ AddCommand(function, args, line);
return true;
}
-void Action::AddCommand(BuiltinFunction f,
- const std::vector<std::string>& args,
- const std::string& filename, int line) {
- commands_.emplace_back(f, args, filename, line);
-}
-
-void Action::CombineAction(const Action& action) {
- for (const auto& c : action.commands_) {
- commands_.emplace_back(c);
- }
+void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
+ commands_.emplace_back(f, args, line);
}
std::size_t Action::NumCommands() const {
@@ -122,7 +103,7 @@
android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
std::string trigger_name = BuildTriggersString();
std::string cmd_str = command.BuildCommandString();
- std::string source = command.BuildSourceString();
+ std::string source = StringPrintf(" (%s:%d)", filename_.c_str(), command.line());
LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << source
<< " returned " << result << " took " << duration_ms << "ms.";
@@ -234,11 +215,6 @@
return event_trigger_.empty() && CheckPropertyTriggers(name, value);
}
-bool Action::TriggersEqual(const Action& other) const {
- return property_triggers_ == other.property_triggers_ &&
- event_trigger_ == other.event_trigger_;
-}
-
std::string Action::BuildTriggersString() const {
std::vector<std::string> triggers;
@@ -306,17 +282,7 @@
}
void ActionManager::AddAction(std::unique_ptr<Action> action) {
- auto old_action_it =
- std::find_if(actions_.begin(), actions_.end(),
- [&action] (std::unique_ptr<Action>& a) {
- return action->TriggersEqual(*a);
- });
-
- if (old_action_it != actions_.end()) {
- (*old_action_it)->CombineAction(*action);
- } else {
- actions_.emplace_back(std::move(action));
- }
+ actions_.emplace_back(std::move(action));
}
void ActionManager::QueueEventTrigger(const std::string& trigger) {
@@ -332,16 +298,15 @@
QueuePropertyTrigger("", "");
}
-void ActionManager::QueueBuiltinAction(BuiltinFunction func,
- const std::string& name) {
- auto action = std::make_unique<Action>(true);
+void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
+ auto action = std::make_unique<Action>(true, "<Builtin Action>", 0);
std::vector<std::string> name_vector{name};
if (!action->InitSingleTrigger(name)) {
return;
}
- action->AddCommand(func, name_vector);
+ action->AddCommand(func, name_vector, 0);
trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
actions_.emplace_back(std::move(action));
@@ -366,7 +331,8 @@
if (current_command_ == 0) {
std::string trigger_name = action->BuildTriggersString();
- LOG(INFO) << "processing action (" << trigger_name << ")";
+ LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
+ << ":" << action->line() << ")";
}
action->ExecuteOneCommand(current_command_);
@@ -397,15 +363,15 @@
}
}
-bool ActionParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
+bool ActionParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
+ int line, std::string* err) {
std::vector<std::string> triggers(args.begin() + 1, args.end());
if (triggers.size() < 1) {
*err = "actions must have a trigger";
return false;
}
- auto action = std::make_unique<Action>(false);
+ auto action = std::make_unique<Action>(false, filename, line);
if (!action->InitTriggers(triggers, err)) {
return false;
}
@@ -414,10 +380,9 @@
return true;
}
-bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const {
- return action_ ? action_->AddCommand(args, filename, line, err) : false;
+bool ActionParser::ParseLineSection(const std::vector<std::string>& args, int line,
+ std::string* err) {
+ return action_ ? action_->AddCommand(args, line, err) : false;
}
void ActionParser::EndSection() {
diff --git a/init/action.h b/init/action.h
index 0bae9f0..25e5a3e 100644
--- a/init/action.h
+++ b/init/action.h
@@ -27,31 +27,26 @@
#include "keyword_map.h"
class Command {
-public:
- Command(BuiltinFunction f, const std::vector<std::string>& args,
- const std::string& filename, int line);
+ public:
+ Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
int InvokeFunc() const;
std::string BuildCommandString() const;
- std::string BuildSourceString() const;
-private:
+ int line() const { return line_; }
+
+ private:
BuiltinFunction func_;
std::vector<std::string> args_;
- std::string filename_;
int line_;
};
class Action {
-public:
- explicit Action(bool oneshot = false);
+ public:
+ explicit Action(bool oneshot, const std::string& filename, int line);
- bool AddCommand(const std::vector<std::string>& args,
- const std::string& filename, int line, std::string* err);
- void AddCommand(BuiltinFunction f,
- const std::vector<std::string>& args,
- const std::string& filename = "", int line = 0);
- void CombineAction(const Action& action);
+ bool AddCommand(const std::vector<std::string>& args, int line, std::string* err);
+ void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
bool InitTriggers(const std::vector<std::string>& args, std::string* err);
bool InitSingleTrigger(const std::string& trigger);
std::size_t NumCommands() const;
@@ -60,11 +55,12 @@
bool CheckEventTrigger(const std::string& trigger) const;
bool CheckPropertyTrigger(const std::string& name,
const std::string& value) const;
- bool TriggersEqual(const Action& other) const;
std::string BuildTriggersString() const;
void DumpState() const;
bool oneshot() const { return oneshot_; }
+ const std::string& filename() const { return filename_; }
+ int line() const { return line_; }
static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
function_map_ = function_map;
}
@@ -80,6 +76,8 @@
std::string event_trigger_;
std::vector<Command> commands_;
bool oneshot_;
+ std::string filename_;
+ int line_;
static const KeywordMap<BuiltinFunction>* function_map_;
};
@@ -115,18 +113,17 @@
};
class ActionParser : public SectionParser {
-public:
+ public:
ActionParser() : action_(nullptr) {
}
- bool ParseSection(const std::vector<std::string>& args,
+ bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
std::string* err) override;
- bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const override;
+ bool ParseLineSection(const std::vector<std::string>& args, int line, std::string* err) override;
void EndSection() override;
void EndFile(const std::string&) override {
}
-private:
+
+ private:
std::unique_ptr<Action> action_;
};
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index 8a2bcc2..f66b2ba 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -20,8 +20,8 @@
#include "util.h"
-bool ImportParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
+bool ImportParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
+ int line, std::string* err) {
if (args.size() != 2) {
*err = "single argument needed for import\n";
return false;
diff --git a/init/import_parser.h b/init/import_parser.h
index 0e91025..e15d555 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -23,20 +23,20 @@
#include <vector>
class ImportParser : public SectionParser {
-public:
+ public:
ImportParser() {
}
- bool ParseSection(const std::vector<std::string>& args,
+ bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
std::string* err) override;
- bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const override {
+ bool ParseLineSection(const std::vector<std::string>& args, int line,
+ std::string* err) override {
return true;
}
void EndSection() override {
}
void EndFile(const std::string& filename) override;
-private:
+
+ private:
std::vector<std::string> imports_;
};
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 53e670b..b425497 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -71,14 +71,13 @@
}
section_parser = section_parsers_[args[0]].get();
std::string ret_err;
- if (!section_parser->ParseSection(args, &ret_err)) {
+ if (!section_parser->ParseSection(args, state.filename, state.line, &ret_err)) {
parse_error(&state, "%s\n", ret_err.c_str());
section_parser = nullptr;
}
} else if (section_parser) {
std::string ret_err;
- if (!section_parser->ParseLineSection(args, state.filename,
- state.line, &ret_err)) {
+ if (!section_parser->ParseLineSection(args, state.line, &ret_err)) {
parse_error(&state, "%s\n", ret_err.c_str());
}
}
diff --git a/init/init_parser.h b/init/init_parser.h
index 6935fdf..64f0cac 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -26,11 +26,10 @@
public:
virtual ~SectionParser() {
}
- virtual bool ParseSection(const std::vector<std::string>& args,
- std::string* err) = 0;
- virtual bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const = 0;
+ virtual bool ParseSection(const std::vector<std::string>& args, const std::string& filename,
+ int line, std::string* err) = 0;
+ virtual bool ParseLineSection(const std::vector<std::string>& args, int line,
+ std::string* err) = 0;
virtual void EndSection() = 0;
virtual void EndFile(const std::string& filename) = 0;
};
diff --git a/init/service.cpp b/init/service.cpp
index 4adbbf0..caf5785 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -157,6 +157,7 @@
gid_(0),
namespace_flags_(0),
seclabel_(""),
+ onrestart_(false, "<Service '" + name + "' onrestart>", 0),
ioprio_class_(IoSchedClass_NONE),
ioprio_pri_(0),
priority_(0),
@@ -180,6 +181,7 @@
capabilities_(capabilities),
namespace_flags_(namespace_flags),
seclabel_(seclabel),
+ onrestart_(false, "<Service '" + name + "' onrestart>", 0),
ioprio_class_(IoSchedClass_NONE),
ioprio_pri_(0),
priority_(0),
@@ -438,7 +440,8 @@
bool Service::ParseOnrestart(const std::vector<std::string>& args, std::string* err) {
std::vector<std::string> str_args(args.begin() + 1, args.end());
- onrestart_.AddCommand(str_args, "", 0, err);
+ int line = onrestart_.NumCommands() + 1;
+ onrestart_.AddCommand(str_args, line, err);
return true;
}
@@ -1092,8 +1095,8 @@
}
}
-bool ServiceParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
+bool ServiceParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
+ int line, std::string* err) {
if (args.size() < 3) {
*err = "services must have a name and a program";
return false;
@@ -1110,9 +1113,8 @@
return true;
}
-bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const {
+bool ServiceParser::ParseLineSection(const std::vector<std::string>& args, int line,
+ std::string* err) {
return service_ ? service_->ParseLine(args, err) : false;
}
diff --git a/init/service.h b/init/service.h
index 5e89b9f..6fe0258 100644
--- a/init/service.h
+++ b/init/service.h
@@ -212,18 +212,15 @@
};
class ServiceParser : public SectionParser {
-public:
- ServiceParser() : service_(nullptr) {
- }
- bool ParseSection(const std::vector<std::string>& args,
+ public:
+ ServiceParser() : service_(nullptr) {}
+ bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
std::string* err) override;
- bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const override;
+ bool ParseLineSection(const std::vector<std::string>& args, int line, std::string* err) override;
void EndSection() override;
- void EndFile(const std::string&) override {
- }
-private:
+ void EndFile(const std::string&) override {}
+
+ private:
bool IsValidName(const std::string& name) const;
std::unique_ptr<Service> service_;
diff --git a/libcutils/include/cutils/multiuser.h b/libcutils/include/cutils/multiuser.h
index 5bd9c7b..9a2305c 100644
--- a/libcutils/include/cutils/multiuser.h
+++ b/libcutils/include/cutils/multiuser.h
@@ -33,6 +33,7 @@
extern gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id);
extern gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id);
+extern gid_t multiuser_get_ext_cache_gid(userid_t user_id, appid_t app_id);
extern gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id);
/* TODO: switch callers over to multiuser_get_shared_gid() */
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 0037f15..bbba853 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -171,6 +171,9 @@
#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
#define AID_EXT_GID_END 39999 /* end of gids for apps to mark external data */
+#define AID_EXT_CACHE_GID_START 40000 /* start of gids for apps to mark external cached data */
+#define AID_EXT_CACHE_GID_END 49999 /* end of gids for apps to mark external cached data */
+
#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
#define AID_SHARED_GID_END 59999 /* end of gids for apps in each user to share */
diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c
index 08d4d6c..61403f4 100644
--- a/libcutils/multiuser.c
+++ b/libcutils/multiuser.c
@@ -45,6 +45,14 @@
}
}
+gid_t multiuser_get_ext_cache_gid(userid_t user_id, appid_t app_id) {
+ if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+ return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_CACHE_GID_START);
+ } else {
+ return -1;
+ }
+}
+
gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id) {
if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_SHARED_GID_START);
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/tests/multiuser_test.cpp
index ae5c416..2f9d854 100644
--- a/libcutils/tests/multiuser_test.cpp
+++ b/libcutils/tests/multiuser_test.cpp
@@ -68,6 +68,14 @@
EXPECT_EQ(1030000U, multiuser_get_ext_gid(10, 10000));
}
+TEST(MultiuserTest, TestExtCache) {
+ EXPECT_EQ(ERR_GID, multiuser_get_ext_cache_gid(0, 0));
+ EXPECT_EQ(ERR_GID, multiuser_get_ext_cache_gid(0, 1000));
+ EXPECT_EQ(40000U, multiuser_get_ext_cache_gid(0, 10000));
+ EXPECT_EQ(ERR_GID, multiuser_get_ext_cache_gid(0, 50000));
+ EXPECT_EQ(1040000U, multiuser_get_ext_cache_gid(10, 10000));
+}
+
TEST(MultiuserTest, TestShared) {
EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 0));
EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 1000));
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 9ece0b3..3764faf 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -22,6 +22,8 @@
/* struct log_time is a wire-format variant of struct timespec */
#define NS_PER_SEC 1000000000ULL
+#define US_PER_SEC 1000000ULL
+#define MS_PER_SEC 1000ULL
#ifndef __struct_log_time_defined
#define __struct_log_time_defined
@@ -148,6 +150,14 @@
uint64_t nsec() const {
return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
}
+ uint64_t usec() const {
+ return static_cast<uint64_t>(tv_sec) * US_PER_SEC +
+ tv_nsec / (NS_PER_SEC / US_PER_SEC);
+ }
+ uint64_t msec() const {
+ return static_cast<uint64_t>(tv_sec) * MS_PER_SEC +
+ tv_nsec / (NS_PER_SEC / MS_PER_SEC);
+ }
#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
static const char default_format[];
diff --git a/liblog/properties.c b/liblog/properties.c
index c71cbcf..11be827 100644
--- a/liblog/properties.c
+++ b/liblog/properties.c
@@ -459,6 +459,9 @@
if (check_flag(property.property, "false")) {
return false;
}
+ if (property.property[0]) {
+ flag &= ~(BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
+ }
if (check_flag(property.property, "eng")) {
flag |= BOOL_DEFAULT_FLAG_ENG;
}
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
new file mode 100644
index 0000000..37e4dbf
--- /dev/null
+++ b/libprocessgroup/Android.bp
@@ -0,0 +1,10 @@
+cc_library {
+ srcs: ["processgroup.cpp"],
+ name: "libprocessgroup",
+ shared_libs: ["libbase"],
+ export_include_dirs: ["include"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/libprocessgroup/Android.mk b/libprocessgroup/Android.mk
deleted file mode 100644
index 0bfc391..0000000
--- a/libprocessgroup/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := processgroup.cpp
-LOCAL_MODULE := libprocessgroup
-LOCAL_STATIC_LIBRARIES := libbase
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := processgroup.cpp
-LOCAL_MODULE := libprocessgroup
-LOCAL_SHARED_LIBRARIES := libbase
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_SHARED_LIBRARY)
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index dd8b5fd..6ec0991 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -10,17 +10,23 @@
"sparse.c",
"sparse_crc32.c",
"sparse_err.c",
- "sparse_read.c",
+ "sparse_read.cpp",
],
cflags: ["-Werror"],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
target: {
host: {
- shared_libs: ["libz-host"],
+ shared_libs: [
+ "libz-host",
+ "libbase",
+ ],
},
android: {
- shared_libs: ["libz"],
+ shared_libs: [
+ "libz",
+ "libbase",
+ ],
},
windows: {
enabled: true,
@@ -38,6 +44,7 @@
static_libs: [
"libsparse",
"libz",
+ "libbase",
],
cflags: ["-Werror"],
@@ -50,6 +57,7 @@
static_libs: [
"libsparse",
"libz",
+ "libbase",
],
cflags: ["-Werror"],
@@ -61,6 +69,7 @@
static_libs: [
"libsparse",
"libz",
+ "libbase",
],
cflags: ["-Werror"],
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
index 2115998..51e60ef 100644
--- a/libsparse/output_file.c
+++ b/libsparse/output_file.c
@@ -584,7 +584,7 @@
.file_hdr_sz = SPARSE_HEADER_LEN,
.chunk_hdr_sz = CHUNK_HEADER_LEN,
.blk_sz = out->block_size,
- .total_blks = out->len / out->block_size,
+ .total_blks = DIV_ROUND_UP(out->len, out->block_size),
.total_chunks = chunks,
.image_checksum = 0
};
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 474c1fc..b67e94e 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -17,6 +17,10 @@
#ifndef _OUTPUT_FILE_H_
#define _OUTPUT_FILE_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <sparse/sparse.h>
struct output_file;
@@ -38,4 +42,8 @@
int read_all(int fd, void *buf, size_t len);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/libsparse/sparse_file.h b/libsparse/sparse_file.h
index 91a12e6..763f43f 100644
--- a/libsparse/sparse_file.h
+++ b/libsparse/sparse_file.h
@@ -17,6 +17,10 @@
#ifndef _LIBSPARSE_SPARSE_FILE_H_
#define _LIBSPARSE_SPARSE_FILE_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <sparse/sparse.h>
struct sparse_file {
@@ -28,5 +32,8 @@
struct output_file *out;
};
+#ifdef __cplusplus
+}
+#endif
#endif /* _LIBSPARSE_SPARSE_FILE_H_ */
diff --git a/libsparse/sparse_format.h b/libsparse/sparse_format.h
index c41f12a..779e038 100644
--- a/libsparse/sparse_format.h
+++ b/libsparse/sparse_format.h
@@ -18,6 +18,10 @@
#define _LIBSPARSE_SPARSE_FORMAT_H_
#include "sparse_defs.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef struct sparse_header {
__le32 magic; /* 0xed26ff3a */
__le16 major_version; /* (0x1) - reject images with higher major versions */
@@ -52,4 +56,8 @@
* For a CRC32 chunk, it's 4 bytes of CRC32
*/
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/libsparse/sparse_read.c b/libsparse/sparse_read.cpp
similarity index 88%
rename from libsparse/sparse_read.c
rename to libsparse/sparse_read.cpp
index a188202..bd66873 100644
--- a/libsparse/sparse_read.c
+++ b/libsparse/sparse_read.cpp
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
+#include <algorithm>
#include <inttypes.h>
#include <fcntl.h>
#include <stdarg.h>
@@ -25,17 +25,19 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
+#include <string>
#include <unistd.h>
#include <sparse/sparse.h>
+#include "android-base/stringprintf.h"
#include "defs.h"
#include "output_file.h"
#include "sparse_crc32.h"
#include "sparse_file.h"
#include "sparse_format.h"
+
#if defined(__APPLE__) && defined(__MACH__)
#define lseek64 lseek
#define off64_t off_t
@@ -45,57 +47,30 @@
#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
-#define COPY_BUF_SIZE (1024U*1024U)
+static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
static char *copybuf;
-#define min(a, b) \
- ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
+static std::string ErrorString(int err)
+{
+ if (err == -EOVERFLOW) return "EOF while reading file";
+ if (err == -EINVAL) return "Invalid sparse file format";
+ if (err == -ENOMEM) return "Failed allocation while reading file";
+ return android::base::StringPrintf("Unknown error %d", err);
+}
static void verbose_error(bool verbose, int err, const char *fmt, ...)
{
- char *s = "";
- char *at = "";
+ if (!verbose) return;
+
+ std::string msg = ErrorString(err);
if (fmt) {
+ msg += " at ";
va_list argp;
- int size;
-
va_start(argp, fmt);
- size = vsnprintf(NULL, 0, fmt, argp);
+ android::base::StringAppendV(&msg, fmt, argp);
va_end(argp);
-
- if (size < 0) {
- return;
- }
-
- at = malloc(size + 1);
- if (at == NULL) {
- return;
- }
-
- va_start(argp, fmt);
- vsnprintf(at, size, fmt, argp);
- va_end(argp);
- at[size] = 0;
- s = " at ";
}
- if (verbose) {
-#ifndef _WIN32
- if (err == -EOVERFLOW) {
- sparse_print_verbose("EOF while reading file%s%s\n", s, at);
- } else
-#endif
- if (err == -EINVAL) {
- sparse_print_verbose("Invalid sparse file format%s%s\n", s, at);
- } else if (err == -ENOMEM) {
- sparse_print_verbose("Failed allocation while reading file%s%s\n",
- s, at);
- } else {
- sparse_print_verbose("Unknown error %d%s%s\n", err, s, at);
- }
- }
- if (fmt) {
- free(at);
- }
+ sparse_print_verbose("%s\n", msg.c_str());
}
static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
@@ -104,7 +79,7 @@
{
int ret;
int chunk;
- unsigned int len = blocks * s->block_size;
+ int64_t len = blocks * s->block_size;
if (chunk_size % s->block_size != 0) {
return -EINVAL;
@@ -121,7 +96,7 @@
if (crc32) {
while (len) {
- chunk = min(len, COPY_BUF_SIZE);
+ chunk = std::min(len, COPY_BUF_SIZE);
ret = read_all(fd, copybuf, chunk);
if (ret < 0) {
return ret;
@@ -168,7 +143,7 @@
}
while (len) {
- chunk = min(len, COPY_BUF_SIZE);
+ chunk = std::min(len, COPY_BUF_SIZE);
*crc32 = sparse_crc32(*crc32, copybuf, chunk);
len -= chunk;
}
@@ -190,7 +165,7 @@
memset(copybuf, 0, COPY_BUF_SIZE);
while (len) {
- int chunk = min(len, COPY_BUF_SIZE);
+ int chunk = std::min(len, COPY_BUF_SIZE);
*crc32 = sparse_crc32(*crc32, copybuf, chunk);
len -= chunk;
}
@@ -284,7 +259,7 @@
off64_t offset;
if (!copybuf) {
- copybuf = malloc(COPY_BUF_SIZE);
+ copybuf = (char *)malloc(COPY_BUF_SIZE);
}
if (!copybuf) {
@@ -357,7 +332,7 @@
static int sparse_file_read_normal(struct sparse_file *s, int fd)
{
int ret;
- uint32_t *buf = malloc(s->block_size);
+ uint32_t *buf = (uint32_t *)malloc(s->block_size);
unsigned int block = 0;
int64_t remain = s->len;
int64_t offset = 0;
@@ -370,7 +345,7 @@
}
while (remain > 0) {
- to_read = min(remain, s->block_size);
+ to_read = std::min(remain, (int64_t)(s->block_size));
ret = read_all(fd, buf, to_read);
if (ret < 0) {
error("failed to read sparse file");
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index a6bea0c..ed46181 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -208,8 +208,7 @@
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
// Log traffic received to total
pthread_mutex_lock(&mLogElementsLock);
- stats.add(elem);
- stats.subtract(elem);
+ stats.addTotal(elem);
pthread_mutex_unlock(&mLogElementsLock);
delete elem;
return -EACCES;
@@ -320,8 +319,7 @@
pthread_mutex_unlock(&mLogElementsLock);
return len;
}
- stats.add(currentLast);
- stats.subtract(currentLast);
+ stats.addTotal(currentLast);
delete currentLast;
swab = total;
event->payload.data = htole32(swab);
@@ -337,8 +335,7 @@
}
}
if (count) {
- stats.add(currentLast);
- stats.subtract(currentLast);
+ stats.addTotal(currentLast);
currentLast->setDropped(count);
}
droppedElements[log_id] = currentLast;
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index af19279..feb105f 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -15,6 +15,7 @@
*/
#include <ctype.h>
+#include <inttypes.h>
#include <poll.h>
#include <sys/prctl.h>
#include <sys/socket.h>
@@ -192,6 +193,12 @@
}
}
+ android::prdebug(
+ "logdr: UID=%d GID=%d PID=%d %c tail=%lu logMask=%x pid=%d "
+ "start=%" PRIu64 "ns timeout=%" PRIu64 "ns\n",
+ cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail,
+ logMask, (int)pid, sequence.nsec(), timeout);
+
FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence, timeout);
// Set acceptable upper limit to wait for slow reader processing b/27242723
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index d3167ad..b41aca5 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -15,6 +15,7 @@
*/
#include <fcntl.h>
+#include <inttypes.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
@@ -70,20 +71,30 @@
}
}
+void LogStatistics::addTotal(LogBufferElement* element) {
+ if (element->getDropped()) return;
+
+ log_id_t log_id = element->getLogId();
+ unsigned short size = element->getMsgLen();
+ mSizesTotal[log_id] += size;
+ SizesTotal += size;
+ ++mElementsTotal[log_id];
+}
+
void LogStatistics::add(LogBufferElement* element) {
log_id_t log_id = element->getLogId();
unsigned short size = element->getMsgLen();
mSizes[log_id] += size;
++mElements[log_id];
+ // When caller adding a chatty entry, they will have already
+ // called add() and subtract() for each entry as they are
+ // evaluated and trimmed, thus recording size and number of
+ // elements, but we must recognize the manufactured dropped
+ // entry as not contributing to the lifetime totals.
if (element->getDropped()) {
++mDroppedElements[log_id];
} else {
- // When caller adding a chatty entry, they will have already
- // called add() and subtract() for each entry as they are
- // evaluated and trimmed, thus recording size and number of
- // elements, but we must recognize the manufactured dropped
- // entry as not contributing to the lifetime totals.
mSizesTotal[log_id] += size;
SizesTotal += size;
++mElementsTotal[log_id];
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 066b7de..e171e4a 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -576,6 +576,7 @@
enable = true;
}
+ void addTotal(LogBufferElement* entry);
void add(LogBufferElement* entry);
void subtract(LogBufferElement* entry);
// entry->setDropped(1) must follow this call
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 56e736e..a94a717 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -245,10 +245,6 @@
class_stop charger
trigger late-init
-# Load properties from /system/ + /factory after fs mount.
-on load_system_props_action
- load_system_props
-
on load_persist_props_action
load_persist_props
start logd
@@ -269,11 +265,6 @@
trigger fs
trigger post-fs
- # Load properties from /system/ + /factory after fs mount. Place
- # this in another action so that the load will be scheduled after the prior
- # issued fs triggers have completed.
- trigger load_system_props_action
-
# Mount fstab in init.{$device}.rc by mount_all with '--late' parameter
# to only mount entries with 'latemount'. This is needed if '--early' is
# specified in the previous mount_all command on the fs stage.
@@ -298,6 +289,13 @@
trigger boot
on post-fs
+ # Load properties from
+ # /system/build.prop,
+ # /odm/build.prop,
+ # /vendor/build.prop and
+ # /factory/factory.prop
+ load_system_props
+ # start essential services
start logd
start hwservicemanager