def set_content_settings(table, key, value): """Set a device content setting.""" content_setting_command = ( 'content insert --uri content://%s --bind name:s:%s --bind value:%s:%s' % (table, key, get_type_binding(value), str(value))) adb.run_adb_shell_command(content_setting_command)
def test_remount(self): """Tests remount.""" system_test_file_path = os.path.join('/system', 'a') adb.write_data_to_file('data', system_test_file_path) self.assertEqual(adb.read_data_from_file(system_test_file_path), 'data') adb.run_adb_shell_command(['rm', system_test_file_path])
def turn_off_display_if_needed(): """Turn off the device screen if needed.""" power_dump_output = adb.run_adb_shell_command(['dumpsys', 'power']) if SCREEN_ON_SEARCH_STRING not in power_dump_output: # Screen display is already off, no work to do. return adb.run_adb_shell_command(['input', 'keyevent', 'KEYCODE_POWER'])
def configure_device_settings(): """Configures device settings for test environment.""" # FIXME: We shouldn't need repeat invocation of this. We need to do this # in case previous invocations of any of the below commands failed. # Write our test environment settings in content database. adb.run_as_root() set_content_settings('com.google.settings/partner', 'use_location_for_services', 0) set_content_settings('settings/global', 'assisted_gps_enabled', 0) set_content_settings('settings/global', 'development_settings_enabled', 0) set_content_settings('settings/global', 'stay_on_while_plugged_in', 3) set_content_settings('settings/global', 'send_action_app_error', 0) set_content_settings('settings/global', 'verifier_verify_adb_installs', 0) set_content_settings('settings/global', 'wifi_scan_always_enabled', 0) set_content_settings('settings/secure', 'anr_show_background', 0) set_content_settings('settings/secure', 'doze_enabled', 0) set_content_settings('settings/secure', 'location_providers_allowed', '') set_content_settings('settings/secure', 'lockscreen.disabled', 1) set_content_settings('settings/secure', 'screensaver_enabled', 0) set_content_settings('settings/system', 'accelerometer_rotation', 0) set_content_settings('settings/system', 'auto_time', 0) set_content_settings('settings/system', 'auto_timezone', 0) set_content_settings('settings/system', 'lockscreen.disabled', 1) set_content_settings('settings/system', 'notification_light_pulse', 0) set_content_settings('settings/system', 'screen_brightness_mode', 0) set_content_settings('settings/system', 'screen_brightness', 1) set_content_settings('settings/system', 'user_rotation', 0) # The following line filled with magic numbers will set media volume to 0 # 3 is the 3rd function in the IAudioServiceList and the following # i32's specify 32 bit integer arguments to the function adb.run_adb_shell_command('service call audio 3 i32 3 i32 0 i32 1') # FIXME: We shouldn't need repeat invocation of this. We need to do this # in case previous invocations of any of the below commands failed. # On certain device/Android configurations we need to disable the lock screen # in a different database. Additionally, the password type must be set to 0. adb.update_key_in_sqlite_db(LOCKSCREEN_DB, LOCKSCREEN_TABLE_NAME, 'lockscreen.disabled', 1) adb.update_key_in_sqlite_db(LOCKSCREEN_DB, LOCKSCREEN_TABLE_NAME, 'lockscreen.password_type', 0) adb.update_key_in_sqlite_db(LOCKSCREEN_DB, LOCKSCREEN_TABLE_NAME, 'lockscreen.password_type_alternate', 0) adb.disable_packages_that_crash_with_gestures() # Create a list of property name and names to be used in local.prop file. local_properties_settings_list = copy.deepcopy(LOCAL_PROP_SETTINGS) # Add debugging flags to local settings list so that they persist across # reboots. local_properties_settings_list += get_debug_props_and_values() # Write the local properties file settings. local_properties_file_contents = '\n'.join(local_properties_settings_list) adb.write_data_to_file(local_properties_file_contents, LOCAL_PROP_PATH)
def get_pid_for_script(script_name): """Get the pid of a running shell script.""" output = adb.run_adb_shell_command("ps | grep ' sh'") pids = PS_REGEX.findall(output) for pid in pids: cmdline = adb.run_adb_shell_command('cat /proc/%s/cmdline' % pid) if script_name in cmdline: return pid return None
def setup_memory_monitor_script_if_needed(): """Run check_process_mem.sh to monitor the memory usage""" # The script should only start if this is a low end device. device_codename = environment.get_value('DEVICE_CODENAME', get_codename()) if device_codename not in MEMORY_CONSTRAINED_DEVICES: return adb.run_as_root() if get_pid_for_script(MEMORY_MONITOR_SCRIPT): # The script is already running, no work to do. return android_directory = environment.get_platform_resources_directory() script_host_path = os.path.join(android_directory, MEMORY_MONITOR_SCRIPT) script_device_path = os.path.join(adb.DEVICE_TMP_DIR, MEMORY_MONITOR_SCRIPT) # Push memory monitor script onto device and make it executable (if needed). if not adb.file_exists(script_device_path): adb.run_adb_command(['push', script_host_path, adb.DEVICE_TMP_DIR]) adb.run_adb_shell_command(['chmod', '0755', script_device_path]) # Run the memory monitor script. adb.run_adb_shell_command('sh %s 2>/dev/null 1>/dev/null &' % script_device_path) # Wait one second to allow the script to run. time.sleep(1) # Change the priority of the process so that it will not be easily killed # by lowmemorykiller. pid = get_pid_for_script(MEMORY_MONITOR_SCRIPT) if not pid: logs.log_error('Memory monitor script failed to run.') return adb.run_adb_shell_command('echo -1000 \\> /proc/%s/oom_score_adj' % pid) adb.run_adb_shell_command('echo 0 \\> /proc/%s/oom_score' % pid) adb.run_adb_shell_command('echo -17 \\> /proc/%s/oom_adj' % pid)
def update_system_web_view(): """Updates the system webview on the device.""" app_directory = environment.get_value('APP_DIR') system_webview_apk = os.path.join(app_directory, SYSTEM_WEBVIEW_APK_NAME) if not os.path.exists(system_webview_apk): logs.log_error('System Webview apk not found.') return adb.set_property('persist.sys.webview.vmsize', SYSTEM_WEBVIEW_VMSIZE_BYTES) adb.run_as_root() if any([adb.directory_exists(d) for d in SYSTEM_WEBVIEW_DIRS]): adb.remount() adb.stop_shell() adb.run_adb_shell_command(['rm', '-rf', ' '.join(SYSTEM_WEBVIEW_DIRS)]) reboot() adb.uninstall_package(SYSTEM_WEBVIEW_PACKAGE) adb.install_package(system_webview_apk) if not adb.is_package_installed(SYSTEM_WEBVIEW_PACKAGE): logs.log_error('Package %s was not installed successfully.' % SYSTEM_WEBVIEW_PACKAGE)
def get_screen_dimensions(): """Return device's screen dimensions.""" window_policy = adb.run_adb_shell_command(['dumpsys', 'window', 'policy']) window_policy = window_policy.split('\r\n') for line in window_policy[1:]: m = re.search(r'mContent=\((\d+),(\d+)\)-\((\d+),(\d+)\)', line) if m: return (int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4))) # Fallback to default dimensions. return (0, 0, 1920, 1200)
def configure_wifi_and_airplane_mode(wifi_enabled=False): """Configure airplane mode and wifi on device.""" # Airplane mode should be disabled in all cases. This can get inadvertently # turned on via gestures. adb.disable_airplane_mode() # Need to disable wifi before changing configuration. adb.disable_wifi() # Check if wifi needs to be enabled. If not, then no need to modify the # supplicant file. wifi_enabled = wifi_enabled or environment.get_value('WIFI', True) if not wifi_enabled: # No more work to do, we already disabled it at start. return if adb.is_gce(): wifi_ssid = 'VirtWifi' wifi_password = '' else: config = db_config.get() if not config.wifi_ssid: logs.log('No wifi ssid is set, skipping wifi config.') return wifi_ssid = config.wifi_ssid wifi_password = config.wifi_password or '' adb.enable_wifi() # Wait 2 seconds to allow the wifi to be enabled. time.sleep(2) wifi_util_apk_path = os.path.join( environment.get_platform_resources_directory(), 'wifi_util.apk') if not adb.is_package_installed(WIFI_UTIL_PACKAGE_NAME): adb.install_package(wifi_util_apk_path) connect_wifi_command = ( 'am instrument -e method connectToNetwork -e ssid {ssid} ') if wifi_password: connect_wifi_command += '-e psk {password} ' connect_wifi_command += '-w {call_path}' output = adb.run_adb_shell_command( connect_wifi_command.format( ssid=quote(wifi_ssid), password=quote(wifi_password), call_path=WIFI_UTIL_CALL_PATH)) if 'result=true' not in output: logs.log_error('Failed to connect to wifi.', output=output)
def unlock_screen_if_locked(): """Unlocks the screen if it is locked.""" window_dump_output = adb.run_adb_shell_command(['dumpsys', 'window']) if SCREEN_LOCK_SEARCH_STRING not in window_dump_output: # Screen is not locked, no work to do. return # Quick power on and off makes this more reliable. adb.run_adb_shell_command(['input', 'keyevent', 'KEYCODE_POWER']) adb.run_adb_shell_command(['input', 'keyevent', 'KEYCODE_POWER']) # This key does the unlock. adb.run_adb_shell_command(['input', 'keyevent', 'KEYCODE_MENU']) # Artifical delay to let the unlock to complete. time.sleep(1)
def get_battery_information(): """Return device's battery level.""" output = adb.run_adb_shell_command(['dumpsys', 'battery']) # Get battery level. m_battery_level = re.match(r'.*level: (\d+).*', output, re.DOTALL) if not m_battery_level: logs.log_error('Error occurred while getting battery status.') return None # Get battery temperature. m_battery_temperature = re.match(r'.*temperature: (\d+).*', output, re.DOTALL) if not m_battery_temperature: logs.log_error('Error occurred while getting battery temperature.') return None level = int(m_battery_level.group(1)) temperature = float(m_battery_temperature.group(1)) / 10.0 return {'level': level, 'temperature': temperature}
def add_test_accounts_if_needed(): """Add test account to work with GmsCore, etc.""" last_test_account_check_time = persistent_cache.get_value( LAST_TEST_ACCOUNT_CHECK_KEY, constructor=datetime.datetime.utcfromtimestamp) needs_test_account_update = (last_test_account_check_time is None or dates.time_has_expired( last_test_account_check_time, seconds=ADD_TEST_ACCOUNT_CHECK_INTERVAL)) if not needs_test_account_update: return config = db_config.get() test_account_email = config.test_account_email test_account_password = config.test_account_password if not test_account_email or not test_account_password: return adb.run_as_root() configure_wifi_and_airplane_mode(wifi_enabled=True) if not adb.is_package_installed(ADD_TEST_ACCOUNT_PKG_NAME): logs.log('Installing helper apk for adding test account.') android_directory = environment.get_platform_resources_directory() add_test_account_apk_path = os.path.join(android_directory, ADD_TEST_ACCOUNT_APK_NAME) adb.install_package(add_test_account_apk_path) logs.log('Trying to add test account.') output = adb.run_adb_shell_command( 'am instrument -e account %s -e password %s -w %s' % (test_account_email, test_account_password, ADD_TEST_ACCOUNT_CALL_PATH), timeout=ADD_TEST_ACCOUNT_TIMEOUT) if not output or test_account_email not in output: logs.log('Failed to add test account, probably due to wifi issues.') return logs.log('Test account added successfully.') persistent_cache.set_value(LAST_TEST_ACCOUNT_CHECK_KEY, time.time())
def get_crash_info(output): """Parse crash output to get (local) minidump path and any other information useful for crash uploading, and store in a CrashReportInfo object.""" crash_stacks_directory = environment.get_value('CRASH_STACKTRACES_DIR') platform = environment.platform() output_lines = output.splitlines() num_lines = len(output_lines) for i, line in enumerate(output_lines): if platform == 'ANDROID': # If we are on Android, the dump extraction is more complicated. # The location placed in the crash-stacktrace is of the dump itself but # in fact only the MIME of the dump exists, and will have a different # extension. We need to pull the MIME and process it. match = re.match(CRASH_DUMP_PATH_MARKER, line) if not match: continue minidump_mime_filename_base = None for j in range(i + 1, num_lines): line = output_lines[j] match = re.match(r'(.*)\.dmp', line) if match: minidump_mime_filename_base = os.path.basename( match.group(1).strip()) break if not minidump_mime_filename_base: logs.log_error( 'Minidump marker was found, but no path in stacktrace.') return None # Look for MIME. If none found, bail. # We might not have copied over the crash dumps yet (copying is buffered), # so we want to search both the original directory and the one to which # the minidumps should later be copied. device_directories_to_search = [ adb.DEVICE_CRASH_DUMPS_DIR, os.path.dirname(line.strip()) ] device_minidump_search_paths = [] device_minidump_mime_path = None for device_directory in device_directories_to_search: device_minidump_mime_potential_paths = adb.run_adb_shell_command( ['ls', '"%s"' % device_directory], root=True).splitlines() device_minidump_search_paths += device_minidump_mime_potential_paths for potential_path in device_minidump_mime_potential_paths: # Check that we actually found a file, and the right one (not logcat). if 'No such file or directory' in potential_path: continue if minidump_mime_filename_base not in potential_path: continue if '.up' in potential_path or '.dmp' in potential_path: device_minidump_mime_path = os.path.join( device_directory, potential_path) break # Break if we found a path. if device_minidump_mime_path is not None: break # If we still didn't find a minidump path, bail. if device_minidump_mime_path is None: logs.log_error('Could not get MIME path from ls:\n%s' % str(device_minidump_search_paths)) return None # Pull out MIME and parse to minidump file and MIME parameters. minidump_mime_filename = '%s.mime' % minidump_mime_filename_base local_minidump_mime_path = os.path.join(crash_stacks_directory, minidump_mime_filename) adb.run_adb_command([ 'pull', '"%s"' % device_minidump_mime_path, local_minidump_mime_path ]) if not os.path.exists(local_minidump_mime_path): logs.log_error( 'Could not pull MIME from %s to %s.' % (device_minidump_mime_path, local_minidump_mime_path)) return None crash_info = parse_mime_to_crash_report_info( local_minidump_mime_path) if crash_info is None: return None crash_info.unsymbolized_stacktrace = output return crash_info else: # Other platforms are not currently supported. logs.log_error( 'Unable to fetch crash information for this platform.') return None # Could not find dump location, bail out. This could also happen when we don't # have a minidump location in stack at all, e.g. when testcase does not crash # during minimization. return None
def configure_build_properties_if_needed(): """Edits /system/build.prop for better boot speed and power use.""" # Check md5 checksum of build.prop to see if already updated, # in which case exit. If build.prop does not exist, something # is very wrong with the device, so bail. old_md5 = persistent_cache.get_value(BUILD_PROP_MD5_KEY) current_md5 = adb.get_file_checksum(BUILD_PROP_PATH) if current_md5 is None: logs.log_error('Unable to find %s on device.' % BUILD_PROP_PATH) return if old_md5 == current_md5: return # Pull to tmp file. bot_tmp_directory = environment.get_value('BOT_TMPDIR') old_build_prop_path = os.path.join(bot_tmp_directory, 'old.prop') adb.run_adb_command(['pull', BUILD_PROP_PATH, old_build_prop_path]) if not os.path.exists(old_build_prop_path): logs.log_error('Unable to fetch %s from device.' % BUILD_PROP_PATH) return # Write new build.prop. new_build_prop_path = os.path.join(bot_tmp_directory, 'new.prop') old_build_prop_file_content = open(old_build_prop_path, 'r') new_build_prop_file_content = open(new_build_prop_path, 'w') new_content_notification = '### CHANGED OR ADDED PROPERTIES ###' for line in old_build_prop_file_content: property_name = line.split('=')[0].strip() if property_name in BUILD_PROPERTIES: continue if new_content_notification in line: continue new_build_prop_file_content.write(line) new_build_prop_file_content.write(new_content_notification + '\n') for flag, value in BUILD_PROPERTIES.iteritems(): new_build_prop_file_content.write('%s=%s\n' % (flag, value)) old_build_prop_file_content.close() new_build_prop_file_content.close() # Keep verified boot disabled for M and higher releases. This makes it easy # to modify system's app_process to load asan libraries. build_version = get_build_version() if is_build_at_least(build_version, 'M'): adb.run_as_root() adb.run_adb_command('disable-verity') reboot() # Make /system writable. adb.run_as_root() adb.remount() # Remove seccomp policies (on N and higher) as ASan requires extra syscalls. if is_build_at_least(build_version, 'N'): policy_files = adb.run_adb_shell_command( ['find', '/system/etc/seccomp_policy/', '-type', 'f']) for policy_file in policy_files.splitlines(): adb.run_adb_shell_command(['rm', policy_file.strip()]) # Remove Google Plus app from non-Google devices. Makes it easy to install # older Gallery app on these devices. Otherwise, we run into duplicate # permission errors. if not google_device(): adb.run_adb_shell_command(['rm', '/system/app/PlusOne.apk']) adb.run_adb_shell_command(['rm', '/system/app/PlusOne/PlusOne.apk']) # Push new build.prop and backup to device. logs.log('Pushing new build properties file on device.') adb.run_adb_command( ['push', '-p', old_build_prop_path, BUILD_PROP_BACKUP_PATH]) adb.run_adb_command(['push', '-p', new_build_prop_path, BUILD_PROP_PATH]) adb.run_adb_shell_command(['chmod', '644', BUILD_PROP_PATH]) # Set persistent cache key containing and md5sum. current_md5 = adb.get_file_checksum(BUILD_PROP_PATH) persistent_cache.set_value(BUILD_PROP_MD5_KEY, current_md5)