Bug 720806 - Improve DeviceManagerADB actions when device is not rooted. r=gbrown

This commit is contained in:
Christian Holler (:decoder) 2012-02-01 11:43:15 -05:00
parent a2170ade48
commit 9838568b07

View File

@ -14,8 +14,10 @@ class DeviceManagerADB(DeviceManager):
self.retries = 0 self.retries = 0
self._sock = None self._sock = None
self.useRunAs = False self.useRunAs = False
self.haveRoot = False
self.useZip = False self.useZip = False
self.packageName = None self.packageName = None
self.tempDir = None
if packageName == None: if packageName == None:
if os.getenv('USER'): if os.getenv('USER'):
packageName = 'org.mozilla.fennec_' + os.getenv('USER') packageName = 'org.mozilla.fennec_' + os.getenv('USER')
@ -36,18 +38,30 @@ class DeviceManagerADB(DeviceManager):
self.verifyZip() self.verifyZip()
except: except:
self.useZip = False self.useZip = False
try:
def verifyRoot():
# a test to see if we have root privs # a test to see if we have root privs
files = self.listFiles("/data/data") files = self.listFiles("/data/data")
if (len(files) == 1): if (len(files) == 1):
if (files[0].find("Permission denied") != -1): if (files[0].find("Permission denied") != -1):
print "NOT running as root" print "NOT running as root"
raise Exception("not running as root") raise Exception("not running as root")
self.haveRoot = True
try:
verifyRoot()
except: except:
try: try:
self.checkCmd(["root"]) self.checkCmd(["root"])
# The root command does not fail even if ADB cannot get
# root rights (e.g. due to production builds), so we have
# to check again ourselves that we have root now.
verifyRoot()
except: except:
print "restarting as root failed" if (self.useRunAs):
print "restarting as root failed, but run-as available"
else:
print "restarting as root failed"
# external function # external function
# returns: # returns:
@ -58,9 +72,12 @@ class DeviceManagerADB(DeviceManager):
if (os.name == "nt"): if (os.name == "nt"):
destname = destname.replace('\\', '/') destname = destname.replace('\\', '/')
if (self.useRunAs): if (self.useRunAs):
remoteTmpFile = self.tmpDir + "/" + os.path.basename(localname) remoteTmpFile = self.getTempDir() + "/" + os.path.basename(localname)
self.checkCmd(["push", os.path.realpath(localname), remoteTmpFile]) self.checkCmd(["push", os.path.realpath(localname), remoteTmpFile])
self.checkCmdAs(["shell", "cp", remoteTmpFile, destname]) if self.useDDCopy:
self.checkCmdAs(["shell", "dd", "if=" + remoteTmpFile, "of=" + destname])
else:
self.checkCmdAs(["shell", "cp", remoteTmpFile, destname])
self.checkCmd(["shell", "rm", remoteTmpFile]) self.checkCmd(["shell", "rm", remoteTmpFile])
else: else:
self.checkCmd(["push", os.path.realpath(localname), destname]) self.checkCmd(["push", os.path.realpath(localname), destname])
@ -329,7 +346,26 @@ class DeviceManagerADB(DeviceManager):
# TODO: add debug flags and allow for printing stdout # TODO: add debug flags and allow for printing stdout
# self.runCmd(["pull", remoteFile, localFile]) # self.runCmd(["pull", remoteFile, localFile])
try: try:
self.runCmd(["pull", remoteFile, localFile]).stdout.read()
# First attempt to pull file regularly
outerr = self.runCmd(["pull", remoteFile, localFile]).communicate()
# Now check stderr for errors
errl = outerr[1].splitlines()
if (len(errl) == 1):
if (((errl[0].find("Permission denied") != -1)
or (errl[0].find("does not exist") != -1))
and self.useRunAs):
# If we lack permissions to read but have run-as, then we should try
# to copy the file to a world-readable location first before attempting
# to pull it again.
remoteTmpFile = self.getTempDir() + "/" + os.path.basename(remoteFile)
self.checkCmdAs(["shell", "dd", "if=" + remoteFile, "of=" + remoteTmpFile])
self.checkCmdAs(["shell", "chmod", "777", remoteTmpFile])
self.runCmd(["pull", remoteTmpFile, localFile]).stdout.read()
# Clean up temporary file
self.checkCmdAs(["shell", "rm", remoteTmpFile])
f = open(localFile) f = open(localFile)
ret = f.read() ret = f.read()
f.close() f.close()
@ -422,6 +458,23 @@ class DeviceManagerADB(DeviceManager):
self.mkDir(testRoot) self.mkDir(testRoot)
return testRoot return testRoot
# Gets the temporary directory we are using on this device
# base on our device root, ensuring also that it exists.
#
# internal function
# returns:
# success: path for temporary directory
# failure: None
def getTempDir(self):
# Cache result to speed up operations depending
# on the temporary directory.
if self.tempDir == None:
self.tempDir = self.getDeviceRoot() + "/tmp"
if (not self.dirExists(self.tempDir)):
return self.mkDir(self.tempDir)
return self.tempDir
# Either we will have /tests/fennec or /tests/firefox but we will never have # Either we will have /tests/fennec or /tests/firefox but we will never have
# both. Return the one that exists # both. Return the one that exists
# TODO: ensure we can support org.mozilla.firefox # TODO: ensure we can support org.mozilla.firefox
@ -543,6 +596,11 @@ class DeviceManagerADB(DeviceManager):
return ret return ret
def runCmd(self, args): def runCmd(self, args):
# If we are not root but have run-as, and we're trying to execute
# a shell command then using run-as is the best we can do
if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"):
args.insert(1, "run-as")
args.insert(2, self.packageName)
args.insert(0, "adb") args.insert(0, "adb")
return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@ -553,6 +611,11 @@ class DeviceManagerADB(DeviceManager):
return self.runCmd(args) return self.runCmd(args)
def checkCmd(self, args): def checkCmd(self, args):
# If we are not root but have run-as, and we're trying to execute
# a shell command then using run-as is the best we can do
if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"):
args.insert(1, "run-as")
args.insert(2, self.packageName)
args.insert(0, "adb") args.insert(0, "adb")
return subprocess.check_call(args) return subprocess.check_call(args)
@ -591,6 +654,11 @@ class DeviceManagerADB(DeviceManager):
if (re.search('Usage', data)): if (re.search('Usage', data)):
return True return True
else: else:
data = self.runCmd(["shell", "dd", "-"]).stdout.read()
if (re.search('unknown operand', data)):
print "'cp' not found, but 'dd' was found as a replacement"
self.useDDCopy = True
return True
print "unable to execute 'cp' on device; consider installing busybox from Android Market" print "unable to execute 'cp' on device; consider installing busybox from Android Market"
return False return False
@ -605,12 +673,14 @@ class DeviceManagerADB(DeviceManager):
self.useRunAs = False self.useRunAs = False
devroot = self.getDeviceRoot() devroot = self.getDeviceRoot()
if (packageName and self.isCpAvailable() and devroot): if (packageName and self.isCpAvailable() and devroot):
self.tmpDir = devroot + "/tmp" tmpDir = self.getTempDir()
if (not self.dirExists(self.tmpDir)):
self.mkDir(self.tmpDir)
self.checkCmd(["shell", "run-as", packageName, "mkdir", devroot + "/sanity"]) self.checkCmd(["shell", "run-as", packageName, "mkdir", devroot + "/sanity"])
self.checkCmd(["push", os.path.abspath(sys.argv[0]), self.tmpDir + "/tmpfile"]) self.checkCmd(["push", os.path.abspath(sys.argv[0]), tmpDir + "/tmpfile"])
self.checkCmd(["shell", "run-as", packageName, "cp", self.tmpDir + "/tmpfile", devroot + "/sanity"]) if self.useDDCopy:
self.checkCmd(["shell", "run-as", packageName, "dd", "if=" + tmpDir + "/tmpfile", "of=" + devroot + "/sanity/tmpfile"])
else:
self.checkCmd(["shell", "run-as", packageName, "cp", tmpDir + "/tmpfile", devroot + "/sanity"])
if (self.fileExists(devroot + "/sanity/tmpfile")): if (self.fileExists(devroot + "/sanity/tmpfile")):
print "will execute commands via run-as " + packageName print "will execute commands via run-as " + packageName
self.packageName = packageName self.packageName = packageName