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 27 from pylib.utils import test_environment # pylint: disable=F0401 28except Exception: 29 android_commands = None 30 31 32def IsAndroidSupported(): 33 return android_commands != None 34 35 36def GetAttachedDevices(): 37 """Returns a list of attached, online android devices. 38 39 If a preferred device has been set with ANDROID_SERIAL, it will be first in 40 the returned list.""" 41 return android_commands.GetAttachedDevices() 42 43 44def CleanupLeftoverProcesses(): 45 test_environment.CleanupLeftoverProcesses() 46 47 48def AllocateTestServerPort(): 49 return ports.AllocateTestServerPort() 50 51 52def ResetTestServerPortAllocation(): 53 return ports.ResetTestServerPortAllocation() 54 55 56class AdbCommands(object): 57 """A thin wrapper around ADB""" 58 59 def __init__(self, device): 60 self._adb = android_commands.AndroidCommands(device, api_strict_mode=True) 61 self._device = device 62 63 def device(self): 64 return self._device 65 66 @property 67 def system_properties(self): 68 return self._adb.system_properties 69 70 def Adb(self): 71 return self._adb 72 73 def Forward(self, local, remote): 74 ret = self._adb.Adb().SendCommand('forward %s %s' % (local, remote)) 75 assert ret == '' 76 77 def RunShellCommand(self, command, timeout_time=20, log_result=False): 78 """Send a command to the adb shell and return the result. 79 80 Args: 81 command: String containing the shell command to send. Must not include 82 the single quotes as we use them to escape the whole command. 83 timeout_time: Number of seconds to wait for command to respond before 84 retrying, used by AdbInterface.SendShellCommand. 85 log_result: Boolean to indicate whether we should log the result of the 86 shell command. 87 88 Returns: 89 list containing the lines of output received from running the command 90 """ 91 return self._adb.RunShellCommand(command, timeout_time, log_result) 92 93 def RunShellCommandWithSU(self, command, timeout_time=20, log_result=False): 94 return self._adb.RunShellCommandWithSU(command, timeout_time, log_result) 95 96 def CloseApplication(self, package): 97 """Attempt to close down the application, using increasing violence. 98 99 Args: 100 package: Name of the process to kill off, e.g. 101 com.google.android.apps.chrome 102 """ 103 self._adb.CloseApplication(package) 104 105 def KillAll(self, process): 106 """Android version of killall, connected via adb. 107 108 Args: 109 process: name of the process to kill off 110 111 Returns: 112 the number of processess killed 113 """ 114 return self._adb.KillAll(process) 115 116 def ExtractPid(self, process_name): 117 """Extracts Process Ids for a given process name from Android Shell. 118 119 Args: 120 process_name: name of the process on the device. 121 122 Returns: 123 List of all the process ids (as strings) that match the given name. 124 If the name of a process exactly matches the given name, the pid of 125 that process will be inserted to the front of the pid list. 126 """ 127 return self._adb.ExtractPid(process_name) 128 129 def Install(self, apk_path): 130 """Installs specified package if necessary. 131 132 Args: 133 apk_path: Path to .apk file to install. 134 """ 135 136 if (os.path.exists(os.path.join( 137 constants.GetOutDirectory('Release'), 'md5sum_bin_host'))): 138 constants.SetBuildType('Release') 139 elif (os.path.exists(os.path.join( 140 constants.GetOutDirectory('Debug'), 'md5sum_bin_host'))): 141 constants.SetBuildType('Debug') 142 143 apk_package_name = apk_helper.GetPackageName(apk_path) 144 return self._adb.ManagedInstall(apk_path, package_name=apk_package_name) 145 146 def StartActivity(self, package, activity, wait_for_completion=False, 147 action='android.intent.action.VIEW', 148 category=None, data=None, 149 extras=None, trace_file_name=None, 150 flags=None): 151 """Starts |package|'s activity on the device. 152 153 Args: 154 package: Name of package to start (e.g. 'com.google.android.apps.chrome'). 155 activity: Name of activity (e.g. '.Main' or 156 'com.google.android.apps.chrome.Main'). 157 wait_for_completion: wait for the activity to finish launching (-W flag). 158 action: string (e.g. 'android.intent.action.MAIN'). Default is VIEW. 159 category: string (e.g. 'android.intent.category.HOME') 160 data: Data string to pass to activity (e.g. 'http://www.example.com/'). 161 extras: Dict of extras to pass to activity. Values are significant. 162 trace_file_name: If used, turns on and saves the trace to this file name. 163 """ 164 return self._adb.StartActivity(package, activity, wait_for_completion, 165 action, 166 category, data, 167 extras, trace_file_name, 168 flags) 169 170 def Push(self, local, remote): 171 return self._adb.Adb().Push(local, remote) 172 173 def Pull(self, remote, local): 174 return self._adb.Adb().Pull(remote, local) 175 176 def FileExistsOnDevice(self, file_name): 177 return self._adb.FileExistsOnDevice(file_name) 178 179 def IsRootEnabled(self): 180 return self._adb.IsRootEnabled() 181 182 def GoHome(self): 183 return self._adb.GoHome() 184 185 def RestartAdbdOnDevice(self): 186 return self._adb.RestartAdbdOnDevice() 187 188 def IsUserBuild(self): 189 return self._adb.GetBuildType() == 'user' 190 191 192def GetBuildTypeOfPath(path): 193 if not path: 194 return None 195 for build_dir, build_type in util.GetBuildDirectories(): 196 if os.path.join(build_dir, build_type) in path: 197 return build_type 198 return None 199 200 201def SetupPrebuiltTools(adb): 202 # TODO(bulach): build the host tools for mac, and the targets for x86/mips. 203 # Prebuilt tools from r226197. 204 has_prebuilt = sys.platform.startswith('linux') 205 if has_prebuilt: 206 abi = adb.system_properties['ro.product.cpu.abi'] 207 has_prebuilt = abi.startswith('armeabi') 208 if not has_prebuilt: 209 logging.error( 210 'Prebuilt android tools only available for Linux host and ARM device.') 211 return False 212 213 prebuilt_tools = [ 214 'forwarder_dist/device_forwarder', 215 'host_forwarder', 216 'md5sum_dist/md5sum_bin', 217 'md5sum_bin_host', 218 'purge_ashmem', 219 ] 220 build_type = None 221 for t in prebuilt_tools: 222 src = os.path.basename(t) 223 android_prebuilt_profiler_helper.GetIfChanged(src) 224 bin_path = util.FindSupportBinary(t) 225 if not build_type: 226 build_type = GetBuildTypeOfPath(bin_path) or 'Release' 227 constants.SetBuildType(build_type) 228 dest = os.path.join(constants.GetOutDirectory(), t) 229 if not bin_path: 230 logging.warning('Setting up prebuilt %s', dest) 231 if not os.path.exists(os.path.dirname(dest)): 232 os.makedirs(os.path.dirname(dest)) 233 prebuilt_path = android_prebuilt_profiler_helper.GetHostPath(src) 234 if not os.path.exists(prebuilt_path): 235 raise NotImplementedError(""" 236%s must be checked into cloud storage. 237Instructions: 238http://www.chromium.org/developers/telemetry/upload_to_cloud_storage 239""" % t) 240 shutil.copyfile(prebuilt_path, dest) 241 os.chmod(dest, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) 242 return True 243 244 245class Forwarder(object): 246 def __init__(self, adb, *port_pairs): 247 self._adb = adb.Adb() 248 self._host_port = port_pairs[0].local_port 249 250 new_port_pairs = [(port_pair.local_port, port_pair.remote_port) 251 for port_pair in port_pairs] 252 253 self._port_pairs = new_port_pairs 254 forwarder.Forwarder.Map(new_port_pairs, self._adb) 255 256 @property 257 def url(self): 258 return 'http://127.0.0.1:%i' % self._host_port 259 260 def Close(self): 261 for (device_port, _) in self._port_pairs: 262 forwarder.Forwarder.UnmapDevicePort(device_port, self._adb) 263