(Appium-Adv-Concepts)-How to Run arbitrary ADB commands via Appium [.executeScript(“mobile: shell”, <arg>);]

Get a step-by-step walkthrough in the video below!
Automated mobile testing with Appium often requires deeper interactions with Android devices — beyond just UI interactions. Fortunately, Appium supports direct execution of ADB (Android Debug Bridge) shell commands through the mobile: shell
command using the .executeScript
method. This feature is incredibly powerful when you need to retrieve system data, manipulate device settings, or simulate complex conditions.
In this article, we’ll explore:
- What
mobile: shell
is - Syntax and usage
- Common use cases
- Code examples in Java
- Best practices and caveats
What Is
mobile: shell
in Appium?
Appium allows testers to run arbitrary shell commands on a connected Android device/emulator using the mobile: shell
command. This capability is exposed via the WebDriver's executeScript
function.
Under the hood, it leverages ADB shell to execute commands as if you were using adb shell
in the terminal.
1. We can execute the given shell command on the device under test via ADB connection.
2. This extension exposes a potential security risk and thus is only enabled when explicitly activated by the adb_shell server command line feature specifier.

This is available only on Android (not iOS), and requires:
- Appium server 1.18.0+
- UIAutomator2 driver
- Proper permissions (some commands may require a rooted device or special privileges)
Syntax:
driver.executeScript("mobile: shell", <arg>);
Parameters:
- command: A string representing the shell command (e.g.,
'pm'
,'am'
,'input'
,'getprop'
, etc.) - args (optional): An array of arguments for the command.
- includeStderr (optional): Whether to include stderr in the response.
- timeout (optional): Command timeout in milliseconds.
Example:
//To change window_animation_scale to 0
System.out.println("change window_animation_scale to 0");
driver.executeScript("mobile: shell",ImmutableMap.of(
"command","settings put global window_animation_scale",
"args","0"));
Appium Server Setup: -
We Need to Start Appium Server with the “ — relaxed-security” flag
appium - relaxed-security
Or
appium - allow-insecure=adb_shell

With Appium running in this mode, we have access to a new “mobile:” command called “mobile: shell”.
The Appium “mobile:” commands are special commands that can be accessed using executeScript
“Complete Code: Optimized and Ready to Use”:
package com.appiumguide.advanceappiumconcepts;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import java.net.*;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
/**
* Script Details - How to Run arbitrary ADB commands via Appium {.executeScript("mobile: shell", <arg>)};
*
* appium-java-client version: 9.3.0
*
* @author 'Ramesh Kodumuru' for AppiumGuide [appiumguide@gmail.com]
*/
/*
Scenario's covered:
-----------------------------------------
1. Need to execute all 3 adb shell commands
Note: will follow 3 different styles to provide arguments data
2. To retrieve the result of the ADB call
Note: will follow 2 different styles to retrieve results
//adb shell dumpsys window displays|grep -e 'mCurrentFocus'
*/
public class ArbitraryAdbCommandDemo {
private AndroidDriver driver;
@BeforeTest
public void setup() throws MalformedURLException {
UiAutomator2Options cap=new UiAutomator2Options();
cap.setPlatformName("android");
cap.setAutomationName("uiautomator2");
cap.setDeviceName("Pixel8Pro");
driver = new AndroidDriver(new URL("http://127.0.0.1:4723"), cap);
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(100));
}
@Test
public void test() throws InterruptedException {
//adb shell settings put global window_animation_scale 0
//adb shell settings put global transition_animation_scale 0
//adb shell settings put global animator_duration_scale 0
//Scenario-1
System.out.println("Scenario-1");
//To change window_animation_scale to 0
System.out.println("change window_animation_scale to 0");
driver.executeScript("mobile: shell",ImmutableMap.of(
"command","settings put global window_animation_scale",
"args","0"));
//To change transition_animation_scale to 0
System.out.println("change transition_animation_scale to 0");
Map<String,Object> transition_animation_scale=ImmutableMap.of(
"command","settings put global transition_animation_scale",
"args","0");
//To change animator_duration_scale to 0
System.out.println("change animator_duration_scale to 0");
driver.executeScript("mobile: shell",transition_animation_scale);
HashMap<String,Object> animator_duration_scale=new HashMap<>();
animator_duration_scale.put("command","settings put global animator_duration_scale");
animator_duration_scale.put("args","0");
driver.executeScript("mobile: shell", animator_duration_scale);
Thread.sleep(3000);
driver.activateApp("io.appium.android.apis");
Thread.sleep(5000);
//Scenario-2
System.out.println("Scenario-2");
//Running the '//adb shell dumpsys window displays|grep -e 'mCurrentFocus' to fetch appPackege & appActivity details
System.out.println("Running the '//adb shell dumpsys window displays|grep -e 'mCurrentFocus' to fetch appPackege & appActivity details");
Object Output1=driver.executeScript("mobile: shell",ImmutableMap.of(
"command","dumpsys window displays|grep -e",
"args","'mCurrentFocus'"));
System.out.println("Output1 value- "+Output1.toString());
//adb shell dumpsys window displays|grep -e 'mCurrentFocus'
String Output2=(String) driver.executeScript("mobile: shell",ImmutableMap.of(
"command","dumpsys window displays|grep -e",
"args","'mCurrentFocus'"));
System.out.println("Output1 value- "+Output2);
}
@AfterTest
public void teardown() {
if(driver!=null) {
driver.quit();
System.out.println("Test Completed");
}
}
}
Caveats and Considerations
- Permissions: Some shell commands require root or elevated permissions.
- Appium Logs: These commands may not show up in the typical Appium logs unless verbosity is increased.
- Cross-platform: This feature is Android-only. For iOS, different mechanisms like XCUITest are required.
- Security: Use with caution on production devices — improper commands can brick or damage system states.
Best Practices
- Always test your shell commands outside of Appium first via terminal (
adb shell
) before automating. - Use conditional logic to verify device state before applying destructive operations (like app data clearance).
GitHub Link:
No comments:
Post a Comment