power: Add activity watcher for body detection

Add the activity sensor detection in UserProximityWatcher.
If there is an available activity sensor, start to listen on it just
like SAR sensor.

Also add following prefs entries for handling activity proximity event:
kSetCellularTransmitPowerForActivityProximityPref
kSetWifiTransmitPowerForActivityProximityPref

Add unittest for testing detection of activity sensor.

BUG=b:123434029
TEST=FEATURES="test" emerge-trogdor power_manager
TEST=On lazor, while the activtity sensor presents, watch the powerd log.
     If the set_wifi_transmit_power_for_activity_proximity is set,
     log shows "[INFO:user_proximity_handler.cc(53)] New proximity sensor with id 17:  wifi".

Cq-Depend: chromium:2589496
Change-Id: Iba0560f80bea4dc9e54f9860731cbe7b841584b9
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2359931
Commit-Queue: Ching-Kang Yen <chingkang@chromium.org>
Tested-by: Ching-Kang Yen <chingkang@chromium.org>
Reviewed-by: Todd Broch <tbroch@chromium.org>
Reviewed-by: Mengqi Guo <mqg@chromium.org>
Reviewed-by: Puthikorn Voravootivat <puthik@chromium.org>
Reviewed-by: Gwendal Grignou <gwendal@chromium.org>
diff --git a/power_manager/common/power_constants.cc b/power_manager/common/power_constants.cc
index 5a9ef2c..d946694 100644
--- a/power_manager/common/power_constants.cc
+++ b/power_manager/common/power_constants.cc
@@ -87,10 +87,14 @@
     "set_wifi_transmit_power_for_tablet_mode";
 const char kSetWifiTransmitPowerForProximityPref[] =
     "set_wifi_transmit_power_for_proximity";
+const char kSetWifiTransmitPowerForActivityProximityPref[] =
+    "set_wifi_transmit_power_for_activity_proximity";
 const char kSetCellularTransmitPowerForTabletModePref[] =
     "set_cellular_transmit_power_for_tablet_mode";
 const char kSetCellularTransmitPowerForProximityPref[] =
     "set_cellular_transmit_power_for_proximity";
+const char kSetCellularTransmitPowerForActivityProximityPref[] =
+    "set_cellular_transmit_power_for_activity_proximity";
 const char kSetCellularTransmitPowerDprGpioPref[] =
     "set_cellular_transmit_power_dpr_gpio";
 const char kEnableConsoleDuringSuspendPref[] = "enable_console_during_suspend";
diff --git a/power_manager/common/power_constants.h b/power_manager/common/power_constants.h
index 9b6ecab..17704d6 100644
--- a/power_manager/common/power_constants.h
+++ b/power_manager/common/power_constants.h
@@ -225,15 +225,21 @@
 // If true, update wifi transmit power when in tablet vs. clamshell mode.
 extern const char kSetWifiTransmitPowerForTabletModePref[];
 
-// If true, update wifi transmit power based on proximity sensors.
+// If true, update wifi transmit power based on proximity SAR sensors.
 extern const char kSetWifiTransmitPowerForProximityPref[];
 
+// If true, update wifi transmit power based on proximity activity sensors.
+extern const char kSetWifiTransmitPowerForActivityProximityPref[];
+
 // If true, update cellular transmit power when in tablet vs. clamshell mode.
 extern const char kSetCellularTransmitPowerForTabletModePref[];
 
-// If true, update cellular transmit power based on proximity sensors.
+// If true, update cellular transmit power based on proximity SAR sensors.
 extern const char kSetCellularTransmitPowerForProximityPref[];
 
+// If true, update cellular transmit power based on proximity activity sensors.
+extern const char kSetCellularTransmitPowerForActivityProximityPref[];
+
 // GPIO number for the dynamic power reduction signal of a built-in cellular
 // modem.
 extern const char kSetCellularTransmitPowerDprGpioPref[];
diff --git a/power_manager/powerd/system/user_proximity_watcher.cc b/power_manager/powerd/system/user_proximity_watcher.cc
index 0075e20..a2f4868 100644
--- a/power_manager/powerd/system/user_proximity_watcher.cc
+++ b/power_manager/powerd/system/user_proximity_watcher.cc
@@ -79,6 +79,11 @@
   prefs->GetBool(kSetWifiTransmitPowerForProximityPref,
                  &use_proximity_for_wifi_);
 
+  prefs->GetBool(kSetCellularTransmitPowerForActivityProximityPref,
+                 &use_activity_proximity_for_cellular_);
+  prefs->GetBool(kSetWifiTransmitPowerForActivityProximityPref,
+                 &use_activity_proximity_for_wifi_);
+
   udev_ = udev;
   udev_->AddSubsystemObserver(kIioUdevSubsystem, this);
 
@@ -167,22 +172,46 @@
   return false;
 }
 
+bool UserProximityWatcher::IsIioActivitySensor(const UdevDeviceInfo& dev,
+                                               std::string* devlink_out) {
+  if (dev.subsystem != kIioUdevSubsystem || dev.devtype != kIioUdevDevice)
+    return false;
+  if (dev.syspath.find("-activity") == std::string::npos)
+    return false;
+
+  *devlink_out = "/dev/" + dev.sysname;
+  return true;
+}
+
 uint32_t UserProximityWatcher::GetUsableSensorRoles(const SensorType type,
                                                     const std::string& path) {
   uint32_t responsibility = UserProximityObserver::SensorRole::SENSOR_ROLE_NONE;
 
-  if (type == SensorType::SAR) {
-    const auto proximity_index = path.find("proximity-");
-    if (proximity_index == std::string::npos)
-      return responsibility;
+  switch (type) {
+    case SensorType::ACTIVITY: {
+      if (use_activity_proximity_for_cellular_)
+        responsibility |= UserProximityObserver::SensorRole::SENSOR_ROLE_LTE;
+      if (use_activity_proximity_for_wifi_)
+        responsibility |= UserProximityObserver::SensorRole::SENSOR_ROLE_WIFI;
+      break;
+    }
+    case SensorType::SAR: {
+      const auto proximity_index = path.find("proximity-");
+      if (proximity_index == std::string::npos)
+        return responsibility;
 
-    if (use_proximity_for_cellular_ &&
-        path.find("-lte", proximity_index) != std::string::npos)
-      responsibility |= UserProximityObserver::SensorRole::SENSOR_ROLE_LTE;
+      if (use_proximity_for_cellular_ &&
+          path.find("-lte", proximity_index) != std::string::npos)
+        responsibility |= UserProximityObserver::SensorRole::SENSOR_ROLE_LTE;
 
-    if (use_proximity_for_wifi_ &&
-        path.find("-wifi", proximity_index) != std::string::npos)
-      responsibility |= UserProximityObserver::SensorRole::SENSOR_ROLE_WIFI;
+      if (use_proximity_for_wifi_ &&
+          path.find("-wifi", proximity_index) != std::string::npos)
+        responsibility |= UserProximityObserver::SensorRole::SENSOR_ROLE_WIFI;
+      break;
+    }
+    default: {
+      LOG(WARNING) << "Unknown type of proximity sensor at " << path;
+    }
   }
 
   return responsibility;
@@ -316,6 +345,16 @@
   return true;
 }
 
+bool UserProximityWatcher::ConfigureActivitySensor(const std::string& syspath,
+                                                   uint32_t role) {
+  std::string enable_path = "events/in_proximity_change_either_en";
+  if (!udev_->SetSysattr(syspath, enable_path, "1")) {
+    LOG(ERROR) << "Could not enable proximity sensor";
+    return false;
+  }
+  return true;
+}
+
 bool UserProximityWatcher::OnSensorDetected(const SensorType type,
                                             const std::string& syspath,
                                             const std::string& devlink) {
@@ -327,9 +366,22 @@
     return true;
   }
 
-  if (type == SensorType::SAR && !ConfigureSarSensor(syspath, role)) {
-    LOG(WARNING) << "Unable to configure sar sensor at " << devlink;
-    return false;
+  switch (type) {
+    case SensorType::SAR:
+      if (!ConfigureSarSensor(syspath, role)) {
+        LOG(WARNING) << "Unable to configure sar sensor at " << devlink;
+        return false;
+      }
+      break;
+    case SensorType::ACTIVITY:
+      if (!ConfigureActivitySensor(syspath, role)) {
+        LOG(WARNING) << "Unable to configure activity sensor at " << devlink;
+        return false;
+      }
+      break;
+    default:
+      LOG(WARNING) << "Unknown type of proximity sensor at " << devlink;
+      return false;
   }
 
   int event_fd = open_iio_events_func_.Run(base::FilePath(devlink));
@@ -362,6 +414,8 @@
   SensorType type = SensorType::UNKNOWN;
   if (IsIioSarSensor(device_info, &devlink))
     type = SensorType::SAR;
+  else if (IsIioActivitySensor(device_info, &devlink))
+    type = SensorType::ACTIVITY;
   else
     return;
 
diff --git a/power_manager/powerd/system/user_proximity_watcher.h b/power_manager/powerd/system/user_proximity_watcher.h
index d6cfd93..b45690c 100644
--- a/power_manager/powerd/system/user_proximity_watcher.h
+++ b/power_manager/powerd/system/user_proximity_watcher.h
@@ -37,7 +37,7 @@
                              public UdevSubsystemObserver {
  public:
   // Sensor type for proximity detection.
-  enum class SensorType { UNKNOWN, SAR };
+  enum class SensorType { UNKNOWN, SAR, ACTIVITY };
 
   // udev subsystem to watch.
   static const char kIioUdevSubsystem[];
@@ -90,6 +90,7 @@
   // the IIO subsystem. If so, |devlink_out| is the path to the file to be used
   // to read proximity events from this device.
   bool IsIioSarSensor(const UdevDeviceInfo& dev, std::string* devlink_out);
+  bool IsIioActivitySensor(const UdevDeviceInfo& dev, std::string* devlink_out);
 
   // Sets proximity IIO attributes for rising, falling, or either direction
   bool SetIioRisingFallingValue(const std::string& syspath,
@@ -99,10 +100,12 @@
                                 const std::string& path_prefix,
                                 const std::string& postfix);
 
-  // Configures the proximity sensor for usage based on values read from
-  // cros_config
+  // Configures the SAR sensor for usage based on values read from cros_config
   bool ConfigureSarSensor(const std::string& syspath, uint32_t role);
 
+  // Configures the activity sensor to enable it.
+  bool ConfigureActivitySensor(const std::string& syspath, uint32_t role);
+
   // Opens a file descriptor suitable for listening to proximity events for
   // the sensor at |devlink|, and notifies registered observers that a new
   // valid proximity sensor exists.
@@ -124,6 +127,8 @@
 
   bool use_proximity_for_cellular_ = false;
   bool use_proximity_for_wifi_ = false;
+  bool use_activity_proximity_for_cellular_ = false;
+  bool use_activity_proximity_for_wifi_ = false;
 };
 
 }  // namespace system
diff --git a/power_manager/powerd/system/user_proximity_watcher_test.cc b/power_manager/powerd/system/user_proximity_watcher_test.cc
index 15ec43c..1e88b24 100644
--- a/power_manager/powerd/system/user_proximity_watcher_test.cc
+++ b/power_manager/powerd/system/user_proximity_watcher_test.cc
@@ -70,13 +70,26 @@
   }
 
   void Init(UserProximityWatcher::SensorType type, uint32_t roles) {
-    if (type == UserProximityWatcher::SensorType::SAR) {
-      prefs_.SetInt64(
-          kSetCellularTransmitPowerForProximityPref,
-          roles & UserProximityObserver::SensorRole::SENSOR_ROLE_LTE);
-      prefs_.SetInt64(
-          kSetWifiTransmitPowerForProximityPref,
-          roles & UserProximityObserver::SensorRole::SENSOR_ROLE_WIFI);
+    switch (type) {
+      case UserProximityWatcher::SensorType::SAR:
+        prefs_.SetInt64(
+            kSetCellularTransmitPowerForProximityPref,
+            roles & UserProximityObserver::SensorRole::SENSOR_ROLE_LTE);
+        prefs_.SetInt64(
+            kSetWifiTransmitPowerForProximityPref,
+            roles & UserProximityObserver::SensorRole::SENSOR_ROLE_WIFI);
+        break;
+      case UserProximityWatcher::SensorType::ACTIVITY:
+        prefs_.SetInt64(
+            kSetCellularTransmitPowerForActivityProximityPref,
+            roles & UserProximityObserver::SensorRole::SENSOR_ROLE_LTE);
+        prefs_.SetInt64(
+            kSetWifiTransmitPowerForActivityProximityPref,
+            roles & UserProximityObserver::SensorRole::SENSOR_ROLE_WIFI);
+        break;
+      default:
+        ADD_FAILURE() << "Unknown sensor type";
+        return;
     }
     CHECK(user_proximity_watcher_->Init(&prefs_, &udev_));
     observer_.reset(
@@ -217,6 +230,36 @@
   EXPECT_EQ(0, GetNumOpenedSensors());
 }
 
+TEST_F(UserProximityWatcherTest, DetectUsableActivityDevice) {
+  Init(UserProximityWatcher::SensorType::ACTIVITY,
+       UserProximityObserver::SensorRole::SENSOR_ROLE_WIFI);
+
+  AddDevice("/sys/cros-ec-activity.6.auto/MOCKSENSOR", "/dev/MOCKSENSOR");
+  EXPECT_EQ(JoinActions("OnNewSensor(roles=0x1)", nullptr),
+            observer_->GetActions());
+  EXPECT_EQ(1, GetNumOpenedSensors());
+}
+
+TEST_F(UserProximityWatcherTest, DetectNotUsableActivityDevice) {
+  Init(UserProximityWatcher::SensorType::ACTIVITY,
+       UserProximityObserver::SensorRole::SENSOR_ROLE_NONE);
+
+  AddDevice("/sys/cros-ec-activity.6.auto/MOCKSENSOR", "/dev/MOCKSENSOR");
+  EXPECT_EQ(JoinActions(nullptr), observer_->GetActions());
+  EXPECT_EQ(0, GetNumOpenedSensors());
+}
+
+TEST_F(UserProximityWatcherTest, ReceiveActivityProximityInfo) {
+  Init(UserProximityWatcher::SensorType::ACTIVITY,
+       UserProximityObserver::SensorRole::SENSOR_ROLE_LTE);
+
+  AddDevice("/sys/cros-ec-activity.6.auto/MOCKSENSOR", "/dev/MOCKSENSOR");
+  observer_->GetActions();  // consume OnNewSensor
+  SendEvent("/dev/MOCKSENSOR", UserProximity::NEAR);
+  EXPECT_EQ(JoinActions("OnProximityEvent(value=near)", nullptr),
+            observer_->GetActions());
+}
+
 }  // namespace
 
 }  // namespace system
diff --git a/power_manager/udev/99-powerd-permissions.rules b/power_manager/udev/99-powerd-permissions.rules
index f6a41fd..4c6af65 100644
--- a/power_manager/udev/99-powerd-permissions.rules
+++ b/power_manager/udev/99-powerd-permissions.rules
@@ -7,6 +7,10 @@
 # Make pluggable keyboard backlights writable by powerd.
 ACTION!="remove", SUBSYSTEM=="leds", DEVPATH=="*:kbd_backlight", RUN+="/lib/udev/chown-sysfs-backlight-dir.sh $sys/$devpath"
 
+# Allow powerd to read activity sensor
+ACTION!="remove", ATTR{name}=="cros-ec-activity", MODE="440", GROUP="power"
+ACTION!="remove", ATTR{name}=="cros-ec-activity", RUN+="/usr/bin/find /sys/bus/iio/devices/$kernel/events -name in_*_en -execdir /bin/chmod 660 {} ; -execdir /bin/chgrp power {} ;"
+
 # Older kernels (<v4.19) put cros_fp under the chromeos class
 # The cros_ec-access group is also required by cros_healthd (the associated user
 # that is part of the cros_ec-access group is "healthd_ec").