[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()