init: don't log in expand_props directly

It's better to pass the error message to the caller to determine how
best to print the error.

Test: build
Change-Id: Id8857c459df2f26c031650166609608d20e4d051
diff --git a/init/action.cpp b/init/action.cpp
index 0c476df..97b1c1a 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -35,9 +35,11 @@
     builtin_arguments.args.resize(args.size());
     builtin_arguments.args[0] = args[0];
     for (std::size_t i = 1; i < args.size(); ++i) {
-        if (!expand_props(args[i], &builtin_arguments.args[i])) {
-            return Error() << "cannot expand '" << args[i] << "'";
+        auto expanded_arg = ExpandProps(args[i]);
+        if (!expanded_arg) {
+            return expanded_arg.error();
         }
+        builtin_arguments.args[i] = std::move(*expanded_arg);
     }
 
     return function(builtin_arguments);
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index 7c0544a..f9a08a5 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -26,6 +26,7 @@
 
 // android/api-level.h
 #define __ANDROID_API_P__ 28
+#define __ANDROID_API_R__ 30
 
 // sys/system_properties.h
 #define PROP_VALUE_MAX 92
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index c72b7d6..1a43508 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -29,15 +29,14 @@
         return Error() << "single argument needed for import\n";
     }
 
-    std::string conf_file;
-    bool ret = expand_props(args[1], &conf_file);
-    if (!ret) {
-        return Error() << "error while expanding import";
+    auto conf_file = ExpandProps(args[1]);
+    if (!conf_file) {
+        return Error() << "Could not expand import: " << conf_file.error();
     }
 
-    LOG(INFO) << "Added '" << conf_file << "' to import list";
+    LOG(INFO) << "Added '" << *conf_file << "' to import list";
     if (filename_.empty()) filename_ = filename;
-    imports_.emplace_back(std::move(conf_file), line);
+    imports_.emplace_back(std::move(*conf_file), line);
     return {};
 }
 
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 3761750..3819042 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -648,13 +648,14 @@
             }
 
             std::string raw_filename(fn);
-            std::string expanded_filename;
-            if (!expand_props(raw_filename, &expanded_filename)) {
-                LOG(ERROR) << "Could not expand filename '" << raw_filename << "'";
+            auto expanded_filename = ExpandProps(raw_filename);
+
+            if (!expanded_filename) {
+                LOG(ERROR) << "Could not expand filename ': " << expanded_filename.error();
                 continue;
             }
 
-            load_properties_from_file(expanded_filename.c_str(), key, properties);
+            load_properties_from_file(expanded_filename->c_str(), key, properties);
         } else {
             value = strchr(key, '=');
             if (!value) continue;
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 54be086..143cdfd 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -497,24 +497,28 @@
 // This function returns the Android version with which the vendor SEPolicy was compiled.
 // It is used for version checks such as whether or not vendor_init should be used
 int SelinuxGetVendorAndroidVersion() {
-    if (!IsSplitPolicyDevice()) {
-        // If this device does not split sepolicy files, it's not a Treble device and therefore,
-        // we assume it's always on the latest platform.
-        return __ANDROID_API_FUTURE__;
-    }
+    static int vendor_android_version = [] {
+        if (!IsSplitPolicyDevice()) {
+            // If this device does not split sepolicy files, it's not a Treble device and therefore,
+            // we assume it's always on the latest platform.
+            return __ANDROID_API_FUTURE__;
+        }
 
-    std::string version;
-    if (!GetVendorMappingVersion(&version)) {
-        LOG(FATAL) << "Could not read vendor SELinux version";
-    }
+        std::string version;
+        if (!GetVendorMappingVersion(&version)) {
+            LOG(FATAL) << "Could not read vendor SELinux version";
+        }
 
-    int major_version;
-    std::string major_version_str(version, 0, version.find('.'));
-    if (!ParseInt(major_version_str, &major_version)) {
-        PLOG(FATAL) << "Failed to parse the vendor sepolicy major version " << major_version_str;
-    }
+        int major_version;
+        std::string major_version_str(version, 0, version.find('.'));
+        if (!ParseInt(major_version_str, &major_version)) {
+            PLOG(FATAL) << "Failed to parse the vendor sepolicy major version "
+                        << major_version_str;
+        }
 
-    return major_version;
+        return major_version;
+    }();
+    return vendor_android_version;
 }
 
 // This function initializes SELinux then execs init to run in the init SELinux context.
diff --git a/init/service.cpp b/init/service.cpp
index 47f4db9..380c93b 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -100,9 +100,11 @@
     expanded_args.resize(args.size());
     c_strings.push_back(const_cast<char*>(args[0].data()));
     for (std::size_t i = 1; i < args.size(); ++i) {
-        if (!expand_props(args[i], &expanded_args[i])) {
-            LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
+        auto expanded_arg = ExpandProps(args[i]);
+        if (!expanded_arg) {
+            LOG(FATAL) << args[0] << ": cannot expand arguments': " << expanded_arg.error();
         }
+        expanded_args[i] = *expanded_arg;
         c_strings.push_back(expanded_args[i].data());
     }
     c_strings.push_back(nullptr);
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 65d96c6..e45e804 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -193,9 +193,9 @@
 Result<void> ServiceParser::ParseKeycodes(std::vector<std::string>&& args) {
     auto it = args.begin() + 1;
     if (args.size() == 2 && StartsWith(args[1], "$")) {
-        std::string expanded;
-        if (!expand_props(args[1], &expanded)) {
-            return Error() << "Could not expand property '" << args[1] << "'";
+        auto expanded = ExpandProps(args[1]);
+        if (!expanded) {
+            return expanded.error();
         }
 
         // If the property is not set, it defaults to none, in which case there are no keycodes
@@ -204,7 +204,7 @@
             return {};
         }
 
-        args = Split(expanded, ",");
+        args = Split(*expanded, ",");
         it = args.begin();
     }
 
@@ -422,9 +422,11 @@
     FileDescriptor file;
     file.type = args[2];
 
-    if (!expand_props(args[1], &file.name)) {
-        return Error() << "Could not expand property in file path '" << args[1] << "'";
+    auto file_name = ExpandProps(args[1]);
+    if (!file_name) {
+        return Error() << "Could not expand file path ': " << file_name.error();
     }
+    file.name = *file_name;
     if (file.name[0] != '/' || file.name.find("../") != std::string::npos) {
         return Error() << "file name must not be relative";
     }
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index a13f0c7..00f91d8 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -151,15 +151,15 @@
 void SubcontextProcess::ExpandArgs(const SubcontextCommand::ExpandArgsCommand& expand_args_command,
                                    SubcontextReply* reply) const {
     for (const auto& arg : expand_args_command.args()) {
-        auto expanded_prop = std::string{};
-        if (!expand_props(arg, &expanded_prop)) {
+        auto expanded_arg = ExpandProps(arg);
+        if (!expanded_arg) {
             auto* failure = reply->mutable_failure();
-            failure->set_error_string("Failed to expand '" + arg + "'");
+            failure->set_error_string(expanded_arg.error().message());
             failure->set_error_errno(0);
             return;
         } else {
             auto* expand_args_reply = reply->mutable_expand_args_reply();
-            expand_args_reply->add_expanded_args(expanded_prop);
+            expand_args_reply->add_expanded_args(*expanded_arg);
         }
     }
 }
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index e120a62..ae89c38 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -166,7 +166,8 @@
         };
         auto result = subcontext.ExpandArgs(args);
         ASSERT_FALSE(result);
-        EXPECT_EQ("Failed to expand '" + args[1] + "'", result.error().message());
+        EXPECT_EQ("unexpected end of string in '" + args[1] + "', looking for }",
+                  result.error().message());
     });
 }
 
diff --git a/init/util.cpp b/init/util.cpp
index 8bfb755..169f086 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -41,8 +41,11 @@
 #include <selinux/android.h>
 
 #if defined(__ANDROID__)
+#include <android/api-level.h>
+
 #include "reboot_utils.h"
 #include "selabel.h"
+#include "selinux.h"
 #else
 #include "host_init_stubs.h"
 #endif
@@ -267,12 +270,10 @@
     return S_ISDIR(info.st_mode);
 }
 
-bool expand_props(const std::string& src, std::string* dst) {
+Result<std::string> ExpandProps(const std::string& src) {
     const char* src_ptr = src.c_str();
 
-    if (!dst) {
-        return false;
-    }
+    std::string dst;
 
     /* - variables can either be $x.y or ${x.y}, in case they are only part
      *   of the string.
@@ -286,19 +287,19 @@
 
         c = strchr(src_ptr, '$');
         if (!c) {
-            dst->append(src_ptr);
-            return true;
+            dst.append(src_ptr);
+            return dst;
         }
 
-        dst->append(src_ptr, c);
+        dst.append(src_ptr, c);
         c++;
 
         if (*c == '$') {
-            dst->push_back(*(c++));
+            dst.push_back(*(c++));
             src_ptr = c;
             continue;
         } else if (*c == '\0') {
-            return true;
+            return dst;
         }
 
         std::string prop_name;
@@ -308,8 +309,7 @@
             const char* end = strchr(c, '}');
             if (!end) {
                 // failed to find closing brace, abort.
-                LOG(ERROR) << "unexpected end of string in '" << src << "', looking for }";
-                return false;
+                return Error() << "unexpected end of string in '" << src << "', looking for }";
             }
             prop_name = std::string(c, end);
             c = end + 1;
@@ -320,29 +320,34 @@
             }
         } else {
             prop_name = c;
-            LOG(ERROR) << "using deprecated syntax for specifying property '" << c << "', use ${name} instead";
+            if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
+                return Error() << "using deprecated syntax for specifying property '" << c
+                               << "', use ${name} instead";
+            } else {
+                LOG(ERROR) << "using deprecated syntax for specifying property '" << c
+                           << "', use ${name} instead";
+            }
             c += prop_name.size();
         }
 
         if (prop_name.empty()) {
-            LOG(ERROR) << "invalid zero-length property name in '" << src << "'";
-            return false;
+            return Error() << "invalid zero-length property name in '" << src << "'";
         }
 
         std::string prop_val = android::base::GetProperty(prop_name, "");
         if (prop_val.empty()) {
             if (def_val.empty()) {
-                LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'";
-                return false;
+                return Error() << "property '" << prop_name << "' doesn't exist while expanding '"
+                               << src << "'";
             }
             prop_val = def_val;
         }
 
-        dst->append(prop_val);
+        dst.append(prop_val);
         src_ptr = c;
     }
 
-    return true;
+    return dst;
 }
 
 static std::string init_android_dt_dir() {
diff --git a/init/util.h b/init/util.h
index 6a12fb6..98de1f2 100644
--- a/init/util.h
+++ b/init/util.h
@@ -52,7 +52,7 @@
                            const std::function<void(const std::string&, const std::string&, bool)>&);
 bool make_dir(const std::string& path, mode_t mode);
 bool is_dir(const char* pathname);
-bool expand_props(const std::string& src, std::string* dst);
+Result<std::string> ExpandProps(const std::string& src);
 
 // Returns the platform's Android DT directory as specified in the kernel cmdline.
 // If the platform does not configure a custom DT path, returns the standard one (based in procfs).