Bug 1214812 - [mozdevice] - devicemanagerADB.py - use su 0 if it is available; check for root updates, r=gbrown.
authorBob Clary <bclary@bclary.com>
Wed, 02 Dec 2015 08:34:19 -0800
changeset 309430 b234d1db6f34b77ac825f014d1755d416be1bf99
parent 309429 239195d791ceab09f2a28b76e94c6e438665ab78
child 309431 80b477107b228d7f954840af5aa20317ae8b1ea4
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgbrown
bugs1214812
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1214812 - [mozdevice] - devicemanagerADB.py - use su 0 if it is available; check for root updates, r=gbrown.
testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
--- a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
+++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
@@ -18,18 +18,19 @@ import mozfile
 class DeviceManagerADB(DeviceManager):
     """
     Implementation of DeviceManager interface that uses the Android "adb"
     utility to communicate with the device. Normally used to communicate
     with a device that is directly connected with the host machine over a USB
     port.
     """
 
-    _haveRootShell = False
-    _haveSu = False
+    _haveRootShell = None
+    _haveSu = None
+    _suModifier = None
     _useZip = False
     _logcatNeedsRoot = False
     _pollingInterval = 0.01
     _packageName = None
     _tempDir = None
     connected = False
 
     def __init__(self, host=None, port=5555, retryLimit=5, packageName='fennec',
@@ -96,27 +97,32 @@ class DeviceManagerADB(DeviceManager):
         if self.host:
             self._disconnectRemoteADB()
 
     def shell(self, cmd, outputfile, env=None, cwd=None, timeout=None, root=False):
         # FIXME: this function buffers all output of the command into memory,
         # always. :(
 
         # If requested to run as root, check that we can actually do that
-        if root and not self._haveRootShell and not self._haveSu:
-            raise DMError("Shell command '%s' requested to run as root but root "
-                          "is not available on this device. Root your device or "
-                          "refactor the test/harness to not require root." %
-                          self._escapedCommandLine(cmd))
+        if root:
+            if self._haveRootShell is None and self._haveSu is None:
+                self._checkForRoot()
+            if not self._haveRootShell and not self._haveSu:
+                raise DMError(
+                    "Shell command '%s' requested to run as root but root "
+                    "is not available on this device. Root your device or "
+                    "refactor the test/harness to not require root." %
+                    self._escapedCommandLine(cmd))
 
         # Getting the return code is more complex than you'd think because adb
         # doesn't actually return the return code from a process, so we have to
         # capture the output to get it
-        if root and not self._haveRootShell:
-            cmdline = "su -c \"%s\"" % self._escapedCommandLine(cmd)
+        if root and self._haveSu:
+            cmdline = "su %s \"%s\"" % (self._suModifier,
+                                        self._escapedCommandLine(cmd))
         else:
             cmdline = self._escapedCommandLine(cmd)
         cmdline += "; echo $?"
 
         # prepend cwd and env to command if necessary
         if cwd:
             cmdline = "cd %s; %s" % (cwd, cmdline)
         if env:
@@ -657,43 +663,58 @@ class DeviceManagerADB(DeviceManager):
             elif deviceStatus != "device":
                 raise DMError("bad status for device %s: %s" % (self._deviceSerial, deviceStatus))
 
         # Check to see if we can connect to device and run a simple command
         if not self._checkCmd(["shell", "echo"], timeout=self.short_timeout) == 0:
             raise DMError("unable to connect to device")
 
     def _checkForRoot(self):
+        self._haveRootShell = False
+        self._haveSu = False
+        # If requested to attempt to run adbd as root, do so before
+        # checking whether adbs is running as root.
+        if self._runAdbAsRoot:
+            self._adb_root()
+
         # Check whether we _are_ root by default (some development boards work
         # this way, this is also the result of some relatively rare rooting
         # techniques)
         proc = self._runCmd(["shell", "id"], timeout=self.short_timeout)
         if proc.output and 'uid=0(root)' in proc.output[0]:
             self._haveRootShell = True
             # if this returns true, we don't care about su
             return
 
         # if root shell is not available, check if 'su' can be used to gain
         # root
-        proc = self._runCmd(["shell", "su", "-c", "id"], timeout=self.short_timeout)
+        def su_id(su_modifier, timeout):
+            proc = self._runCmd(["shell", "su", su_modifier, "id"],
+                                timeout=timeout)
 
-        # wait for response for maximum of 15 seconds, in case su prompts for a
-        # password or triggers the Android SuperUser prompt
-        start_time = time.time()
-        retcode = None
-        while (time.time() - start_time) <= 15 and retcode is None:
-            retcode = proc.poll()
-        if retcode is None: # still not terminated, kill
-            proc.kill()
+            # wait for response for maximum of 15 seconds, in case su
+            # prompts for a password or triggers the Android SuperUser
+            # prompt
+            start_time = time.time()
+            retcode = None
+            while (time.time() - start_time) <= 15 and retcode is None:
+                retcode = proc.poll()
+            if retcode is None: # still not terminated, kill
+                proc.kill()
 
-        if proc.output and 'uid=0(root)' in proc.output[0]:
-            self._haveSu = True
+            if proc.output and 'uid=0(root)' in proc.output[0]:
+                return True
+            return False
 
-        if self._runAdbAsRoot:
-            self._adb_root()
+        if su_id('0', self.short_timeout):
+            self._haveSu = True
+            self._suModifier = '0'
+        elif su_id('-c', self.short_timeout):
+            self._haveSu = True
+            self._suModifier = '-c'
 
     def _isUnzipAvailable(self):
         data = self._runCmd(["shell", "unzip"]).output
         for line in data:
             if (re.search('Usage', line)):
                 return True
         return False