Add support for serialized property contexts

This adds support for reading a serialized
/dev/__properties__/property_info file, which contains a
serialized trie that maps property names to the SELinux context to
which they belong.

Performance wise on walleye, this change reduces the start up cost in
libc from ~3000us to ~430us.  On a benchmark that calls
__system_property_find() for each property set on the system, it
reduces the time per iteration from ~650us to ~292us.

Bug: 36001741
Test: Boot bullhead, walleye, run unit tests
Test: Benchmark initialization and lookup performance

Change-Id: I0887a3a7da88eb51b6d1bd494fa5bce593423599
diff --git a/libc/system_properties/system_properties.cpp b/libc/system_properties/system_properties.cpp
index 89a6fe7..f67fc4d 100644
--- a/libc/system_properties/system_properties.cpp
+++ b/libc/system_properties/system_properties.cpp
@@ -59,6 +59,7 @@
 #include "context_node.h"
 #include "contexts.h"
 #include "contexts_pre_split.h"
+#include "contexts_serialized.h"
 #include "contexts_split.h"
 #include "prop_area.h"
 #include "prop_info.h"
@@ -71,6 +72,7 @@
 static union ContextsUnion {
   ContextsUnion() {}
   ~ContextsUnion() {}
+  ContextsSerialized contexts_serialized;
   ContextsSplit contexts_split;
   ContextsPreSplit contexts_pre_split;
 } contexts_union;
@@ -283,11 +285,19 @@
   }
   contexts = nullptr;
   if (is_dir(property_filename)) {
-    new (&contexts_union.contexts_split) ContextsSplit();
-    if (!contexts_union.contexts_split.Initialize(false)) {
-      return -1;
+    if (access("/dev/__properties__/property_info", R_OK) == 0) {
+      new (&contexts_union.contexts_serialized) ContextsSerialized();
+      if (!contexts_union.contexts_serialized.Initialize(false)) {
+        return -1;
+      }
+      contexts = &contexts_union.contexts_serialized;
+    } else {
+      new (&contexts_union.contexts_split) ContextsSplit();
+      if (!contexts_union.contexts_split.Initialize(false)) {
+        return -1;
+      }
+      contexts = &contexts_union.contexts_split;
     }
-    contexts = &contexts_union.contexts_split;
   } else {
     new (&contexts_union.contexts_pre_split) ContextsPreSplit();
     if (!contexts_union.contexts_pre_split.Initialize(false)) {
@@ -314,9 +324,9 @@
   }
   // We set this unconditionally as we want tests to continue on regardless of if this failed
   // and property_service will abort on an error condition, so no harm done.
-  new (&contexts_union.contexts_split) ContextsSplit;
-  contexts = &contexts_union.contexts_split;
-  if (!contexts_union.contexts_split.Initialize(true)) {
+  new (&contexts_union.contexts_serialized) ContextsSerialized;
+  contexts = &contexts_union.contexts_serialized;
+  if (!contexts_union.contexts_serialized.Initialize(true)) {
     return -1;
   }
   return 0;