NAME

Android::ElectricSheep::Automator - Do Androids Dream of Electric Sheep? Smartphone control from your desktop.

VERSION

Version 0.09

WARNING

Current distribution is extremely alpha. API may change.

SYNOPSIS

The present package fascilitates the control of a USB-debugging-enabled Android device, e.g. a real smartphone, or an emulated (virtual) Android device, from your desktop computer using Perl. It's basically a thickishly-thin wrapper to the omnipotent Android Debug Bridge (adb) program.

Note that absolutely nothing is installed on the connected device, neither any of its settings will be modified by this package. See "WILL ANYTHING BE INSTALLED ON THE DEVICE?".

use Android::ElectricSheep::Automator;

my $mother = Android::ElectricSheep::Automator->new({
  # optional as there is a default, but you may have
  # problems with the location of the adb executable
  '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 identification is required for the method call
# if there is only one connected device:
$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();

# Pull the apk(s) for an app from device and save locally
my $res = $mother->pull_app_apk_from_device({
  package => 'com.google.android.calendar'
    # or qr/calendar/i
  'output-dir' => '/tmp/apks-of-calendar-app',
});
print $res->{'com.google.android.calendar'}->[0]->['local-path'};

# Install apk(s) for an app onto the device
$mother->install_app({
  'apk-filename' => ['/tmp/apks/base.apk', '/tmp/apks/config.apk'],
    # or just a string scalar '/tmp/apks/1.apk'
  # optional params to the adb install command
  'install-parameters' => ['-r', '-g']
});

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:

METHODS

Note:

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:

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:

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:

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:

adb shell screenrecord --help contains some more documentation.

list_physical_displays()

It lists the IDs of all the physical displays connected to the device, including the main one and returns these back as a HASH_REF keyed on display ID. It needs that connect_device() to have been called prior to this call

It returns undef on failure or the results as a HASH_REF keyed on display ID.

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:

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:

pidof($params)

It returns the PID of the specified command name. The specified command name must match the app or command name exactly. Use L/pgrep() if you want to match command names with a regular expression>.

$params is a HASH_REF which should contain:

It returns undef on failure or the PID of the matched command on success.

pgrep($params)

It returns the PIDs matching the specified command or app name (which can be an extended regular expression that pgrep understands). The returned array will contain zero, one or more hashes with keys pid and command. The former key is the pid of the command whose full name (as per the process table) will be under the latter key. Unless parameter dont-show-command-name was set to 1.

$params is a HASH_REF which should contain:

It returns undef on failure or an ARRAY_REF containing a HASH_REF of data for each command matched (under keys pid and command). The returned ARRAY_REF can contain 0, 1 or more items depending on what was matched.

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:

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 HASH_REF 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 HASH_REF with the following keys:

pull_app_apk_from_device($params)

It pulls the APK file (bytecode) for the app(s) matched by the specified package specification, from the device and writes them into the specified output directory, locally.

$params is a HASH_REF which should contain:

It returns undef on failure. On success it returns a HASH_REF with as many items as the packages matched which had APK files available on the device. Each entry is keyed on the fully qualified package name and its value is a ARRAY_REF with as many APK files pulled from the device and saved locally. Each item in this array is a HASH_REF with keys device-path containing the path of the APK in the device's storage and local-path pointing to the location where the APK was saved, locally.

Note that there is a script available which utilises this method, see electric-sheep-pull-app-apk.pl.

install_app($params)

It installs the app from its specified APK (bytecode archive and more) file.

$params is a HASH_REF which should contain:

It returns 1 on failure. It returns 0 on success.

Note that there is a script available which utilises this method, see electric-sheep-install-app.pl.

is_app_running($params)

It checks if the specified app is running on the device. The name of the app must be exact. Note that you can search for running apps / commands with extended regular expressions using L/pgrep()>

$params is a HASH_REF which should contain:

It returns undef on failure, 1 if the app is running or 0 if the app is not running.

find_current_device_properties($params)

It enquires the device currently connected, and specified with "connect_device($params)", 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($params)", "open_app($params)", etc. will be blocked until the caller signals that a device is now connected to the desktop.

Using "connect_device($params)" 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($params)", "open_app($params)", 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($params)" which is called via "connect_device($params)" 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:

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:

It returns 0 on success, 1 on failure.

input_text($params)

It "types" the specified text into the specified position, where a text-input widget is expected to exist. At first it taps at the widget's location in order to get the focus. And then it enters the text. You need to find the position of the desired text-input widget by first getting the current screen UI (using "dump_current_screen_ui($params)") and then using an XPath selector to identify the desired widget by name/id/attributes. See the source code of method "send_message()" in file lib/Android/ElectricSheep/Automator/Plugins/Apps/Viber.pm for how this is done for the message-sending text-input widget of the Viber app.

$params is a HASH_REF which must contain text and one of the two position (of the text-edit widget) specifiers position or bounds:

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:

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.

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.

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.

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($params)", "close_app($params)", "search_app($params)", "find_installed_apps($params)".

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($params)" and "close_app($params)". This list is available using $self-apps> (where $self is a Android::ElectricSheep::Automator object.

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($params)" 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:

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($params)" first to populate it.

$params is a HASH_REF which may or should contain:

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($params)" 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:

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($params)" 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($params)" 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:

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($params)" 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:

electric-sheep-find-installed-apps.pl

Find all install packages in the connected device. E.g.

electric-sheep-find-installed-apps.pl --configfile config/myapp.conf --device Pixel_2_API_30_x86_ --output myapps.json

electric-sheep-find-installed-apps.pl --configfile config/myapp.conf --device Pixel_2_API_30_x86_ --output myapps.json --fast

electric-sheep-open-app.pl

Open an app by its exact name or a keyword matching it (uniquely):

electric-sheep-open-app.pl --configfile config/myapp.conf --name com.android.settings

electric-sheep-open-app.pl --configfile config/myapp.conf --keyword 'clock'

Note that it constructs a regular expression from escaped user input.

electric-sheep-close-app.pl

Close an app by its exact name or a keyword matching it (uniquely):

electric-sheep-close-app.pl --configfile config/myapp.conf --name com.android.settings

electric-sheep-close-app.pl --configfile config/myapp.conf --keyword 'clock'

Note that it constructs a regular expression from escaped user input.

electric-sheep-dump-ui.pl

Dump the current screen UI as XML to STDOUT or to a file:

electric-sheep-dump-ui.pl --configfile config/myapp.conf --output ui.xml

Note that it constructs a regular expression from escaped user input.

electric-sheep-dump-current-location.pl

Dump the GPS / geo-location position for the device from its various providers, if enabled.

electric-sheep-dump-current-location.pl --configfile config/myapp.conf --output geolocation.json

electric-sheep-emulator-geofix.pl

Set the GPS / geo-location position to the specified coordinates.

electric-sheep-dump-ui.pl --configfile config/myapp.conf --latitude 12.3 --longitude 45.6

electric-sheep-dump-screen-shot.pl

Take a screenshot of the device (current screen) and save to a PNG file.

electric-sheep-dump-screen-shot.pl --configfile config/myapp.conf --output screenshot.png

electric-sheep-dump-screen-video.pl

Record a video of the device's current screen and save to an MP4 file.

electric-sheep-dump-screen-video.pl --configfile config/myapp.conf --output video.mp4 --time-limit 30

electric-sheep-pull-app-apk.pl

Extract the APK file (java bytecode) for an app installed on the device and save locally, perhaps, for disassembly and/or modification and/or re-installation.

electric-sheep-pull-app-apk.pl --package calendar2 --wildcard --output anoutdir --configfile config/myapp.conf --device Pixel_2_API_30_x86_

electric-sheep-install-app

Install an APK file onto the device, passing extra installation parameters -r (for re-install) and -g (for granting permissions),

electric-sheep-install-app --apk-filename test.apk -p '-r' -p '-g' --configfile config/myapp.conf --device Pixel_2_API_30_x86_

electric-sheep-viber-send-message.pl

Send a message using the Viber app.

electric-sheep-viber-send-message.pl --message 'hello%sthere' --recipient 'george' --configfile config/myapp.conf --device Pixel_2_API_30_x86_

This one saves a lot of debugging information to debug which can be used to deal with special cases or different versions of Viber:

electric-sheep-viber-send-message.pl --outbase debug --verbosity 1 --message 'hello%sthere' --recipient 'george' --configfile config/myapp.conf --device Pixel_2_API_30_x86_

TESTING

The normal tests under the t/ directory, initiated with make test command, are quite limited in scope because they do not assume a connected device. That is, they do not check any functions which require interaction with a connected device.

The live tests under the xt/live directory, initiated with make livetest command, require an Android emulator or real device (the latter is not recommended) connected to your desktop computer on which you are doing the testing. Note that testing with your smartphone is not a good idea, please do not do this, unless it is some phone which you do not store important data. It is very easy to get an emulated Android device running on any OS.

So, prior to make livetest make sure you have an android emulator up and running with, for example, emulator -avd Pixel_2_API_30_x86_ . See section "Android Emulators" for how to install, list and run them buggers.

At least one of the author tests under the xt/author directory, initiated with make authortest command, require an APK file (to be installed on the connected device) which is quite large and it is not included in the distribution bundle of this module. Anyway, it is not a good idea to install an unknown APK to your device. But if you want to make this test then pull an APK of an existing app on your connected device with electric-sheep-pull-app-apk.pl and point the test file to this APK.

Testing will not send any messages via the device's apps. E.g. the plugin Android::ElectricSheep::Automator::Plugins::Apps::Viber will not send a message via Viber but it will mock it.

The live tests will sometimes fail because, so far, something unexpected happened in the device. For example, in testing sending input text to a text-edit widget, the calendar will be opened and a new entry will be added and its text-edit widget will be targeted. Well, sometimes the calendar app will give you some notification on startup and this messes up with the focus. Other times, the OS will detect that some app is taking too long to launch and pops up a notification about "something is not responding, shall I close it". This steals the focus and sometimes it causes the tests to fail.

PREREQUISITES

Android Studio

This is not a prerequisite but it is highly recommended to install it (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. You will not be using the IDE or anything, just the accompaniying binaries and libraries it comes with.

Additionally, Android Studio offers possibly the easiest way to create Android Virtual Devices (AVD) which emulate an Android phone of various specifications, phone models and sizes, API levels, etc. 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. Perhaps you will need an Android emulator image which comes with Google Play Services, if you are installing apps from their store. This is great for experimenting without plugging 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 module.

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:

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 (no SIM card).

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. You will need to do this just once for creating the device, you can then uninstall Android Studio.

Android Studio 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 availabe to you to create: avdmanager list target

List all available devices you can emulate: avdmanager list device

List all available devices which have been created already and are available to boot right now: avdmanager list avd

Create virtual device: avdmanager create avd -d "Nexus 6" -n myavd -k "system-images;android-29;google_apis;x86"

(source: https://stackoverflow.com/a/77599934)

In Linux, the Android emulator image files are stored at ~/.config/.android/avd and/or at ~/.android/avd Each image consists of an .avd file and a .ini file. As said before, you can boot a device with emulator -avd 'Pixel_9' (the images will be Pixel_9.avd and Pixel_9.ini)

NO ACCESS TO GOOGLE PLAY?

See here if your Android Emulator has no access to Google's App Store:

https://stackoverflow.com/questions/71815181/how-can-i-get-google-play-to-work-on-android-emulator-in-android-studio-bumblebe

Your mileage will lean on the low side.

USING YOUR REAL SMARTPHONE

Using your real smartphone with such a powerful tool may not be such a good idea.

One can only imagine what kind of viruses MICROSOFT WINDOWS can pass on to an Android device connected to it. Refrain from doing so unless you are using a more secure OS.

Start with an emulator.

WILL ANYTHING BE INSTALLED ON THE DEVICE?

Absolutely NOTHING!

This package does not mess with the connected device, neither it installs anything on it neither it modifies any of its settings. Unless the user explicitly does something, e.g. explicitly a user installs / uninstalls apps programmatically using this package.

Unlike this Python library: https://github.com/openatx/uiautomator2, (not to be confused with google's namesake), which sneakily installs their ADB server to your device!

AUTHOR

Andreas Hadjiprocopis, <bliako at cpan.org>

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:

SEE ALSO

HUGS

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)