mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-01-18 20:11:20 +00:00
Fix applaunch workload
This patch fixes following issues: * Starting from Android 11 it is not possible to use Runtime.exec to execute commands that require shell permissions like `am start`. * Engineering Android versions has su that doesn't support -c argument.
This commit is contained in:
parent
e0bf7668b8
commit
3f5707e63c
@ -14,7 +14,7 @@
|
||||
#
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
|
||||
from wa import ApkUiautoWorkload, Parameter
|
||||
from wa import ApkUiautoWorkload, Parameter, TargetError
|
||||
from wa.framework import pluginloader
|
||||
|
||||
|
||||
@ -89,6 +89,54 @@ class Applaunch(ApkUiautoWorkload):
|
||||
"""),
|
||||
]
|
||||
|
||||
def __init__(self, target, **kwargs):
|
||||
super(Applaunch, self).__init__(target, **kwargs)
|
||||
# Android doesn't allow to writable dex files starting 14 version and
|
||||
# default location (/sdcard/devlib-target) doesn't support readonly files
|
||||
# so we use /data/local/tmp as asset directory for this workload.
|
||||
self.asset_directory = '/data/local/tmp'
|
||||
self._su_has_command_option = None
|
||||
|
||||
def workload_apk(self):
|
||||
return self.deployed_assets[-1]
|
||||
|
||||
def initialize(self, context):
|
||||
super(Applaunch, self).initialize(context)
|
||||
|
||||
worload_apk = self.workload_apk()
|
||||
self.gui.uiauto_params['workload_apk'] = worload_apk
|
||||
|
||||
# Make workload apk readonly to comply with Android >= 14.
|
||||
if self.target.get_sdk_version() >= 34:
|
||||
self.target.execute(f'chmod -w {worload_apk}')
|
||||
|
||||
# Check installed su version and return whether it supports -c argument.
|
||||
#
|
||||
# Targets can have different versions of su
|
||||
# - Targets with engineering Android version have su with following usage:
|
||||
# su [WHO [COMMAND...]]
|
||||
# - Targets with rooted user Android version have su that supports passing
|
||||
# command via -c argument.
|
||||
def su_has_command_option(self):
|
||||
if self._su_has_command_option is None:
|
||||
try:
|
||||
self.target.execute('su -c id')
|
||||
self._su_has_command_option = True
|
||||
except TargetError:
|
||||
self._su_has_command_option = False
|
||||
|
||||
if self._su_has_command_option is False:
|
||||
try:
|
||||
self.target.execute('su root id')
|
||||
except TargetError:
|
||||
raise WorkloadError(
|
||||
'su must be installed and support passing command '
|
||||
'via -c argument (su -c <command>) or as positional '
|
||||
'argument after user (su <user> <command>)'
|
||||
)
|
||||
|
||||
return self._su_has_command_option
|
||||
|
||||
def init_resources(self, context):
|
||||
super(Applaunch, self).init_resources(context)
|
||||
self.workload_params['markers_enabled'] = True
|
||||
@ -112,6 +160,7 @@ class Applaunch(ApkUiautoWorkload):
|
||||
self.gui.uiauto_params['launch_activity'] = "None"
|
||||
self.gui.uiauto_params['applaunch_type'] = self.applaunch_type
|
||||
self.gui.uiauto_params['applaunch_iterations'] = self.applaunch_iterations
|
||||
self.gui.uiauto_params['su_has_command_option'] = self.su_has_command_option()
|
||||
|
||||
def setup(self, context):
|
||||
self.workload.gui.uiauto_params['package_name'] = self.workload.apk.apk_info.package
|
||||
|
Binary file not shown.
@ -3,12 +3,12 @@ apply plugin: 'com.android.application'
|
||||
def packageName = "com.arm.wa.uiauto.applaunch"
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
buildToolsVersion "28.0.3"
|
||||
namespace "${packageName}"
|
||||
compileSdkVersion 34
|
||||
defaultConfig {
|
||||
applicationId "${packageName}"
|
||||
minSdkVersion 18
|
||||
targetSdkVersion 28
|
||||
targetSdkVersion 34
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
@ -25,11 +25,5 @@ dependencies {
|
||||
implementation 'com.android.support.test:runner:0.5'
|
||||
implementation 'com.android.support.test:rules:0.5'
|
||||
implementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
|
||||
implementation(name: 'uiauto', ext:'aar')
|
||||
}
|
||||
|
||||
repositories {
|
||||
flatDir {
|
||||
dirs 'libs'
|
||||
}
|
||||
implementation files('libs/uiauto.aar')
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.arm.wa.uiauto.applaunch"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0"
|
||||
android:allowBackup="false">
|
||||
|
@ -15,7 +15,9 @@
|
||||
|
||||
package com.arm.wa.uiauto.applaunch;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.support.test.uiautomator.UiObject;
|
||||
import android.util.Log;
|
||||
@ -30,7 +32,10 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import dalvik.system.DexClassLoader;
|
||||
|
||||
@ -52,18 +57,19 @@ public class UiAutomation extends BaseUiAutomation {
|
||||
protected Bundle parameters;
|
||||
protected String packageName;
|
||||
protected String packageID;
|
||||
protected boolean suHasCommandOption;
|
||||
|
||||
@Before
|
||||
public void initialize() throws Exception {
|
||||
parameters = getParams();
|
||||
packageID = getPackageID(parameters);
|
||||
|
||||
suHasCommandOption = parameters.getBoolean("su_has_command_option");
|
||||
|
||||
// Get workload apk file parameters
|
||||
String packageName = parameters.getString("package_name");
|
||||
packageName = parameters.getString("package_name");
|
||||
String workload = parameters.getString("workload");
|
||||
String workloadAPKPath = parameters.getString("workdir");
|
||||
String workloadName = String.format("com.arm.wa.uiauto.%1s.apk", workload);
|
||||
String workloadAPKFile = String.format("%1s/%2s", workloadAPKPath, workloadName);
|
||||
String workloadAPKFile = parameters.getString("workload_apk");
|
||||
|
||||
// Load the apk file
|
||||
File apkFile = new File(workloadAPKFile);
|
||||
@ -153,7 +159,6 @@ public class UiAutomation extends BaseUiAutomation {
|
||||
private String testTag;
|
||||
private String launchCommand;
|
||||
private ActionLogger logger;
|
||||
Process launch_p;
|
||||
|
||||
public AppLaunch(String testTag, String launchCommand) {
|
||||
this.testTag = testTag;
|
||||
@ -169,24 +174,13 @@ public class UiAutomation extends BaseUiAutomation {
|
||||
|
||||
// Launches the application.
|
||||
public void launchMain() throws Exception{
|
||||
launch_p = Runtime.getRuntime().exec(launchCommand);
|
||||
launchValidate(launch_p);
|
||||
}
|
||||
|
||||
// Called by launchMain() to check if app launch is successful
|
||||
public void launchValidate(Process launch_p) throws Exception {
|
||||
launch_p.waitFor();
|
||||
Integer exit_val = launch_p.exitValue();
|
||||
if (exit_val != 0) {
|
||||
throw new Exception("Application could not be launched");
|
||||
}
|
||||
executeShellCommand(launchCommand);
|
||||
}
|
||||
|
||||
// Marks the end of application launch of the workload.
|
||||
public void endLaunch() throws Exception{
|
||||
waitObject(launchEndObject, launch_timeout);
|
||||
logger.stop();
|
||||
launch_p.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,15 +197,14 @@ public class UiAutomation extends BaseUiAutomation {
|
||||
|
||||
// Kills the application process
|
||||
public void killApplication() throws Exception{
|
||||
Process kill_p;
|
||||
String command = String.format("am force-stop %s", packageName);
|
||||
kill_p = Runtime.getRuntime().exec(new String[] { "su", "-c", command});
|
||||
kill_p.waitFor();
|
||||
kill_p.destroy();
|
||||
executeShellCommand(command);
|
||||
}
|
||||
|
||||
// Kills the background processes
|
||||
public void killBackground() throws Exception{
|
||||
// Workload has KILL_BACKGROUND_PROCESSES permission so it can execute following
|
||||
// command and it is not necessary to execute it with shell permissions.
|
||||
Process kill_p;
|
||||
kill_p = Runtime.getRuntime().exec("am kill-all");
|
||||
kill_p.waitFor();
|
||||
@ -220,15 +213,80 @@ public class UiAutomation extends BaseUiAutomation {
|
||||
|
||||
// Drop the caches
|
||||
public void dropCaches() throws Exception{
|
||||
Process sync;
|
||||
sync = Runtime.getRuntime().exec(new String[] { "su", "-c", "sync"});
|
||||
sync.waitFor();
|
||||
sync.destroy();
|
||||
executeShellCommand("sync", /*asRoot=*/true);
|
||||
executeShellCommand("echo 3 > /proc/sys/vm/drop_caches", /*asRoot=*/true);
|
||||
}
|
||||
|
||||
Process drop_cache;
|
||||
String command = "echo 3 > /proc/sys/vm/drop_caches";
|
||||
drop_cache = Runtime.getRuntime().exec(new String[] { "su", "-c", command});
|
||||
drop_cache.waitFor();
|
||||
drop_cache.destroy();
|
||||
private String getSuCommand(String command) {
|
||||
if (suHasCommandOption) {
|
||||
return String.format("su -c '%s'", command);
|
||||
}
|
||||
// If su doesn't support -c argument we assume that it has following usage:
|
||||
// su [WHO [COMMAND]]
|
||||
// that corresponds to su from engineering Android version.
|
||||
return String.format("su root %s", command);
|
||||
}
|
||||
|
||||
private void executeShellCommand(String command) throws Exception {
|
||||
executeShellCommand(command, /*asRoot=*/false);
|
||||
}
|
||||
|
||||
private static ParcelFileDescriptor[] executeShellCommand(android.app.UiAutomation uiAutomation,
|
||||
String command) throws IOException {
|
||||
ParcelFileDescriptor[] result = new ParcelFileDescriptor[2];
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
ParcelFileDescriptor[] fds = uiAutomation.executeShellCommandRwe(command);
|
||||
fds[1].close(); // close stdin
|
||||
result[0] = fds[0]; // stdout
|
||||
result[1] = fds[2]; // stderr
|
||||
return result;
|
||||
}
|
||||
|
||||
result[0] = uiAutomation.executeShellCommand(command);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void executeShellCommand(String command, boolean asRoot) throws Exception {
|
||||
android.app.UiAutomation uiAutomation = mInstrumentation.getUiAutomation();
|
||||
|
||||
String shellCommand = command;
|
||||
if (asRoot) {
|
||||
shellCommand = getSuCommand(command);
|
||||
}
|
||||
|
||||
Log.d("Shell command: ", shellCommand);
|
||||
ParcelFileDescriptor[] fds = UiAutomation.executeShellCommand(uiAutomation, command);
|
||||
ParcelFileDescriptor fdOut = fds[0];
|
||||
ParcelFileDescriptor fdErr = fds[1];
|
||||
|
||||
String out = readStreamAndClose(fdOut);
|
||||
Log.d("Shell out: ", out);
|
||||
|
||||
String err = readStreamAndClose(fdErr);
|
||||
if (!err.isEmpty()) {
|
||||
Log.e("Shell err: ", err);
|
||||
String msg = String.format("Shell command '%s' failed with error: '%s'", command, err);
|
||||
throw new Exception(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private static String readStreamAndClose(ParcelFileDescriptor fd) throws IOException {
|
||||
if (fd == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try (BufferedReader in = new BufferedReader(new InputStreamReader(
|
||||
new ParcelFileDescriptor.AutoCloseInputStream(fd)))) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (true) {
|
||||
String line = in.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
sb.append(line);
|
||||
sb.append('\n');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ buildscript {
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.2.1'
|
||||
classpath 'com.android.tools.build:gradle:8.4.0'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
|
||||
|
Loading…
x
Reference in New Issue
Block a user