NAME Android::ElectricSheep::Automator - Do Androids Dream of Electric Sheep? Smartphone control from your desktop VERSION Version 0.01 WARNING Current distribution is extremely alpha. API may change. SYNOPSIS The present package fascilitates the control of a USB-debugging-enabled Android smartphone from a desktop computer. use Android::ElectricSheep::Automator; my $mother = Android::ElectricSheep::Automator->new({ 'configfile' => $configfile, 'verbosity' => 1, # we already have a device connected and ready to control 'device-is-connected' => 1, }); # find the devices connected to desktop and set one. my @devices = $mother->adb->devices; $mother->connect_device({'serial' => $devices->[0]->serial}) or die; # no device needs to be specified if just one: $mother->connect_device() if scalar(@devices)==0; # Go Home $mother->home_screen() or die; # swipe up/down/left/right $mother->swipe({'direction'=>up}) or die; # dt is the time to swipe in millis, # the shorter the faster the swipe $mother->swipe({'direction'=>left, 'dt'=>100}) or die; # tap $mother->tap({'position'=>[100,200]}); # uses swipe() to move in screens (horizontally): $mother->next_screen() or die; $mother->previous_screen() or die; # bottom navigation: # the "triangle" back button $mother->navigation_menu_back_button() or die; # the "circle" home button $mother->navigation_menu_home_button() or die; # the "square" overview button $mother->navigation_menu_overview_button() or die; # open/close apps $mother->open_app({'package'=>qr/calendar$/i}) or die; $mother->close_app({'package'=>qr/calendar$/i}) or die; # push pull files $mother->adb->pull($deviceFile, $localFile); $mother->adb->push($localFile, $deviceFileOrDir); # guess what! my $xmlstr = $mother->dump_current_screen_ui(); CONSTRUCTOR new($params) Creates a new Android::ElectricSheep::Automator object. $params is a hash reference used to pass initialization options which may or should include the following: confighash or configfile the configuration file holds configuration parameters and its format is "enhanced" JSON (see "use Config::JSON::Enhanced") which is basically JSON which allows comments between . Here is an example configuration file to get you started: { "adb" : { "path-to-executable" : "/usr/local/android-sdk/platform-tools/adb" }, "debug" : { "verbosity" : 0, "cleanup" : 1 }, "logger" : { "filename" : "my.log" } } All sections are mandatory. Setting "adb" to the wrong path will yield in problems. confighash is a hash of configuration options with structure as above and can be supplied to the constructor instead of the configuration file. device-serial or device-object optionally specify the serial of a device to connect to on instantiation, or a Android::ElectricSheep::Automator::DeviceProperties object you already have handy. Alternatively, use connect_device() to set the connected device at a later time. Note that there is no need to specify a device if there is exactly one connected device. adb optionally specify an Android::ADB. Otherwise, a fresh object will be created based on the configuration under the adb section of the configuration. device-is-connected optionally set it to 1 in order to communicate with the device and get some information about it like screen size, resolution, orientation, etc. And also allow use of functionality which needs communicating with a device like swipe(), home_screen(), open_app(), etc. After instantiation, you can use the method connect_device() and disconnect_device() for conveying this information to the module. Also note that if there are more than one devices connected to the desktop, make sure you specify which one with the device parameter. Default value is 0. logger optionally specify a logger object to be used (instead of creating a fresh one). This object must implement info(), warn(), error(). For example Mojo::Log. logfile optionally specify a file to save logging output to. This overrides the filename key under section logger of the configuration. verbosity optionally specify a verbosity level which will override what the configuration contains. Default is 0. cleanup optionally specify a flag to clean up any temp files after exit which will override what the configuration contains. Default is 1, meaning Yes!. METHODS devices() Lists all Android devices connected to your desktop and returns these as an ARRAY_REF which can be empty. It returns undef on failure. connect_device($params) Specifies the current Android device to control. Its use is required only if you have more than one devices connected. $params is a HASH_REF which should contain exactly one of the following: serial should contain the serial (string) of the connected device as returned by devices(). device-object should be an already existing Android::ElectricSheep::Automator::DeviceProperties object. It returns 0 on success, 1 on failure. dump_current_screen_ui($params) It dumps the current screen as XML and returns that as a string, optionally saving it to the specified file. $params is a HASH_REF which may or should contain: filename optionally save the returned XML string to the specified file. It returns undef on failure or the UI XML dump, as a string, on success. dump_current_screen_shot($params) It dumps the current screen as a PNG image and returns that as a Image::PNG object, optionally saving it to the specified file. $params is a HASH_REF which may or should contain: filename optionally save the returned XML string to the specified file. It returns undef on failure or a Image::PNG image, on success. dump_current_screen_video($params) It dumps the current screen as MP4 video and saves that in specified file. $params is a HASH_REF which may or should contain: filename save the recorded video to the specified file in MP4 format. This is required. time-limit optionally specify the duration of the recorded video, in seconds. Default is 10 seconds. bit-rate optionally specify the bit rate of the recorded video in bits per second. Default is 20Mbps. # Optionally specify %size = ('width' => ..., 'height' => ...) size optionally specify the size (geometry) of the recorded video as a HASHref with keys width and height, in pixels. Default is "the device's main display resolution". bugreport optionally set this flag to 1 to have Android overlay debug information on the recorded video, e.g. timestamp. # Optionally specify 'display-id'. =item display-id for a device set up with multiple physical displays, optionally specify which one to record -- if not the main display -- by providing the display id. You can find display ids with list_physical_displays() or, from the CLI, by adb shell dumpsys SurfaceFlinger --display-id adb shell screenrecord --help contains some more documentation. list_running_processes($params) It finds the running processes on device (using a `ps`), optionally can save the (parsed) `ps` results as JSON to the specified 'filename'. It returns undef on failure or the results as a hash of hashes on success. $params is a HASH_REF which may or should contain: extra-fields optionally add more fields (columns) to the report by ps, as an ARRAYref. For example, ['TTY','TIME']. It needs that connect_device() to have been called prior to this call It returns undef on failure or a hash with these keys on success: raw : contains the raw `ps` output as a string. perl : contains the parsed raw output as a Perl hash with each item corresponding to one process, keyed on process command and arguments (as reported by `ps`, verbatim), as a hash keyed on each field (column) of the `ps` output. json : the above data converted into a JSON string. geofix($params) It fixes the geolocation of the device to the specified coordinates. After this, app API calls to get current geolocation will result to this position (unless they use their own, roundabout way). $params is a HASH_REF which should contain: latitude the latitude of the position as a floating point number. longitude the longitude of the position as a floating point number. It returns 1 on failure or a 0 on success. dump_current_location() It finds the current GPS location of the device according to ALL the GPS providers available. It needs that connect_device() to have been called prior to this call It takes no parameters. On failure, it returns undef. On success, it returns a hashref of results. Each item will be keyed on provider name (e.g. 'network provider') and will contain the parsed output of what each GPS provider returned as a hashref with the following keys: provider : the provider name. This is also the key of the item in the parent hash. latitude : the latitude as a floating point number (can be negative too) or if the provider failed to return valid output. longitude : the longitude as a floating point number (can be negative too) or if the provider failed to return valid output. last-location-string : the last location string, or if the provider failed to return valid output. find_current_device_properties($params) It enquires the device currently connected, and specified with connect_device(), if needed, and returns back an Android::ElectricSheep::Automator::DeviceProperties object containing this information, for example screen size, resolution, serial number, etc. It returns Android::ElectricSheep::Automator::DeviceProperties object on success or undef on failure. connect_device() It signals to our object that there is now a device connected to the desktop and its enquiry and subsequent control can commence. If this is not called and neither device-is-connected => 1 is specified as a parameter to the constructor, then the functionality will be limited and access to functions like swipe(), open_app(), etc. will be blocked until the caller signals that a device is now connected to the desktop. Using connect_device() to specify which device to target in the case of multiple devices connected to the desktop will also call this method. This method will try to enquire the connected device about some of its properties, like screen size, resolution, orientation, serial number etc. This information will subsequently be available via $self->device_properties()>. It returns 0 on success, 1 on failure. disconnect_device() Signals to our object that it should consider that there is currently no device connected to the desktop (irrespective of that is true or not) which will block access to swipe(), open_app(), etc. device_properties() It returns the currently connected device properties as a Android::ElectricSheep::Automator::DeviceProperties object or undef if there is no connected device. The returned object is constructed during a call to find_current_device_properties() which is called via connect_device() and will persist for the duration of the connection. However, after a call to disconnect_device() this object will be discarded and undef will be returned. swipe($params) Emulates a "swipe" in four directions. Sets the current Android device to control. It is only required if you have more than one device connected. $params is a HASH_REF which may or should contain: direction should be one of up down left right dt denotes the time taken for the swipe in milliseconds. The smaller its value the faster the swipe. A value of 100 is fast enough to swipe to the next screen. It returns 0 on success, 1 on failure. tap($params) Emulates a "tap" at the specified location. $params is a HASH_REF which must contain one of the following items: position should be an ARRAY_REF as the X,Y coordinates of the point to "tap". bounds should be an ARRAY_REF of a bounding rectangle of the widget to tap. Which contains two ARRAY_REFs for the top-left and bottom-right coordinates, e.g. [ [tlX,tlY], [brX,brY] ] . This is convenient when the widget is extracted from an XML dump of the UI (see dump_current_screen_ui()) which contains exactly this bounding rectangle. It returns 0 on success, 1 on failure. input_text($params) It inputs the specified text into a text-input widget at specified position. At first it taps at the widget's location in order to get the focus. And then it enters the text. $params is a HASH_REF which must contain text and one of the two position (of the text-edit widget) specifiers position or bounds: text the text to write on the text edit widget. At the moment, this must be plain ASCII string, not unicode. No spaces are accepted. Each space character must be replaced with %s. position should be an ARRAY_REF as the X,Y coordinates of the point to "tap" in order to get the focus of the text edit widget, preceding the text input. bounds should be an ARRAY_REF of a bounding rectangle of the widget to tap, in order to get the focus, preceding the text input. Which contains two ARRAY_REFs for the top-left and bottom-right coordinates, e.g. [ [tlX,tlY], [brX,brY] ] . This is convenient when the widget is extracted from an XML dump of the UI (see dump_current_screen_ui()) which contains exactly this bounding rectangle. It returns 0 on success, 1 on failure. clear_input_field($params) It clears the contents of a text-input widget at specified location. There are several ways to do this. The simplest way (with keycombination) does not work in some devices, in which case a failsafe way is employed which deletes characters one after the other for 250 times. $params is a HASH_REF which must contain one of the two position (of the text-edit widget) specifiers position or bounds: position should be an ARRAY_REF as the X,Y coordinates of the point to "tap" in order to get the focus of the text edit widget, preceding the text input. bounds should be an ARRAY_REF of a bounding rectangle of the widget to tap, in order to get the focus, preceding the text input. Which contains two ARRAY_REFs for the top-left and bottom-right coordinates, e.g. [ [tlX,tlY], [brX,brY] ] . This is convenient when the widget is extracted from an XML dump of the UI (see dump_current_screen_ui()) which contains exactly this bounding rectangle. num-characters how many times to press the backspace? Default is 250! But if you know the length of the text currently at the text-edit widget then enter this here. It returns 0 on success, 1 on failure. home_screen() Go to the "home" screen. It returns 0 on success, 1 on failure. wake_up() "Wake" up the device. It returns 0 on success, 1 on failure. next_screen() Swipe to the next screen (on the right). It returns 0 on success, 1 on failure. previous_screen() Swipe to the previous screen (on the left). It returns 0 on success, 1 on failure. navigation_menu_back_button() Press the "back" button which is the triangular button at the left of the navigation menu at the bottom. It returns 0 on success, 1 on failure. navigation_menu_home_button() Press the "home" button which is the circular button in the middle of the navigation menu at the bottom. It returns 0 on success, 1 on failure. navigation_menu_overview_button() Press the "overview" button which is the square button at the right of the navigation menu at the bottom. It returns 0 on success, 1 on failure. apps() It returns a HASH_REF containing all the packages (apps) installed on the device keyed on package name (which is like com.android.settings. The list of installed apps is populated either if device-is-connected is set to 1 during construction or a call has been made to any of these methods: open_app(), close_app(), search_app(), find_installed_apps(). find_installed_apps($params) It enquires the device about all the installed packages (apps) it has for the purpose of opening and closing apps with open_app() and close_app(). This list is available using $self-apps>. Finding the package names is done in a single operation and does not take long. But enquiring with the connected device about the main activity/ies of each package takes some time as there should be one enquiry for each package. By default, find_installed_apps() will find all the package names but will not enquire each package (fast). This enquiry will be done lazily if and when you need to open or close that app. $params is a HASH_REF which may or should contain: packages is a list of package names to enquire about with the device. It can be a scalar string with the exact package name, e.g. com.android.settings, or a Regexp object which is a compiled regular expression created by e.g. qr/^\.com.+?\.settings$/i, or an ARRAY_REF of package names. Or a HASH_REF where keys are package names. For each of the packages matched witht this specification a full enquiry will be made with the connected device. The information will be saved in a Android::ElectricSheep::Automator::AppProperties object and will include the main activity/ies, permissions requested etc. lazy is a flag to denote whether to enquire information about each package (app) at the time of this call (set it to 1) or lazily, on a if-and-when-needed basis (set it to 0 which is the default). lazy affects all packages except those specified in packages, if any. Default is 1. force-reload-apps-list' can be set to 1 to erase previous packages information and start fresh. Default is 0. It returns a HASH_REF of packages names (keys) along with enquired information (as a Android::ElectricSheep::Automator::AppProperties object) or undef if this information was not obtained (e.g. when lazy is set to 1). It also sets the exact same data to be available via $self-apps>. search_app($params) It searches the list of installed packages (apps) on the current device and returns the match(es) as a HASH_REF keyed on package name which may have as values Android::ElectricSheep::Automator::AppProperties objects with packages information. If there are no entries yet in the list of installed packages, it calls the find_installed_apps() first to populate it. $params is a HASH_REF which may or should contain: package is required. It can either be a scalar string with the exact package name or a Regexp object which is a compiled regular expression created by e.g. qr/^\.com.+?\.settings$/i. lazy is a flag to be passed on to find_installed_apps(), if needed, to denote whether to enquire information about each package (app) at the time of this call (set it to 1) or lazily, on a if-and-when-needed basis (set it to 0 which is the default). lazy affects all packages except those specified in packages, if any. Default is 1. force-reload-apps-list' is a flag to be passed on to find_installed_apps(), if needed, and can be set to 1 to erase previous packages information and start fresh. Default is 0. It returns a HASH_REF of matched packages names (keys) along with enquired information (as a Android::ElectricSheep::Automator::AppProperties object) or undef if this information was not obtained (e.g. when lazy is set to 1). open_app($params) It opens the package specified in $params on the current device. If there are no entries yet in the list of installed packages, it calls the find_installed_apps() first to populate it. It will refuse to open multiple apps matched perhaps by a regular expression in the package specification. $params is a HASH_REF which may or should contain: package is required. It can either be a scalar string with the exact package name or a Regexp object which is a compiled regular expression created by e.g. qr/^\.com.+?\.settings$/i. If a regular expression, the call will fail if there is not exactly one match. lazy is a flag to be passed on to find_installed_apps(), if needed, to denote whether to enquire information about each package (app) at the time of this call (set it to 1) or lazily, on a if-and-when-needed basis (set it to 0 which is the default). lazy affects all packages except those specified in packages, if any. Default is 1. force-reload-apps-list' is a flag to be passed on to find_installed_apps(), if needed, and can be set to 1 to erase previous packages information and start fresh. Default is 0. It returns a HASH_REF of matched packages names (keys) along with enquired information (as a Android::ElectricSheep::Automator::AppProperties object). At the moment, because open_app() allows opening only a single app, this hash will contain only one entry unless we allow opening multiple apps (e.g. via a regex which it is already supported) in the future. close_app($params) It closes the package specified in $params on the current device. If there are no entries yet in the list of installed packages, it calls the find_installed_apps() first to populate it. It will refuse to close multiple apps matched perhaps by a regular expression in the package specification. $params is a HASH_REF which may or should contain: package is required. It can either be a scalar string with the exact package name or a Regexp object which is a compiled regular expression created by e.g. qr/^\.com.+?\.settings$/i. If a regular expression, the call will fail if there is not exactly one match. lazy is a flag to be passed on to find_installed_apps(), if needed, to denote whether to enquire information about each package (app) at the time of this call (set it to 1) or lazily, on a if-and-when-needed basis (set it to 0 which is the default). lazy affects all packages except those specified in packages, if any. Default is 1. force-reload-apps-list' is a flag to be passed on to find_installed_apps(), if needed, and can be set to 1 to erase previous packages information and start fresh. Default is 0. It returns a HASH_REF of matched packages names (keys) along with enquired information (as a Android::ElectricSheep::Automator::AppProperties object). At the moment, because close_app() allows closing only a single app, this hash will contain only one entry unless we allow closing multiple apps (e.g. via a regex which it is already supported) in the future. SCRIPTS For convenience, a few simple scripts are provided: script/electric-sheep-find-installed-apps.pl Find all install packages in the connected device. E.g. script/electric-sheep-find-installed-apps.pl --configfile config/myapp.conf --device Pixel_2_API_30_x86_ --output myapps.json script/electric-sheep-find-installed-apps.pl --configfile config/myapp.conf --device Pixel_2_API_30_x86_ --output myapps.json --fast script/electric-sheep-open-app.pl Open an app by its exact name or a keyword matching it (uniquely): script/electric-sheep-open-app.pl --configfile config/myapp.conf --name com.android.settings script/electric-sheep-open-app.pl --configfile config/myapp.conf --keyword 'clock' Note that it constructs a regular expression from escaped user input. script/electric-sheep-close-app.pl Close an app by its exact name or a keyword matching it (uniquely): script/electric-sheep-close-app.pl --configfile config/myapp.conf --name com.android.settings script/electric-sheep-close-app.pl --configfile config/myapp.conf --keyword 'clock' Note that it constructs a regular expression from escaped user input. script/electric-sheep-dump-ui.pl Dump the current screen UI as XML to STDOUT or to a file: script/electric-sheep-dump-ui.pl --configfile config/myapp.conf --output ui.xml Note that it constructs a regular expression from escaped user input. script/electric-sheep-dump-current-location.pl Dump the GPS / geo-location position for the device from its various providers, if enabled. script/electric-sheep-dump-current-location.pl --configfile config/myapp.conf --output geolocation.json script/electric-sheep-emulator-geofix.pl Set the GPS / geo-location position to the specified coordinates. script/electric-sheep-dump-ui.pl --configfile config/myapp.conf --latitude 12.3 --longitude 45.6 script/electric-sheep-dump-screen-shot.pl Take a screenshot of the device (current screen) and save to a PNG file. script/electric-sheep-dump-screen-shot.pl --configfile config/myapp.conf --output screenshot.png script/electric-sheep-dump-screen-video.pl Record a video of the device's current screen and save to an MP4 file. script/electric-sheep-dump-screen-video.pl --configfile config/myapp.conf --output video.mp4 --time-limit 30 PREREQUISITES Android Studio This is not a prerequisite but it is highly recommended to install (from https://developer.android.com/studio) on your desktop computer because it contains all the executables you will need, saved in a well documented file system hierarchy, which can then be accessed from the command line. Additionally, Android Studio offers possibly the easiest way to create Android Virtual Devices (AVD) which emulate an Android phone of various specifications. I mention this because one can install apps on an AVD and control them from your desktop as long as you are able to receive sms verification codes from a real phone. This is great for experimenting without pluggin in your real smartphone on your desktop. The bottom line is that by installing Android Studio, you have all the executables you need for running things from the command line and, additionally, you have the easiest way for creating Android Virtual Devices, which emulate Android devices: phones, tablets, automotive displays. Once you have this set up, you will not need to open Android Studio ever again unless you want to update your kit. All the functionality will be accessible from the command line. ADB Android Debug Bridge (ADB) is the program which communicates with your smartphone or an Android Virtual Device from your desktop (Linux, osx and the unnamed 0$). If you do not want to install Android Studio, the adb executable is included in the package called "Android SDK Platform Tools" available from the Android official site, here: https://developer.android.com/tools/releases/platform-tools#downloads You will need the adb executable to be on your path or specify its fullpath in the configuration file supplied to Android::ElectricSheep::Automator's constructor. USB Debugging The targeted smartphone must have "USB Debugging" enabled via the "Developer mode". This is not to be confused with 'rooted' or 'jailbroken' modes, none of these are required for experimenting with the current modul. In order to enable "USB Debugging", you need to set the smartphone to enter "Developer" mode by following this procedure: Go to Settings->System->About Phone Tap on Build Number 7 times [sic!]. Enter your phone pin and you are in developer mode. You can exit Developer Mode by going to Settings->System->Developer and turn it off. It is highly advised to turn off Developer Mode for everyday use of your phone. Do not connect your smartphone to public WIFI networks with Developer Mode ON. Do not leave home with Developer Mode ON. Once you have enabled "USB Debugging", you have two options for making your device visible to your desktop and, consequently, to ADB and to this module: connect your android device via a USB cable to your desktop computer. I am not sure if you also need to tap on the USB charging options and allow "Transfer Files". connect your device to the same WIFI network as your desktop computer. Then follow instructions from, e.g., here https://developer.android.com. This requires a newer Android version. Android Emulators It is possible to do most things your smartphone does with an Android Virtual Device. You can install apps on the the virtual device which you can register by supplying your real smartphone number. List all virtual devices currently available in your desktop computer, with emulator -list-avds which outputs something like: Pixel_2_API_27_x86_ Pixel_2_API_30_x86_ Start a virtual device with emulator -avd Pixel_2_API_30_x86_ And hey, you have an android phone running on your desktop in its own space, able to access the network but not the telephone network. It is possible to create a virtual device from the command line. But perhaps it is easier if you download Android Studio from: https://developer.android.com/studio and follow the setup there using the GUI. It will download all the required files and will create some Android Virtual Devices (the "emulators") for you. It will also be easy to update your stack in the future. Once you have done the above, you no longer need to run Android Studio except perhaps for checking for updates and all the required executables by this package will be available from the command line. Otherwise, download "Android SDK Platform Tools" available from the Android official site, here: https://developer.android.com/tools/releases/platform-tools#downloads (this download is mentioned in ADB if you already fetched it). Fetch the required packages with this command: sdkmanager --sdk_root=/usr/local/android-sdk "platform-tools" "platforms;android-30" "cmdline-tools;latest" "emulator" Note that sdkmanager --list will list the latest android versions etc. Now you should have access to avdmanager executable (it should be located here: /usr/local/android-sdk/cmdline-tools/latest/bin/avdmanager) which you can use to create an emulator. List all available android virtual devices you can create: avdmanager list target List all available devices you can emulate: avdmanager list device List all available devices you have created already: avdmanager list avd Create virtual device: avdmanager create avd -d "Nexus 6" -n myavd -k "system-images;android-29;google_apis;x86" See https://stackoverflow.com/a/77599934 USING YOUR REAL SMARTPHONE AUTHOR Andreas Hadjiprocopis, BUGS Please report any bugs or feature requests to bug-Android-ElectricSheep-Automator at rt.cpan.org, or through the web interface at https://rt.cpan.org/NoAuth/ReportBug.html?Queue=Android-ElectricSheep-Automator. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. SUPPORT You can find documentation for this module with the perldoc command. perldoc Android::ElectricSheep::Automator You can also look for information at: * RT: CPAN's request tracker (report bugs here) https://rt.cpan.org/NoAuth/Bugs.html?Dist=Android-ElectricSheep-Automator * AnnoCPAN: Annotated CPAN documentation http://annocpan.org/dist/Android-ElectricSheep-Automator * Search CPAN https://metacpan.org/release/Android-ElectricSheep-Automator SEE ALSO * Android::ADB is a thin wrapper of the adb command created by Marius Gavrilescu, marius@ieval.ro. It is used by current module, albeit modified. HUGS * Πτηνού, my chicken, now laying in the big coop in the sky ... LICENSE AND COPYRIGHT This software is Copyright (c) 2025 by Andreas Hadjiprocopis. This is free software, licensed under: The Artistic License 2.0 (GPL Compatible)