/* * 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 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. *
* Requires a device where 'adb root' is possible, typically a userdebug build type. *
* Should be performed after a new build is flashed. *
*/ @OptionClass(alias = "device-setup") public class DeviceSetup implements ITargetPreparer, 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 = "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 = "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 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.IGNORE; // ON: settings put system auto_timezone 1 // OFF: settings put system 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" // Test harness @Option(name = "disable", description = "Disable the device 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 = "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* 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. *
* @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. ** Exposed so that children classes may override this. *
* * @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(mWifi, mGlobalSettings, "wifi_on", "1", "0"); setCommandForBinaryState(mWifi, mRunCommandAfterSettings, "svc wifi enable", "svc wifi disable"); setSettingForBinaryState(mWifiWatchdog, mGlobalSettings, "wifi_watchdog", "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, mSystemSettings, "auto_time", "1", "0"); setSettingForBinaryState(mAutoUpdateTimezone, mSystemSettings, "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* 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. *
* Exposed so children classes may override. *
* * @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)) { 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)) { 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)) { 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