diff --git a/init/README.md b/init/README.md index 58a8d6b02..64c6b1c72 100644 --- a/init/README.md +++ b/init/README.md @@ -77,6 +77,43 @@ monolithic init .rc files. This additionally will aid in merge conflict resolution when multiple services are added to the system, as each one will go into a separate file. +Versioned RC files within APEXs +------------------------------- + +With the arrival of mainline on Android Q, the individual mainline +modules carry their own init.rc files within their boundaries. Init +processes these files according to the naming pattern `/apex/*/etc/*rc`. + +Because APEX modules must run on more than one release of Android, +they may require different parameters as part of the services they +define. This is achieved, starting in Android T, by incorporating +the SDK version information in the name of the init file. The suffix +is changed from `.rc` to `.#rc` where # is the first SDK where that +RC file is accepted. An init file specific to SDK=31 might be named +`init.31rc`. With this scheme, an APEX may include multiple init files. An +example is appropriate. + +For an APEX module with the following files in /apex/sample-module/apex/etc/: + + 1. init.rc + 2. init.32rc + 4. init.35rc + +The selection rule chooses the highest `.#rc` value that does not +exceed the SDK of the currently running system. The unadorned `.rc` +is interpreted as sdk=0. + +When this APEX is installed on a device with SDK <=31, the system will +process init.rc. When installed on a device running SDK 32, 33, or 34, +it will use init.32rc. When installed on a device running SDKs >= 35, +it will choose init.35rc + +This versioning scheme is used only for the init files within APEX +modules; it does not apply to the init files stored in /system/etc/init, +/vendor/etc/init, or other directories. + +This naming scheme is available after Android S. + Actions ------- Actions are named sequences of commands. Actions have a trigger which diff --git a/init/builtins.cpp b/init/builtins.cpp index 994eed9e0..763a147f3 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,7 @@ #include #include +#include #include #include @@ -1313,7 +1315,7 @@ static Result do_update_linker_config(const BuiltinArguments&) { static Result parse_apex_configs() { glob_t glob_result; - static constexpr char glob_pattern[] = "/apex/*/etc/*.rc"; + static constexpr char glob_pattern[] = "/apex/*/etc/*rc"; const int ret = glob(glob_pattern, GLOB_MARK, nullptr, &glob_result); if (ret != 0 && ret != GLOB_NOMATCH) { globfree(&glob_result); @@ -1330,17 +1332,66 @@ static Result parse_apex_configs() { if (paths.size() >= 3 && paths[2].find('@') != std::string::npos) { continue; } + // Filter directories + if (path.back() == '/') { + continue; + } configs.push_back(path); } globfree(&glob_result); - bool success = true; + // Compare all files /apex/path.#rc and /apex/path.rc with the same "/apex/path" prefix, + // choosing the one with the highest # that doesn't exceed the system's SDK. + // (.rc == .0rc for ranking purposes) + // + int active_sdk = android::base::GetIntProperty("ro.build.version.sdk", INT_MAX); + + std::map> script_map; + for (const auto& c : configs) { - if (c.back() == '/') { - // skip if directory + int sdk = 0; + const std::vector parts = android::base::Split(c, "."); + std::string base; + if (parts.size() < 2) { continue; } - success &= parser.ParseConfigFile(c); + + // parts[size()-1], aka the suffix, should be "rc" or "#rc" + // any other pattern gets discarded + + const auto& suffix = parts[parts.size() - 1]; + if (suffix == "rc") { + sdk = 0; + } else { + char trailer[9] = {0}; + int r = sscanf(suffix.c_str(), "%d%8s", &sdk, trailer); + if (r != 2) { + continue; + } + if (strlen(trailer) > 2 || strcmp(trailer, "rc") != 0) { + continue; + } + } + + if (sdk < 0 || sdk > active_sdk) { + continue; + } + + base = parts[0]; + for (unsigned int i = 1; i < parts.size() - 1; i++) { + base = base + "." + parts[i]; + } + + // is this preferred over what we already have + auto it = script_map.find(base); + if (it == script_map.end() || it->second.second < sdk) { + script_map[base] = std::make_pair(c, sdk); + } + } + + bool success = true; + for (const auto& m : script_map) { + success &= parser.ParseConfigFile(m.second.first); } ServiceList::GetInstance().MarkServicesUpdate(); if (success) {