Bug 1334613 - [mozdevice] - Use root to kill processes on Android 7+. r=gbrown, a=test-only
authorBob Clary <bclary@bclary.com>
Mon, 30 Jan 2017 16:09:14 -0800
changeset 375766 2678b49156fb6cc9b7f1cb61e90983b78f2c849a
parent 375765 8e911bf5de822c82d33e78bc5da07a0c811e635e
child 375767 42caf6d5245ff30d0a4bd7249d1d7ebb7044d6c7
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgbrown, test-only
bugs1334613
milestone53.0a2
Bug 1334613 - [mozdevice] - Use root to kill processes on Android 7+. r=gbrown, a=test-only
testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
--- a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
+++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
@@ -9,16 +9,17 @@ import tempfile
 import time
 import traceback
 
 from distutils import dir_util
 
 from devicemanager import DeviceManager, DMError
 from mozprocess import ProcessHandler
 import mozfile
+import version_codes
 
 
 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.
@@ -29,16 +30,17 @@ class DeviceManagerADB(DeviceManager):
     _suModifier = None
     _lsModifier = None
     _useZip = False
     _logcatNeedsRoot = False
     _pollingInterval = 0.01
     _packageName = None
     _tempDir = None
     _adb_version = None
+    _sdk_version = None
     connected = False
 
     def __init__(self, host=None, port=5555, retryLimit=5, packageName='fennec',
                  adbPath=None, deviceSerial=None, deviceRoot=None,
                  logLevel=logging.ERROR, autoconnect=True, runAdbAsRoot=False,
                  serverHost=None, serverPort=None, **kwargs):
         DeviceManager.__init__(self, logLevel=logLevel,
                                deviceRoot=deviceRoot)
@@ -443,25 +445,41 @@ class DeviceManagerADB(DeviceManager):
             acmd.append(uri)
 
         acmd = ["shell", ' '.join(map(lambda x: '"' + x + '"', ["am", "start"] + acmd))]
         self._logger.info(acmd)
         self._checkCmd(acmd)
         return outputFile
 
     def killProcess(self, appname, sig=None):
+        shell_args = ["shell"]
+        if self._sdk_version >= version_codes.N:
+            # Bug 1334613 - force use of root
+            if self._haveRootShell is None and self._haveSu is None:
+                self._checkForRoot()
+            if not self._haveRootShell and not self._haveSu:
+                raise DMError(
+                    "killProcess '%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." %
+                    appname)
+            if not self._haveRootShell:
+                shell_args.extend(["su", self._suModifier])
+
         procs = self.getProcessList()
         for (pid, name, user) in procs:
             if name == appname:
-                args = ["shell", "kill"]
+                args = list(shell_args)
+                args.append("kill")
                 if sig:
                     args.append("-%d" % sig)
                 args.append(str(pid))
                 p = self._runCmd(args, timeout=self.short_timeout)
-                if p.returncode != 0:
+                if p.returncode != 0 and len(p.output) > 0 and \
+                   'No such process' not in p.output[0]:
                     raise DMError("Error killing process "
                                   "'%s': %s" % (appname, p.output))
 
     def _runPull(self, remoteFile, localFile):
         """
         Pulls remoteFile from device to host
         """
         try:
@@ -733,20 +751,24 @@ class DeviceManagerADB(DeviceManager):
         """
         Check to see if adb itself can be executed.
         """
         if self._adbPath != 'adb':
             if not os.access(self._adbPath, os.X_OK):
                 raise DMError("invalid adb path, or adb not executable: %s" % self._adbPath)
 
         try:
+            re_version = re.compile(r'Android Debug Bridge version (.*)')
             proc = self._runCmd(["version"], timeout=self.short_timeout)
-            re_version = re.compile(r'Android Debug Bridge version (.*)')
             self._adb_version = re_version.match(proc.output[0]).group(1)
             self._logger.info("Detected adb %s", self._adb_version)
+            proc = self._runCmd(["shell", "getprop", "ro.build.version.sdk"],
+                                timeout=self.short_timeout)
+            self._sdk_version = int(proc.output[0])
+            self._logger.info("Detected Android sdk %s", self._sdk_version)
         except os.error as err:
             raise DMError(
                 "unable to execute ADB (%s): ensure Android SDK is installed "
                 "and adb is in your $PATH" % err)
 
     def _verifyDevice(self):
         # If there is a device serial number, see if adb is connected to it
         if self._deviceSerial: