1# Copyright (C) 2016 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15'''Module that contains the class UtilBundle, representing a collection of RS 16binaries.''' 17 18from __future__ import absolute_import 19 20import os 21import time 22from . import util_constants 23from . import util_log 24from .exception import TestSuiteException 25 26 27class UtilBundle(object): 28 '''Represents the collection of RS binaries that are debugged.''' 29 30 # Map of binary name to package name of all Java apps debugged 31 _tests_apk = { 32 'JavaInfiniteLoop': 'com.android.rs.infiniteloop', 33 'JavaDebugWaitAttach': 'com.android.rs.waitattachdebug', 34 'JavaNoDebugWaitAttach': 'com.android.rs.waitattachnodebug', 35 'BranchingFunCalls': 'com.android.rs.branchingfuncalls', 36 'KernelVariables': 'com.android.rs.kernelvariables', 37 'Allocations': 'com.android.rs.allocations', 38 'MultipleRSFiles': 'com.android.rs.multiplersfiles', 39 'SingleSource': 'com.android.rs.singlesource', 40 'ScriptGroup': 'com.android.rs.scriptgroup', 41 'Reduction': 'com.android.rs.lldbreductiontest', 42 } 43 44 _tests_jni = { 45 'JNIInfiniteLoop': 'com.android.rs.jniinfiniteloop', 46 'JNIDebugWaitAttach': 'com.android.rs.jnidebugwaitattach', 47 'JNINoDebugWaitAttach': 'com.android.rs.jninodebugwaitattach', 48 'JNIBranchingFunCalls': 'com.android.rs.jnibranchingfuncalls', 49 'JNIKernelVariables': 'com.android.rs.jnikernelvariables', 50 'JNIAllocations': 'com.android.rs.jniallocations', 51 'JNIMultipleRSFiles': 'com.android.rs.jnimultiplersfiles' 52 } 53 54 _tests_ndk = {'CppInfiniteLoop', 'CppNoDebugWaitAttach', 55 'CppDebugWaitAttach', 'CppBranchingFunCalls', 56 'CppKernelVariables', 'CppAllocations', 'CppMultipleRSFiles'} 57 58 _missing_path_msg = ( 59 'No product path has been provided. If using `lunch` ensure ' 60 'the `ANDROID_PRODUCT_OUT` environment variable has been set correctly. ' 61 'Alternatively, include it in the config file or specify it explicitly ' 62 'on the command line (`--aosp-product-path`)' 63 ) 64 65 def __init__(self, android, aosp_product_path): 66 assert android 67 self._android = android # Link to the android module 68 self._aosp_product_path = aosp_product_path 69 self._log = util_log.get_logger() 70 71 def is_apk(self, name): 72 '''Checks if a binary of a given name is an apk. 73 74 Checks whether the name of the apk is in the dictionary of apks. 75 76 Args: 77 name: The string that is the name of the binary to check. 78 79 Returns: 80 True if the binary is an apk, False if it is not. 81 82 Raises: 83 TestSuiteException: The string does not match any item in the list 84 of APK or NDK binaries. 85 ''' 86 if name in self._tests_apk: 87 return True 88 if name not in self._tests_ndk and name not in self._tests_jni: 89 raise TestSuiteException('test not apk or ndk') 90 return False 91 92 def uninstall_all(self): 93 '''Uninstall/Delete all the testsuite's apks and binaries on the device. 94 95 Raises: 96 TestSuiteException: One or more apks could not be uninstalled. 97 ''' 98 self.uninstall_all_apk() 99 self._delete_all_ndk() 100 self._uninstall_all_jni() 101 102 def uninstall_all_apk(self): 103 '''Uninstall all apks used by the test suite from the device. 104 105 Raises: 106 TestSuiteException: An apk could not be uninstalled. 107 ''' 108 max_num_attempts = 3 109 timeout = 180 110 111 for app, package in self._tests_apk.items(): 112 self._log.info('Uninstalling the application: %s', app) 113 output = self._android.adb_retry('uninstall ' + package, 114 max_num_attempts, timeout) 115 116 if output is None: 117 raise TestSuiteException('Repeated timeouts when uninstalling ' 118 'the application: ' + app) 119 elif 'Success' not in output: 120 outmsg = '\n' + output.rstrip() if output else '<empty>' 121 self._log.error('Cannot match the string "Success" in the ' 122 'output: %s', outmsg) 123 raise TestSuiteException('Unable to uninstall app ' + app) 124 else: 125 self._log.debug('Application uninstalled: %r', app) 126 127 if 'Success' not in output: 128 self._log.warning('unable to uninstall app ' + app) 129 130 def _uninstall_all_jni(self): 131 '''Uninstall all apks used by the test suite from the device. 132 133 Raises: 134 TestSuiteException: An apk could not be uninstalled. 135 ''' 136 for app, package in self._tests_jni.items(): 137 output = self._android.adb('uninstall ' + package) 138 139 if 'Success' not in output: 140 raise TestSuiteException('unable to uninstall app ' + app) 141 142 def _delete_all_ndk(self): 143 '''Delete all ndk binaries that were pushed to the device. 144 145 Raises: 146 TestSuiteException: A binary could not be deleted from the device. 147 ''' 148 for app in self._tests_ndk: 149 output = self._android.shell('rm /data/' + app) 150 if 'No such file or directory' in output: 151 self._log.warning('unable to uninstall app ' + app) 152 153 154 def push_all(self): 155 '''Push all apk and ndk binaries required by the testsuite to the device 156 157 Raises: 158 TestSuiteException: One or more apks could not be installed or 159 previously running processes thereof could not 160 be killed. 161 ''' 162 self._push_all_java() 163 self._push_all_ndk() 164 self._push_all_jni() 165 166 def _install_apk(self, app, package): 167 '''Push an apk files to the device. 168 169 This involves uninstalling any old installation and installing again. 170 171 Args: 172 app: A string that is the name of the apk. 173 package: A string that is the name of the package of the apk. 174 175 Raises: 176 TestSuiteException: The apk could not be installed. 177 ''' 178 self._log.info('pushing {0}'.format(app)) 179 180 self._android.stop_app(package) 181 182 self._android.adb('uninstall ' + package) 183 # Ignore the output of uninstall. 184 # The app may not have been installed in the first place. That's ok. 185 186 flags = '' 187 188 product_folder = self._aosp_product_path 189 if not product_folder: 190 raise TestSuiteException(self._missing_path_msg) 191 192 app_folder = os.path.join(product_folder, 'data/app') 193 194 cmd = 'install {0} {1}/{2}/{2}.apk'.format(flags, app_folder, app) 195 output = self._android.adb(cmd, False, True, 196 util_constants.PUSH_TIMEOUT) 197 if ('Success' not in output) or ("can't find" in output): 198 raise TestSuiteException('unable to install app {}: {}'.format( 199 app, output)) 200 201 def _push_all_java(self): 202 '''Push all apk files to the device. 203 204 This involves uninstalling any old installations and installing again. 205 206 Raises: 207 TestSuiteException: An apk could not be installed. 208 ''' 209 for app, package in self._tests_apk.items(): 210 self._install_apk(app, package) 211 212 def _push_all_ndk(self): 213 '''Push all ndk binaries to the device. 214 215 Raises: 216 TestSuiteException: A binary could not be pushed to the device or 217 a previous process could not be killed. 218 ''' 219 product_folder = self._aosp_product_path 220 if not product_folder: 221 raise TestSuiteException(self._missing_path_msg) 222 223 bin_folder = os.path.join(product_folder, 'system/bin') 224 225 for app in self._tests_ndk: 226 self._log.info('pushing {0}'.format(app)) 227 228 self._android.kill_all_processes(app) 229 230 cmd = 'push %s/%s /data' % (bin_folder, app) 231 output = self._android.adb(cmd, False, True, 232 util_constants.PUSH_TIMEOUT) 233 if ('failed to copy' in output or 234 'No such file or directory' in output): 235 raise TestSuiteException('unable to push binary ' + app) 236 237 # be sure to set the execute bit for NDK binaries 238 self._android.shell('chmod 777 /data/{0}'.format(app)) 239 240 def _push_all_jni(self): 241 '''Push all JNI apk files to the device. 242 243 This involves uninstalling any old installations and installing again. 244 245 Raises: 246 TestSuiteException: An apk could not be installed. 247 ''' 248 product_folder = self._aosp_product_path 249 if not product_folder: 250 raise TestSuiteException(self._missing_path_msg) 251 252 app_folder = os.path.join(product_folder, 'system/lib') 253 254 # Ensure the system/lib directory is writable 255 self._android.make_device_writeable() 256 257 for app, package in self._tests_jni.items(): 258 self._install_apk(app, package) 259 260 def delete_ndk_cache(self): 261 '''Deletes NDK cached scripts from the device. 262 263 The NDK caches compiled scripts as shared libraries in 264 the folder specified when calling `rs->init()`. 265 266 For all out tests this is set to '/data/rscache'. 267 ''' 268 self._android.shell('rm -r /data/rscache') 269 270 def get_package(self, app_name): 271 '''From a given apk name get the name of its package. 272 273 Args: 274 app_name: The string that is the name of the apk. 275 276 Returns: 277 A string representing the name of the package of the app. 278 279 Raises: 280 TestSuiteException: The app name is not in the list of apks. 281 ''' 282 if app_name in self._tests_apk: 283 return self._tests_apk[app_name] 284 elif app_name in self._tests_jni: 285 return self._tests_jni[app_name] 286 else: 287 msg = ('unknown app %s. (Do you need to add an ' 288 'entry to bundle.py :: test_apps_?)' % app_name) 289 raise TestSuiteException(msg) 290 return self._tests_apk[app_name] 291 292 def launch(self, app_name): 293 '''Launch an apk/ndk app on a remote device. 294 295 Args: 296 app_name: The string that is the name of the APK or NDK executable. 297 298 Returns: 299 The Process ID of the launched executable, otherwise None 300 301 Raises: 302 TestSuiteException: Previous processes of this apk could not be 303 killed. 304 ''' 305 process_name = '' 306 success = False 307 if app_name in self._tests_apk: 308 process_name = self._tests_apk[app_name] 309 310 self._android.kill_all_processes(process_name) 311 312 success = self._android.launch_app(process_name, 'MainActivity') 313 elif app_name in self._tests_ndk: 314 process_name = app_name 315 self._android.kill_all_processes(process_name) 316 success = self._android.launch_elf(process_name) 317 elif app_name in self._tests_jni: 318 package = self._tests_jni[app_name] 319 320 self._android.kill_process(package) 321 322 success = self._android.launch_app(package, 'MainActivity') 323 if not success: 324 self._log.log_and_print(app_name + 325 ' is not installed. Try removing the --no-install option?') 326 return None 327 328 return self._android.find_app_pid(package) 329 else: 330 self._log.error('Executable {0} neither Java nor NDK.' 331 .format(app_name)) 332 333 self._log.fatal('Failed to launch test executable {0}' 334 .format(app_name)) 335 return None 336 337 if not success: 338 self._log.log_and_print(app_name + 339 ' is not installed. Try removing the --no-install option?') 340 return None 341 342 return self._android.find_app_pid(process_name) 343 344 def check_apps_installed(self, java_only): 345 ''' Check whether all Java/JNI/NDK apps are installed on the device. 346 347 Args: 348 java_only: Boolean to specify whether only the Java apks should be 349 checked (in case of --wimpy mode for example). 350 351 Raises: 352 TestSuiteException: Not all apps are installed. 353 ''' 354 java_and_jni_apks = self._tests_apk.copy() 355 356 if not java_only: 357 java_and_jni_apks.update(self._tests_jni) 358 359 installed = self._android.shell('pm list packages -f') 360 361 for app, package in java_and_jni_apks.items(): 362 if package not in installed: 363 raise TestSuiteException('apk %s is not installed.' % app) 364 365 if not java_only: 366 ls_data = self._android.shell('ls /data') 367 for app in self._tests_ndk: 368 if app not in ls_data: 369 raise TestSuiteException('app %s is not installed.' % app) 370