~ubuntu-branches/ubuntu/quantal/lightning-extension/quantal-security

« back to all changes in this revision

Viewing changes to mozilla/build/mobile/devicemanagerADB.py

  • Committer: Package Import Robot
  • Author(s): Chris Coulson
  • Date: 2012-05-21 16:09:39 UTC
  • mfrom: (1.3.1) (25.1.1 precise-security)
  • Revision ID: package-import@ubuntu.com-20120521160939-0702473a46xfq3tl
Tags: 1.5~b2+build1-0ubuntu1
New upstream release from the beta channel (CALENDAR_1_5b2_BUILD1)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
import subprocess
2
 
from devicemanager import DeviceManager, DMError
 
2
from devicemanager import DeviceManager, DMError, _pop_last_line
3
3
import re
4
4
import os
5
5
import sys
14
14
    self.retries = 0
15
15
    self._sock = None
16
16
    self.useRunAs = False
 
17
    self.haveRoot = False
17
18
    self.useZip = False
18
19
    self.packageName = None
 
20
    self.tempDir = None
19
21
    if packageName == None:
20
22
      if os.getenv('USER'):
21
23
        packageName = 'org.mozilla.fennec_' + os.getenv('USER')
23
25
        packageName = 'org.mozilla.fennec_'
24
26
    self.Init(packageName)
25
27
 
 
28
  def __del__(self):
 
29
    if self.host:
 
30
      self.disconnectRemoteADB()
 
31
 
26
32
  def Init(self, packageName):
27
33
    # Initialization code that may fail: Catch exceptions here to allow
28
34
    # successful initialization even if, for example, adb is not installed.
29
35
    try:
30
36
      self.verifyADB()
 
37
      if self.host:
 
38
        self.connectRemoteADB()
31
39
      self.verifyRunAs(packageName)
32
40
    except:
33
41
      self.useRunAs = False
34
 
      self.packageName = None
 
42
      self.packageName = packageName
35
43
    try:
36
44
      self.verifyZip()
37
45
    except:
38
46
      self.useZip = False
39
 
    try:
 
47
 
 
48
    def verifyRoot():
40
49
      # a test to see if we have root privs
41
50
      files = self.listFiles("/data/data")
42
51
      if (len(files) == 1):
43
52
        if (files[0].find("Permission denied") != -1):
44
53
          print "NOT running as root"
45
54
          raise Exception("not running as root")
 
55
      self.haveRoot = True
 
56
 
 
57
    try:
 
58
      verifyRoot()
46
59
    except:
47
60
      try:
48
61
        self.checkCmd(["root"])
 
62
        # The root command does not fail even if ADB cannot get
 
63
        # root rights (e.g. due to production builds), so we have
 
64
        # to check again ourselves that we have root now.
 
65
        verifyRoot()
49
66
      except:
50
 
        print "restarting as root failed"
 
67
        if (self.useRunAs):
 
68
          print "restarting as root failed, but run-as available"
 
69
        else:
 
70
          print "restarting as root failed"
 
71
 
 
72
  # external function: executes shell command on device
 
73
  # returns:
 
74
  # success: <return code>
 
75
  # failure: None
 
76
  def shell(self, cmd, outputfile, env=None, cwd=None):
 
77
    # need to quote special characters here
 
78
    for (index, arg) in enumerate(cmd):
 
79
      if arg.find(" ") or arg.find("(") or arg.find(")") or arg.find("\""):
 
80
        cmd[index] = '\'%s\'' % arg
 
81
 
 
82
    # This is more complex than you'd think because adb doesn't actually
 
83
    # return the return code from a process, so we have to capture the output
 
84
    # to get it
 
85
    # FIXME: this function buffers all output of the command into memory,
 
86
    # always. :(
 
87
    cmdline = " ".join(cmd) + "; echo $?"
 
88
 
 
89
    # prepend cwd and env to command if necessary
 
90
    if cwd:
 
91
      cmdline = "cd %s; %s" % (cwd, cmdline)
 
92
    if env:
 
93
      envstr = '; '.join(map(lambda x: 'export %s=%s' % (x[0], x[1]), env.iteritems()))
 
94
      cmdline = envstr + "; " + cmdline
 
95
 
 
96
    # all output should be in stdout
 
97
    proc = subprocess.Popen(["adb", "shell", cmdline],
 
98
                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
99
    (stdout, stderr) = proc.communicate()
 
100
    outputfile.write(stdout.rstrip('\n'))
 
101
 
 
102
    lastline = _pop_last_line(outputfile)
 
103
    if lastline:
 
104
      m = re.search('([0-9]+)', lastline)
 
105
      if m:
 
106
        return_code = m.group(1)
 
107
        outputfile.seek(-2, 2)
 
108
        outputfile.truncate() # truncate off the return code
 
109
        return return_code
 
110
 
 
111
    return None
 
112
 
 
113
  def connectRemoteADB(self):
 
114
    self.checkCmd(["connect", self.host + ":" + str(self.port)])
 
115
 
 
116
  def disconnectRemoteADB(self):
 
117
    self.checkCmd(["disconnect", self.host + ":" + str(self.port)])
51
118
 
52
119
  # external function
53
120
  # returns:
58
125
      if (os.name == "nt"):
59
126
        destname = destname.replace('\\', '/')
60
127
      if (self.useRunAs):
61
 
        remoteTmpFile = self.tmpDir + "/" + os.path.basename(localname)
 
128
        remoteTmpFile = self.getTempDir() + "/" + os.path.basename(localname)
62
129
        self.checkCmd(["push", os.path.realpath(localname), remoteTmpFile])
63
 
        self.checkCmdAs(["shell", "cp", remoteTmpFile, destname])
 
130
        if self.useDDCopy:
 
131
          self.checkCmdAs(["shell", "dd", "if=" + remoteTmpFile, "of=" + destname])
 
132
        else:
 
133
          self.checkCmdAs(["shell", "cp", remoteTmpFile, destname])
64
134
        self.checkCmd(["shell", "rm", remoteTmpFile])
65
135
      else:
66
136
        self.checkCmd(["push", os.path.realpath(localname), destname])
112
182
    # files; we either zip/unzip or push file-by-file to get around this 
113
183
    # limitation
114
184
    try:
 
185
      if (not self.dirExists(remoteDir)):
 
186
        self.mkDirs(remoteDir+"/x")
115
187
      if (self.useZip):
116
 
        localZip = tempfile.mktemp()+".zip"
117
 
        remoteZip = remoteDir + "/adbdmtmp.zip"
118
 
        subprocess.check_output(["zip", "-r", localZip, '.'], cwd=localDir)
119
 
        self.pushFile(localZip, remoteZip)
120
 
        os.remove(localZip)
121
 
        self.checkCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir])
122
 
        self.checkCmdAs(["shell", "rm", remoteZip])
 
188
        try:
 
189
          localZip = tempfile.mktemp()+".zip"
 
190
          remoteZip = remoteDir + "/adbdmtmp.zip"
 
191
          subprocess.check_output(["zip", "-r", localZip, '.'], cwd=localDir)
 
192
          self.pushFile(localZip, remoteZip)
 
193
          os.remove(localZip)
 
194
          data = self.runCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir]).stdout.read()
 
195
          self.checkCmdAs(["shell", "rm", remoteZip])
 
196
          if (re.search("unzip: exiting", data) or re.search("Operation not permitted", data)):
 
197
            raise Exception("unzip failed, or permissions error")
 
198
        except:
 
199
          print "zip/unzip failure: falling back to normal push"
 
200
          self.useZip = False
 
201
          self.pushDir(localDir, remoteDir)
123
202
      else:
124
 
        if (not self.dirExists(remoteDir)):
125
 
          self.mkDirs(remoteDir+"/x")
126
203
        for root, dirs, files in os.walk(localDir, followlinks='true'):
127
204
          relRoot = os.path.relpath(root, localDir)
128
205
          for file in files:
213
290
  def listFiles(self, rootdir):
214
291
      p = self.runCmd(["shell", "ls", "-a", rootdir])
215
292
      data = p.stdout.readlines()
 
293
      data[:] = [item.rstrip('\r\n') for item in data]
216
294
      if (len(data) == 1):
217
295
          if (data[0] == rootdir):
218
296
              return []
239
317
    return ret
240
318
 
241
319
  # external function
 
320
  # DEPRECATED: Use shell() or launchApplication() for new code
242
321
  # returns:
243
322
  #  success: pid
244
323
  #  failure: None
250
329
    return self.launchProcess(parts, failIfRunning)
251
330
 
252
331
  # external function
 
332
  # DEPRECATED: Use shell() or launchApplication() for new code
253
333
  # returns:
254
334
  #  success: output filename
255
335
  #  failure: None
256
336
  def launchProcess(self, cmd, outputFile = "process.txt", cwd = '', env = '', failIfRunning=False):
257
 
    acmd = ["shell", "am","start"]
 
337
    if cmd[0] == "am":
 
338
      self.checkCmd(["shell"] + cmd)
 
339
      return outputFile
 
340
 
 
341
    acmd = ["shell", "am", "start", "-W"]
258
342
    cmd = ' '.join(cmd).strip()
259
343
    i = cmd.find(" ")
260
344
    # SUT identifies the URL by looking for :\\ -- another strategy to consider
269
353
      args = cmd[i:].strip()
270
354
    acmd.append("-n")
271
355
    acmd.append(cmd[0:i] + "/.App")
272
 
    acmd.append("--es")
273
356
    if args != "":
 
357
      acmd.append("--es")
274
358
      acmd.append("args")
275
359
      acmd.append(args)
 
360
    if env != '' and env != None:
 
361
      envCnt = 0
 
362
      # env is expected to be a dict of environment variables
 
363
      for envkey, envval in env.iteritems():
 
364
        acmd.append("--es")
 
365
        acmd.append("env" + str(envCnt))
 
366
        acmd.append(envkey + "=" + envval);
 
367
        envCnt += 1
276
368
    if uri != "":
277
369
      acmd.append("-d")
278
370
      acmd.append(''.join(['\'',uri, '\'']));
279
371
    print acmd
280
372
    self.checkCmd(acmd)
281
 
    return outputFile;
 
373
    return outputFile
282
374
 
283
375
  # external function
284
376
  # returns:
290
382
      if name == appname:
291
383
        p = self.runCmdAs(["shell", "kill", pid])
292
384
        return p.stdout.read()
 
385
 
293
386
    return None
294
387
 
295
388
  # external function
318
411
    # TODO: add debug flags and allow for printing stdout
319
412
    # self.runCmd(["pull", remoteFile, localFile])
320
413
    try:
321
 
      self.runCmd(["pull",  remoteFile, localFile]).stdout.read()
 
414
 
 
415
      # First attempt to pull file regularly
 
416
      outerr = self.runCmd(["pull",  remoteFile, localFile]).communicate()
 
417
 
 
418
      # Now check stderr for errors
 
419
      errl = outerr[1].splitlines()
 
420
      if (len(errl) == 1):
 
421
        if (((errl[0].find("Permission denied") != -1)
 
422
          or (errl[0].find("does not exist") != -1))
 
423
          and self.useRunAs):
 
424
          # If we lack permissions to read but have run-as, then we should try
 
425
          # to copy the file to a world-readable location first before attempting
 
426
          # to pull it again.
 
427
          remoteTmpFile = self.getTempDir() + "/" + os.path.basename(remoteFile)
 
428
          self.checkCmdAs(["shell", "dd", "if=" + remoteFile, "of=" + remoteTmpFile])
 
429
          self.checkCmdAs(["shell", "chmod", "777", remoteTmpFile])
 
430
          self.runCmd(["pull",  remoteTmpFile, localFile]).stdout.read()
 
431
          # Clean up temporary file
 
432
          self.checkCmdAs(["shell", "rm", remoteTmpFile])
 
433
 
322
434
      f = open(localFile)
323
435
      ret = f.read()
324
436
      f.close()
337
449
  def getDirectory(self, remoteDir, localDir, checkDir=True):
338
450
    ret = []
339
451
    p = self.runCmd(["pull", remoteDir, localDir])
340
 
    p.stderr.readline()
341
 
    line = p.stderr.readline()
 
452
    p.stdout.readline()
 
453
    line = p.stdout.readline()
342
454
    while (line):
343
455
      els = line.split()
344
456
      f = els[len(els) - 1]
351
463
      if (i > 0):
352
464
        f = f[0:i]
353
465
      ret.append(f)
354
 
      line =  p.stderr.readline()
 
466
      line =  p.stdout.readline()
355
467
    #the last line is a summary
356
468
    if (len(ret) > 0):
357
469
      ret.pop()
411
523
      self.mkDir(testRoot)
412
524
    return testRoot
413
525
 
 
526
  # Gets the temporary directory we are using on this device
 
527
  # base on our device root, ensuring also that it exists.
 
528
  #
 
529
  # internal function
 
530
  # returns:
 
531
  #  success: path for temporary directory
 
532
  #  failure: None
 
533
  def getTempDir(self):
 
534
    # Cache result to speed up operations depending
 
535
    # on the temporary directory.
 
536
    if self.tempDir == None:
 
537
      self.tempDir = self.getDeviceRoot() + "/tmp"
 
538
      if (not self.dirExists(self.tempDir)):
 
539
        return self.mkDir(self.tempDir)
 
540
 
 
541
    return self.tempDir
 
542
 
414
543
  # Either we will have /tests/fennec or /tests/firefox but we will never have
415
544
  # both.  Return the one that exists
416
545
  # TODO: ensure we can support org.mozilla.firefox
418
547
  # returns:
419
548
  #  success: path for app root
420
549
  #  failure: None
421
 
  def getAppRoot(self):
 
550
  def getAppRoot(self, packageName):
422
551
    devroot = self.getDeviceRoot()
423
552
    if (devroot == None):
424
553
      return None
425
554
 
426
 
    if (self.dirExists(devroot + '/fennec')):
427
 
      return devroot + '/fennec'
428
 
    elif (self.dirExists(devroot + '/firefox')):
429
 
      return devroot + '/firefox'
 
555
    if (packageName and self.dirExists('/data/data/' + packageName)):
 
556
      self.packageName = packageName
 
557
      return '/data/data/' + packageName
430
558
    elif (self.packageName and self.dirExists('/data/data/' + self.packageName)):
431
559
      return '/data/data/' + self.packageName
432
560
 
533
661
    return ret
534
662
 
535
663
  def runCmd(self, args):
 
664
    # If we are not root but have run-as, and we're trying to execute 
 
665
    # a shell command then using run-as is the best we can do
 
666
    if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"):
 
667
      args.insert(1, "run-as")
 
668
      args.insert(2, self.packageName)
536
669
    args.insert(0, "adb")
537
 
    return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
670
    return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
538
671
 
539
672
  def runCmdAs(self, args):
540
673
    if self.useRunAs:
543
676
    return self.runCmd(args)
544
677
 
545
678
  def checkCmd(self, args):
 
679
    # If we are not root but have run-as, and we're trying to execute 
 
680
    # a shell command then using run-as is the best we can do
 
681
    if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"):
 
682
      args.insert(1, "run-as")
 
683
      args.insert(2, self.packageName)
546
684
    args.insert(0, "adb")
547
685
    return subprocess.check_call(args)
548
686
 
571
709
    # Check to see if adb itself can be executed.
572
710
    try:
573
711
      self.runCmd(["version"])
574
 
    except Exception as (ex):
 
712
    except:
575
713
      print "unable to execute ADB: ensure Android SDK is installed and adb is in your $PATH"
576
714
    
577
715
  def isCpAvailable(self):
581
719
    if (re.search('Usage', data)):
582
720
      return True
583
721
    else:
 
722
      data = self.runCmd(["shell", "dd", "-"]).stdout.read()
 
723
      if (re.search('unknown operand', data)):
 
724
        print "'cp' not found, but 'dd' was found as a replacement"
 
725
        self.useDDCopy = True
 
726
        return True
584
727
      print "unable to execute 'cp' on device; consider installing busybox from Android Market"
585
728
      return False
586
729
 
595
738
    self.useRunAs = False
596
739
    devroot = self.getDeviceRoot()
597
740
    if (packageName and self.isCpAvailable() and devroot):
598
 
      self.tmpDir = devroot + "/tmp"
599
 
      if (not self.dirExists(self.tmpDir)):
600
 
        self.mkDir(self.tmpDir)
601
 
      self.checkCmd(["shell", "run-as", packageName, "mkdir", devroot + "/sanity"])
602
 
      self.checkCmd(["push", os.path.abspath(sys.argv[0]), self.tmpDir + "/tmpfile"])
603
 
      self.checkCmd(["shell", "run-as", packageName, "cp", self.tmpDir + "/tmpfile", devroot + "/sanity"])
 
741
      tmpDir = self.getTempDir()
 
742
 
 
743
      # The problem here is that run-as doesn't cause a non-zero exit code
 
744
      # when failing because of a non-existent or non-debuggable package :(
 
745
      runAsOut = self.runCmd(["shell", "run-as", packageName, "mkdir", devroot + "/sanity"]).communicate()[0]
 
746
      if runAsOut.startswith("run-as:") and ("not debuggable" in runAsOut[0] or
 
747
                                             "is unknown" in runAsOut[0]):
 
748
        raise DMError("run-as failed sanity check")
 
749
 
 
750
      self.checkCmd(["push", os.path.abspath(sys.argv[0]), tmpDir + "/tmpfile"])
 
751
      if self.useDDCopy:
 
752
        self.checkCmd(["shell", "run-as", packageName, "dd", "if=" + tmpDir + "/tmpfile", "of=" + devroot + "/sanity/tmpfile"])
 
753
      else:
 
754
        self.checkCmd(["shell", "run-as", packageName, "cp", tmpDir + "/tmpfile", devroot + "/sanity"])
604
755
      if (self.fileExists(devroot + "/sanity/tmpfile")):
605
756
        print "will execute commands via run-as " + packageName
606
757
        self.packageName = packageName
609
760
      self.checkCmd(["shell", "run-as", packageName, "rm", "-r", devroot + "/sanity"])
610
761
      
611
762
  def isUnzipAvailable(self):
612
 
    data = self.runCmd(["shell", "unzip"]).stdout.read()
 
763
    data = self.runCmdAs(["shell", "unzip"]).stdout.read()
613
764
    if (re.search('Usage', data)):
614
765
      return True
615
766
    else: