aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tradefed/targetprep
diff options
context:
space:
mode:
authorEric Rowe <erowe@google.com>2015-04-21 18:25:38 -0700
committerEric Rowe <erowe@google.com>2015-04-24 11:58:37 -0700
commit4ced6f3e55a5ca8baa82faacd8496464a12aba1a (patch)
treed976af3de16abf1d79def4611fe31cc0f87cb77e /src/com/android/tradefed/targetprep
parentb846e0b0e4eec6fbfd7890189cc331092749e635 (diff)
downloadtradefederation-4ced6f3e55a5ca8baa82faacd8496464a12aba1a.tar.gz
Add DeviceSetup2
Change-Id: Iff7f1b6353c6fc287f9e191f1571b32ea5b5031a
Diffstat (limited to 'src/com/android/tradefed/targetprep')
-rw-r--r--src/com/android/tradefed/targetprep/DeviceSetup.java2
-rw-r--r--src/com/android/tradefed/targetprep/DeviceSetup2.java702
2 files changed, 704 insertions, 0 deletions
diff --git a/src/com/android/tradefed/targetprep/DeviceSetup.java b/src/com/android/tradefed/targetprep/DeviceSetup.java
index 0ef2aa0bc..6ec9d041b 100644
--- a/src/com/android/tradefed/targetprep/DeviceSetup.java
+++ b/src/com/android/tradefed/targetprep/DeviceSetup.java
@@ -36,7 +36,9 @@ import java.util.regex.Pattern;
* Requires a device where 'adb root' is possible, typically a userdebug build type.
* <p/>
* Should be performed *after* a new build is flashed.
+ * @deprecated Use {@link DeviceSetup2}
*/
+@Deprecated
@OptionClass(alias = "device-setup")
public class DeviceSetup implements ITargetPreparer, ITargetCleaner {
diff --git a/src/com/android/tradefed/targetprep/DeviceSetup2.java b/src/com/android/tradefed/targetprep/DeviceSetup2.java
new file mode 100644
index 000000000..73ab1c7e6
--- /dev/null
+++ b/src/com/android/tradefed/targetprep/DeviceSetup2.java
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 2015 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.log.LogUtil.CLog;
+import com.android.tradefed.util.MultiMap;
+
+import java.io.File;
+import java.util.ArrayList;
+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><p>
+ * Should be performed *after* a new build is flashed.
+ * </p><p>
+ * The preparer {@link DeviceSetup} requires all settings requires all properties and commands
+ * to be run manually, while this preparer emphasizes using {@link Option}s to enable or disable
+ * features.
+ * </p>
+ */
+@OptionClass(alias = "device-setup-2")
+public class DeviceSetup2 implements ITargetPreparer, ITargetCleaner{
+
+ /**
+ * Enum used to record ON/OFF state with a DEFAULT no-op state.
+ */
+ protected enum BinaryState {
+ DEFAULT,
+ ON,
+ OFF;
+ }
+
+ // Networking
+ @Option(name = "airplane-mode",
+ description = "Turn airplane mode on or off")
+ protected BinaryState mAirplaneMode = BinaryState.DEFAULT;
+ // 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 = "wifi",
+ description = "Turn wifi on or off")
+ protected BinaryState mWifi = BinaryState.DEFAULT;
+ // 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.DEFAULT;
+ // ON: settings put global wifi_watchdog 1
+ // OFF: settings put global wifi_watchdog 0
+
+ @Option(name = "wifi-scan-always-enabled",
+ description = "Turn wifi scan always enabled on or off")
+ protected BinaryState mWifiScanAlwaysEnabled = BinaryState.DEFAULT;
+ // 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.DEFAULT;
+ // ON: ifconfig eth0 up
+ // OFF: ifconfig eth0 down
+
+ @Option(name = "bluetooth",
+ description = "Turn bluetooth on or off")
+ protected BinaryState mBluetooth = BinaryState.DEFAULT;
+ // ON: service call bluetooth_manager 6
+ // OFF: service call bluetooth_manager 8
+
+ // Screen
+ @Option(name = "screen-adaptive-brightness",
+ description = "Turn screen adaptive brightness on or off")
+ protected BinaryState mScreenAdaptiveBrightness = BinaryState.DEFAULT;
+ // 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.DEFAULT;
+ // 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.DEFAULT;
+ // 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.DEFAULT;
+ // 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.DEFAULT;
+ // 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.DEFAULT;
+ // ON: settings put system notification_light_pulse 1
+ // OFF: settings put system notification_light_pulse 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}
+
+ // Location
+ @Option(name = "location-gps",
+ description = "Turn the GPS location on or off")
+ protected BinaryState mLocationGps = BinaryState.DEFAULT;
+ // 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.DEFAULT;
+ // 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.DEFAULT;
+ // ON: settings put system accelerometer_rotation 1
+ // OFF: settings put system accelerometer_rotation 0
+
+ // Power
+ @Option(name = "low-power-mode",
+ description = "Turn low power mode manually on or off. If OFF but battery is less " +
+ "low-power-trigger, the device will still go into low power mode")
+ protected BinaryState mLowPower = BinaryState.DEFAULT;
+ // ON: settings put global low_power 1
+ // OFF: settings put global low_power 0
+
+ @Option(name = "low-power-trigger",
+ description = "Set the low power trigger level. Should be [1-99] to enable, or 0 to " +
+ "disable automatic low power mode")
+ protected Integer mLowPowerTrigger = null;
+ // settings put global low_power_trigger_level $N
+
+ // Time
+ @Option(name = "auto-update-time",
+ description = "Turn auto update time on or off")
+ protected BinaryState mAutoUpdateTime = BinaryState.DEFAULT;
+ // ON: settings put system auto_time 1
+ // OFF: settings put system auto_time 0
+
+ @Option(name = "auto-update-timezone",
+ description = "Turn auto update timezone on or off")
+ protected BinaryState mAutoUpdateTimezone = BinaryState.DEFAULT;
+ // ON: settings put system auto_timezone 1
+ // OFF: settings put system auto_timezone 0
+
+ // 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
+ @Option(name = "disable-audio",
+ description = "Disable the audio")
+ protected boolean mDisableAudio = true;
+ // setprop ro.audio.silent 1"
+
+ // Test harness
+ @Option(name = "disable",
+ description = "Disable the entire setup")
+ protected boolean mDisable = false;
+
+ @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="setprop",
+ description="Set the specified property on boot. May be repeated.")
+ protected Map<String, String> mSetProps = new HashMap<>();
+
+ @Option(name = "system-setting",
+ description = "Change a system (non-secure) setting. May be repeated.")
+ protected MultiMap<String, String> mSystemSettings = new MultiMap<>();
+
+ @Option(name = "secure-setting",
+ description = "Change a secure setting. May be repeated.")
+ protected MultiMap<String, String> mSecureSettings = new MultiMap<>();
+
+ @Option(name = "global-setting",
+ description = "Change a global setting. May be repeated.")
+ protected MultiMap<String, String> mGlobalSettings = new MultiMap<>();
+
+ @Option(name = "run-command",
+ description = "Run an adb shell command. May be repeated")
+ protected List<String> mRunCommands = new ArrayList<>();
+
+ @Option(name = "disconnect-wifi-after-test",
+ description = "Disconnect from wifi network after test completes.")
+ private boolean mDisconnectWifiAfterTest = true;
+
+ @Option(name = "min-external-store-space-kb",
+ description="The minimum amount of free space in KB that must be present on device's " +
+ "external storage.")
+ protected long mMinExternalStoreSpace = 500;
+
+ @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;
+
+ private static final String PERSIST_PREFIX = "persist.";
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp(ITestDevice device, IBuildInfo buildInfo) throws DeviceNotAvailableException,
+ TargetSetupError {
+ if (mDisable) {
+ return;
+ }
+
+ CLog.i("Performing setup on %s", device.getSerialNumber());
+
+ if (!device.enableAdbRoot()) {
+ throw new TargetSetupError(String.format("Failed to enable adb root on %s",
+ device.getSerialNumber()));
+ }
+
+ processOptions(device);
+ changeSystemProps(device);
+ changeSettings(device);
+ runCommands(device);
+ syncTestData(device);
+ checkExternalStoreSpace(device);
+
+ device.clearErrorDialogs();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)
+ throws DeviceNotAvailableException {
+ if (mDisable) {
+ return;
+ }
+
+ CLog.i("Performing teardown on %s", device.getSerialNumber());
+
+ if (e instanceof DeviceFailedToBootError) {
+ CLog.d("boot failure: skipping teardown");
+ return;
+ }
+
+ if (mDisconnectWifiAfterTest && 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());
+ }
+ }
+ }
+
+ /**
+ * 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
+ */
+ @SuppressWarnings("unused")
+ protected void processOptions(ITestDevice device) throws DeviceNotAvailableException,
+ TargetSetupError {
+ setSettingForBinaryState(mAirplaneMode, mGlobalSettings, "airplane_mode_on", "1", "0");
+ setCommandForBinaryState(mAirplaneMode,
+ "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true",
+ "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false");
+
+ setSettingForBinaryState(mWifi, mGlobalSettings, "wifi_on", "1", "0");
+ setCommandForBinaryState(mWifi, "svc wifi enable", "svc wifi disable");
+
+ setSettingForBinaryState(mWifiWatchdog, mGlobalSettings, "wifi_watchdog", "1", "0");
+
+ setSettingForBinaryState(mWifiScanAlwaysEnabled, mGlobalSettings,
+ "wifi_scan_always_enabled", "1", "0");
+
+ setCommandForBinaryState(mEthernet, "ifconfig eth0 up", "ifconfig eth0 down");
+
+ setCommandForBinaryState(mBluetooth,
+ "service call bluetooth_manager 6", "service call bluetooth_manager 8");
+
+ if (mScreenBrightness != null && BinaryState.ON.equals(mScreenAdaptiveBrightness)) {
+ throw new TargetSetupError("Option screen-brightness cannot be set when " +
+ "screen-adaptive-brightness is set to ON");
+ }
+
+ setSettingForBinaryState(mScreenAdaptiveBrightness, mSystemSettings,
+ "screen_brightness_mode", "1", "0");
+
+ if (mScreenBrightness != null) {
+ mSystemSettings.put("screen_brightness", Integer.toString(mScreenBrightness));
+ }
+
+ // Screen timeout cannot be set if screen always on is set to ON
+ if (mScreenTimeoutSecs != null && BinaryState.ON.equals(mScreenAlwaysOn)) {
+ throw new TargetSetupError("Option screen-timeout-secs cannot be set when " +
+ "screen-always-on is set to ON");
+ }
+
+ setCommandForBinaryState(mScreenAlwaysOn,
+ "svc power stayon true", "svc power stayon false");
+
+ 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");
+
+ if (mTriggerMediaMounted) {
+ mRunCommands.add("am broadcast -a android.intent.action.MEDIA_MOUNTED -d " +
+ "file://${EXTERNAL_STORAGE}");
+ }
+
+ setSettingForBinaryState(mLocationGps, mSecureSettings,
+ "location_providers_allowed", "gps", "-gps");
+
+ setSettingForBinaryState(mLocationNetwork, mSecureSettings,
+ "location_providers_allowed", "network", "-network");
+
+ setSettingForBinaryState(mAutoRotate, mSystemSettings, "accelerometer_rotation", "1", "0");
+
+ setSettingForBinaryState(mLowPower, mGlobalSettings, "low_power", "1", "0");
+
+ if (mLowPowerTrigger != null) {
+ mGlobalSettings.put("low_power_trigger_level", Integer.toString(mLowPowerTrigger));
+ }
+
+ setSettingForBinaryState(mAutoUpdateTime, mSystemSettings, "auto_time", "1", "0");
+
+ setSettingForBinaryState(mAutoUpdateTimezone, mSystemSettings, "auto_timezone", "1", "0");
+
+ 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) {
+ 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;
+ }
+
+ boolean result = device.pushString(sb.toString(), "/data/local.prop");
+ if (!result) {
+ throw new TargetSetupError(String.format("Failed to push file to %s",
+ device.getSerialNumber()));
+ }
+ // Set reasonable permissions for /data/local.prop
+ device.executeShellCommand("chmod 644 /data/local.prop");
+ CLog.i("Setup requires system property change. Reboot of %s required",
+ device.getSerialNumber());
+ device.reboot();
+ }
+
+ /**
+ * 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
+ */
+ protected void changeSettings(ITestDevice device) throws DeviceNotAvailableException,
+ TargetSetupError {
+ if (mForceSkipSettings) {
+ return;
+ }
+
+ if (mSystemSettings.isEmpty() && mSecureSettings.isEmpty() && mGlobalSettings.isEmpty()) {
+ return;
+ }
+
+ if (device.getApiLevel() < 22) {
+ throw new TargetSetupError(String.format("Changing setting not supported on %s, " +
+ "must be API 22+", device.getSerialNumber()));
+ }
+
+ for (String key : mSystemSettings.keySet()) {
+ for (String value : mSystemSettings.get(key)) {
+ CLog.d("Changing system setting %s to %s", key, value);
+ device.executeShellCommand(String.format("settings put system %s %s", key, value));
+ }
+ }
+ for (String key : mSecureSettings.keySet()) {
+ for (String value : mSecureSettings.get(key)) {
+ CLog.d("Changing secure setting %s to %s", key, value);
+ device.executeShellCommand(String.format("settings put secure %s %s", key, value));
+ }
+ }
+
+ for (String key : mGlobalSettings.keySet()) {
+ for (String value : mGlobalSettings.get(key)) {
+ CLog.d("Changing global setting %s to %s", key, value);
+ device.executeShellCommand(String.format("settings put global %s %s", key, value));
+ }
+ }
+ }
+
+ /**
+ * Execute additional commands 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 settings
+ */
+ private void runCommands(ITestDevice device) throws DeviceNotAvailableException,
+ TargetSetupError {
+ if (mForceSkipRunCommands) {
+ 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()));
+ }
+ }
+
+ for (String command : mRunCommands) {
+ device.executeShellCommand(command);
+ }
+ }
+
+ /**
+ * 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()));
+ }
+ 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()));
+ }
+ 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()));
+ }
+ }
+
+ /**
+ * 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 (mMinExternalStoreSpace <= 0) {
+ return;
+ }
+
+ long freeSpace = device.getExternalStoreFreeSpace();
+ if (freeSpace < mMinExternalStoreSpace) {
+ throw new DeviceNotAvailableException(String.format(
+ "External store free space %dK is less than required %dK for device %s",
+ freeSpace , mMinExternalStoreSpace, 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
+ */
+ protected 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 DEFAULT:
+ // Do nothing
+ break;
+ }
+ }
+
+ /**
+ * Helper method to add an ON/OFF run command to be executed on the device.
+ *
+ * @param state The {@link BinaryState}
+ * @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}
+ */
+ protected void setCommandForBinaryState(BinaryState state, String onCommand,
+ String offCommand) {
+ switch (state) {
+ case ON:
+ if (onCommand != null) {
+ mRunCommands.add(onCommand);
+ }
+ break;
+ case OFF:
+ if (offCommand != null) {
+ mRunCommands.add(offCommand);
+ }
+ break;
+ case DEFAULT:
+ // Do nothing
+ break;
+ }
+ }
+}