client: Add udevadm helpers module

Add a couple of wrapper classes around udevadm info and trigger.

BUG=b:157577850
TEST=Ran bluetooth_AdapterSRSanity a few times

Change-Id: I8aa5395a1df98e3405e0e455e7be84ff66091278
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/2262281
Tested-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
Reviewed-by: Daniel Winkler <danielwinkler@google.com>
Reviewed-by: Shijin Abraham <shijinabraham@google.com>
Commit-Queue: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
diff --git a/client/cros/udev_helpers.py b/client/cros/udev_helpers.py
new file mode 100644
index 0000000..5da9d51
--- /dev/null
+++ b/client/cros/udev_helpers.py
@@ -0,0 +1,92 @@
+# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+# Lint as: python3
+""" Udevadm helper classes and functions.
+"""
+
+import subprocess
+
+class UdevadmInfo():
+    """ Use udevadm info on a specific path.
+    """
+
+    @classmethod
+    def GetProperties(cls, syspath):
+        """ Get all properties of given syspath as a dict.
+
+        Args:
+            syspath: System path to get properties for.
+
+        Returns:
+            Dict with attribute/property as key and it's value. All keys are
+            converted to lowercase. Example: {'subsystem': 'input'}
+        """
+        props = {}
+        rawprops = subprocess.check_output(' '.join(
+                ['udevadm', 'info', '-q', 'property', '-p', syspath]),
+                                           shell=True)
+
+        for line in rawprops.splitlines():
+            upper_key, value = line.split('=', 1)
+            props[upper_key.lower()] = value.strip('"')
+
+        return props
+
+class UdevadmTrigger():
+    """ Use udevadm trigger with specific rules.
+    """
+
+    def __init__(self,
+                 verbose=True,
+                 event_type=None,
+                 attr_match=[],
+                 attr_nomatch=[],
+                 subsystem_match=[],
+                 subsystem_nomatch=[]):
+        """ Constructor
+
+        Args:
+            verbose: Whether to output triggered syspaths
+            event_type: What type of events to trigger (device or subsystem)
+            attr_match: What attributes to match
+            attr_nomatch: What attributes not to match
+            subsystem_match: What subsystems to match
+            subsystem_nomatch: What subsystems not to match
+        """
+        cmd = ['udevadm', 'trigger']
+
+        if verbose:
+            cmd.append('-v')
+
+        if event_type:
+            cmd.append('-t')
+            cmd.append('"{}"'.format(event_type))
+
+        for attr in attr_match:
+            cmd.append('-a')
+            cmd.append('"{}"'.format(attr))
+
+        for attr in attr_nomatch:
+            cmd.append('-A')
+            cmd.append('"{}"'.format(attr))
+
+        for subsystem in subsystem_match:
+            cmd.append('-s')
+            cmd.append('"{}"'.format(subsystem))
+
+        for subsystem in subsystem_nomatch:
+            cmd.append('-S')
+            cmd.append('"{}"'.format(subsystem))
+
+        self.cmd = cmd
+
+    def DryRun(self):
+        """ Do a dry run using initialized trigger rules.
+
+        Returns:
+            List of syspaths that would be triggered.
+        """
+        cmd = self.cmd + ['-n']
+        lines = subprocess.check_output(' '.join(cmd), shell=True)
+        return lines.splitlines() if lines else []