#!/usr/bin/env python # Copyright 2015 Google Inc. # # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. from __future__ import with_statement # Imports the monkeyrunner modules used by this program from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice import ast import os import subprocess import time # Time to wait between performing UI actions and capturing the SKP. WAIT_FOR_SKP_CAPTURE = 1 class DragAction: """Action describing a touch drag.""" def __init__(self, start, end, duration, points): self.start = start self.end = end self.duration = duration self.points = points def run(self, device): """Perform the action.""" return device.drag(self.start, self.end, self.duration, self.points) class PressAction: """Action describing a button press.""" def __init__(self, button, press_type): self.button = button self.press_type = press_type def run(self, device): """Perform the action.""" return device.press(self.button, self.press_type) def parse_action(action_dict): """Parse a dict describing an action and return an Action object.""" if action_dict['type'] == 'drag': return DragAction(tuple(action_dict['start']), tuple(action_dict['end']), action_dict['duration'], action_dict['points']) elif action_dict['type'] == 'press': return PressAction(action_dict['button'], action_dict['press_type']) else: raise TypeError('Unsupported action type: %s' % action_dict['type']) class App: """Class which describes an app to launch and actions to run.""" def __init__(self, name, package, activity, app_launch_delay, actions): self.name = name self.package = package self.activity = activity self.app_launch_delay = app_launch_delay self.run_component = '%s/%s' % (self.package, self.activity) self.actions = [parse_action(a) for a in actions] def launch(self, device): """Launch the app on the device.""" device.startActivity(component=self.run_component) time.sleep(self.app_launch_delay) def kill(self): """Kill the app.""" adb_shell('am force-stop %s' % self.package) def check_output(cmd): """Convenience implementation of subprocess.check_output.""" proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if proc.wait() != 0: raise Exception('Command failed: %s' % ' '.join(cmd)) return proc.communicate()[0] def adb_shell(cmd): """Run the given ADB shell command and emulate the exit code.""" output = check_output(['adb', 'shell', cmd + '; echo $?']).strip() lines = output.splitlines() if lines[-1] != '0': raise Exception('ADB command failed: %s\n\nOutput:\n%s' % (cmd, output)) return '\n'.join(lines[:-1]) def remote_file_exists(filename): """Return True if the given file exists on the device and False otherwise.""" try: adb_shell('test -f %s' % filename) return True except Exception: return False def capture_skp(skp_file, package, device): """Capture an SKP.""" remote_path = '/data/data/%s/cache/%s' % (package, os.path.basename(skp_file)) try: adb_shell('rm %s' % remote_path) except Exception: if remote_file_exists(remote_path): raise adb_shell('setprop debug.hwui.capture_frame_as_skp %s' % remote_path) try: # Spin, wait for the SKP to be written. timeout = 10 # Seconds start = time.time() device.drag((300, 300), (300, 350), 1, 10) # Dummy action to force a draw. while not remote_file_exists(remote_path): if time.time() - start > timeout: raise Exception('Timed out waiting for SKP capture.') time.sleep(1) # Pull the SKP from the device. cmd = ['adb', 'pull', remote_path, skp_file] check_output(cmd) finally: adb_shell('setprop debug.hwui.capture_frame_as_skp ""') def load_app(filename): """Load the JSON file describing an app and return an App instance.""" with open(filename) as f: app_dict = ast.literal_eval(f.read()) return App(app_dict['name'], app_dict['package'], app_dict['activity'], app_dict['app_launch_delay'], app_dict['actions']) def main(): """Capture SKPs for all apps.""" device = MonkeyRunner.waitForConnection() # TODO(borenet): Kill all apps. device.wake() device.drag((600, 600), (10, 10), 0.2, 10) apps_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'apps') app_files = [os.path.join(apps_dir, app) for app in os.listdir(apps_dir)] for app_file in app_files: app = load_app(app_file) print app.name print ' Package %s' % app.package app.launch(device) print ' Launched activity %s' % app.activity for action in app.actions: print ' %s' % action.__class__.__name__ action.run(device) time.sleep(WAIT_FOR_SKP_CAPTURE) print ' Capturing SKP.' skp_file = '%s.skp' % app.name capture_skp(skp_file, app.package, device) print ' Wrote SKP to %s' % skp_file print app.kill() if __name__ == '__main__': main()