Bug 728298 - DeviceManager needs a good, standard way of starting an Android application. r=jmaher

This commit is contained in:
William Lachance 2012-02-23 10:32:28 -05:00
Родитель 0ca6d51457
Коммит f9f8f417d0
5 изменённых файлов: 397 добавлений и 138 удалений

Просмотреть файл

@ -61,7 +61,6 @@ class DMError(Exception):
def __str__(self):
return self.msg
def abstractmethod(method):
line = method.func_code.co_firstlineno
filename = method.func_code.co_filename
@ -70,9 +69,18 @@ def abstractmethod(method):
'should be implemented by a concrete class' %
(repr(method), filename,line))
return not_implemented
class DeviceManager:
@abstractmethod
def shell(self, cmd, outputfile, env=None, cwd=None):
"""
executes shell command on device
returns:
success: Return code from command
failure: None
"""
@abstractmethod
def pushFile(self, localname, destname):
"""
@ -168,25 +176,27 @@ class DeviceManager:
success: array of process tuples
failure: None
"""
@abstractmethod
def fireProcess(self, appname, failIfRunning=False):
"""
external function
DEPRECATED: Use shell() or launchApplication() for new code
returns:
success: pid
failure: None
"""
@abstractmethod
def launchProcess(self, cmd, outputFile = "process.txt", cwd = '', env = '', failIfRunning=False):
"""
external function
DEPRECATED: Use shell() or launchApplication() for new code
returns:
success: output filename
failure: None
"""
def communicate(self, process, timeout = 600, interval = 5):
"""
loops until 'process' has exited or 'timeout' seconds is reached
@ -581,3 +591,35 @@ class NetworkTools:
print "Socket error trying to find open port"
return seed
def _pop_last_line(file):
'''
Utility function to get the last line from a file (shared between ADB and
SUT device managers). Function also removes it from the file. Intended to
strip off the return code from a shell command.
'''
bytes_from_end = 1
file.seek(0, 2)
length = file.tell() + 1
while bytes_from_end <= length:
file.seek((-1)*bytes_from_end, 2)
data = file.read()
if bytes_from_end == length and len(data) == 0: # no data, return None
return None
if data[0] == '\n' or bytes_from_end == length:
# found the last line, which should have the return value
if data[0] == '\n':
data = data[1:]
# truncate off the return code line
file.truncate(length - bytes_from_end)
file.seek(0,2)
file.write('\0')
return data
bytes_from_end += 1
return None

Просмотреть файл

@ -1,5 +1,5 @@
import subprocess
from devicemanager import DeviceManager, DMError
from devicemanager import DeviceManager, DMError, _pop_last_line
import re
import os
import sys
@ -63,6 +63,47 @@ class DeviceManagerADB(DeviceManager):
else:
print "restarting as root failed"
# external function: executes shell command on device
# returns:
# success: <return code>
# failure: None
def shell(self, cmd, outputfile, env=None, cwd=None):
# need to quote special characters here
for (index, arg) in enumerate(cmd):
if arg.find(" ") or arg.find("(") or arg.find(")") or arg.find("\""):
cmd[index] = '\'%s\'' % arg
# This 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
# FIXME: this function buffers all output of the command into memory,
# always. :(
cmdline = subprocess.list2cmdline(cmd) + "; 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
proc = subprocess.Popen(["adb", "shell", cmdline],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(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 return_code
return None
# external function
# returns:
# success: True
@ -264,6 +305,7 @@ class DeviceManagerADB(DeviceManager):
return ret
# external function
# DEPRECATED: Use shell() or launchApplication() for new code
# returns:
# success: pid
# failure: None
@ -275,6 +317,7 @@ class DeviceManagerADB(DeviceManager):
return self.launchProcess(parts, failIfRunning)
# external function
# DEPRECATED: Use shell() or launchApplication() for new code
# returns:
# success: output filename
# failure: None
@ -327,6 +370,7 @@ class DeviceManagerADB(DeviceManager):
if name == appname:
p = self.runCmdAs(["shell", "kill", pid])
return p.stdout.read()
return None
# external function
@ -611,7 +655,7 @@ class DeviceManagerADB(DeviceManager):
args.insert(1, "run-as")
args.insert(2, self.packageName)
args.insert(0, "adb")
return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
def runCmdAs(self, args):
if self.useRunAs:

Просмотреть файл

@ -47,7 +47,18 @@ import subprocess
from threading import Thread
import traceback
import sys
from devicemanager import DeviceManager, DMError, FileError, NetworkTools
import StringIO
from devicemanager import DeviceManager, DMError, FileError, NetworkTools, _pop_last_line
class AgentError(Exception):
"SUTAgent-specific exception."
def __init__(self, msg= '', fatal = False):
self.msg = msg
self.fatal = fatal
def __str__(self):
return self.msg
class DeviceManagerSUT(DeviceManager):
host = ''
@ -76,7 +87,7 @@ class DeviceManagerSUT(DeviceManager):
self._sock = None
self.getDeviceRoot()
def cmdNeedsResponse(self, cmd):
def _cmdNeedsResponse(self, cmd):
""" Not all commands need a response from the agent:
* if the cmd matches the pushRE then it is the first half of push
and therefore we want to wait until the second half before looking
@ -93,18 +104,44 @@ class DeviceManagerSUT(DeviceManager):
for c in noResponseCmds:
if (c.match(cmd)):
return False
# If the command is not in our list, then it gets a response
return True
def shouldCmdCloseSocket(self, cmd):
def _stripPrompt(self, data):
'''
internal function
take a data blob and strip instances of the prompt '$>\x00'
'''
promptre = re.compile(self.prompt_regex + '.*')
retVal = []
lines = data.split('\n')
for line in lines:
foundPrompt = False
try:
while (promptre.match(line)):
foundPrompt = True
pieces = line.split(self.prompt_sep)
index = pieces.index('$>')
pieces.pop(index)
line = self.prompt_sep.join(pieces)
except(ValueError):
pass
# we don't want to append lines that are blank after stripping the
# prompt (those are basically "prompts")
if not foundPrompt or line:
retVal.append(line)
return '\n'.join(retVal)
def _shouldCmdCloseSocket(self, cmd):
""" Some commands need to close the socket after they are sent:
* push
* rebt
* uninst
* quit
"""
socketClosingCmds = [re.compile('^push .*$'),
re.compile('^quit.*'),
re.compile('^rebt.*'),
@ -116,85 +153,86 @@ class DeviceManagerSUT(DeviceManager):
return False
# convenience function to enable checks for agent errors
def verifySendCMD(self, cmdline, newline = True):
return self.sendCMD(cmdline, newline, False)
#
# create a wrapper for sendCMD that loops up to self.retrylimit iterations.
# this allows us to move the retry logic outside of the _doCMD() to make it
# easier for debugging in the future.
# note that since cmdline is a list of commands, they will all be retried if
# one fails. this is necessary in particular for pushFile(), where we don't want
# to accidentally send extra data if a failure occurs during data transmission.
#
def sendCMD(self, cmdline, newline = True, ignoreAgentErrors = True):
def sendCmds(self, cmdlist, outputfile, timeout = None, newline = True):
'''
a wrapper for _doCmds that loops up to self.retrylimit iterations.
this allows us to move the retry logic outside of the _doCmds() to make it
easier for debugging in the future.
note that since cmdlist is a list of commands, they will all be retried if
one fails. this is necessary in particular for pushFile(), where we don't want
to accidentally send extra data if a failure occurs during data transmission.
'''
done = False
while (not done):
retVal = self._doCMD(cmdline, newline)
if (retVal is None):
while self.retries < self.retrylimit:
try:
self._doCmds(cmdlist, outputfile, timeout, newline)
return
except AgentError, err:
# re-raise error if it's fatal (i.e. the device got the command but
# couldn't execute it). retry otherwise
if err.fatal:
raise err
if self.debug >= 2:
print err
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 AgentError("unable to connect to %s after %s attempts" % (self.host, self.retrylimit))
raise DMError("unable to connect to %s after %s attempts" % (self.host, self.retrylimit))
def runCmds(self, cmdlist, timeout = None, newline = True):
'''
similar to sendCmds, but just returns any output as a string instead of
writing to a file. this is normally what you want to call to send a set
of commands to the agent
'''
outputfile = StringIO.StringIO()
self.sendCmds(cmdlist, outputfile, timeout, newline)
outputfile.seek(0)
return outputfile.read()
def _doCMD(self, cmdline, newline = True):
def _doCmds(self, cmdlist, outputfile, timeout, newline):
promptre = re.compile(self.prompt_regex + '$')
data = ""
shouldCloseSocket = False
recvGuard = 1000
if (self._sock == None):
if not self._sock:
try:
if (self.debug >= 1):
if self.debug >= 1:
print "reconnecting socket"
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except:
self._sock = None
if (self.debug >= 2):
print "unable to create socket"
return None
raise AgentError("unable to create socket")
try:
self._sock.connect((self.host, int(self.port)))
self._sock.recv(1024)
except:
self._sock.close()
self._sock = None
if (self.debug >= 2):
print "unable to connect socket"
return None
for cmd in cmdline:
raise AgentError("unable to connect socket")
for cmd in cmdlist:
if newline: cmd += '\r\n'
try:
numbytes = self._sock.send(cmd)
if (numbytes != len(cmd)):
print "ERROR: our cmd was " + str(len(cmd)) + " bytes and we only sent " + str(numbytes)
return None
raise AgentError("ERROR: our cmd was %s bytes and we only sent %s" % (len(cmd),
numbytes))
if (self.debug >= 4): print "send cmd: " + str(cmd)
except:
self._sock.close()
self._sock = None
return None
return False
# Check if the command should close the socket
shouldCloseSocket = self.shouldCmdCloseSocket(cmd)
shouldCloseSocket = self._shouldCmdCloseSocket(cmd)
# Handle responses from commands
if (self.cmdNeedsResponse(cmd)):
if (self._cmdNeedsResponse(cmd)):
found = False
loopguard = 0
data = ""
while (found == False and (loopguard < recvGuard)):
temp = ''
@ -207,14 +245,14 @@ class DeviceManagerSUT(DeviceManager):
except:
self._sock.close()
self._sock = None
return None
raise AgentError("Error receiving data from socket")
data += temp
# If something goes wrong in the agent it will send back a string that
# starts with '##AGENT-ERROR##'
if self.agentErrorRE.match(data):
break
raise AgentError("Agent Error processing command: %s" % cmd, fatal=True)
for line in data.splitlines():
if promptre.match(line):
@ -222,40 +260,54 @@ class DeviceManagerSUT(DeviceManager):
data = self._stripPrompt(data)
break
# periodically flush data to output file to make sure it doesn't get
# too big/unwieldly
if len(data) > 1024:
outputfile.write(data[0:1024])
data = data[1024:]
# If we violently lose the connection to the device, this loop tends to spin,
# this guard prevents that
if (temp == ''):
loopguard += 1
if (shouldCloseSocket == True):
# Write any remaining data to outputfile
outputfile.write(data)
if shouldCloseSocket:
try:
self._sock.close()
self._sock = None
except:
self._sock = None
return None
raise AgentError("Error closing socket")
return data
# internal function
# take a data blob and strip instances of the prompt '$>\x00'
def _stripPrompt(self, data):
promptre = re.compile(self.prompt_regex + '.*')
retVal = []
lines = data.split('\n')
for line in lines:
try:
while (promptre.match(line)):
pieces = line.split(self.prompt_sep)
index = pieces.index('$>')
pieces.pop(index)
line = self.prompt_sep.join(pieces)
except(ValueError):
pass
retVal.append(line)
# external function: executes shell command on device
# returns:
# success: <return code>
# failure: None
def shell(self, cmd, outputfile, env=None, cwd=None):
cmdline = subprocess.list2cmdline(cmd)
if env:
cmdline = '%s %s' % (self.formatEnvString(env), cmdline)
return '\n'.join(retVal)
try:
if cwd:
self.sendCmds(['execcwd %s %s' % (cwd, cmdline)], outputfile)
else:
self.sendCmds(['exec %s' % cmdline], outputfile)
except AgentError:
return None
# 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 m.group(1)
# woops, we couldn't find an end of line/return value
return None
# external function
# returns:
@ -286,8 +338,8 @@ class DeviceManagerSUT(DeviceManager):
f.close()
try:
retVal = self.verifySendCMD(['push ' + destname + ' ' + str(filesize) + '\r\n', data], newline = False)
except(DMError):
retVal = self.runCmds(['push ' + destname + ' ' + str(filesize) + '\r\n', data], newline = False)
except AgentError:
retVal = False
if (self.debug >= 3): print "push returned: " + str(retVal)
@ -323,8 +375,8 @@ class DeviceManagerSUT(DeviceManager):
return name
else:
try:
retVal = self.verifySendCMD(['mkdr ' + name])
except(DMError):
retVal = self.runCmds(['mkdr ' + name])
except AgentError:
retVal = None
return retVal
@ -376,8 +428,8 @@ class DeviceManagerSUT(DeviceManager):
match = ".*" + dirname + "$"
dirre = re.compile(match)
try:
data = self.verifySendCMD(['cd ' + dirname, 'cwd'])
except(DMError):
data = self.runCmds(['cd ' + dirname, 'cwd'])
except AgentError:
return False
found = False
@ -412,8 +464,8 @@ class DeviceManagerSUT(DeviceManager):
if (self.dirExists(rootdir) == False):
return []
try:
data = self.verifySendCMD(['cd ' + rootdir, 'ls'])
except(DMError):
data = self.runCmds(['cd ' + rootdir, 'ls'])
except AgentError:
return []
files = filter(lambda x: x, data.splitlines())
@ -429,8 +481,8 @@ class DeviceManagerSUT(DeviceManager):
def removeFile(self, filename):
if (self.debug>= 2): print "removing file: " + filename
try:
retVal = self.verifySendCMD(['rm ' + filename])
except(DMError):
retVal = self.runCmds(['rm ' + filename])
except AgentError:
return None
return retVal
@ -442,8 +494,8 @@ class DeviceManagerSUT(DeviceManager):
# failure: None
def removeDir(self, remoteDir):
try:
retVal = self.verifySendCMD(['rmdr ' + remoteDir])
except(DMError):
retVal = self.runCmds(['rmdr ' + remoteDir])
except AgentError:
return None
return retVal
@ -454,8 +506,8 @@ class DeviceManagerSUT(DeviceManager):
# failure: []
def getProcessList(self):
try:
data = self.verifySendCMD(['ps'])
except DMError:
data = self.runCmds(['ps'])
except AgentError:
return []
files = []
@ -470,6 +522,7 @@ class DeviceManagerSUT(DeviceManager):
return files
# external function
# DEPRECATED: Use shell() or launchApplication() for new code
# returns:
# success: pid
# failure: None
@ -486,8 +539,8 @@ class DeviceManagerSUT(DeviceManager):
return None
try:
data = self.verifySendCMD(['exec ' + appname])
except(DMError):
data = self.runCmds(['exec ' + appname])
except AgentError:
return None
# wait up to 30 seconds for process to start up
@ -503,6 +556,7 @@ class DeviceManagerSUT(DeviceManager):
return process
# external function
# DEPRECATED: Use shell() or launchApplication() for new code
# returns:
# success: output filename
# failure: None
@ -532,8 +586,8 @@ class DeviceManagerSUT(DeviceManager):
# failure: None
def killProcess(self, appname):
try:
data = self.verifySendCMD(['kill ' + appname])
except(DMError):
data = self.runCmds(['kill ' + appname])
except AgentError:
return None
return data
@ -544,8 +598,8 @@ class DeviceManagerSUT(DeviceManager):
# failure: None
def getTempDir(self):
try:
data = self.verifySendCMD(['tmpd'])
except(DMError):
data = self.runCmds(['tmpd'])
except AgentError:
return None
return data.strip()
@ -556,12 +610,12 @@ class DeviceManagerSUT(DeviceManager):
# failure: None
def catFile(self, remoteFile):
try:
data = self.verifySendCMD(['cat ' + remoteFile])
except(DMError):
data = self.runCmds(['cat ' + remoteFile])
except AgentError:
return None
return data
# external function
# returns:
# success: output of pullfile, string
@ -625,8 +679,8 @@ class DeviceManagerSUT(DeviceManager):
# or, if error,
# <filename>,-1\n<error message>
try:
data = self.verifySendCMD(['pull ' + remoteFile])
except(DMError):
data = self.runCmds(['pull ' + remoteFile])
except AgentError:
return None
# read metadata; buffer the rest
@ -744,8 +798,8 @@ class DeviceManagerSUT(DeviceManager):
# Throws a FileError exception when null (invalid dir/filename)
def isDir(self, remotePath):
try:
data = self.verifySendCMD(['isdir ' + remotePath])
except(DMError):
data = self.runCmds(['isdir ' + remotePath])
except AgentError:
# normally there should be no error here; a nonexistent file/directory will
# return the string "<filename>: No such file or directory".
# However, I've seen AGENT-WARNING returned before.
@ -780,8 +834,8 @@ class DeviceManagerSUT(DeviceManager):
# failure: None
def getRemoteHash(self, filename):
try:
data = self.verifySendCMD(['hash ' + filename])
except(DMError):
data = self.runCmds(['hash ' + filename])
except AgentError:
return None
retVal = None
@ -809,7 +863,7 @@ class DeviceManagerSUT(DeviceManager):
# failure: None
def getDeviceRoot(self):
try:
data = self.verifySendCMD(['testroot'])
data = self.runCmds(['testroot'])
except:
return None
@ -823,7 +877,7 @@ class DeviceManagerSUT(DeviceManager):
def getAppRoot(self, packageName):
try:
data = self.verifySendCMD(['getapproot '+packageName])
data = self.runCmds(['getapproot '+packageName])
except:
return None
@ -851,8 +905,8 @@ class DeviceManagerSUT(DeviceManager):
return None
try:
data = self.verifySendCMD(['cd ' + dir, 'unzp ' + filename])
except(DMError):
data = self.runCmds(['cd ' + dir, 'unzp ' + filename])
except AgentError:
return None
return data
@ -872,8 +926,8 @@ class DeviceManagerSUT(DeviceManager):
try:
destname = '/data/data/com.mozilla.SUTAgentAndroid/files/update.info'
data = "%s,%s\rrebooting\r" % (ipAddr, port)
self.verifySendCMD(['push ' + destname + ' ' + str(len(data)) + '\r\n', data], newline = False)
except(DMError):
self.runCmds(['push ' + destname + ' ' + str(len(data)) + '\r\n', data], newline = False)
except AgentError:
return None
ip, port = self.getCallbackIpAndPort(ipAddr, port)
@ -882,8 +936,8 @@ class DeviceManagerSUT(DeviceManager):
callbacksvr = callbackServer(ip, port, self.debug)
try:
status = self.verifySendCMD([cmd])
except(DMError):
status = self.runCmds([cmd])
except AgentError:
return None
if (ipAddr is not None):
@ -918,7 +972,7 @@ class DeviceManagerSUT(DeviceManager):
directives = [directive]
for d in directives:
data = self.verifySendCMD(['info ' + d])
data = self.runCmds(['info ' + d])
if (data is None):
continue
data = collapseSpaces.sub(' ', data)
@ -955,8 +1009,8 @@ class DeviceManagerSUT(DeviceManager):
if destPath:
cmd += ' ' + destPath
try:
data = self.verifySendCMD([cmd])
except(DMError):
data = self.runCmds([cmd])
except AgentError:
return None
f = re.compile('Failure')
@ -981,8 +1035,8 @@ class DeviceManagerSUT(DeviceManager):
if installPath:
cmd += ' ' + installPath
try:
data = self.verifySendCMD([cmd])
except(DMError):
data = self.runCmds([cmd])
except AgentError:
return None
if (self.debug > 3): print "uninstallAppAndReboot: " + str(data)
@ -1026,8 +1080,8 @@ class DeviceManagerSUT(DeviceManager):
if (self.debug >= 3): print "INFO: updateApp using command: " + str(cmd)
try:
status = self.verifySendCMD([cmd])
except(DMError):
status = self.runCmds([cmd])
except AgentError:
return None
if ipAddr is not None:
@ -1046,8 +1100,8 @@ class DeviceManagerSUT(DeviceManager):
# failure: None
def getCurrentTime(self):
try:
data = self.verifySendCMD(['clok'])
except(DMError):
data = self.runCmds(['clok'])
except AgentError:
return None
return data.strip()
@ -1079,8 +1133,8 @@ class DeviceManagerSUT(DeviceManager):
return None
try:
data = self.verifySendCMD(['cd ' + dir, 'unzp ' + filename])
except(DMError):
data = self.runCmds(['cd ' + dir, 'unzp ' + filename])
except AgentError:
return None
return data
@ -1150,9 +1204,9 @@ class DeviceManagerSUT(DeviceManager):
if (self.debug >= 3): print "INFO: adjusting screen resolution to %s, %s and rebooting" % (width, height)
try:
self.verifySendCMD(["exec setprop persist.tegra.dpy%s.mode.width %s" % (screentype, width)])
self.verifySendCMD(["exec setprop persist.tegra.dpy%s.mode.height %s" % (screentype, height)])
except(DMError):
self.runCmds(["exec setprop persist.tegra.dpy%s.mode.width %s" % (screentype, width)])
self.runCmds(["exec setprop persist.tegra.dpy%s.mode.height %s" % (screentype, height)])
except AgentError:
return False
return True

87
build/mobile/droid.py Normal file
Просмотреть файл

@ -0,0 +1,87 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Test Automation Framework.
#
# The Initial Developer of the Original Code is
# Mozilla foundation
#
# Portions created by the Initial Developer are Copyright (C) 2009
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Joel Maher <joel.maher@gmail.com> (Original Developer)
# William Lachance <wlachance@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
from devicemanagerADB import DeviceManagerADB
from devicemanagerSUT import DeviceManagerSUT
class DroidMixin(object):
"""Mixin to extend DeviceManager with Android-specific functionality"""
def launchApplication(self, app, activity="App",
intent="android.intent.action.VIEW", env=None,
url=None, extra_args=None):
"""
Launches an Android application
returns:
success: True
failure: False
"""
# only one instance of an application may be running at once
if self.processExist(app):
return False
acmd = [ "am", "start", "-a", intent, "-W", "-n", "%s/.%s" % (app, activity)]
if extra_args:
acmd.extend(["--es", "args", " ".join(args)])
if env:
envCnt = 0
# env is expected to be a dict of environment variables
for envkey, envval in env.iteritems():
acmd.extend(["--es", "env" + str(envCnt), envkey + "=" + envval])
envCnt += 1
if url:
acmd.extend(["-d", ''.join(['"', url, '"'])])
# shell output not that interesting and debugging logs should already
# show what's going on here... so just create an empty memory buffer
# and ignore
shellOutput = StringIO.StringIO()
if self.shell(acmd, shellOutput) == 0:
return True
return False
class DroidADB(DeviceManagerADB, DroidMixin):
pass
class DroidSUT(DeviceManagerSUT, DroidMixin):
pass

Просмотреть файл

@ -136,12 +136,13 @@ public class DoCommand {
String ffxProvider = "org.mozilla.ffxcp";
String fenProvider = "org.mozilla.fencp";
private final String prgVersion = "SUTAgentAndroid Version 1.05";
private final String prgVersion = "SUTAgentAndroid Version 1.06";
public enum Command
{
RUN ("run"),
EXEC ("exec"),
EXECCWD ("execcwd"),
ENVRUN ("envrun"),
KILL ("kill"),
PS ("ps"),
@ -692,7 +693,25 @@ public class DoCommand {
theArgs[lcv - 1] = Argv[lcv];
}
strReturn = StartPrg2(theArgs, cmdOut);
strReturn = StartPrg2(theArgs, cmdOut, null);
}
else
{
strReturn = sErrorPrefix + "Wrong number of arguments for " + Argv[0] + " command!";
}
break;
case EXECCWD:
if (Argc >= 3)
{
String [] theArgs = new String [Argc - 2];
for (int lcv = 2; lcv < Argc; lcv++)
{
theArgs[lcv - 2] = Argv[lcv];
}
strReturn = StartPrg2(theArgs, cmdOut, Argv[1]);
}
else
{
@ -1262,6 +1281,11 @@ private void CancelNotification()
{
String sRet = null;
File tmpFile = new java.io.File("/data/local/tests");
if (tmpFile.exists() && tmpFile.isDirectory())
{
return("/data/local");
}
if (Environment.getExternalStorageState().equalsIgnoreCase(Environment.MEDIA_MOUNTED))
{
sRet = Environment.getExternalStorageDirectory().getAbsolutePath();
@ -3463,7 +3487,7 @@ private void CancelNotification()
return (sRet);
}
public String StartPrg2(String [] progArray, OutputStream out)
public String StartPrg2(String [] progArray, OutputStream out, String cwd)
{
String sRet = "";
@ -3553,7 +3577,15 @@ private void CancelNotification()
if (theArgs[0].contains("/") || theArgs[0].contains("\\") || !theArgs[0].contains("."))
{
pProc = Runtime.getRuntime().exec(theArgs, envArray);
if (cwd != null)
{
File f = new File(cwd);
pProc = Runtime.getRuntime().exec(theArgs, envArray, f);
}
else
{
pProc = Runtime.getRuntime().exec(theArgs, envArray);
}
RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
outThrd.start();