Merge "Versioning for apex init.rc files"
diff --git a/init/README.md b/init/README.md
index 58a8d6b..64c6b1c 100644
--- a/init/README.md
+++ b/init/README.md
@@ -77,6 +77,43 @@
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 994eed9..763a147 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -28,6 +28,7 @@
#include <net/if.h>
#include <sched.h>
#include <signal.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -42,6 +43,7 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <map>
#include <memory>
#include <ApexProperties.sysprop.h>
@@ -1313,7 +1315,7 @@
static Result<void> 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 @@
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<std::string, std::pair<std::string, int>> script_map;
+
for (const auto& c : configs) {
- if (c.back() == '/') {
- // skip if directory
+ int sdk = 0;
+ const std::vector<std::string> 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) {