1 # Copyright 2013 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 """Brings in Chrome Android's android_commands module, which itself is a
5 thin(ish) wrapper around adb."""
13 from telemetry.core import util
14 from telemetry.core.platform.profiler import android_prebuilt_profiler_helper
16 # This is currently a thin wrapper around Chrome Android's
17 # build scripts, located in chrome/build/android. This file exists mainly to
18 # deal with locating the module.
20 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
22 from pylib import android_commands # pylint: disable=F0401
23 from pylib import constants # pylint: disable=F0401
24 from pylib import forwarder # pylint: disable=F0401
25 from pylib import ports # pylint: disable=F0401
26 from pylib.utils import apk_helper # #pylint: disable=F0401
28 android_commands = None
31 def IsAndroidSupported():
32 return android_commands != None
35 def GetAttachedDevices():
36 """Returns a list of attached, online android devices.
38 If a preferred device has been set with ANDROID_SERIAL, it will be first in
40 return android_commands.GetAttachedDevices()
43 def AllocateTestServerPort():
44 return ports.AllocateTestServerPort()
47 def ResetTestServerPortAllocation():
48 return ports.ResetTestServerPortAllocation()
51 class AdbCommands(object):
52 """A thin wrapper around ADB"""
54 def __init__(self, device):
55 self._adb = android_commands.AndroidCommands(device)
64 def Forward(self, local, remote):
65 ret = self._adb.Adb().SendCommand('forward %s %s' % (local, remote))
68 def RunShellCommand(self, command, timeout_time=20, log_result=False):
69 """Send a command to the adb shell and return the result.
72 command: String containing the shell command to send. Must not include
73 the single quotes as we use them to escape the whole command.
74 timeout_time: Number of seconds to wait for command to respond before
75 retrying, used by AdbInterface.SendShellCommand.
76 log_result: Boolean to indicate whether we should log the result of the
80 list containing the lines of output received from running the command
82 return self._adb.RunShellCommand(command, timeout_time, log_result)
84 def CloseApplication(self, package):
85 """Attempt to close down the application, using increasing violence.
88 package: Name of the process to kill off, e.g.
89 com.google.android.apps.chrome
91 self._adb.CloseApplication(package)
93 def KillAll(self, process):
94 """Android version of killall, connected via adb.
97 process: name of the process to kill off
100 the number of processess killed
102 return self._adb.KillAll(process)
104 def ExtractPid(self, process_name):
105 """Extracts Process Ids for a given process name from Android Shell.
108 process_name: name of the process on the device.
111 List of all the process ids (as strings) that match the given name.
112 If the name of a process exactly matches the given name, the pid of
113 that process will be inserted to the front of the pid list.
115 return self._adb.ExtractPid(process_name)
117 def Install(self, apk_path):
118 """Installs specified package if necessary.
121 apk_path: Path to .apk file to install.
124 if (os.path.exists(os.path.join(
125 constants.GetOutDirectory('Release'), 'md5sum_bin_host'))):
126 constants.SetBuildType('Release')
127 elif (os.path.exists(os.path.join(
128 constants.GetOutDirectory('Debug'), 'md5sum_bin_host'))):
129 constants.SetBuildType('Debug')
131 apk_package_name = apk_helper.GetPackageName(apk_path)
132 return self._adb.ManagedInstall(apk_path, package_name=apk_package_name)
134 def StartActivity(self, package, activity, wait_for_completion=False,
135 action='android.intent.action.VIEW',
136 category=None, data=None,
137 extras=None, trace_file_name=None,
139 """Starts |package|'s activity on the device.
142 package: Name of package to start (e.g. 'com.google.android.apps.chrome').
143 activity: Name of activity (e.g. '.Main' or
144 'com.google.android.apps.chrome.Main').
145 wait_for_completion: wait for the activity to finish launching (-W flag).
146 action: string (e.g. 'android.intent.action.MAIN'). Default is VIEW.
147 category: string (e.g. 'android.intent.category.HOME')
148 data: Data string to pass to activity (e.g. 'http://www.example.com/').
149 extras: Dict of extras to pass to activity. Values are significant.
150 trace_file_name: If used, turns on and saves the trace to this file name.
152 return self._adb.StartActivity(package, activity, wait_for_completion,
155 extras, trace_file_name,
158 def Push(self, local, remote):
159 return self._adb.Adb().Push(local, remote)
161 def Pull(self, remote, local):
162 return self._adb.Adb().Pull(remote, local)
164 def FileExistsOnDevice(self, file_name):
165 return self._adb.FileExistsOnDevice(file_name)
167 def IsRootEnabled(self):
168 return self._adb.IsRootEnabled()
171 return self._adb.GoHome()
174 def SetupPrebuiltTools(device):
175 # TODO(bulach): build the host tools for mac, and the targets for x86/mips.
176 # Prebuilt tools from r226197.
177 has_prebuilt = sys.platform.startswith('linux')
179 adb = AdbCommands(device)
180 abi = adb.RunShellCommand('getprop ro.product.cpu.abi')
181 has_prebuilt = abi and abi[0].startswith('armeabi')
183 logging.error('Prebuilt tools only available for ARM.')
187 'forwarder_dist/device_forwarder',
189 'md5sum_dist/md5sum_bin',
192 for t in prebuilt_tools:
193 src = os.path.basename(t)
194 android_prebuilt_profiler_helper.GetIfChanged(src)
195 dest = os.path.join(constants.GetOutDirectory(), t)
196 if not os.path.exists(dest):
197 logging.warning('Setting up prebuilt %s', dest)
198 if not os.path.exists(os.path.dirname(dest)):
199 os.makedirs(os.path.dirname(dest))
200 shutil.copyfile(android_prebuilt_profiler_helper.GetHostPath(src), dest)
201 os.chmod(dest, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
204 def HasForwarder(buildtype=None):
206 return (HasForwarder(buildtype='Release') or
207 HasForwarder(buildtype='Debug'))
208 device_forwarder = os.path.join(
209 constants.GetOutDirectory(build_type=buildtype),
210 'forwarder_dist', 'device_forwarder')
211 host_forwarder = os.path.join(
212 constants.GetOutDirectory(build_type=buildtype), 'host_forwarder')
213 return os.path.exists(device_forwarder) and os.path.exists(host_forwarder)
216 class Forwarder(object):
217 def __init__(self, adb, *port_pairs):
218 self._adb = adb.Adb()
219 self._host_port = port_pairs[0].local_port
221 new_port_pairs = [(port_pair.local_port, port_pair.remote_port)
222 for port_pair in port_pairs]
224 self._port_pairs = new_port_pairs
225 if HasForwarder('Release'):
226 constants.SetBuildType('Release')
227 elif HasForwarder('Debug'):
228 constants.SetBuildType('Debug')
230 raise Exception('Build forwarder2')
231 forwarder.Forwarder.Map(new_port_pairs, self._adb)
235 return 'http://127.0.0.1:%i' % self._host_port
238 for (device_port, _) in self._port_pairs:
239 forwarder.Forwarder.UnmapDevicePort(device_port, self._adb)