Merge "Otapreopt: Load properties from files" into nyc-dev
diff --git a/cmds/installd/file_parsing.h b/cmds/installd/file_parsing.h
new file mode 100644
index 0000000..3e2f815
--- /dev/null
+++ b/cmds/installd/file_parsing.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef OTAPREOPT_FILE_PARSING_H_
+#define OTAPREOPT_FILE_PARSING_H_
+
+#include <fstream>
+#include <functional>
+#include <string>
+
+namespace android {
+namespace installd {
+
+bool ParseFile(const std::string& strFile, std::function<bool (const std::string&)> parse) {
+    std::ifstream input_stream(strFile);
+
+    if (!input_stream.is_open()) {
+        return false;
+    }
+
+    while (!input_stream.eof()) {
+        // Read the next line.
+        std::string line;
+        getline(input_stream, line);
+
+        // Is the line empty? Simplifies the next check.
+        if (line.empty()) {
+            continue;
+        }
+
+        // Is this a comment (starts with pound)?
+        if (line[0] == '#') {
+            continue;
+        }
+
+        if (!parse(line)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+}  // namespace installd
+}  // namespace android
+
+#endif  // OTAPREOPT_FILE_PARSING_H_
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index da454a7..89a4225 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -17,6 +17,7 @@
 #include <algorithm>
 #include <inttypes.h>
 #include <random>
+#include <regex>
 #include <selinux/android.h>
 #include <selinux/avc.h>
 #include <stdlib.h>
@@ -35,6 +36,7 @@
 #include <private/android_filesystem_config.h>
 
 #include <commands.h>
+#include <file_parsing.h>
 #include <globals.h>
 #include <installd_deps.h>  // Need to fill in requirements of commands.
 #include <string_helpers.h>
@@ -54,8 +56,8 @@
 namespace android {
 namespace installd {
 
-static constexpr const char* kBootClassPathPropertyName = "env.BOOTCLASSPATH";
-static constexpr const char* kAndroidRootPathPropertyName = "env.ANDROID_ROOT";
+static constexpr const char* kBootClassPathPropertyName = "BOOTCLASSPATH";
+static constexpr const char* kAndroidRootPathPropertyName = "ANDROID_ROOT";
 static constexpr const char* kOTARootDirectory = "/system-b";
 static constexpr size_t kISAIndex = 3;
 
@@ -131,41 +133,55 @@
 
 private:
     bool ReadSystemProperties() {
-        // TODO(agampe): What to do about the things in default.prop? It's only heap sizes, so it's easy
-        //               to emulate for now, but has issues (e.g., vendors modifying the boot classpath
-        //               may require larger values here - revisit). That's why this goes first, so that
-        //               if those dummy values are overridden in build.prop, that's what we'll get.
-        //
-        // Note: It seems we'll get access to the B root partition, so we should read the default.prop
-        //       file.
-        // if (!system_properties_.Load("/default.prop")) {
-        //   return false;
-        // }
-        system_properties_.SetProperty("dalvik.vm.image-dex2oat-Xms", "64m");
-        system_properties_.SetProperty("dalvik.vm.image-dex2oat-Xmx", "64m");
-        system_properties_.SetProperty("dalvik.vm.dex2oat-Xms", "64m");
-        system_properties_.SetProperty("dalvik.vm.dex2oat-Xmx", "512m");
+        static constexpr const char* kPropertyFiles[] = {
+                "/default.prop", "/system/build.prop"
+        };
 
-        // TODO(agampe): Do this properly/test.
-        return system_properties_.Load("/system/build.prop");
+        for (size_t i = 0; i < arraysize(kPropertyFiles); ++i) {
+            if (!system_properties_.Load(kPropertyFiles[i])) {
+                return false;
+            }
+        }
+
+        return true;
     }
 
     bool ReadEnvironment() {
-        // Read important environment variables. For simplicity, store them as
-        // system properties.
-        // TODO(agampe): We'll have to parse init.environ.rc for BOOTCLASSPATH.
-        //               For now, just the A version.
-        const char* boot_classpath = getenv("BOOTCLASSPATH");
-        if (boot_classpath == nullptr) {
-            return false;
-        }
-        system_properties_.SetProperty(kBootClassPathPropertyName, boot_classpath);
+        // Parse the environment variables from init.environ.rc, which have the form
+        //   export NAME VALUE
+        // For simplicity, don't respect string quotation. The values we are interested in can be
+        // encoded without them.
+        std::regex export_regex("\\s*export\\s+(\\S+)\\s+(\\S+)");
+        bool parse_result = ParseFile("/init.environ.rc", [&](const std::string& line) {
+            std::smatch export_match;
+            if (!std::regex_match(line, export_match, export_regex)) {
+                return true;
+            }
 
-        const char* root_path = getenv("ANDROID_ROOT");
-        if (root_path == nullptr) {
+            if (export_match.size() != 3) {
+                return true;
+            }
+
+            std::string name = export_match[1].str();
+            std::string value = export_match[2].str();
+
+            system_properties_.SetProperty(name, value);
+
+            return true;
+        });
+        if (!parse_result) {
             return false;
         }
-        system_properties_.SetProperty(kAndroidRootPathPropertyName, root_path);
+
+        // Check that we found important properties.
+        constexpr const char* kRequiredProperties[] = {
+                kBootClassPathPropertyName, kAndroidRootPathPropertyName
+        };
+        for (size_t i = 0; i < arraysize(kRequiredProperties); ++i) {
+            if (system_properties_.GetProperty(kRequiredProperties[i]) == nullptr) {
+                return false;
+            }
+        }
 
         return true;
     }
diff --git a/cmds/installd/system_properties.h b/cmds/installd/system_properties.h
index 1b5fb3a..2d940a3 100644
--- a/cmds/installd/system_properties.h
+++ b/cmds/installd/system_properties.h
@@ -21,6 +21,8 @@
 #include <string>
 #include <unordered_map>
 
+#include <file_parsing.h>
+
 namespace android {
 namespace installd {
 
@@ -28,31 +30,11 @@
 class SystemProperties {
  public:
     bool Load(const std::string& strFile) {
-        std::ifstream input_stream(strFile);
-
-        if (!input_stream.is_open()) {
-            return false;
-        }
-
-        while (!input_stream.eof()) {
-            // Read the next line.
-            std::string line;
-            getline(input_stream, line);
-
-            // Is the line empty? Simplifies the next check.
-            if (line.empty()) {
-                continue;
-            }
-
-            // Is this a comment (starts with pound)?
-            if (line[0] == '#') {
-                continue;
-            }
-
+        return ParseFile(strFile, [&](const std::string& line) {
             size_t equals_pos = line.find('=');
             if (equals_pos == std::string::npos || equals_pos == 0) {
                 // Did not find equals sign, or it's the first character - isn't a valid line.
-                continue;
+                return true;
             }
 
             std::string key = line.substr(0, equals_pos);
@@ -60,9 +42,9 @@
                                             line.length() - equals_pos + 1);
 
             properties_.insert(std::make_pair(key, value));
-        }
 
-        return true;
+            return true;
+        });
     }
 
     // Look up the key in the map. Returns null if the key isn't mapped.