1# Copyright (c) 2015 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import optparse
6import subprocess
7import sys
8
9
10class OptionParserIgnoreErrors(optparse.OptionParser):
11  """Wrapper for OptionParser that ignores errors and produces no output."""
12
13  def error(self, msg):
14    pass
15
16  def exit(self):
17    pass
18
19  def print_usage(self):
20    pass
21
22  def print_help(self):
23    pass
24
25  def print_version(self):
26    pass
27
28
29def add_adb_serial(adb_command, device_serial):
30  if device_serial is not None:
31    adb_command.insert(1, device_serial)
32    adb_command.insert(1, '-s')
33
34
35def construct_adb_shell_command(shell_args, device_serial):
36  adb_command = ['adb', 'shell', ' '.join(shell_args)]
37  add_adb_serial(adb_command, device_serial)
38  return adb_command
39
40
41def run_adb_shell(shell_args, device_serial):
42  """Runs "adb shell" with the given arguments.
43
44  Args:
45    shell_args: array of arguments to pass to adb shell.
46    device_serial: if not empty, will add the appropriate command-line
47        parameters so that adb targets the given device.
48  Returns:
49    A tuple containing the adb output (stdout & stderr) and the return code
50    from adb.  Will exit if adb fails to start.
51  """
52  adb_command = construct_adb_shell_command(shell_args, device_serial)
53
54  adb_output = []
55  adb_return_code = 0
56  try:
57    adb_output = subprocess.check_output(adb_command, stderr=subprocess.STDOUT,
58                                         shell=False, universal_newlines=True)
59  except OSError as error:
60    # This usually means that the adb executable was not found in the path.
61    print >> sys.stderr, ('\nThe command "%s" failed with the following error:'
62                          % ' '.join(adb_command))
63    print >> sys.stderr, '    %s\n' % str(error)
64    print >> sys.stderr, 'Is adb in your path?'
65    sys.exit(1)
66  except subprocess.CalledProcessError as error:
67    # The process exited with an error.
68    adb_return_code = error.returncode
69    adb_output = error.output
70
71  return (adb_output, adb_return_code)
72
73
74def get_device_sdk_version():
75  """Uses adb to attempt to determine the SDK version of a running device."""
76
77  getprop_args = ['getprop', 'ro.build.version.sdk']
78
79  # get_device_sdk_version() is called before we even parse our command-line
80  # args.  Therefore, parse just the device serial number part of the
81  # command-line so we can send the adb command to the correct device.
82  parser = OptionParserIgnoreErrors()
83  parser.add_option('-e', '--serial', dest='device_serial', type='string')
84  options, unused_args = parser.parse_args()
85
86  success = False
87
88  adb_output, adb_return_code = run_adb_shell(getprop_args,
89                                              options.device_serial)
90
91  if adb_return_code == 0:
92    # ADB may print output other than the version number (e.g. it chould
93    # print a message about starting the ADB server).
94    # Break the ADB output into white-space delimited segments.
95    parsed_output = str.split(adb_output)
96    if parsed_output:
97      # Assume that the version number is the last thing printed by ADB.
98      version_string = parsed_output[-1]
99      if version_string:
100        try:
101          # Try to convert the text into an integer.
102          version = int(version_string)
103        except ValueError:
104          version = -1
105        else:
106          success = True
107
108  if not success:
109    print >> sys.stderr, (
110        '\nThe command "%s" failed with the following message:'
111        % ' '.join(getprop_args))
112    print >> sys.stderr, adb_output
113    sys.exit(1)
114
115  return version
116