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

« 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-06-20 23:50:32 UTC
  • mfrom: (1.3.2)
  • Revision ID: package-import@ubuntu.com-20120620235032-fx5tuhrswvsju9z3
Tags: 1.6~b1+build1-0ubuntu1
New upstream release from the beta channel (CALENDAR_1_6b1_BUILD1)

Show diffs side-by-side

added added

removed removed

Lines of Context:
7
7
 
8
8
class DeviceManagerADB(DeviceManager):
9
9
 
10
 
  def __init__(self, host = None, port = 20701, retrylimit = 5, packageName = None):
 
10
  def __init__(self, host=None, port=20701, retrylimit=5, packageName='fennec',
 
11
               adbPath='adb', deviceSerial=None):
11
12
    self.host = host
12
13
    self.port = port
13
14
    self.retrylimit = retrylimit
15
16
    self._sock = None
16
17
    self.useRunAs = False
17
18
    self.haveRoot = False
 
19
    self.useDDCopy = False
18
20
    self.useZip = False
19
21
    self.packageName = None
20
22
    self.tempDir = None
21
 
    if packageName == None:
 
23
 
 
24
    # the path to adb, or 'adb' to assume that it's on the PATH
 
25
    self.adbPath = adbPath
 
26
 
 
27
    # The serial number of the device to use with adb, used in cases
 
28
    # where multiple devices are being managed by the same adb instance.
 
29
    self.deviceSerial = deviceSerial
 
30
 
 
31
    if packageName == 'fennec':
22
32
      if os.getenv('USER'):
23
 
        packageName = 'org.mozilla.fennec_' + os.getenv('USER')
 
33
        self.packageName = 'org.mozilla.fennec_' + os.getenv('USER')
24
34
      else:
25
 
        packageName = 'org.mozilla.fennec_'
26
 
    self.Init(packageName)
27
 
 
28
 
  def __del__(self):
 
35
        self.packageName = 'org.mozilla.fennec_'
 
36
    elif packageName:
 
37
      self.packageName = packageName
 
38
 
 
39
    # verify that we can run the adb command. can't continue otherwise
 
40
    self.verifyADB()
 
41
 
 
42
    # try to connect to the device over tcp/ip if we have a hostname
29
43
    if self.host:
30
 
      self.disconnectRemoteADB()
31
 
 
32
 
  def Init(self, packageName):
33
 
    # Initialization code that may fail: Catch exceptions here to allow
34
 
    # successful initialization even if, for example, adb is not installed.
35
 
    try:
36
 
      self.verifyADB()
37
 
      if self.host:
38
 
        self.connectRemoteADB()
39
 
      self.verifyRunAs(packageName)
40
 
    except:
41
 
      self.useRunAs = False
42
 
      self.packageName = packageName
43
 
    try:
44
 
      self.verifyZip()
45
 
    except:
46
 
      self.useZip = False
47
 
 
48
 
    def verifyRoot():
49
 
      # a test to see if we have root privs
50
 
      files = self.listFiles("/data/data")
51
 
      if (len(files) == 1):
52
 
        if (files[0].find("Permission denied") != -1):
53
 
          print "NOT running as root"
54
 
          raise Exception("not running as root")
55
 
      self.haveRoot = True
56
 
 
57
 
    try:
58
 
      verifyRoot()
59
 
    except:
 
44
      self.connectRemoteADB()
 
45
 
 
46
    # verify that we can connect to the device. can't continue
 
47
    self.verifyDevice()
 
48
 
 
49
    # Can we use run-as? (currently not required)
 
50
    try:
 
51
      self.verifyRunAs()
 
52
    except DMError:
 
53
      pass
 
54
 
 
55
    # Can we run things as root? (currently not required)
 
56
    useRunAsTmp = self.useRunAs
 
57
    self.useRunAs = False
 
58
    try:
 
59
      self.verifyRoot()
 
60
    except DMError, e:
60
61
      try:
61
62
        self.checkCmd(["root"])
62
63
        # The root command does not fail even if ADB cannot get
63
64
        # root rights (e.g. due to production builds), so we have
64
65
        # to check again ourselves that we have root now.
65
 
        verifyRoot()
66
 
      except:
67
 
        if (self.useRunAs):
 
66
        self.verifyRoot()
 
67
      except DMError:
 
68
        if useRunAsTmp:
68
69
          print "restarting as root failed, but run-as available"
69
70
        else:
70
71
          print "restarting as root failed"
 
72
    self.useRunAs = useRunAsTmp
 
73
 
 
74
    # can we use zip to speed up some file operations? (currently not
 
75
    # required)
 
76
    try:
 
77
      self.verifyZip()
 
78
    except DMError:
 
79
      pass
 
80
 
 
81
  def __del__(self):
 
82
    if self.host:
 
83
      self.disconnectRemoteADB()
71
84
 
72
85
  # external function: executes shell command on device
73
86
  # returns:
94
107
      cmdline = envstr + "; " + cmdline
95
108
 
96
109
    # all output should be in stdout
97
 
    proc = subprocess.Popen(["adb", "shell", cmdline],
 
110
    args=[self.adbPath]
 
111
    if self.deviceSerial:
 
112
        args.extend(['-s', self.deviceSerial])
 
113
    args.extend(["shell", cmdline])
 
114
    proc = subprocess.Popen(args,
98
115
                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
99
116
    (stdout, stderr) = proc.communicate()
100
117
    outputfile.write(stdout.rstrip('\n'))
147
164
  #  failure: None
148
165
  def mkDir(self, name):
149
166
    try:
150
 
      self.checkCmdAs(["shell", "mkdir", name])
 
167
      result = self.runCmdAs(["shell", "mkdir", name]).stdout.read()
 
168
      if 'read-only file system' in result.lower():
 
169
        return None
151
170
      self.chmodDir(name)
152
171
      return name
153
172
    except:
298
317
              return []
299
318
          if (data[0].find("Not a directory") != -1):
300
319
              return []
 
320
          if (data[0].find("Permission denied") != -1):
 
321
              return []
 
322
          if (data[0].find("opendir failed") != -1):
 
323
              return []
301
324
      return data
302
325
 
303
326
  # external function
374
397
 
375
398
  # external function
376
399
  # returns:
377
 
  #  success: output from testagent
378
 
  #  failure: None
379
 
  def killProcess(self, appname):
 
400
  #  success: True
 
401
  #  failure: False
 
402
  def killProcess(self, appname, forceKill=False):
380
403
    procs = self.getProcessList()
 
404
    didKillProcess = False
381
405
    for (pid, name, user) in procs:
382
406
      if name == appname:
383
 
        p = self.runCmdAs(["shell", "kill", pid])
384
 
        return p.stdout.read()
 
407
         args = ["shell", "kill"]
 
408
         if forceKill:
 
409
           args.append("-9")
 
410
         args.append(pid)
 
411
         p = self.runCmdAs(args)
 
412
         didKillProcess = True
385
413
 
386
 
    return None
 
414
    return didKillProcess
387
415
 
388
416
  # external function
389
417
  # returns:
416
444
      outerr = self.runCmd(["pull",  remoteFile, localFile]).communicate()
417
445
 
418
446
      # 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])
 
447
      if outerr[1]:
 
448
        errl = outerr[1].splitlines()
 
449
        if (len(errl) == 1):
 
450
          if (((errl[0].find("Permission denied") != -1)
 
451
            or (errl[0].find("does not exist") != -1))
 
452
            and self.useRunAs):
 
453
            # If we lack permissions to read but have run-as, then we should try
 
454
            # to copy the file to a world-readable location first before attempting
 
455
            # to pull it again.
 
456
            remoteTmpFile = self.getTempDir() + "/" + os.path.basename(remoteFile)
 
457
            self.checkCmdAs(["shell", "dd", "if=" + remoteFile, "of=" + remoteTmpFile])
 
458
            self.checkCmdAs(["shell", "chmod", "777", remoteTmpFile])
 
459
            self.runCmd(["pull",  remoteTmpFile, localFile]).stdout.read()
 
460
            # Clean up temporary file
 
461
            self.checkCmdAs(["shell", "rm", remoteTmpFile])
433
462
 
434
463
      f = open(localFile)
435
464
      ret = f.read()
436
465
      f.close()
437
 
      return ret;      
 
466
      return ret
438
467
    except:
439
468
      return None
440
469
 
515
544
    testRoot = "/data/local/tests"
516
545
    if (self.dirExists(testRoot)):
517
546
      return testRoot
 
547
 
518
548
    root = "/mnt/sdcard"
519
 
    if (not self.dirExists(root)):
520
 
      root = "/data/local"
521
 
    testRoot = root + "/tests"
 
549
    if self.dirExists(root):
 
550
      testRoot = root + "/tests"
 
551
      if self.mkDir(testRoot):
 
552
        return testRoot
 
553
 
 
554
    testRoot = "/data/local/tests"
522
555
    if (not self.dirExists(testRoot)):
523
556
      self.mkDir(testRoot)
524
557
    return testRoot
663
696
  def runCmd(self, args):
664
697
    # If we are not root but have run-as, and we're trying to execute 
665
698
    # a shell command then using run-as is the best we can do
 
699
    finalArgs = [self.adbPath]
 
700
    if self.deviceSerial:
 
701
      finalArgs.extend(['-s', self.deviceSerial])
666
702
    if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"):
667
703
      args.insert(1, "run-as")
668
704
      args.insert(2, self.packageName)
669
 
    args.insert(0, "adb")
670
 
    return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
 
705
    finalArgs.extend(args)
 
706
    return subprocess.Popen(finalArgs, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
671
707
 
672
708
  def runCmdAs(self, args):
673
709
    if self.useRunAs:
678
714
  def checkCmd(self, args):
679
715
    # If we are not root but have run-as, and we're trying to execute 
680
716
    # a shell command then using run-as is the best we can do
 
717
    finalArgs = [self.adbPath]
 
718
    if self.deviceSerial:
 
719
      finalArgs.extend(['-s', self.deviceSerial])
681
720
    if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"):
682
721
      args.insert(1, "run-as")
683
722
      args.insert(2, self.packageName)
684
 
    args.insert(0, "adb")
685
 
    return subprocess.check_call(args)
 
723
    finalArgs.extend(args)
 
724
    return subprocess.check_call(finalArgs)
686
725
 
687
726
  def checkCmdAs(self, args):
688
727
    if (self.useRunAs):
707
746
 
708
747
  def verifyADB(self):
709
748
    # Check to see if adb itself can be executed.
710
 
    try:
711
 
      self.runCmd(["version"])
712
 
    except:
713
 
      print "unable to execute ADB: ensure Android SDK is installed and adb is in your $PATH"
714
 
    
 
749
    if self.adbPath != 'adb':
 
750
      if not os.access(self.adbPath, os.X_OK):
 
751
        raise DMError("invalid adb path, or adb not executable: %s", self.adbPath)
 
752
 
 
753
    try:
 
754
      self.checkCmd(["version"])
 
755
    except os.error, err:
 
756
      raise DMError("unable to execute ADB (%s): ensure Android SDK is installed and adb is in your $PATH" % err)
 
757
    except subprocess.CalledProcessError:
 
758
      raise DMError("unable to execute ADB: ensure Android SDK is installed and adb is in your $PATH")
 
759
 
 
760
  def verifyDevice(self):
 
761
    # If there is a device serial number, see if adb is connected to it
 
762
    if self.deviceSerial:
 
763
      deviceStatus = None
 
764
      proc = subprocess.Popen([self.adbPath, "devices"],
 
765
                              stdout=subprocess.PIPE,
 
766
                              stderr=subprocess.STDOUT)
 
767
      for line in proc.stdout:
 
768
        m = re.match('(.+)?\s+(.+)$', line)
 
769
        if m:
 
770
          if self.deviceSerial == m.group(1):
 
771
            deviceStatus = m.group(2)
 
772
      if deviceStatus == None:
 
773
        raise DMError("device not found: %s" % self.deviceSerial)
 
774
      elif deviceStatus != "device":
 
775
        raise DMError("bad status for device %s: %s" % (self.deviceSerial,
 
776
                                                        deviceStatus))
 
777
 
 
778
    # Check to see if we can connect to device and run a simple command
 
779
    try:
 
780
      self.checkCmd(["shell", "echo"])
 
781
    except subprocess.CalledProcessError:
 
782
      raise DMError("unable to connect to device: is it plugged in?")
 
783
 
 
784
  def verifyRoot(self):
 
785
    # a test to see if we have root privs
 
786
    files = self.listFiles("/data/data")
 
787
    if (len(files) == 0):
 
788
      print "NOT running as root"
 
789
      raise DMError("not running as root")
 
790
 
 
791
    self.haveRoot = True
 
792
 
715
793
  def isCpAvailable(self):
716
794
    # Some Android systems may not have a cp command installed,
717
 
    # or it may not be executable by the user. 
 
795
    # or it may not be executable by the user.
718
796
    data = self.runCmd(["shell", "cp"]).stdout.read()
719
797
    if (re.search('Usage', data)):
720
798
      return True
727
805
      print "unable to execute 'cp' on device; consider installing busybox from Android Market"
728
806
      return False
729
807
 
730
 
  def verifyRunAs(self, packageName):
 
808
  def verifyRunAs(self):
731
809
    # If a valid package name is available, and certain other
732
810
    # conditions are met, devicemanagerADB can execute file operations
733
811
    # via the "run-as" command, so that pushed files and directories 
737
815
    # file copy via run-as.
738
816
    self.useRunAs = False
739
817
    devroot = self.getDeviceRoot()
740
 
    if (packageName and self.isCpAvailable() and devroot):
 
818
    if (self.packageName and self.isCpAvailable() and devroot):
741
819
      tmpDir = self.getTempDir()
742
820
 
743
821
      # The problem here is that run-as doesn't cause a non-zero exit code
744
822
      # 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]):
 
823
      runAsOut = self.runCmd(["shell", "run-as", self.packageName, "mkdir", devroot + "/sanity"]).communicate()[0]
 
824
      if runAsOut.startswith("run-as:") and ("not debuggable" in runAsOut or
 
825
                                             "is unknown" in runAsOut):
748
826
        raise DMError("run-as failed sanity check")
749
827
 
750
 
      self.checkCmd(["push", os.path.abspath(sys.argv[0]), tmpDir + "/tmpfile"])
 
828
      tmpfile = tempfile.NamedTemporaryFile()
 
829
      self.checkCmd(["push", tmpfile.name, tmpDir + "/tmpfile"])
751
830
      if self.useDDCopy:
752
 
        self.checkCmd(["shell", "run-as", packageName, "dd", "if=" + tmpDir + "/tmpfile", "of=" + devroot + "/sanity/tmpfile"])
 
831
        self.checkCmd(["shell", "run-as", self.packageName, "dd", "if=" + tmpDir + "/tmpfile", "of=" + devroot + "/sanity/tmpfile"])
753
832
      else:
754
 
        self.checkCmd(["shell", "run-as", packageName, "cp", tmpDir + "/tmpfile", devroot + "/sanity"])
 
833
        self.checkCmd(["shell", "run-as", self.packageName, "cp", tmpDir + "/tmpfile", devroot + "/sanity"])
755
834
      if (self.fileExists(devroot + "/sanity/tmpfile")):
756
 
        print "will execute commands via run-as " + packageName
757
 
        self.packageName = packageName
 
835
        print "will execute commands via run-as " + self.packageName
758
836
        self.useRunAs = True
759
837
      self.checkCmd(["shell", "rm", devroot + "/tmp/tmpfile"])
760
 
      self.checkCmd(["shell", "run-as", packageName, "rm", "-r", devroot + "/sanity"])
761
 
      
 
838
      self.checkCmd(["shell", "run-as", self.packageName, "rm", "-r", devroot + "/sanity"])
 
839
 
762
840
  def isUnzipAvailable(self):
763
841
    data = self.runCmdAs(["shell", "unzip"]).stdout.read()
764
842
    if (re.search('Usage', data)):
781
859
    if (self.isUnzipAvailable() and self.isLocalZipAvailable()):
782
860
      print "will use zip to push directories"
783
861
      self.useZip = True
 
862
    else:
 
863
      raise DMError("zip not available")