init: allow properties to be used for the keycodes option

Bug: 117828597
Test: bugreport launches with a test property set to appropriate keys
Test: bugreport doesn't launch with the test property unset
Test: no errors seen in build or boot in either of the above cases
Change-Id: Iea27032080a0a7863932b1c1b573857ac66b56b5
diff --git a/init/README.md b/init/README.md
index f938ccc..6c51b37 100644
--- a/init/README.md
+++ b/init/README.md
@@ -226,6 +226,15 @@
   keycodes are pressed at once, the service will start. This is typically used to start the
   bugreport service.
 
+> This option may take a property instead of a list of keycodes. In this case, only one option is
+  provided: the property name in the typical property expansion format. The property must contain
+  a comma separated list of keycode values or the text 'none' to indicate that
+  this service does not respond to keycodes.
+
+> For example, `keycodes ${some.property.name:-none}` where some.property.name expands
+  to "123,124,125". Since keycodes are handled very early in init,
+  only PRODUCT_DEFAULT_PROPERTY_OVERRIDES properties can be used.
+
 `memcg.limit_in_bytes <value>`
 > Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
   which must be equal or greater than 0.
diff --git a/init/service.cpp b/init/service.cpp
index b7de45d..7f49423 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -60,6 +60,7 @@
 using android::base::GetProperty;
 using android::base::Join;
 using android::base::ParseInt;
+using android::base::Split;
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::unique_fd;
@@ -543,15 +544,32 @@
 }
 
 Result<Success> Service::ParseKeycodes(std::vector<std::string>&& args) {
-    for (std::size_t i = 1; i < args.size(); i++) {
+    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] << "'";
+        }
+
+        // If the property is not set, it defaults to none, in which case there are no keycodes
+        // for this service.
+        if (expanded == "none") {
+            return Success();
+        }
+
+        args = Split(expanded, ",");
+        it = args.begin();
+    }
+
+    for (; it != args.end(); ++it) {
         int code;
-        if (ParseInt(args[i], &code, 0, KEY_MAX)) {
+        if (ParseInt(*it, &code, 0, KEY_MAX)) {
             for (auto& key : keycodes_) {
-                if (key == code) return Error() << "duplicate keycode: " << args[i];
+                if (key == code) return Error() << "duplicate keycode: " << *it;
             }
             keycodes_.insert(std::upper_bound(keycodes_.begin(), keycodes_.end(), code), code);
         } else {
-            return Error() << "invalid keycode: " << args[i];
+            return Error() << "invalid keycode: " << *it;
         }
     }
     return Success();