blob: fd59eaa1dcf14811d93b992234db9520772be582 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tradefed.targetprep;
import com.android.ddmlib.IDevice;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.config.Option;
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.device.StubDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.util.BinaryState;
import com.android.tradefed.util.MultiMap;
import com.google.common.annotations.VisibleForTesting;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A {@link ITargetPreparer} that configures a device for testing based on provided {@link Option}s.
*
* <p>Requires a device where 'adb root' is possible, typically a userdebug build type.
*
* <p>Should be performed <strong>after</strong> a new build is flashed.
*/
@OptionClass(alias = "device-setup")
public class DeviceSetup extends BaseTargetPreparer implements ITargetCleaner {
// Networking
@Option(name = "airplane-mode",
description = "Turn airplane mode on or off")
protected BinaryState mAirplaneMode = BinaryState.IGNORE;
// ON: settings put global airplane_mode_on 1
// am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true
// OFF: settings put global airplane_mode_on 0
// am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false
@Option(name = "data", description = "Turn mobile data on or off")
protected BinaryState mData = BinaryState.IGNORE;
// ON: settings put global mobile_data 1
// svc data enable
// OFF: settings put global mobile_data 0
// svc data disable
@Option(name = "cell", description = "Turn cellular radio on or off")
protected BinaryState mCell = BinaryState.IGNORE;
// ON: settings put global cell_on 1
// OFF: settings put global cell_on 0
@Option(name = "cell-auto-setting", description = "Turn wear cellular mediator on or off")
protected BinaryState mCellAutoSetting = BinaryState.IGNORE;
// ON: settings put global clockwork_cell_auto_setting 1
// OFF: settings put global clockwork_cell_auto_setting 0
@Option(name = "wifi", description = "Turn wifi on or off")
protected BinaryState mWifi = BinaryState.IGNORE;
// ON: settings put global wifi_on 1
// svc wifi enable
// OFF: settings put global wifi_off 0
// svc wifi disable
@Option(name = "wifi-network",
description = "The SSID of the network to connect to. Will only attempt to " +
"connect to a network if set")
protected String mWifiSsid = null;
@Option(name = "wifi-psk",
description = "The passphrase used to connect to a secured network")
protected String mWifiPsk = null;
@Option(name = "wifi-watchdog",
description = "Turn wifi watchdog on or off")
protected BinaryState mWifiWatchdog = BinaryState.IGNORE;
// ON: settings put global wifi_watchdog 1
// OFF: settings put global wifi_watchdog 0
@Option(name = "disable-cw-wifi-mediator", description = "Turn wifi mediator on or off")
protected BinaryState mDisableCwWifiMediator = BinaryState.IGNORE;
// ON: settings put global cw_disable_wifimediator 1
// OFF: settings put global cw_disable_wifimediator 0
@Option(
name = "wifi-scan-always-enabled",
description = "Turn wifi scan always enabled on or off"
)
protected BinaryState mWifiScanAlwaysEnabled = BinaryState.IGNORE;
// ON: settings put global wifi_scan_always_enabled 1
// OFF: settings put global wifi_scan_always_enabled 0
@Option(name = "ethernet",
description = "Turn ethernet on or off")
protected BinaryState mEthernet = BinaryState.IGNORE;
// ON: ifconfig eth0 up
// OFF: ifconfig eth0 down
@Option(name = "bluetooth",
description = "Turn bluetooth on or off")
protected BinaryState mBluetooth = BinaryState.IGNORE;
// ON: service call bluetooth_manager 6
// OFF: service call bluetooth_manager 8
@Option(name = "nfc",
description = "Turn nfc on or off")
protected BinaryState mNfc = BinaryState.IGNORE;
// ON: svc nfc enable
// OFF: svc nfc disable
// Screen
@Option(name = "screen-adaptive-brightness",
description = "Turn screen adaptive brightness on or off")
protected BinaryState mScreenAdaptiveBrightness = BinaryState.IGNORE;
// ON: settings put system screen_brightness_mode 1
// OFF: settings put system screen_brightness_mode 0
@Option(name = "screen-brightness",
description = "Set the screen brightness. This is uncalibrated from product to product")
protected Integer mScreenBrightness = null;
// settings put system screen_brightness $N
@Option(name = "screen-always-on",
description = "Turn 'screen always on' on or off. If ON, then screen-timeout-secs " +
"must be unset. Will only work when the device is plugged in")
protected BinaryState mScreenAlwaysOn = BinaryState.ON;
// ON: svc power stayon true
// OFF: svc power stayon false
@Option(name = "screen-timeout-secs",
description = "Set the screen timeout in seconds. If set, then screen-always-on must " +
"be OFF or DEFAULT")
protected Long mScreenTimeoutSecs = null;
// settings put system screen_off_timeout $(N * 1000)
@Option(name = "screen-ambient-mode",
description = "Turn screen ambient mode on or off")
protected BinaryState mScreenAmbientMode = BinaryState.IGNORE;
// ON: settings put secure doze_enabled 1
// OFF: settings put secure doze_enabled 0
@Option(name = "wake-gesture",
description = "Turn wake gesture on or off")
protected BinaryState mWakeGesture = BinaryState.IGNORE;
// ON: settings put secure wake_gesture_enabled 1
// OFF: settings put secure wake_gesture_enabled 0
@Option(name = "screen-saver",
description = "Turn screen saver on or off")
protected BinaryState mScreenSaver = BinaryState.IGNORE;
// ON: settings put secure screensaver_enabled 1
// OFF: settings put secure screensaver_enabled 0
@Option(name = "notification-led",
description = "Turn the notification led on or off")
protected BinaryState mNotificationLed = BinaryState.IGNORE;
// ON: settings put system notification_light_pulse 1
// OFF: settings put system notification_light_pulse 0
@Option(name = "install-non-market-apps",
description = "Allow or prevent non-market app to initiate an apk install request")
protected BinaryState mInstallNonMarketApps = BinaryState.IGNORE;
// ON: settings put secure install_non_market_apps 1
// OFF: settings put secure install_non_market_apps 0
// Media
@Option(name = "trigger-media-mounted",
description = "Trigger a MEDIA_MOUNTED broadcast")
protected boolean mTriggerMediaMounted = false;
// am broadcast -a android.intent.action.MEDIA_MOUNTED -d file://${EXTERNAL_STORAGE}
// --receiver-include-background
// Location
@Option(name = "location-gps", description = "Turn the GPS location on or off")
protected BinaryState mLocationGps = BinaryState.IGNORE;
// ON: settings put secure location_providers_allowed +gps
// OFF: settings put secure location_providers_allowed -gps
@Option(name = "location-network",
description = "Turn the network location on or off")
protected BinaryState mLocationNetwork = BinaryState.IGNORE;
// ON: settings put secure location_providers_allowed +network
// OFF: settings put secure location_providers_allowed -network
// Sensor
@Option(name = "auto-rotate",
description = "Turn auto rotate on or off")
protected BinaryState mAutoRotate = BinaryState.IGNORE;
// ON: settings put system accelerometer_rotation 1
// OFF: settings put system accelerometer_rotation 0
// Power
@Option(name = "battery-saver-mode",
description = "Turn battery saver mode manually on or off. If OFF but battery is " +
"less battery-saver-trigger, the device will still go into battery saver mode")
protected BinaryState mBatterySaver = BinaryState.IGNORE;
// ON: dumpsys battery set usb 0
// settings put global low_power 1
// OFF: settings put global low_power 0
@Option(name = "battery-saver-trigger",
description = "Set the battery saver trigger level. Should be [1-99] to enable, or " +
"0 to disable automatic battery saver mode")
protected Integer mBatterySaverTrigger = null;
// settings put global low_power_trigger_level $N
@Option(name = "enable-full-battery-stats-history",
description = "Enable full history for batterystats. This option is only " +
"applicable for L+")
protected boolean mEnableFullBatteryStatsHistory = false;
// dumpsys batterystats --enable full-history
@Option(name = "disable-doze",
description = "Disable device from going into doze mode. This option is only " +
"applicable for M+")
protected boolean mDisableDoze = false;
// dumpsys deviceidle disable
// Time
@Option(name = "auto-update-time",
description = "Turn auto update time on or off")
protected BinaryState mAutoUpdateTime = BinaryState.IGNORE;
// ON: settings put global auto_time 1
// OFF: settings put global auto_time 0
@Option(name = "auto-update-timezone", description = "Turn auto update timezone on or off")
protected BinaryState mAutoUpdateTimezone = BinaryState.IGNORE;
// ON: settings put global auto_timezone 1
// OFF: settings put global auto_timezone 0
@Option(
name = "set-timezone",
description =
"Set timezone property by TZ name "
+ "(http://en.wikipedia.org/wiki/List_of_tz_database_time_zones)"
)
protected String mTimezone = null;
// Calling
@Option(name = "disable-dialing",
description = "Disable dialing")
protected boolean mDisableDialing = true;
// setprop ro.telephony.disable-call true"
@Option(name = "default-sim-data",
description = "Set the default sim card slot for data. Leave unset for single SIM " +
"devices")
protected Integer mDefaultSimData = null;
// settings put global multi_sim_data_call $N
@Option(name = "default-sim-voice",
description = "Set the default sim card slot for voice calls. Leave unset for single " +
"SIM devices")
protected Integer mDefaultSimVoice = null;
// settings put global multi_sim_voice_call $N
@Option(name = "default-sim-sms",
description = "Set the default sim card slot for SMS. Leave unset for single SIM " +
"devices")
protected Integer mDefaultSimSms = null;
// settings put global multi_sim_sms $N
// Audio
private static final boolean DEFAULT_DISABLE_AUDIO = true;
@Option(name = "disable-audio",
description = "Disable the audio")
protected boolean mDisableAudio = DEFAULT_DISABLE_AUDIO;
// setprop ro.audio.silent 1"
@Option(name = "force-skip-system-props",
description = "Force setup to not modify any device system properties. All other " +
"system property options will be ignored")
protected boolean mForceSkipSystemProps = false;
@Option(name = "force-skip-settings",
description = "Force setup to not modify any device settings. All other setting " +
"options will be ignored.")
protected boolean mForceSkipSettings = false;
@Option(name = "force-skip-run-commands",
description = "Force setup to not run any additional commands. All other commands " +
"will be ignored.")
protected boolean mForceSkipRunCommands = false;
@Option(name = "set-test-harness",
description = "Set the read-only test harness flag on boot")
protected boolean mSetTestHarness = true;
// setprop ro.monkey 1
// setprop ro.test_harness 1
@Option(name = "disable-dalvik-verifier",
description = "Disable the dalvik verifier on device. Allows package-private " +
"framework tests to run.")
protected boolean mDisableDalvikVerifier = false;
// setprop dalvik.vm.dexopt-flags v=n
@Option(name = "set-property",
description = "Set the specified property on boot. Option may be repeated but only " +
"the last value for a given key will be set.")
protected Map<String, String> mSetProps = new HashMap<>();
@Option(
name = "restore-properties",
description =
"Restore previous /data/local.prop on tear down, restoring any properties DeviceSetup changed by modifying /data/local.prop."
)
protected boolean mRestoreProperties = false;
protected File mPreviousProperties;
@Option(name = "set-system-setting",
description = "Change a system (non-secure) setting. Option may be repeated and all " +
"key/value pairs will be set in order.")
// Use a Multimap since it is possible for a setting to have multiple values for the same key
protected MultiMap<String, String> mSystemSettings = new MultiMap<>();
@Option(name = "set-secure-setting",
description = "Change a secure setting. Option may be repeated and all key/value " +
"pairs will be set in order.")
// Use a Multimap since it is possible for a setting to have multiple values for the same key
protected MultiMap<String, String> mSecureSettings = new MultiMap<>();
@Option(name = "set-global-setting",
description = "Change a global setting. Option may be repeated and all key/value " +
"pairs will be set in order.")
// Use a Multimap since it is possible for a setting to have multiple values for the same key
protected MultiMap<String, String> mGlobalSettings = new MultiMap<>();
@Option(
name = "restore-settings",
description = "Restore settings modified by this preparer on tear down."
)
protected boolean mRestoreSettings = false;
private Map<String, String> mPreviousSystemSettings = new HashMap<>();
private Map<String, String> mPreviousSecureSettings = new HashMap<>();
private Map<String, String> mPreviousGlobalSettings = new HashMap<>();
protected List<String> mRunCommandBeforeSettings = new ArrayList<>();
@Option(name = "run-command",
description = "Run an adb shell command. Option may be repeated")
protected List<String> mRunCommandAfterSettings = new ArrayList<>();
@Option(name = "disconnect-wifi-after-test",
description = "Disconnect from wifi network after test completes.")
private boolean mDisconnectWifiAfterTest = true;
private static final long DEFAULT_MIN_EXTERNAL_STORAGE_KB = 500;
@Option(name = "min-external-storage-kb",
description="The minimum amount of free space in KB that must be present on device's " +
"external storage.")
protected long mMinExternalStorageKb = DEFAULT_MIN_EXTERNAL_STORAGE_KB;
@Option(name = "local-data-path",
description = "Optional local file path of test data to sync to device's external " +
"storage. Use --remote-data-path to set remote location.")
protected File mLocalDataFile = null;
@Option(name = "remote-data-path",
description = "Optional file path on device's external storage to sync test data. " +
"Must be used with --local-data-path.")
protected String mRemoteDataPath = null;
// Deprecated options follow
/**
* @deprecated use min-external-storage-kb instead.
*/
@Option(name = "min-external-store-space",
description = "deprecated, use option min-external-storage-kb. The minimum amount of " +
"free space in KB that must be present on device's external storage.")
@Deprecated
private long mDeprecatedMinExternalStoreSpace = DEFAULT_MIN_EXTERNAL_STORAGE_KB;
/**
* @deprecated use option disable-audio instead.
*/
@Option(name = "audio-silent",
description = "deprecated, use option disable-audio. set ro.audio.silent on boot.")
@Deprecated
private boolean mDeprecatedSetAudioSilent = DEFAULT_DISABLE_AUDIO;
/**
* @deprecated use option set-property instead.
*/
@Option(name = "setprop",
description = "deprecated, use option set-property. set the specified property on " +
"boot. Format: --setprop key=value. May be repeated.")
@Deprecated
private Collection<String> mDeprecatedSetProps = new ArrayList<String>();
private static final String PERSIST_PREFIX = "persist.";
/**
* {@inheritDoc}
*/
@Override
public void setUp(ITestDevice device, IBuildInfo buildInfo) throws DeviceNotAvailableException,
TargetSetupError {
if (isDisabled()) {
return;
}
CLog.i("Performing setup on %s", device.getSerialNumber());
if (device.getOptions().isEnableAdbRoot() && !device.enableAdbRoot()) {
throw new TargetSetupError(String.format("Failed to enable adb root on %s",
device.getSerialNumber()), device.getDeviceDescriptor());
}
// Convert deprecated options into current options
processDeprecatedOptions(device);
// Convert options into settings and run commands
processOptions(device);
// Change system props (will reboot device)
changeSystemProps(device);
// Handle screen always on setting
handleScreenAlwaysOnSetting(device);
// Run commands designated to be run before changing settings
runCommands(device, mRunCommandBeforeSettings);
// Change settings
changeSettings(device);
// Connect wifi after settings since this may take a while
connectWifi(device);
// Sync data after settings since this may take a while
syncTestData(device);
// Run commands designated to be run after changing settings
runCommands(device, mRunCommandAfterSettings);
// Throw an error if there is not enough storage space
checkExternalStoreSpace(device);
device.clearErrorDialogs();
}
/**
* {@inheritDoc}
*/
@Override
public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)
throws DeviceNotAvailableException {
// ignore tearDown if it's a stub device, since there is no real device to clean.
if (isDisabled() || device.getIDevice() instanceof StubDevice) {
return;
}
CLog.i("Performing teardown on %s", device.getSerialNumber());
if (e instanceof DeviceFailedToBootError) {
CLog.d("boot failure: skipping teardown");
return;
}
// Only try to disconnect if wifi ssid is set since isWifiEnabled() is a heavy operation
// which should be avoided when possible
if (mDisconnectWifiAfterTest && mWifiSsid != null && device.isWifiEnabled()) {
boolean result = device.disconnectFromWifi();
if (result) {
CLog.i("Successfully disconnected from wifi network on %s",
device.getSerialNumber());
} else {
CLog.w("Failed to disconnect from wifi network on %s", device.getSerialNumber());
}
}
if (mRestoreProperties) {
if (mPreviousProperties != null) {
device.pushFile(mPreviousProperties, "/data/local.prop");
} else {
device.executeShellCommand("rm -f /data/local.prop");
}
device.reboot();
}
if (mRestoreSettings) {
for (Map.Entry<String, String> entry : mPreviousSystemSettings.entrySet()) {
device.setSetting("system", entry.getKey(), entry.getValue());
}
for (Map.Entry<String, String> entry : mPreviousGlobalSettings.entrySet()) {
device.setSetting("global", entry.getKey(), entry.getValue());
}
for (Map.Entry<String, String> entry : mPreviousSecureSettings.entrySet()) {
device.setSetting("secure", entry.getKey(), entry.getValue());
}
}
}
/**
* Processes the deprecated options converting them into the currently used options.
* <p>
* This method should be run before any other processing methods. Will throw a
* {@link TargetSetupError} if the deprecated option overrides a specified non-deprecated
* option.
* </p>
* @throws TargetSetupError if there is a conflict
*/
public void processDeprecatedOptions(ITestDevice device) throws TargetSetupError {
if (mDeprecatedMinExternalStoreSpace != DEFAULT_MIN_EXTERNAL_STORAGE_KB) {
if (mMinExternalStorageKb != DEFAULT_MIN_EXTERNAL_STORAGE_KB) {
throw new TargetSetupError("Deprecated option min-external-store-space conflicts " +
"with option min-external-storage-kb", device.getDeviceDescriptor());
}
mMinExternalStorageKb = mDeprecatedMinExternalStoreSpace;
}
if (mDeprecatedSetAudioSilent != DEFAULT_DISABLE_AUDIO) {
if (mDisableAudio != DEFAULT_DISABLE_AUDIO) {
throw new TargetSetupError("Deprecated option audio-silent conflicts with " +
"option disable-audio", device.getDeviceDescriptor());
}
mDisableAudio = mDeprecatedSetAudioSilent;
}
if (!mDeprecatedSetProps.isEmpty()) {
if (!mSetProps.isEmpty()) {
throw new TargetSetupError("Deprecated option setprop conflicts with option " +
"set-property ", device.getDeviceDescriptor());
}
for (String prop : mDeprecatedSetProps) {
String[] parts = prop.split("=", 2);
String key = parts[0].trim();
String value = parts.length == 2 ? parts[1].trim() : "";
mSetProps.put(key, value);
}
}
}
/**
* Process all the {@link Option}s and turn them into system props, settings, or run commands.
* Does not run any commands on the device at this time.
* <p>
* Exposed so that children classes may override this.
* </p>
*
* @param device The {@link ITestDevice}
* @throws DeviceNotAvailableException if the device is not available
* @throws TargetSetupError if the {@link Option}s conflict
*/
public void processOptions(ITestDevice device) throws DeviceNotAvailableException,
TargetSetupError {
setSettingForBinaryState(mData, mGlobalSettings, "mobile_data", "1", "0");
setCommandForBinaryState(
mData, mRunCommandAfterSettings, "svc data enable", "svc data disable");
setSettingForBinaryState(mCell, mGlobalSettings, "cell_on", "1", "0");
setSettingForBinaryState(
mCellAutoSetting, mGlobalSettings, "clockwork_cell_auto_setting", "1", "0");
setSettingForBinaryState(mWifi, mGlobalSettings, "wifi_on", "1", "0");
setCommandForBinaryState(
mWifi, mRunCommandAfterSettings, "svc wifi enable", "svc wifi disable");
setSettingForBinaryState(mWifiWatchdog, mGlobalSettings, "wifi_watchdog", "1", "0");
setSettingForBinaryState(
mDisableCwWifiMediator, mGlobalSettings, "cw_disable_wifimediator", "1", "0");
setSettingForBinaryState(mWifiScanAlwaysEnabled, mGlobalSettings,
"wifi_scan_always_enabled", "1", "0");
setCommandForBinaryState(mEthernet, mRunCommandAfterSettings,
"ifconfig eth0 up", "ifconfig eth0 down");
setCommandForBinaryState(mBluetooth, mRunCommandAfterSettings,
"service call bluetooth_manager 6", "service call bluetooth_manager 8");
setCommandForBinaryState(mNfc, mRunCommandAfterSettings,
"svc nfc enable", "svc nfc disable");
if (mScreenBrightness != null && BinaryState.ON.equals(mScreenAdaptiveBrightness)) {
throw new TargetSetupError("Option screen-brightness cannot be set when " +
"screen-adaptive-brightness is set to ON", device.getDeviceDescriptor());
}
setSettingForBinaryState(mScreenAdaptiveBrightness, mSystemSettings,
"screen_brightness_mode", "1", "0");
if (mScreenBrightness != null) {
mSystemSettings.put("screen_brightness", Integer.toString(mScreenBrightness));
}
if (mScreenTimeoutSecs != null) {
mSystemSettings.put("screen_off_timeout", Long.toString(mScreenTimeoutSecs * 1000));
}
setSettingForBinaryState(mScreenAmbientMode, mSecureSettings, "doze_enabled", "1", "0");
setSettingForBinaryState(mWakeGesture, mSecureSettings, "wake_gesture_enabled", "1", "0");
setSettingForBinaryState(mScreenSaver, mSecureSettings, "screensaver_enabled", "1", "0");
setSettingForBinaryState(mNotificationLed, mSystemSettings,
"notification_light_pulse", "1", "0");
setSettingForBinaryState(mInstallNonMarketApps, mSecureSettings,
"install_non_market_apps", "1", "0");
if (mTriggerMediaMounted) {
mRunCommandAfterSettings.add(
"am broadcast -a android.intent.action.MEDIA_MOUNTED -d "
+ "file://${EXTERNAL_STORAGE} --receiver-include-background");
}
setSettingForBinaryState(mLocationGps, mSecureSettings,
"location_providers_allowed", "+gps", "-gps");
setSettingForBinaryState(mLocationNetwork, mSecureSettings,
"location_providers_allowed", "+network", "-network");
setSettingForBinaryState(mAutoRotate, mSystemSettings, "accelerometer_rotation", "1", "0");
if (device.getApiLevel() < 22) {
setCommandForBinaryState(mBatterySaver, mRunCommandBeforeSettings,
"dumpsys battery set usb 0", null);
} else {
setCommandForBinaryState(mBatterySaver, mRunCommandBeforeSettings,
"dumpsys battery unplug", null);
}
setSettingForBinaryState(mBatterySaver, mGlobalSettings, "low_power", "1", "0");
if (mBatterySaverTrigger != null) {
mGlobalSettings.put("low_power_trigger_level", Integer.toString(mBatterySaverTrigger));
}
if (mEnableFullBatteryStatsHistory) {
mRunCommandAfterSettings.add("dumpsys batterystats --enable full-history");
}
if (mDisableDoze) {
mRunCommandAfterSettings.add("dumpsys deviceidle disable");
}
setSettingForBinaryState(mAutoUpdateTime, mGlobalSettings, "auto_time", "1", "0");
setSettingForBinaryState(mAutoUpdateTimezone, mGlobalSettings, "auto_timezone", "1", "0");
if (mTimezone != null) {
mSetProps.put("persist.sys.timezone", mTimezone);
}
if (mDisableDialing) {
mSetProps.put("ro.telephony.disable-call", "true");
}
if (mDefaultSimData != null) {
mGlobalSettings.put("multi_sim_data_call", Integer.toString(mDefaultSimData));
}
if (mDefaultSimVoice != null) {
mGlobalSettings.put("multi_sim_voice_call", Integer.toString(mDefaultSimVoice));
}
if (mDefaultSimSms != null) {
mGlobalSettings.put("multi_sim_sms", Integer.toString(mDefaultSimSms));
}
if (mDisableAudio) {
mSetProps.put("ro.audio.silent", "1");
}
if (mSetTestHarness) {
// set both ro.monkey and ro.test_harness, for compatibility with older platforms
mSetProps.put("ro.monkey", "1");
mSetProps.put("ro.test_harness", "1");
}
if (mDisableDalvikVerifier) {
mSetProps.put("dalvik.vm.dexopt-flags", "v=n");
}
}
/**
* Change the system properties on the device.
*
* @param device The {@link ITestDevice}
* @throws DeviceNotAvailableException if the device is not available
* @throws TargetSetupError if there was a failure setting the system properties
*/
private void changeSystemProps(ITestDevice device) throws DeviceNotAvailableException,
TargetSetupError {
if (mForceSkipSystemProps) {
CLog.d("Skipping system props due to force-skip-system-props");
return;
}
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> prop : mSetProps.entrySet()) {
if (prop.getKey().startsWith(PERSIST_PREFIX)) {
String command = String.format("setprop \"%s\" \"%s\"",
prop.getKey(), prop.getValue());
device.executeShellCommand(command);
} else {
sb.append(String.format("%s=%s\n", prop.getKey(), prop.getValue()));
}
}
if (sb.length() == 0) {
return;
}
if (mRestoreProperties) {
mPreviousProperties = device.pullFile("/data/local.prop");
}
CLog.d("Pushing the following properties to /data/local.prop:\n%s", sb.toString());
boolean result = device.pushString(sb.toString(), "/data/local.prop");
if (!result) {
throw new TargetSetupError(String.format("Failed to push /data/local.prop to %s",
device.getSerialNumber()), device.getDeviceDescriptor());
}
// Set reasonable permissions for /data/local.prop
device.executeShellCommand("chmod 644 /data/local.prop");
CLog.i("Rebooting %s due to system property change", device.getSerialNumber());
device.reboot();
}
/**
* Handles screen always on settings.
* <p>
* This is done in a dedicated function because special handling is required in case of setting
* screen to always on.
* @throws DeviceNotAvailableException
*/
private void handleScreenAlwaysOnSetting(ITestDevice device)
throws DeviceNotAvailableException {
String cmd = "svc power stayon %s";
switch (mScreenAlwaysOn) {
case ON:
CLog.d("Setting screen always on to true");
device.executeShellCommand(String.format(cmd, "true"));
// send MENU press in case keygaurd needs to be dismissed again
device.executeShellCommand("input keyevent 82");
// send HOME press in case keyguard was already dismissed, so we bring device back
// to home screen
device.executeShellCommand("input keyevent 3");
break;
case OFF:
CLog.d("Setting screen always on to false");
device.executeShellCommand(String.format(cmd, "false"));
break;
case IGNORE:
break;
}
}
/**
* Change the settings on the device.
* <p>
* Exposed so children classes may override.
* </p>
*
* @param device The {@link ITestDevice}
* @throws DeviceNotAvailableException if the device is not available
* @throws TargetSetupError if there was a failure setting the settings
*/
public void changeSettings(ITestDevice device) throws DeviceNotAvailableException,
TargetSetupError {
if (mForceSkipSettings) {
CLog.d("Skipping settings due to force-skip-setttings");
return;
}
if (mSystemSettings.isEmpty() && mSecureSettings.isEmpty() && mGlobalSettings.isEmpty() &&
BinaryState.IGNORE.equals(mAirplaneMode)) {
CLog.d("No settings to change");
return;
}
if (device.getApiLevel() < 22) {
throw new TargetSetupError(String.format("Changing setting not supported on %s, " +
"must be API 22+", device.getSerialNumber()), device.getDeviceDescriptor());
}
// Special case airplane mode since it needs to be set before other connectivity settings
// For example, it is possible to enable airplane mode and then turn wifi on
String command = "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state %s";
switch (mAirplaneMode) {
case ON:
CLog.d("Changing global setting airplane_mode_on to 1");
device.setSetting("global", "airplane_mode_on", "1");
if (!mForceSkipRunCommands) {
device.executeShellCommand(String.format(command, "true"));
}
break;
case OFF:
CLog.d("Changing global setting airplane_mode_on to 0");
device.setSetting("global", "airplane_mode_on", "0");
if (!mForceSkipRunCommands) {
device.executeShellCommand(String.format(command, "false"));
}
break;
case IGNORE:
// No-op
break;
}
for (String key : mSystemSettings.keySet()) {
for (String value : mSystemSettings.get(key)) {
if (mRestoreSettings) {
String previousSetting = device.getSetting("system", key);
mPreviousSystemSettings.put(key, previousSetting);
}
CLog.d("Changing system setting %s to %s", key, value);
device.setSetting("system", key, value);
}
}
for (String key : mSecureSettings.keySet()) {
for (String value : mSecureSettings.get(key)) {
if (mRestoreSettings) {
String previousSetting = device.getSetting("secure", key);
mPreviousSecureSettings.put(key, previousSetting);
}
CLog.d("Changing secure setting %s to %s", key, value);
device.setSetting("secure", key, value);
}
}
for (String key : mGlobalSettings.keySet()) {
for (String value : mGlobalSettings.get(key)) {
if (mRestoreSettings) {
String previousSetting = device.getSetting("global", key);
mPreviousGlobalSettings.put(key, previousSetting);
}
CLog.d("Changing global setting %s to %s", key, value);
device.setSetting("global", key, value);
}
}
}
/**
* Execute additional commands on the device.
*
* @param device The {@link ITestDevice}
* @param commands The list of commands to run
* @throws DeviceNotAvailableException if the device is not available
* @throws TargetSetupError if there was a failure setting the settings
*/
private void runCommands(ITestDevice device, List<String> commands)
throws DeviceNotAvailableException, TargetSetupError {
if (mForceSkipRunCommands) {
CLog.d("Skipping run commands due to force-skip-run-commands");
return;
}
for (String command : commands) {
device.executeShellCommand(command);
}
}
/**
* Connects device to Wifi if SSID is specified.
*
* @param device The {@link ITestDevice}
* @throws DeviceNotAvailableException if the device is not available
* @throws TargetSetupError if there was a failure setting the settings
*/
private void connectWifi(ITestDevice device) throws DeviceNotAvailableException,
TargetSetupError {
if (mForceSkipRunCommands) {
CLog.d("Skipping connect wifi due to force-skip-run-commands");
return;
}
if (mWifiSsid != null) {
if (!device.connectToWifiNetwork(mWifiSsid, mWifiPsk)) {
throw new TargetSetupError(String.format(
"Failed to connect to wifi network %s on %s", mWifiSsid,
device.getSerialNumber()), device.getDeviceDescriptor());
}
}
}
/**
* Syncs a set of test data files, specified via local-data-path, to devices external storage.
*
* @param device The {@link ITestDevice}
* @throws DeviceNotAvailableException if the device is not available
* @throws TargetSetupError if data fails to sync
*/
private void syncTestData(ITestDevice device) throws DeviceNotAvailableException,
TargetSetupError {
if (mLocalDataFile == null) {
return;
}
if (!mLocalDataFile.exists() || !mLocalDataFile.isDirectory()) {
throw new TargetSetupError(String.format(
"local-data-path %s is not a directory", mLocalDataFile.getAbsolutePath()),
device.getDeviceDescriptor());
}
String fullRemotePath = device.getIDevice().getMountPoint(IDevice.MNT_EXTERNAL_STORAGE);
if (fullRemotePath == null) {
throw new TargetSetupError(String.format(
"failed to get external storage path on device %s", device.getSerialNumber()),
device.getDeviceDescriptor());
}
if (mRemoteDataPath != null) {
fullRemotePath = String.format("%s/%s", fullRemotePath, mRemoteDataPath);
}
boolean result = device.syncFiles(mLocalDataFile, fullRemotePath);
if (!result) {
// TODO: get exact error code and respond accordingly
throw new TargetSetupError(String.format(
"failed to sync test data from local-data-path %s to %s on device %s",
mLocalDataFile.getAbsolutePath(), fullRemotePath, device.getSerialNumber()),
device.getDeviceDescriptor());
}
}
/**
* Check that device external store has the required space
*
* @param device The {@link ITestDevice}
* @throws DeviceNotAvailableException if the device is not available or if the device does not
* have the required space
*/
private void checkExternalStoreSpace(ITestDevice device) throws DeviceNotAvailableException {
if (mMinExternalStorageKb <= 0) {
return;
}
long freeSpace = device.getExternalStoreFreeSpace();
if (freeSpace < mMinExternalStorageKb) {
throw new DeviceNotAvailableException(String.format(
"External store free space %dK is less than required %dK for device %s",
freeSpace , mMinExternalStorageKb, device.getSerialNumber()),
device.getSerialNumber());
}
}
/**
* Helper method to add an ON/OFF setting to a setting map.
*
* @param state The {@link BinaryState}
* @param settingsMap The {@link MultiMap} used to store the settings.
* @param setting The setting key
* @param onValue The value if ON
* @param offValue The value if OFF
*/
public static void setSettingForBinaryState(BinaryState state,
MultiMap<String, String> settingsMap, String setting, String onValue, String offValue) {
switch (state) {
case ON:
settingsMap.put(setting, onValue);
break;
case OFF:
settingsMap.put(setting, offValue);
break;
case IGNORE:
// Do nothing
break;
}
}
/**
* Helper method to add an ON/OFF run command to be executed on the device.
*
* @param state The {@link BinaryState}
* @param commands The list of commands to add the on or off command to.
* @param onCommand The command to run if ON. Ignored if the command is {@code null}
* @param offCommand The command to run if OFF. Ignored if the command is {@code null}
*/
public static void setCommandForBinaryState(BinaryState state, List<String> commands,
String onCommand, String offCommand) {
switch (state) {
case ON:
if (onCommand != null) {
commands.add(onCommand);
}
break;
case OFF:
if (offCommand != null) {
commands.add(offCommand);
}
break;
case IGNORE:
// Do nothing
break;
}
}
/**
* Exposed for unit testing
*/
protected void setAirplaneMode(BinaryState airplaneMode) {
mAirplaneMode = airplaneMode;
}
/* Exposed for unit testing */
@VisibleForTesting
protected void setData(BinaryState data) {
mData = data;
}
/* Exposed for unit testing */
@VisibleForTesting
protected void setCell(BinaryState cell) {
mCell = cell;
}
/* Exposed for unit testing */
@VisibleForTesting
protected void setCellAutoSetting(BinaryState cellAutoSetting) {
mCellAutoSetting = cellAutoSetting;
}
/**
* Exposed for unit testing
*/
protected void setWifi(BinaryState wifi) {
mWifi = wifi;
}
/**
* Exposed for unit testing
*/
protected void setWifiNetwork(String wifiNetwork) {
mWifiSsid = wifiNetwork;
}
/**
* Exposed for unit testing
*/
protected void setWifiWatchdog(BinaryState wifiWatchdog) {
mWifiWatchdog = wifiWatchdog;
}
/* Exposed for unit testing */
@VisibleForTesting
protected void setDisableCwWifiMediator(BinaryState disableCwWifiMediator) {
mDisableCwWifiMediator = disableCwWifiMediator;
}
/**
* Exposed for unit testing
*/
protected void setWifiScanAlwaysEnabled(BinaryState wifiScanAlwaysEnabled) {
mWifiScanAlwaysEnabled = wifiScanAlwaysEnabled;
}
/**
* Exposed for unit testing
*/
protected void setEthernet(BinaryState ethernet) {
mEthernet = ethernet;
}
/**
* Exposed for unit testing
*/
protected void setBluetooth(BinaryState bluetooth) {
mBluetooth = bluetooth;
}
/**
* Exposed for unit testing
*/
protected void setNfc(BinaryState nfc) {
mNfc = nfc;
}
/**
* Exposed for unit testing
*/
protected void setScreenAdaptiveBrightness(BinaryState screenAdaptiveBrightness) {
mScreenAdaptiveBrightness = screenAdaptiveBrightness;
}
/**
* Exposed for unit testing
*/
protected void setScreenBrightness(Integer screenBrightness) {
mScreenBrightness = screenBrightness;
}
/**
* Exposed for unit testing
*/
protected void setScreenAlwaysOn(BinaryState screenAlwaysOn) {
mScreenAlwaysOn = screenAlwaysOn;
}
/**
* Exposed for unit testing
*/
protected void setScreenTimeoutSecs(Long screenTimeoutSecs) {
mScreenTimeoutSecs = screenTimeoutSecs;
}
/**
* Exposed for unit testing
*/
protected void setScreenAmbientMode(BinaryState screenAmbientMode) {
mScreenAmbientMode = screenAmbientMode;
}
/**
* Exposed for unit testing
*/
protected void setWakeGesture(BinaryState wakeGesture) {
mWakeGesture = wakeGesture;
}
/**
* Exposed for unit testing
*/
protected void setScreenSaver(BinaryState screenSaver) {
mScreenSaver = screenSaver;
}
/**
* Exposed for unit testing
*/
protected void setNotificationLed(BinaryState notificationLed) {
mNotificationLed = notificationLed;
}
/**
* Exposed for unit testing
*/
protected void setInstallNonMarketApps(BinaryState installNonMarketApps) {
mInstallNonMarketApps = installNonMarketApps;
}
/**
* Exposed for unit testing
*/
protected void setTriggerMediaMounted(boolean triggerMediaMounted) {
mTriggerMediaMounted = triggerMediaMounted;
}
/**
* Exposed for unit testing
*/
protected void setLocationGps(BinaryState locationGps) {
mLocationGps = locationGps;
}
/**
* Exposed for unit testing
*/
protected void setLocationNetwork(BinaryState locationNetwork) {
mLocationNetwork = locationNetwork;
}
/**
* Exposed for unit testing
*/
protected void setAutoRotate(BinaryState autoRotate) {
mAutoRotate = autoRotate;
}
/**
* Exposed for unit testing
*/
protected void setBatterySaver(BinaryState batterySaver) {
mBatterySaver = batterySaver;
}
/**
* Exposed for unit testing
*/
protected void setBatterySaverTrigger(Integer batterySaverTrigger) {
mBatterySaverTrigger = batterySaverTrigger;
}
/**
* Exposed for unit testing
*/
protected void setEnableFullBatteryStatsHistory(boolean enableFullBatteryStatsHistory) {
mEnableFullBatteryStatsHistory = enableFullBatteryStatsHistory;
}
/**
* Exposed for unit testing
*/
protected void setDisableDoze(boolean disableDoze) {
mDisableDoze = disableDoze;
}
/**
* Exposed for unit testing
*/
protected void setAutoUpdateTime(BinaryState autoUpdateTime) {
mAutoUpdateTime = autoUpdateTime;
}
/**
* Exposed for unit testing
*/
protected void setAutoUpdateTimezone(BinaryState autoUpdateTimezone) {
mAutoUpdateTimezone = autoUpdateTimezone;
}
/**
* Exposed for unit testing
*/
protected void setTimezone(String timezone) {
mTimezone = timezone;
}
/**
* Exposed for unit testing
*/
protected void setDisableDialing(boolean disableDialing) {
mDisableDialing = disableDialing;
}
/**
* Exposed for unit testing
*/
protected void setDefaultSimData(Integer defaultSimData) {
mDefaultSimData = defaultSimData;
}
/**
* Exposed for unit testing
*/
protected void setDefaultSimVoice(Integer defaultSimVoice) {
mDefaultSimVoice = defaultSimVoice;
}
/**
* Exposed for unit testing
*/
protected void setDefaultSimSms(Integer defaultSimSms) {
mDefaultSimSms = defaultSimSms;
}
/**
* Exposed for unit testing
*/
protected void setDisableAudio(boolean disable) {
mDisableAudio = disable;
}
/**
* Exposed for unit testing
*/
protected void setTestHarness(boolean setTestHarness) {
mSetTestHarness = setTestHarness;
}
/**
* Exposed for unit testing
*/
protected void setDisableDalvikVerifier(boolean disableDalvikVerifier) {
mDisableDalvikVerifier = disableDalvikVerifier;
}
/**
* Exposed for unit testing
*/
protected void setLocalDataPath(File path) {
mLocalDataFile = path;
}
/**
* Exposed for unit testing
*/
protected void setMinExternalStorageKb(long storageKb) {
mMinExternalStorageKb = storageKb;
}
/**
* Exposed for unit testing
*/
protected void setProperty(String key, String value) {
mSetProps.put(key, value);
}
/** Exposed for unit testing */
public void setGlobalSetting(String key, String value) {
mGlobalSettings.put(key, value);
}
/** Exposed for unit testing */
public void setSecureSetting(String key, String value) {
mSecureSettings.put(key, value);
}
/** Exposed for unit testing */
public void setSystemSetting(String key, String value) {
mSystemSettings.put(key, value);
}
/** Exposed for unit testing */
protected void setRestoreProperties(boolean restoreProperties) {
mRestoreProperties = restoreProperties;
}
/** Exposed for unit testing */
protected void setRestoreSettings(boolean restoreSettings) {
mRestoreSettings = restoreSettings;
}
/**
* Exposed for unit testing
* @deprecated use {@link #setMinExternalStorageKb(long)} instead.
*/
@Deprecated
protected void setDeprecatedMinExternalStoreSpace(long storeSpace) {
mDeprecatedMinExternalStoreSpace = storeSpace;
}
/**
* Exposed for unit testing
* @deprecated use {@link #setDisableAudio(boolean)} instead.
*/
@Deprecated
protected void setDeprecatedAudioSilent(boolean silent) {
mDeprecatedSetAudioSilent = silent;
}
/**
* Exposed for unit testing
* @deprecated use {@link #setProperty(String, String)} instead.
*/
@Deprecated
protected void setDeprecatedSetProp(String prop) {
mDeprecatedSetProps.add(prop);
}
}