def _runPull(self, remoteFile, localFile):
     """
     Pulls remoteFile from device to host
     """
     try:
         self._runCmd(["pull",  remoteFile, localFile])
     except (OSError, ValueError):
         raise DMError("Error pulling remote file '%s' to '%s'" % (remoteFile, localFile))
Exemple #2
0
    def pushFile(self, localname, destname, retryLimit=None, createDir=True):
        # you might expect us to put the file *in* the directory in this case,
        # but that would be inconsistent with historical behavior.
        retryLimit = retryLimit or self.retryLimit
        if self.dirExists(destname):
            raise DMError(
                "Attempted to push a file (%s) to a directory (%s)!" %
                (localname, destname))
        if not os.access(localname, os.F_OK):
            raise DMError("File not found: %s" % localname)

        proc = self._runCmd(
            ["push", os.path.realpath(localname), destname],
            retryLimit=retryLimit)
        if proc.returncode != 0:
            raise DMError("Error pushing file %s -> %s; output: %s" %
                          (localname, destname, proc.output))
 def getCurrentTime(self):
     """
     Returns device time in milliseconds since the epoch
     """
     timestr = self._runCmd(["shell", "date", "+%s"]).stdout.read().strip()
     if (not timestr or not timestr.isdigit()):
         raise DMError("Unable to get current time using date (got: '%s')" % timestr)
     return str(int(timestr)*1000)
Exemple #4
0
    def _verifyADB(self):
        """
        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)
            self._adb_version = re_version.match(proc.output[0]).group(1)
            self._logger.info("Detected adb %s" % self._adb_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)
Exemple #5
0
    def verifyRoot(self):
        # a test to see if we have root privs
        files = self.listFiles("/data/data")
        if (len(files) == 0):
            print "NOT running as root"
            raise DMError("not running as root")

        self.haveRoot = True
Exemple #6
0
    def pushFile(self, localname, destname, retryLimit=None, createDir=True):
        # you might expect us to put the file *in* the directory in this case,
        # but that would be different behaviour from devicemanagerSUT. Throw
        # an exception so we have the same behaviour between the two
        # implementations
        retryLimit = retryLimit or self.retryLimit
        if self.dirExists(destname):
            raise DMError("Attempted to push a file (%s) to a directory (%s)!" %
                          (localname, destname))
        if not os.access(localname, os.F_OK):
            raise DMError("File not found: %s" % localname)

        proc = self._runCmd(["push", os.path.realpath(localname), destname],
                            retryLimit=retryLimit)
        if proc.returncode != 0:
            raise DMError("Error pushing file %s -> %s; output: %s" %
                          (localname, destname, proc.output))
    def shell(self, cmd, outputfile, env=None, cwd=None, timeout=None, root=False):
        cmdline = self._escapedCommandLine(cmd)
        if env:
            cmdline = '%s %s' % (self._formatEnvString(env), cmdline)

        # execcwd/execcwdsu currently unsupported in Negatus; see bug 824127.
        if cwd and self.agentProductName == 'SUTAgentNegatus':
            raise DMError("Negatus does not support execcwd/execcwdsu")

        haveExecSu = (self.agentProductName == 'SUTAgentNegatus' or
                      StrictVersion(self.agentVersion) >= StrictVersion('1.13'))

        # Depending on agent version we send one of the following commands here:
        # * exec (run as normal user)
        # * execsu (run as privileged user)
        # * execcwd (run as normal user from specified directory)
        # * execcwdsu (run as privileged user from specified directory)

        cmd = "exec"
        if cwd:
            cmd += "cwd"
        if root and haveExecSu:
            cmd += "su"

        if cwd:
            self._sendCmds([{ 'cmd': '%s %s %s' % (cmd, cwd, cmdline) }], outputfile, timeout)
        else:
            if (not root) or haveExecSu:
                self._sendCmds([{ 'cmd': '%s %s' % (cmd, cmdline) }], outputfile, timeout)
            else:
                # need to manually inject su -c for backwards compatibility (this may
                # not work on ICS or above!!)
                # (FIXME: this backwards compatibility code is really ugly and should
                # be deprecated at some point in the future)
                self._sendCmds([ { 'cmd': '%s su -c "%s"' % (cmd, cmdline) }], outputfile,
                               timeout)

        # dig through the output to get the return code
        lastline = _pop_last_line(outputfile)
        if lastline:
            m = re.search('return code \[([0-9]+)\]', lastline)
            if m:
                return int(m.group(1))

        # woops, we couldn't find an end of line/return value
        raise DMError("Automation Error: Error finding end of line/return value when running '%s'" % cmdline)
Exemple #8
0
 def getCurrentTime(self):
     timestr = str(
         self._runCmd(["shell", "date", "+%s"],
                      timeout=self.short_timeout).output[0])
     if (not timestr or not timestr.isdigit()):
         raise DMError("Unable to get current time using date (got: '%s')" %
                       timestr)
     return int(timestr) * 1000
    def _verifyDevice(self):
        # If there is a device serial number, see if adb is connected to it
        if self._deviceSerial:
            deviceStatus = None
            for line in self._runCmd(["devices"]).output:
                m = re.match('(.+)?\s+(.+)$', line)
                if m:
                    if self._deviceSerial == m.group(1):
                        deviceStatus = m.group(2)
            if deviceStatus == None:
                raise DMError("device not found: %s" % self._deviceSerial)
            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 shell(self,
              cmd,
              outputfile,
              env=None,
              cwd=None,
              timeout=None,
              root=False):
        # FIXME: this function buffers all output of the command into memory,
        # always. :(

        # 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:
            cmdline = "su -c \"%s\"" % 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:
            envstr = '; '.join(
                map(lambda x: 'export %s=%s' % (x[0], x[1]), env.iteritems()))
            cmdline = envstr + "; " + cmdline

        # all output should be in stdout
        args = [self.adbPath]
        if self.deviceSerial:
            args.extend(['-s', self.deviceSerial])
        args.extend(["shell", cmdline])
        proc = subprocess.Popen(args,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
        if timeout:
            timeout = int(timeout)
            start_time = time.time()
            ret_code = proc.poll()
            while ((time.time() - start_time) <= timeout) and ret_code == None:
                time.sleep(1)
                ret_code = proc.poll()
            if ret_code == None:
                proc.kill()
                raise DMError("Timeout exceeded for shell call")
        (stdout, stderr) = proc.communicate()
        outputfile.write(stdout.rstrip('\n'))

        lastline = _pop_last_line(outputfile)
        if lastline:
            m = re.search('([0-9]+)', lastline)
            if m:
                return_code = m.group(1)
                outputfile.seek(-2, 2)
                outputfile.truncate()  # truncate off the return code
                return int(return_code)

        return None
Exemple #11
0
    def getFile(self, remoteFile, localFile):
        data = self.pullFile(remoteFile)

        fhandle = open(localFile, 'wb')
        fhandle.write(data)
        fhandle.close()
        if not self.validateFile(remoteFile, localFile):
            raise DMError("Automation Error: Failed to validate file when downloading %s" %
                          remoteFile)
Exemple #12
0
    def installApp(self, appBundlePath, destPath=None):
        cmd = 'inst ' + appBundlePath
        if destPath:
            cmd += ' ' + destPath

        data = self._runCmds([{ 'cmd': cmd }])

        if 'installation complete [0]' not in data:
            raise DMError("Remove Device Error: Error installing app. Error message: %s" % data)
Exemple #13
0
 def _adb_root(self):
     """ Some devices require us to reboot adbd as root.
         This function takes care of it.
     """
     if self.processInfo("adbd")[2] != "root":
         self._checkCmd(["root"])
         self._checkCmd(["wait-for-device"])
         if self.processInfo("adbd")[2] != "root":
             raise DMError("We tried rebooting adbd as root, however, it failed.")
    def sendCMD(self, cmdline, newline=True, ignoreAgentErrors=True):
        done = False
        while (not done):
            retVal = self._doCMD(cmdline, newline)
            if (retVal is None):
                self.retries += 1
            else:
                self.retries = 0
                if ignoreAgentErrors == False:
                    if (self.agentErrorRE.match(retVal)):
                        raise DMError("error on the agent executing '%s'" %
                                      cmdline)
                return retVal

            if (self.retries >= self.retrylimit):
                done = True

        raise DMError("unable to connect to %s after %s attempts" %
                      (self.host, self.retrylimit))
Exemple #15
0
    def dirExists(self, remotePath):
        """
        Return True if remotePath is an existing directory on the device.
        """
        ret = self._runCmds([{'cmd': 'isdir ' + remotePath}]).strip()
        if not ret:
            raise DMError(
                'Automation Error: DeviceManager isdir returned null')

        return ret == 'TRUE'
Exemple #16
0
 def _verifyZip(self):
     # If "zip" can be run locally, and "unzip" can be run remotely, then pushDir
     # can use these to push just one file per directory -- a significant
     # optimization for large directories.
     self._useZip = False
     if (self._isUnzipAvailable() and self._isLocalZipAvailable()):
         self._logger.info("will use zip to push directories")
         self._useZip = True
     else:
         raise DMError("zip not available")
Exemple #17
0
    def pushFile(self, localname, destname, retryLimit = None):
        retryLimit = retryLimit or self.retryLimit
        self.mkDirs(destname)

        try:
            filesize = os.path.getsize(localname)
            with open(localname, 'rb') as f:
                remoteHash = self._runCmds([{ 'cmd': 'push ' + destname + ' ' + str(filesize),
                                              'data': f.read() }], retryLimit=retryLimit).strip()
        except OSError:
            raise DMError("DeviceManager: Error reading file to push")

        self._logger.debug("push returned: %s" % remoteHash)

        localHash = self._getLocalHash(localname)

        if localHash != remoteHash:
            raise DMError("Automation Error: Push File failed to Validate! (localhash: %s, "
                          "remotehash: %s)" % (localHash, remoteHash))
    def installApp(self, appBundlePath, destPath=None):
        cmd = 'inst ' + appBundlePath
        if destPath:
            cmd += ' ' + destPath

        data = self._runCmds([{ 'cmd': cmd }])

        f = re.compile('Failure')
        for line in data.split():
            if (f.match(line)):
                raise DMError("Remove Device Error: Error installing app. Error message: %s" % data)
Exemple #19
0
 def pushDir(self, localDir, remoteDir, retryLimit=None, timeout=None):
     # adb "push" accepts a directory as an argument, but if the directory
     # contains symbolic links, the links are pushed, rather than the linked
     # files; we either zip/unzip or re-copy the directory into a temporary
     # one to get around this limitation
     retryLimit = retryLimit or self.retryLimit
     if self._useZip:
         self.removeDir(remoteDir)
         self.mkDirs(remoteDir + "/x")
         try:
             localZip = tempfile.mktemp() + ".zip"
             remoteZip = remoteDir + "/adbdmtmp.zip"
             proc = ProcessHandler(["zip", "-r", localZip, '.'], cwd=localDir,
                                   processOutputLine=self._log)
             proc.run()
             proc.wait()
             self.pushFile(localZip, remoteZip, retryLimit=retryLimit, createDir=False)
             mozfile.remove(localZip)
             data = self._runCmd(["shell", "unzip", "-o", remoteZip,
                                  "-d", remoteDir]).output[0]
             self._checkCmd(["shell", "rm", remoteZip],
                            retryLimit=retryLimit, timeout=self.short_timeout)
             if re.search("unzip: exiting", data) or re.search("Operation not permitted", data):
                 raise Exception("unzip failed, or permissions error")
         except:
             self._logger.warning(traceback.format_exc())
             self._logger.warning("zip/unzip failure: falling back to normal push")
             self._useZip = False
             self.pushDir(localDir, remoteDir, retryLimit=retryLimit, timeout=timeout)
     else:
         localDir = os.path.normpath(localDir)
         remoteDir = os.path.normpath(remoteDir)
         tempParent = tempfile.mkdtemp()
         remoteName = os.path.basename(remoteDir)
         newLocal = os.path.join(tempParent, remoteName)
         dir_util.copy_tree(localDir, newLocal)
         # See do_sync_push in
         # https://android.googlesource.com/platform/system/core/+/master/adb/file_sync_client.cpp
         # Work around change in behavior in adb 1.0.36 where if
         # the remote destination directory exists, adb push will
         # copy the source directory *into* the destination
         # directory otherwise it will copy the source directory
         # *onto* the destination directory.
         if self._adb_version >= '1.0.36':
             remoteDir = '/'.join(remoteDir.rstrip('/').split('/')[:-1])
         try:
             if self._checkCmd(["push", newLocal, remoteDir],
                               retryLimit=retryLimit, timeout=timeout):
                 raise DMError("failed to push %s (copy of %s) to %s" %
                               (newLocal, localDir, remoteDir))
         except:
             raise
         finally:
             mozfile.remove(tempParent)
    def uninstallApp(self, appName, installPath=None):
        cmd = 'uninstall ' + appName
        if installPath:
            cmd += ' ' + installPath
        data = self._runCmds([{'cmd': cmd}])

        status = data.split('\n')[0].strip()
        self._logger.debug("uninstallApp: '%s'" % status)
        if status == 'Success':
            return
        raise DMError("Remote Device Error: uninstall failed for %s" % appName)
    def uninstallApp(self, appName, installPath=None):
        """
        Uninstalls the named application from device and DOES NOT cause a reboot

        appName - the name of the application (e.g org.mozilla.fennec)
        installPath - the path to where the application was installed (optional)
        """
        data = self._runCmd(["uninstall", appName]).stdout.read().strip()
        status = data.split('\n')[0].strip()
        if status != 'Success':
            raise DMError("uninstall failed for %s. Got: %s" % (appName, status))
Exemple #22
0
 def remove_forward(self, local=None):
     """
     Turn off forwarding of socket connection.
     """
     cmd = ['forward']
     if local is None:
         cmd.extend(['--remove-all'])
     else:
         cmd.extend(['--remove', local])
     if not self._checkCmd(cmd, timeout=self.short_timeout) == 0:
         raise DMError("Failed to remove connection forwarding.")
    def verifyRoot(self):
        # a test to see if we have root privs
        p = self.runCmd(["shell", "id"])
        response = p.stdout.readline()
        response = response.rstrip()
        response = response.split(' ')
        if (response[0].find('uid=0') < 0 or response[1].find('gid=0') < 0):
            print "NOT running as root ", response[0].find('uid=0')
            raise DMError("not running as root")

        self.haveRoot = True
    def pushFile(self, localname, destname, retryLimit=None, createDir=True):
        # you might expect us to put the file *in* the directory in this case,
        # but that would be different behaviour from devicemanagerSUT. Throw
        # an exception so we have the same behaviour between the two
        # implementations
        retryLimit = retryLimit or self.retryLimit
        if self.dirExists(destname):
            raise DMError("Attempted to push a file (%s) to a directory (%s)!" %
                          (localname, destname))
        if not os.access(localname, os.F_OK):
            raise DMError("File not found: %s" % localname)

        if self._useRunAs:
            remoteTmpFile = self.getTempDir() + "/" + os.path.basename(localname)
            self._checkCmd(["push", os.path.realpath(localname), remoteTmpFile],
                    retryLimit=retryLimit)
            self.shellCheckOutput(["dd", "if=" + remoteTmpFile, "of=" + destname])
            self.shellCheckOutput(["rm", remoteTmpFile])
        else:
            self._checkCmd(["push", os.path.realpath(localname), destname],
                    retryLimit=retryLimit)
Exemple #25
0
 def killProcess(self, appname, sig=None):
     procs = self.getProcessList()
     for (pid, name, user) in procs:
         if name == appname:
             args = ["shell", "kill"]
             if sig:
                 args.append("-%d" % sig)
             args.append(str(pid))
             p = self._runCmd(args)
             if p.returncode != 0:
                 raise DMError("Error killing process "
                               "'%s': %s" % (appname, p.output))
    def getAppRoot(self, packageName):
        devroot = self.getDeviceRoot()
        if (devroot == None):
            return None

        if (packageName and self.dirExists('/data/data/' + packageName)):
            self._packageName = packageName
            return '/data/data/' + packageName
        elif (self._packageName and self.dirExists('/data/data/' + self._packageName)):
            return '/data/data/' + self._packageName

        # Failure (either not installed or not a recognized platform)
        raise DMError("Failed to get application root for: %s" % packageName)
 def killProcess(self, appname, forceKill=False):
     procs = self.getProcessList()
     for (pid, name, user) in procs:
         if name == appname:
             args = ["shell", "kill"]
             if forceKill:
                 args.append("-9")
             args.append(str(pid))
             p = self._runCmdAs(args)
             p.communicate()
             if p.returncode != 0:
                 raise DMError("Error killing process "
                               "'%s': %s" % (appname, p.stdout.read()))
    def _verifyDevice(self):
        # If there is a device serial number, see if adb is connected to it
        if self._deviceSerial:
            deviceStatus = None
            proc = subprocess.Popen([self._adbPath, "devices"],
                                    stdout=subprocess.PIPE,
                                    stderr=subprocess.STDOUT)
            for line in proc.stdout:
                m = re.match('(.+)?\s+(.+)$', line)
                if m:
                    if self._deviceSerial == m.group(1):
                        deviceStatus = m.group(2)
            if deviceStatus == None:
                raise DMError("device not found: %s" % self._deviceSerial)
            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
        try:
            self._checkCmd(["shell", "echo"])
        except subprocess.CalledProcessError:
            raise DMError("unable to connect to device: is it plugged in?")
Exemple #29
0
    def forward(self, local, remote):
        """
        Forward socket connections.

        Forward specs are one of:
          tcp:<port>
          localabstract:<unix domain socket name>
          localreserved:<unix domain socket name>
          localfilesystem:<unix domain socket name>
          dev:<character device name>
          jdwp:<process pid> (remote only)
        """
        if not self._checkCmd(['forward', local, remote], timeout=self.short_timeout) == 0:
            raise DMError("Failed to forward socket connection.")
    def fireProcess(self, appname, failIfRunning=False, maxWaitTime=30):
        """
        Starts a process

        returns: pid

        DEPRECATED: Use shell() or launchApplication() for new code
        """
        if not appname:
            raise DMError(
                "Automation Error: fireProcess called with no command to run")

        self._logger.info("FIRE PROC: '%s'" % appname)

        if (self.processExist(appname) != None):
            self._logger.warning("process %s appears to be running already\n" %
                                 appname)
            if (failIfRunning):
                raise DMError("Automation Error: Process is already running")

        self._runCmds([{'cmd': 'exec ' + appname}])

        # The 'exec' command may wait for the process to start and end, so checking
        # for the process here may result in process = None.
        # The normal case is to launch the process and return right away
        # There is one case with robotium (am instrument) where exec returns at the end
        pid = None
        waited = 0
        while pid is None and waited < maxWaitTime:
            pid = self.processExist(appname)
            if pid:
                break
            time.sleep(1)
            waited += 1

        self._logger.debug("got pid: %s for process: %s" % (pid, appname))
        return pid