Distinguish unset system properties from errors reading a property.

Also expose error enum rather than wrapping it in anyhow, so users of
the API can match on the error variants and handle them as they wish.

Bug: 217728265
Test: adb shell /apex/com.android.virt/bin/vm info
Change-Id: Icff27ab12b52e7eff74b522f3841f50f86e982fc
diff --git a/system_properties.rs b/system_properties.rs
index 294fef8..4a20124 100644
--- a/system_properties.rs
+++ b/system_properties.rs
@@ -15,7 +15,7 @@
 //! This crate provides the PropertyWatcher type, which watches for changes
 //! in Android system properties.
 
-use anyhow::{anyhow, Context, Result as AnyhowResult};
+use anyhow::Context;
 use system_properties_bindgen::prop_info as PropInfo;
 use std::os::raw::c_char;
 use std::ptr::null;
@@ -195,11 +195,16 @@
 }
 
 /// Reads a system property.
-pub fn read(name: &str) -> AnyhowResult<String> {
-    PropertyWatcher::new(name)
-        .context("Failed to create a PropertyWatcher.")?
+///
+/// Returns `Ok(None)` if the property doesn't exist.
+pub fn read(name: &str) -> Result<Option<String>> {
+    match PropertyWatcher::new(name)?
         .read(|_name, value| Ok(value.to_owned()))
-        .with_context(|| format!("Failed to read the system property {}.", name))
+    {
+        Ok(value) => Ok(Some(value)),
+        Err(PropertyWatcherError::SystemPropertyAbsent) => Ok(None),
+        Err(e) => Err(e),
+    }
 }
 
 fn parse_bool(value: &str) -> Option<bool> {
@@ -214,12 +219,15 @@
 
 /// Returns true if the system property `name` has the value "1", "y", "yes", "on", or "true",
 /// false for "0", "n", "no", "off", or "false", or `default_value` otherwise.
-pub fn read_bool(name: &str, default_value: bool) -> AnyhowResult<bool> {
-    Ok(parse_bool(read(name)?.as_str()).unwrap_or(default_value))
+pub fn read_bool(name: &str, default_value: bool) -> Result<bool> {
+    Ok(read(name)?
+        .as_deref()
+        .and_then(parse_bool)
+        .unwrap_or(default_value))
 }
 
 /// Writes a system property.
-pub fn write(name: &str, value: &str) -> AnyhowResult<()> {
+pub fn write(name: &str, value: &str) -> Result<()> {
     if
     // Unsafe required for FFI call. Input and output are both const and valid strings.
     unsafe {
@@ -236,7 +244,7 @@
     {
         Ok(())
     } else {
-        Err(anyhow!(PropertyWatcherError::SetPropertyFailed))
+        Err(PropertyWatcherError::SetPropertyFailed)
     }
 }
 
@@ -256,4 +264,12 @@
              assert_eq!(parse_bool(s), None, "testing with {}", s);
          }
      }
+
+    #[test]
+    fn read_absent_bool_test() {
+        let prop = "certainly.does.not.exist";
+        assert!(matches!(read(prop), Ok(None)));
+        assert!(read_bool(prop, true).unwrap_or(false));
+        assert!(!read_bool(prop, false).unwrap_or(true));
+    }
 }