[adb] Add a util to retrieve the current user ID. (#645)

diff --git a/mobly/controllers/android_device_lib/adb.py b/mobly/controllers/android_device_lib/adb.py
index ee82952..24af1f8 100644
--- a/mobly/controllers/android_device_lib/adb.py
+++ b/mobly/controllers/android_device_lib/adb.py
@@ -17,6 +17,7 @@
 
 import logging
 import psutil
+import re
 import subprocess
 import time
 import threading
@@ -55,6 +56,7 @@
             This is an empty string if the adb command is not specific to a
             device.
     """
+
     def __init__(self, cmd, stdout, stderr, ret_code, serial=''):
         self.cmd = cmd
         self.stdout = stdout
@@ -78,6 +80,7 @@
             This is an empty string if the adb command is not specific to a
             device.
     """
+
     def __init__(self, cmd, timeout, serial=''):
         self.cmd = cmd
         self.timeout = timeout
@@ -132,6 +135,7 @@
     possible by supplying shell=True, but try to avoid this if possible:
     >> adb.shell('cat /foo > /tmp/file', shell=True)
     """
+
     def __init__(self, serial=''):
         self.serial = serial
 
@@ -306,6 +310,25 @@
             results[name] = value
         return results
 
+    @property
+    def current_user_id(self):
+        """Gets the ID of the current Android user.
+
+        Some adb commands require the current user ID to work properly.
+        This convenient method is meant for 
+
+        Returns:
+            int, the ID of the current user.
+        """
+        sdk_int = int(self.getprop('ro.build.version.sdk'))
+        if sdk_int >= 24:
+            return int(self.shell(['am', 'get-current-user']))
+        if sdk_int >= 21:
+            user_info_str = self.shell(['dumpsys', 'user']).decode('utf-8')
+            return int(re.findall(r'\{(\d+):', user_info_str)[0])
+        # Multi-user is not supported in SDK < 21, only user 0 exists.
+        return 0
+
     def getprop(self, prop_name):
         """Get a property of the device.
 
diff --git a/tests/mobly/controllers/android_device_lib/adb_test.py b/tests/mobly/controllers/android_device_lib/adb_test.py
index 9bec31d..e4180cf 100755
--- a/tests/mobly/controllers/android_device_lib/adb_test.py
+++ b/tests/mobly/controllers/android_device_lib/adb_test.py
@@ -52,6 +52,7 @@
 class AdbTest(unittest.TestCase):
     """Unit tests for mobly.controllers.android_device_lib.adb.
     """
+
     def _mock_process(self, mock_psutil_process, mock_popen):
         # the created proc object in adb._exec_cmd()
         mock_proc = mock.Mock()
@@ -654,6 +655,7 @@
         """Verifies the AndroidDevice object's instrument command is correct
         with a handler passed in.
         """
+
         def mock_handler(raw_line):
             pass
 
@@ -672,6 +674,7 @@
         """Verifies the AndroidDevice object's instrument command is correct
         with a handler passed in and a runner specified.
         """
+
         def mock_handler(raw_line):
             pass
 
@@ -692,6 +695,7 @@
         """Verifies the AndroidDevice object's instrument command is correct
         with a handler passed in and options.
         """
+
         def mock_handler(raw_line):
             pass
 
@@ -740,6 +744,39 @@
             self.assertFalse(
                 adb.AdbProxy().has_shell_command(MOCK_SHELL_COMMAND))
 
+    @mock.patch.object(adb.AdbProxy, 'getprop')
+    @mock.patch.object(adb.AdbProxy, '_exec_cmd')
+    def test_current_user_id_25_and_above(self, mock_exec_cmd, mock_getprop):
+        mock_getprop.return_value = b'25'
+        mock_exec_cmd.return_value = b'123'
+        user_id = adb.AdbProxy().current_user_id
+        mock_exec_cmd.assert_called_once_with(
+            ['adb', 'shell', 'am', 'get-current-user'],
+            shell=False,
+            stderr=None,
+            timeout=None)
+        self.assertEqual(user_id, 123)
+
+    @mock.patch.object(adb.AdbProxy, 'getprop')
+    @mock.patch.object(adb.AdbProxy, '_exec_cmd')
+    def test_current_user_id_between_21_and_24(self, mock_exec_cmd,
+                                               mock_getprop):
+        mock_getprop.return_value = b'23'
+        mock_exec_cmd.return_value = (b'Users:\n'
+                                      b'UserInfo{123:Owner:13} serialNo=0\n'
+                                      b'Created: <unknown>\n'
+                                      b'Last logged in: +1h22m12s497ms ago\n'
+                                      b'UserInfo{456:Owner:14} serialNo=0\n'
+                                      b'Created: <unknown>\n'
+                                      b'Last logged in: +1h01m12s497ms ago\n')
+        user_id = adb.AdbProxy().current_user_id
+        mock_exec_cmd.assert_called_once_with(
+            ['adb', 'shell', 'dumpsys', 'user'],
+            shell=False,
+            stderr=None,
+            timeout=None)
+        self.assertEqual(user_id, 123)
+
 
 if __name__ == '__main__':
     unittest.main()