adb_commands.py revision f2477e01787aa58f445919b809d89e252beef54f
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 5thin(ish) wrapper around adb.""" 6 7import logging 8import os 9import shutil 10import stat 11import sys 12 13from telemetry.core import util 14from telemetry.core.platform.profiler import android_prebuilt_profiler_helper 15 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. 19 20util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') 21try: 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 27except Exception: 28 android_commands = None 29 30 31def IsAndroidSupported(): 32 return android_commands != None 33 34 35def GetAttachedDevices(): 36 """Returns a list of attached, online android devices. 37 38 If a preferred device has been set with ANDROID_SERIAL, it will be first in 39 the returned list.""" 40 return android_commands.GetAttachedDevices() 41 42 43def AllocateTestServerPort(): 44 return ports.AllocateTestServerPort() 45 46 47def ResetTestServerPortAllocation(): 48 return ports.ResetTestServerPortAllocation() 49 50 51class AdbCommands(object): 52 """A thin wrapper around ADB""" 53 54 def __init__(self, device): 55 self._adb = android_commands.AndroidCommands(device) 56 self._device = device 57 58 def device(self): 59 return self._device 60 61 def Adb(self): 62 return self._adb 63 64 def Forward(self, local, remote): 65 ret = self._adb.Adb().SendCommand('forward %s %s' % (local, remote)) 66 assert ret == '' 67 68 def RunShellCommand(self, command, timeout_time=20, log_result=False): 69 """Send a command to the adb shell and return the result. 70 71 Args: 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 77 shell command. 78 79 Returns: 80 list containing the lines of output received from running the command 81 """ 82 return self._adb.RunShellCommand(command, timeout_time, log_result) 83 84 def CloseApplication(self, package): 85 """Attempt to close down the application, using increasing violence. 86 87 Args: 88 package: Name of the process to kill off, e.g. 89 com.google.android.apps.chrome 90 """ 91 self._adb.CloseApplication(package) 92 93 def KillAll(self, process): 94 """Android version of killall, connected via adb. 95 96 Args: 97 process: name of the process to kill off 98 99 Returns: 100 the number of processess killed 101 """ 102 return self._adb.KillAll(process) 103 104 def ExtractPid(self, process_name): 105 """Extracts Process Ids for a given process name from Android Shell. 106 107 Args: 108 process_name: name of the process on the device. 109 110 Returns: 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. 114 """ 115 return self._adb.ExtractPid(process_name) 116 117 def Install(self, apk_path): 118 """Installs specified package if necessary. 119 120 Args: 121 apk_path: Path to .apk file to install. 122 """ 123 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') 130 131 apk_package_name = apk_helper.GetPackageName(apk_path) 132 return self._adb.ManagedInstall(apk_path, package_name=apk_package_name) 133 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, 138 flags=None): 139 """Starts |package|'s activity on the device. 140 141 Args: 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. 151 """ 152 return self._adb.StartActivity(package, activity, wait_for_completion, 153 action, 154 category, data, 155 extras, trace_file_name, 156 flags) 157 158 def Push(self, local, remote): 159 return self._adb.Adb().Push(local, remote) 160 161 def Pull(self, remote, local): 162 return self._adb.Adb().Pull(remote, local) 163 164 def FileExistsOnDevice(self, file_name): 165 return self._adb.FileExistsOnDevice(file_name) 166 167 def IsRootEnabled(self): 168 return self._adb.IsRootEnabled() 169 170 def GoHome(self): 171 return self._adb.GoHome() 172 173 def RestartAdbdOnDevice(self): 174 return self._adb.RestartAdbdOnDevice() 175 176 177def GetBuildTypeOfPath(path): 178 if not path: 179 return None 180 for build_dir, build_type in util.GetBuildDirectories(): 181 if os.path.join(build_dir, build_type) in path: 182 return build_type 183 return None 184 185 186def SetupPrebuiltTools(adb): 187 # TODO(bulach): build the host tools for mac, and the targets for x86/mips. 188 # Prebuilt tools from r226197. 189 has_prebuilt = sys.platform.startswith('linux') 190 if has_prebuilt: 191 abi = adb.RunShellCommand('getprop ro.product.cpu.abi') 192 has_prebuilt = abi and abi[0].startswith('armeabi') 193 if not has_prebuilt: 194 logging.error( 195 'Prebuilt android tools only available for Linux host and ARM device.') 196 return False 197 198 prebuilt_tools = [ 199 'forwarder_dist/device_forwarder', 200 'host_forwarder', 201 'md5sum_dist/md5sum_bin', 202 'md5sum_bin_host', 203 'purge_ashmem', 204 ] 205 build_type = None 206 for t in prebuilt_tools: 207 src = os.path.basename(t) 208 android_prebuilt_profiler_helper.GetIfChanged(src) 209 bin_path = util.FindSupportBinary(t) 210 if not build_type: 211 build_type = GetBuildTypeOfPath(bin_path) or 'Release' 212 constants.SetBuildType(build_type) 213 dest = os.path.join(constants.GetOutDirectory(), t) 214 if not bin_path: 215 logging.warning('Setting up prebuilt %s', dest) 216 if not os.path.exists(os.path.dirname(dest)): 217 os.makedirs(os.path.dirname(dest)) 218 prebuilt_path = android_prebuilt_profiler_helper.GetHostPath(src) 219 if not os.path.exists(prebuilt_path): 220 raise NotImplementedError(""" 221%s must be checked into cloud storage. 222Instructions: 223http://www.chromium.org/developers/telemetry/upload_to_cloud_storage 224""" % t) 225 shutil.copyfile(prebuilt_path, dest) 226 os.chmod(dest, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) 227 return True 228 229 230class Forwarder(object): 231 def __init__(self, adb, *port_pairs): 232 self._adb = adb.Adb() 233 self._host_port = port_pairs[0].local_port 234 235 new_port_pairs = [(port_pair.local_port, port_pair.remote_port) 236 for port_pair in port_pairs] 237 238 self._port_pairs = new_port_pairs 239 forwarder.Forwarder.Map(new_port_pairs, self._adb) 240 241 @property 242 def url(self): 243 return 'http://127.0.0.1:%i' % self._host_port 244 245 def Close(self): 246 for (device_port, _) in self._port_pairs: 247 forwarder.Forwarder.UnmapDevicePort(device_port, self._adb) 248