1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tradefed.targetprep;
18 
19 import com.android.ddmlib.IDevice;
20 import com.android.tradefed.config.Option;
21 import com.android.tradefed.config.OptionClass;
22 import com.android.tradefed.dependencies.ExternalDependency;
23 import com.android.tradefed.dependencies.IExternalDependency;
24 import com.android.tradefed.dependencies.connectivity.BluetoothDependency;
25 import com.android.tradefed.dependencies.connectivity.EthernetDependency;
26 import com.android.tradefed.dependencies.connectivity.NetworkDependency;
27 import com.android.tradefed.dependencies.connectivity.TelephonyDependency;
28 import com.android.tradefed.device.DeviceNotAvailableException;
29 import com.android.tradefed.device.ITestDevice;
30 import com.android.tradefed.device.LocalAndroidVirtualDevice;
31 import com.android.tradefed.device.StubDevice;
32 import com.android.tradefed.device.TestDevice;
33 import com.android.tradefed.device.TestDeviceState;
34 import com.android.tradefed.device.cloud.NestedRemoteDevice;
35 import com.android.tradefed.device.cloud.RemoteAndroidVirtualDevice;
36 import com.android.tradefed.invoker.TestInformation;
37 import com.android.tradefed.invoker.logger.InvocationMetricLogger;
38 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey;
39 import com.android.tradefed.invoker.tracing.CloseableTraceScope;
40 import com.android.tradefed.log.LogUtil.CLog;
41 import com.android.tradefed.result.error.DeviceErrorIdentifier;
42 import com.android.tradefed.result.error.InfraErrorIdentifier;
43 import com.android.tradefed.util.BinaryState;
44 import com.android.tradefed.util.CommandResult;
45 import com.android.tradefed.util.CommandStatus;
46 import com.android.tradefed.util.MultiMap;
47 import com.android.tradefed.util.RunUtil;
48 import com.android.tradefed.util.executor.ParallelDeviceExecutor;
49 
50 import com.google.common.annotations.VisibleForTesting;
51 import com.google.common.base.Strings;
52 
53 import java.io.File;
54 import java.util.ArrayList;
55 import java.util.Collection;
56 import java.util.HashMap;
57 import java.util.LinkedHashMap;
58 import java.util.LinkedHashSet;
59 import java.util.List;
60 import java.util.Map;
61 import java.util.Set;
62 import java.util.TimeZone;
63 import java.util.concurrent.Callable;
64 import java.util.concurrent.TimeUnit;
65 
66 /**
67  * A {@link ITargetPreparer} that configures a device for testing based on provided {@link Option}s.
68  *
69  * <p>Requires a device where 'adb root' is possible, typically a userdebug build type.
70  *
71  * <p>Should be performed <strong>after</strong> a new build is flashed.
72  *
73  * <p><strong>Note:</strong> this preparer is meant for continuous testing labs and assumes that the
74  * device under test will be flashed and wiped before the next run. As such, it does minimal clean
75  * up during teardown and should not be used in a test module.
76  */
77 @OptionClass(alias = "device-setup")
78 public class DeviceSetup extends BaseTargetPreparer implements IExternalDependency {
79 
80     // Networking
81     @Option(name = "airplane-mode",
82             description = "Turn airplane mode on or off")
83     protected BinaryState mAirplaneMode = BinaryState.IGNORE;
84     // ON:  settings put global airplane_mode_on 1
85     //      am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true
86     // OFF: settings put global airplane_mode_on 0
87     //      am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false
88 
89     @Option(name = "data", description = "Turn mobile data on or off")
90     protected BinaryState mData = BinaryState.IGNORE;
91     // ON:  settings put global mobile_data 1
92     //      svc data enable
93     // OFF: settings put global mobile_data 0
94     //      svc data disable
95 
96     @Option(name = "cell", description = "Turn cellular radio on or off")
97     protected BinaryState mCell = BinaryState.IGNORE;
98     // ON:  settings put global cell_on 1
99     // OFF: settings put global cell_on 0
100 
101     @Option(name = "cell-auto-setting", description = "Turn wear cellular mediator on or off")
102     protected BinaryState mCellAutoSetting = BinaryState.IGNORE;
103     // ON:  settings put global clockwork_cell_auto_setting 1
104     // OFF: settings put global clockwork_cell_auto_setting 0
105 
106     @Option(name = "wifi", description = "Turn wifi on or off")
107     protected BinaryState mWifi = BinaryState.IGNORE;
108     // ON:  settings put global wifi_on 1
109     //      svc wifi enable
110     // OFF: settings put global wifi_off 0
111     //      svc wifi disable
112 
113     @Option(
114             name = "skip-wifi-connection",
115             description = "Whether or not to completely skip connecting to wifi.")
116     private boolean mSkipWifi = false;
117 
118     @Option(name = "wifi-network",
119             description = "The SSID of the network to connect to. Will only attempt to " +
120             "connect to a network if set")
121     protected String mWifiSsid = null;
122 
123     @Option(name = "wifi-psk",
124             description = "The passphrase used to connect to a secured network")
125     protected String mWifiPsk = null;
126 
127     @Option(name = "wifi-ssid-to-psk", description = "A map of wifi SSIDs to passwords.")
128     protected Map<String, String> mWifiSsidToPsk = new LinkedHashMap<>();
129 
130     @Option(name = "wifi-watchdog",
131             description = "Turn wifi watchdog on or off")
132     protected BinaryState mWifiWatchdog = BinaryState.IGNORE;
133     // ON:  settings put global wifi_watchdog 1
134     // OFF: settings put global wifi_watchdog 0
135 
136     @Option(name = "disable-cw-wifi-mediator", description = "Turn wifi mediator on or off")
137     protected BinaryState mDisableCwWifiMediator = BinaryState.IGNORE;
138     // ON:  settings put global cw_disable_wifimediator 1
139     // OFF: settings put global cw_disable_wifimediator 0
140 
141     @Option(
142         name = "wifi-scan-always-enabled",
143         description = "Turn wifi scan always enabled on or off"
144     )
145     protected BinaryState mWifiScanAlwaysEnabled = BinaryState.IGNORE;
146     // ON:  settings put global wifi_scan_always_enabled 1
147     // OFF: settings put global wifi_scan_always_enabled 0
148 
149     @Option(name = "ethernet",
150             description = "Turn ethernet on or off")
151     protected BinaryState mEthernet = BinaryState.IGNORE;
152     // ON:  ifconfig eth0 up
153     // OFF: ifconfig eth0 down
154 
155     @Option(name = "bluetooth",
156             description = "Turn bluetooth on or off")
157     protected BinaryState mBluetooth = BinaryState.IGNORE;
158     // ON:  svc bluetooth enable
159     // OFF: svc bluetooth disable
160 
161     @Option(name = "nfc",
162             description = "Turn nfc on or off")
163     protected BinaryState mNfc = BinaryState.IGNORE;
164     // ON:  svc nfc enable
165     // OFF: svc nfc disable
166 
167     // Screen
168     @Option(name = "screen-adaptive-brightness",
169             description = "Turn screen adaptive brightness on or off")
170     protected BinaryState mScreenAdaptiveBrightness = BinaryState.IGNORE;
171     // ON:  settings put system screen_brightness_mode 1
172     // OFF: settings put system screen_brightness_mode 0
173 
174     @Option(name = "screen-brightness",
175             description = "Set the screen brightness. This is uncalibrated from product to product")
176     protected Integer mScreenBrightness = null;
177     // settings put system screen_brightness $N
178 
179     @Option(name = "screen-always-on",
180             description = "Turn 'screen always on' on or off. If ON, then screen-timeout-secs " +
181             "must be unset. Will only work when the device is plugged in")
182     protected BinaryState mScreenAlwaysOn = BinaryState.ON;
183     // ON:  svc power stayon true
184     // OFF: svc power stayon false
185 
186     @Option(name = "screen-timeout-secs",
187             description = "Set the screen timeout in seconds. If set, then screen-always-on must " +
188             "be OFF or DEFAULT")
189     protected Long mScreenTimeoutSecs = null;
190     // settings put system screen_off_timeout $(N * 1000)
191 
192     @Option(name = "screen-ambient-mode",
193             description = "Turn screen ambient mode on or off")
194     protected BinaryState mScreenAmbientMode = BinaryState.IGNORE;
195     // ON:  settings put secure doze_enabled 1
196     // OFF: settings put secure doze_enabled 0
197 
198     @Option(name = "wake-gesture",
199             description = "Turn wake gesture on or off")
200     protected BinaryState mWakeGesture = BinaryState.IGNORE;
201     // ON:  settings put secure wake_gesture_enabled 1
202     // OFF: settings put secure wake_gesture_enabled 0
203 
204     @Option(name = "screen-saver",
205             description = "Turn screen saver on or off")
206     protected BinaryState mScreenSaver = BinaryState.IGNORE;
207     // ON:  settings put secure screensaver_enabled 1
208     // OFF: settings put secure screensaver_enabled 0
209 
210     @Option(name = "notification-led",
211             description = "Turn the notification led on or off")
212     protected BinaryState mNotificationLed = BinaryState.IGNORE;
213     // ON:  settings put system notification_light_pulse 1
214     // OFF: settings put system notification_light_pulse 0
215 
216     @Option(name = "install-non-market-apps",
217             description = "Allow or prevent non-market app to initiate an apk install request")
218     protected BinaryState mInstallNonMarketApps = BinaryState.IGNORE;
219     // ON:  settings put secure install_non_market_apps 1
220     // OFF: settings put secure install_non_market_apps 0
221 
222     // Media
223     @Option(name = "trigger-media-mounted",
224             description = "Trigger a MEDIA_MOUNTED broadcast")
225     protected boolean mTriggerMediaMounted = false;
226     // am broadcast -a android.intent.action.MEDIA_MOUNTED -d file://${EXTERNAL_STORAGE}
227     // --receiver-include-background
228 
229     // Location
230     @Option(name = "location-gps", description = "Turn the GPS location on or off")
231     protected BinaryState mLocationGps = BinaryState.IGNORE;
232     // ON:  settings put secure location_providers_allowed +gps
233     // OFF: settings put secure location_providers_allowed -gps
234 
235     @Option(name = "location-network",
236             description = "Turn the network location on or off")
237     protected BinaryState mLocationNetwork = BinaryState.IGNORE;
238     // ON:  settings put secure location_providers_allowed +network
239     // OFF: settings put secure location_providers_allowed -network
240 
241     // Sensor
242     @Option(name = "auto-rotate",
243             description = "Turn auto rotate on or off")
244     protected BinaryState mAutoRotate = BinaryState.IGNORE;
245     // ON:  settings put system accelerometer_rotation 1
246     // OFF: settings put system accelerometer_rotation 0
247 
248     // Power
249     @Option(name = "battery-saver-mode",
250             description = "Turn battery saver mode manually on or off. If OFF but battery is " +
251             "less battery-saver-trigger, the device will still go into battery saver mode")
252     protected BinaryState mBatterySaver = BinaryState.IGNORE;
253     // ON:  dumpsys battery set usb 0
254     //      settings put global low_power 1
255     // OFF: settings put global low_power 0
256 
257     @Option(name = "battery-saver-trigger",
258             description = "Set the battery saver trigger level. Should be [1-99] to enable, or " +
259             "0 to disable automatic battery saver mode")
260     protected Integer mBatterySaverTrigger = null;
261     // settings put global low_power_trigger_level $N
262 
263     @Option(name = "enable-full-battery-stats-history",
264             description = "Enable full history for batterystats. This option is only " +
265             "applicable for L+")
266     protected boolean mEnableFullBatteryStatsHistory = false;
267     // dumpsys batterystats --enable full-history
268 
269     @Option(name = "disable-doze",
270             description = "Disable device from going into doze mode. This option is only " +
271             "applicable for M+")
272     protected boolean mDisableDoze = false;
273     // dumpsys deviceidle disable
274 
275     // Time
276     @Option(name = "auto-update-time",
277             description = "Turn auto update time on or off")
278     protected BinaryState mAutoUpdateTime = BinaryState.IGNORE;
279     // ON:  settings put global auto_time 1
280     // OFF: settings put global auto_time 0
281 
282     @Option(name = "auto-update-timezone", description = "Turn auto update timezone on or off")
283     protected BinaryState mAutoUpdateTimezone = BinaryState.IGNORE;
284     // ON:  settings put global auto_timezone 1
285     // OFF: settings put global auto_timezone 0
286 
287     @Option(
288             name = "set-timezone",
289             description =
290                     "Set timezone property by TZ name "
291                             + "(http://en.wikipedia.org/wiki/List_of_tz_database_time_zones)")
292     protected String mTimezone = null;
293 
294     @Option(name = "sync-timezone-with-host",
295             description =
296                     "Turn on or off that make the time zone of device sync with host")
297     protected BinaryState mSyncTimezoneWithHost = BinaryState.IGNORE;
298     // ON:  settings put global sync_timezone 1
299     // OFF: settings put global sync_timezone 0
300 
301     // Calling
302     @Option(name = "disable-dialing",
303             description = "Disable dialing")
304     protected boolean mDisableDialing = true;
305     // setprop ro.telephony.disable-call true"
306 
307     @Option(name = "default-sim-data",
308             description = "Set the default sim card slot for data. Leave unset for single SIM " +
309             "devices")
310     protected Integer mDefaultSimData = null;
311     // settings put global multi_sim_data_call $N
312 
313     @Option(name = "default-sim-voice",
314             description = "Set the default sim card slot for voice calls. Leave unset for single " +
315             "SIM devices")
316     protected Integer mDefaultSimVoice = null;
317     // settings put global multi_sim_voice_call $N
318 
319     @Option(name = "default-sim-sms",
320             description = "Set the default sim card slot for SMS. Leave unset for single SIM " +
321             "devices")
322     protected Integer mDefaultSimSms = null;
323     // settings put global multi_sim_sms $N
324 
325     // Audio
326     private static final boolean DEFAULT_DISABLE_AUDIO = true;
327     @Option(name = "disable-audio",
328             description = "Disable the audio")
329     protected boolean mDisableAudio = DEFAULT_DISABLE_AUDIO;
330     // setprop ro.audio.silent 1"
331 
332     @Option(name = "force-skip-system-props",
333             description = "Force setup to not modify any device system properties. All other " +
334             "system property options will be ignored")
335     protected boolean mForceSkipSystemProps = false;
336 
337     @Option(
338             name = "force-root-setup",
339             description =
340                     "Force switching to root before the setup.Root should only be need for system"
341                         + " props, but adding this flag while transitioning in case someone reports"
342                         + " issues.")
343     private boolean mForceRoot = false;
344 
345     @Option(name = "force-skip-settings",
346             description = "Force setup to not modify any device settings. All other setting " +
347             "options will be ignored.")
348     protected boolean mForceSkipSettings = false;
349 
350     @Option(name = "force-skip-run-commands",
351             description = "Force setup to not run any additional commands. All other commands " +
352             "will be ignored.")
353     protected boolean mForceSkipRunCommands = false;
354 
355     @Option(name = "set-test-harness",
356             description = "Set the read-only test harness flag on boot")
357     protected boolean mSetTestHarness = true;
358     // setprop ro.monkey 1
359     // setprop ro.test_harness 1
360     // setprop persist.sys.test_harness 1
361 
362     @Option(name = "hide-error-dialogs", description = "Turn on or off the error dialogs.")
363     protected BinaryState mHideErrorDialogs = BinaryState.ON;
364     // ON:  settings put global hide_error_dialogs 1
365     // OFF: settings put global hide_error_dialogs 0
366 
367     @Option(
368             name = "disable-dalvik-verifier",
369             description =
370                     "Disable the dalvik verifier on device. Allows package-private "
371                             + "framework tests to run.")
372     protected boolean mDisableDalvikVerifier = false;
373     // setprop dalvik.vm.dexopt-flags v=n
374 
375     @Option(name = "set-property",
376             description = "Set the specified property on boot. Option may be repeated but only " +
377             "the last value for a given key will be set.")
378     protected Map<String, String> mSetProps = new HashMap<>();
379 
380     @Option(
381             name = "restore-properties",
382             description =
383                     "Restore previous /data/local.prop on tear down, restoring any properties"
384                             + " DeviceSetup changed by modifying /data/local.prop.")
385     protected boolean mRestoreProperties = false;
386 
387     protected File mPreviousProperties;
388 
389     @Option(name = "set-system-setting",
390             description = "Change a system (non-secure) setting. Option may be repeated and all " +
391             "key/value pairs will be set in order.")
392     // Use a Multimap since it is possible for a setting to have multiple values for the same key
393     protected MultiMap<String, String> mSystemSettings = new MultiMap<>();
394 
395     @Option(name = "set-secure-setting",
396             description = "Change a secure setting. Option may be repeated and all key/value " +
397             "pairs will be set in order.")
398     // Use a Multimap since it is possible for a setting to have multiple values for the same key
399     protected MultiMap<String, String> mSecureSettings = new MultiMap<>();
400 
401     @Option(name = "set-global-setting",
402             description = "Change a global setting. Option may be repeated and all key/value " +
403             "pairs will be set in order.")
404     // Use a Multimap since it is possible for a setting to have multiple values for the same key
405     protected MultiMap<String, String> mGlobalSettings = new MultiMap<>();
406 
407     @Option(
408         name = "restore-settings",
409         description = "Restore settings modified by this preparer on tear down."
410     )
411     protected boolean mRestoreSettings = false;
412 
413     @Option(
414             name = "optimized-non-persistent-setup",
415             description = "Feature to evaluate a faster non-persistent props setup.")
416     private boolean mOptimizeNonPersistentSetup = true;
417 
418     @Option(
419             name = "dismiss-setup-wizard",
420             description = "Attempt to dismiss the setup wizard if present.")
421     private boolean mDismissSetupWizard = true;
422 
423     @Option(
424             name = "dismiss-setup-wizard-timeout",
425             description = "Set the timeout for dismissing setup wizard in milli seconds.")
426     private Long mDismissSetupWizardTimeout = 60 * 1000L;
427 
428     @Option(
429             name = "dismiss-setup-wizard-retry-count",
430             description = "Number of times to retry to dismiss setup wizard.")
431     private int mDismissSetupWizardRetry = 2;
432 
433     private Map<String, String> mPreviousSystemSettings = new HashMap<>();
434     private Map<String, String> mPreviousSecureSettings = new HashMap<>();
435     private Map<String, String> mPreviousGlobalSettings = new HashMap<>();
436 
437     protected List<String> mRunCommandBeforeSettings = new ArrayList<>();
438 
439     @Option(name = "run-command",
440             description = "Run an adb shell command. Option may be repeated")
441     protected List<String> mRunCommandAfterSettings = new ArrayList<>();
442 
443     @Option(name = "disconnect-wifi-after-test",
444             description = "Disconnect from wifi network after test completes.")
445     private boolean mDisconnectWifiAfterTest = true;
446 
447     private static final long DEFAULT_MIN_EXTERNAL_STORAGE_KB = 500;
448     @Option(name = "min-external-storage-kb",
449             description="The minimum amount of free space in KB that must be present on device's " +
450             "external storage.")
451     protected long mMinExternalStorageKb = DEFAULT_MIN_EXTERNAL_STORAGE_KB;
452 
453     @Option(name = "local-data-path",
454             description = "Optional local file path of test data to sync to device's external " +
455             "storage. Use --remote-data-path to set remote location.")
456     protected File mLocalDataFile = null;
457 
458     @Option(name = "remote-data-path",
459             description = "Optional file path on device's external storage to sync test data. " +
460             "Must be used with --local-data-path.")
461     protected String mRemoteDataPath = null;
462 
463     @Option(
464             name = "optimized-property-setting",
465             description =
466                     "If a property is already set to the desired value, don't reboot the device")
467     protected boolean mOptimizedPropertySetting = true;
468 
469     // Deprecated options follow
470     /**
471      * @deprecated use min-external-storage-kb instead.
472      */
473     @Option(name = "min-external-store-space",
474             description = "deprecated, use option min-external-storage-kb. The minimum amount of " +
475             "free space in KB that must be present on device's external storage.")
476     @Deprecated
477     private long mDeprecatedMinExternalStoreSpace = DEFAULT_MIN_EXTERNAL_STORAGE_KB;
478 
479     /**
480      * @deprecated use option disable-audio instead.
481      */
482     @Option(name = "audio-silent",
483             description = "deprecated, use option disable-audio. set ro.audio.silent on boot.")
484     @Deprecated
485     private boolean mDeprecatedSetAudioSilent = DEFAULT_DISABLE_AUDIO;
486 
487     /**
488      * @deprecated use option set-property instead.
489      */
490     @Option(name = "setprop",
491             description = "deprecated, use option set-property. set the specified property on " +
492             "boot. Format: --setprop key=value. May be repeated.")
493     @Deprecated
494     private Collection<String> mDeprecatedSetProps = new ArrayList<String>();
495 
496     @Option(
497             name = "skip-virtual-device-teardown",
498             description = "Whether or not to skip the teardown if it's a virtual device.")
499     private boolean mSkipVirtualDeviceTeardown = true;
500 
501     @Option(
502             name = "disable-device-config-sync",
503             description = "Disable syncing device config with remote configuration server.")
504     private boolean mDisableDeviceConfigSync = false;
505     // device_config set_sync_disabled_for_tests persistent
506 
507     @Option(
508             name = "disable-ramdump",
509             description = "Will set the flag to disable ramdump on the device.")
510     private boolean mDisableRamdump = false;
511 
512     @Option(name = "parallelize-core-setup")
513     private boolean mParallelCoreSetup = false;
514 
515     @Option(name = "dismiss-keyguard-via-wm", description = "Flag to dismiss keyguard via wm")
516     private boolean mDismissViaWm = false;
517 
518     private static final String PERSIST_PREFIX = "persist.";
519     private static final String MEMTAG_BOOTCTL = "arm64.memtag.bootctl";
520 
521     private static final List<String> PROPERTIES_NEEDING_REBOOT =
522             List.of(
523                     // MEMTAG_BOOTCTL stores a value in the misc partition that gets applied on
524                     // reboot.
525                     MEMTAG_BOOTCTL,
526                     // Zygote caches the value of this property because it's expected to reboot the
527                     // system whenever this property changes.
528                     "persist.debug.dalvik.vm.jdwp.enabled");
529 
getDevice(TestInformation testInfo)530     public ITestDevice getDevice(TestInformation testInfo) {
531         return testInfo.getDevice();
532     }
533 
534     /** {@inheritDoc} */
535     @Override
setUp(TestInformation testInfo)536     public void setUp(TestInformation testInfo)
537             throws DeviceNotAvailableException, BuildError, TargetSetupError {
538         ITestDevice device = getDevice(testInfo);
539         CLog.i("Performing setup on %s", device.getSerialNumber());
540 
541         if (mForceRoot && device.getOptions().isEnableAdbRoot()) {
542             if (!device.enableAdbRoot()) {
543                 throw new TargetSetupError(
544                         String.format("Failed to enable adb root on %s", device.getSerialNumber()),
545                         device.getDeviceDescriptor(),
546                         DeviceErrorIdentifier.DEVICE_UNEXPECTED_RESPONSE);
547             }
548         }
549 
550         // Convert deprecated options into current options
551         processDeprecatedOptions(device);
552         // Convert options into settings and run commands
553         processOptions(device);
554         // Change system props (will reboot device)
555         changeSystemProps(device);
556         // Run commands designated to be run before changing settings
557         runCommands(device, mRunCommandBeforeSettings);
558         List<Callable<Boolean>> callableTasks = new ArrayList<>();
559 
560         callableTasks.add(
561                 () -> {
562                     // Handle screen always on setting
563                     handleScreenAlwaysOnSetting(device);
564                     return true;
565                 });
566         callableTasks.add(
567                 () -> {
568                     // Change settings
569                     changeSettings(device);
570                     return true;
571                 });
572         callableTasks.add(
573                 () -> {
574                     // Connect wifi after settings since this may take a while
575                     connectWifi(device);
576                     return true;
577                 });
578         callableTasks.add(
579                 () -> {
580                     // Sync data after settings since this may take a while
581                     syncTestData(device);
582                     return true;
583                 });
584         callableTasks.add(
585                 () -> {
586                     // Throw an error if there is not enough storage space
587                     checkExternalStoreSpace(device);
588                     return true;
589                 });
590         if (mDismissSetupWizard) {
591             callableTasks.add(
592                     () -> {
593                         dismissSetupWizard(device);
594                         return true;
595                     });
596         }
597         if (mParallelCoreSetup) {
598             ParallelDeviceExecutor<Boolean> executor =
599                     new ParallelDeviceExecutor<Boolean>(callableTasks.size());
600             executor.invokeAll(callableTasks, 5, TimeUnit.MINUTES);
601             if (executor.hasErrors()) {
602                 List<Throwable> errors = executor.getErrors();
603                 // TODO: Handle throwing multi-exceptions, right now throw the first one.
604                 for (Throwable error : errors) {
605                     if (error instanceof TargetSetupError) {
606                         throw (TargetSetupError) error;
607                     }
608                     if (error instanceof BuildError) {
609                         throw (BuildError) error;
610                     }
611                     if (error instanceof DeviceNotAvailableException) {
612                         throw (DeviceNotAvailableException) error;
613                     }
614                     throw new RuntimeException(error);
615                 }
616             }
617         } else {
618             // Handle screen always on setting
619             handleScreenAlwaysOnSetting(device);
620             // Change settings
621             changeSettings(device);
622             // Connect wifi after settings since this may take a while
623             connectWifi(device);
624             // Sync data after settings since this may take a while
625             syncTestData(device);
626             // Throw an error if there is not enough storage space
627             checkExternalStoreSpace(device);
628             if (mDismissSetupWizard) {
629                 dismissSetupWizard(device);
630             }
631         }
632         // Run commands designated to be run after changing settings
633         runCommands(device, mRunCommandAfterSettings);
634 
635         device.clearErrorDialogs();
636     }
637 
638     /** {@inheritDoc} */
639     @Override
tearDown(TestInformation testInfo, Throwable e)640     public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException {
641         ITestDevice device = testInfo.getDevice();
642         // ignore tearDown if it's a stub device, since there is no real device to clean.
643         if (device.getIDevice() instanceof StubDevice) {
644             return;
645         }
646         if (device instanceof RemoteAndroidVirtualDevice && mSkipVirtualDeviceTeardown) {
647             CLog.d("Skipping teardown on virtual device that will be deleted.");
648             return;
649         }
650         if (e instanceof DeviceFailedToBootError) {
651             CLog.d("boot failure: skipping teardown");
652             return;
653         }
654         if (e instanceof DeviceNotAvailableException) {
655             CLog.d("device not available: skipping teardown");
656             return;
657         }
658         if (!TestDeviceState.ONLINE.equals(device.getDeviceState())) {
659             CLog.d("device offline: skipping teardown");
660             return;
661         }
662         CLog.i("Performing teardown on %s", device.getSerialNumber());
663 
664         // Only try to disconnect if wifi ssid is set since isWifiEnabled() is a heavy operation
665         // which should be avoided when possible
666         boolean wifiSet = mWifiSsid != null || !mWifiSsidToPsk.isEmpty();
667         if (mDisconnectWifiAfterTest && wifiSet && device.isWifiEnabled()) {
668             boolean result = device.disconnectFromWifi();
669             if (result) {
670                 CLog.i("Successfully disconnected from wifi network on %s",
671                         device.getSerialNumber());
672             } else {
673                 CLog.w("Failed to disconnect from wifi network on %s", device.getSerialNumber());
674             }
675         }
676 
677         if (mRestoreProperties) {
678             if (mPreviousProperties != null) {
679                 device.pushFile(mPreviousProperties, "/data/local.prop");
680             } else {
681                 device.deleteFile("/data/local.prop");
682             }
683             device.reboot();
684         }
685 
686         if (mRestoreSettings) {
687             for (Map.Entry<String, String> entry : mPreviousSystemSettings.entrySet()) {
688                 device.setSetting("system", entry.getKey(), entry.getValue());
689             }
690             for (Map.Entry<String, String> entry : mPreviousGlobalSettings.entrySet()) {
691                 device.setSetting("global", entry.getKey(), entry.getValue());
692             }
693             for (Map.Entry<String, String> entry : mPreviousSecureSettings.entrySet()) {
694                 device.setSetting("secure", entry.getKey(), entry.getValue());
695             }
696         }
697     }
698 
699     /**
700      * Processes the deprecated options converting them into the currently used options.
701      * <p>
702      * This method should be run before any other processing methods. Will throw a
703      * {@link TargetSetupError} if the deprecated option overrides a specified non-deprecated
704      * option.
705      * </p>
706      * @throws TargetSetupError if there is a conflict
707      */
processDeprecatedOptions(ITestDevice device)708     public void processDeprecatedOptions(ITestDevice device) throws TargetSetupError {
709         if (mDeprecatedMinExternalStoreSpace != DEFAULT_MIN_EXTERNAL_STORAGE_KB) {
710             if (mMinExternalStorageKb != DEFAULT_MIN_EXTERNAL_STORAGE_KB) {
711                 throw new TargetSetupError("Deprecated option min-external-store-space conflicts " +
712                         "with option min-external-storage-kb", device.getDeviceDescriptor());
713             }
714             mMinExternalStorageKb = mDeprecatedMinExternalStoreSpace;
715         }
716 
717         if (mDeprecatedSetAudioSilent != DEFAULT_DISABLE_AUDIO) {
718             if (mDisableAudio != DEFAULT_DISABLE_AUDIO) {
719                 throw new TargetSetupError("Deprecated option audio-silent conflicts with " +
720                         "option disable-audio", device.getDeviceDescriptor());
721             }
722             mDisableAudio = mDeprecatedSetAudioSilent;
723         }
724 
725         if (!mDeprecatedSetProps.isEmpty()) {
726             if (!mSetProps.isEmpty()) {
727                 throw new TargetSetupError("Deprecated option setprop conflicts with option " +
728                         "set-property ", device.getDeviceDescriptor());
729             }
730             for (String prop : mDeprecatedSetProps) {
731                 String[] parts = prop.split("=", 2);
732                 String key = parts[0].trim();
733                 String value = parts.length == 2 ? parts[1].trim() : "";
734                 mSetProps.put(key, value);
735             }
736         }
737     }
738 
739     /**
740      * Process all the {@link Option}s and turn them into system props, settings, or run commands.
741      * Does not run any commands on the device at this time.
742      * <p>
743      * Exposed so that children classes may override this.
744      * </p>
745      *
746      * @param device The {@link ITestDevice}
747      * @throws DeviceNotAvailableException if the device is not available
748      * @throws TargetSetupError if the {@link Option}s conflict
749      */
processOptions(ITestDevice device)750     public void processOptions(ITestDevice device) throws DeviceNotAvailableException,
751             TargetSetupError {
752         setSettingForBinaryState(mData, mGlobalSettings, "mobile_data", "1", "0");
753         setCommandForBinaryState(
754                 mData, mRunCommandAfterSettings, "svc data enable", "svc data disable");
755 
756         setSettingForBinaryState(mCell, mGlobalSettings, "cell_on", "1", "0");
757         setSettingForBinaryState(
758                 mCellAutoSetting, mGlobalSettings, "clockwork_cell_auto_setting", "1", "0");
759 
760         setSettingForBinaryState(mWifi, mGlobalSettings, "wifi_on", "1", "0");
761         setCommandForBinaryState(
762                 mWifi, mRunCommandAfterSettings, "svc wifi enable", "svc wifi disable");
763 
764         setSettingForBinaryState(mWifiWatchdog, mGlobalSettings, "wifi_watchdog", "1", "0");
765         setSettingForBinaryState(
766                 mDisableCwWifiMediator, mGlobalSettings, "cw_disable_wifimediator", "1", "0");
767 
768         setSettingForBinaryState(mWifiScanAlwaysEnabled, mGlobalSettings,
769                 "wifi_scan_always_enabled", "1", "0");
770 
771         setCommandForBinaryState(mEthernet, mRunCommandAfterSettings,
772                 "ifconfig eth0 up", "ifconfig eth0 down");
773 
774         setCommandForBinaryState(
775                 mBluetooth,
776                 mRunCommandAfterSettings,
777                 "cmd bluetooth_manager enable && cmd bluetooth_manager wait-for-state:STATE_ON",
778                 "cmd bluetooth_manager disable && cmd bluetooth_manager wait-for-state:STATE_OFF");
779 
780         setCommandForBinaryState(mNfc, mRunCommandAfterSettings,
781                 "svc nfc enable", "svc nfc disable");
782 
783         if (mScreenBrightness != null && BinaryState.ON.equals(mScreenAdaptiveBrightness)) {
784             throw new TargetSetupError("Option screen-brightness cannot be set when " +
785                     "screen-adaptive-brightness is set to ON", device.getDeviceDescriptor());
786         }
787 
788         setSettingForBinaryState(mScreenAdaptiveBrightness, mSystemSettings,
789                 "screen_brightness_mode", "1", "0");
790 
791         if (mScreenBrightness != null) {
792             mSystemSettings.put("screen_brightness", Integer.toString(mScreenBrightness));
793         }
794 
795         if (mScreenTimeoutSecs != null) {
796             mSystemSettings.put("screen_off_timeout", Long.toString(mScreenTimeoutSecs * 1000));
797         }
798 
799         setSettingForBinaryState(mScreenAmbientMode, mSecureSettings, "doze_enabled", "1", "0");
800 
801         setSettingForBinaryState(mWakeGesture, mSecureSettings, "wake_gesture_enabled", "1", "0");
802 
803         setSettingForBinaryState(mScreenSaver, mSecureSettings, "screensaver_enabled", "1", "0");
804 
805         setSettingForBinaryState(mNotificationLed, mSystemSettings,
806                 "notification_light_pulse", "1", "0");
807 
808         setSettingForBinaryState(mInstallNonMarketApps, mSecureSettings,
809                 "install_non_market_apps", "1", "0");
810 
811         if (mTriggerMediaMounted) {
812             mRunCommandAfterSettings.add(
813                     "am broadcast -a android.intent.action.MEDIA_MOUNTED -d "
814                             + "file://${EXTERNAL_STORAGE} --receiver-include-background");
815         }
816 
817         setSettingForBinaryState(mLocationGps, mSecureSettings,
818                 "location_providers_allowed", "+gps", "-gps");
819 
820         setSettingForBinaryState(mLocationNetwork, mSecureSettings,
821                 "location_providers_allowed", "+network", "-network");
822 
823         setSettingForBinaryState(mAutoRotate, mSystemSettings, "accelerometer_rotation", "1", "0");
824 
825         if (device.getApiLevel() < 22) {
826             setCommandForBinaryState(mBatterySaver, mRunCommandBeforeSettings,
827                 "dumpsys battery set usb 0", null);
828         } else {
829             setCommandForBinaryState(mBatterySaver, mRunCommandBeforeSettings,
830                 "dumpsys battery unplug", null);
831         }
832         setSettingForBinaryState(mBatterySaver, mGlobalSettings, "low_power", "1", "0");
833 
834         if (mBatterySaverTrigger != null) {
835             mGlobalSettings.put("low_power_trigger_level", Integer.toString(mBatterySaverTrigger));
836         }
837 
838         if (mEnableFullBatteryStatsHistory) {
839             mRunCommandAfterSettings.add("dumpsys batterystats --enable full-history");
840         }
841 
842         if (mDisableDoze) {
843             mRunCommandAfterSettings.add("dumpsys deviceidle disable");
844         }
845 
846         setSettingForBinaryState(mAutoUpdateTime, mGlobalSettings, "auto_time", "1", "0");
847 
848         setSettingForBinaryState(mAutoUpdateTimezone, mGlobalSettings, "auto_timezone", "1", "0");
849 
850         if (BinaryState.ON.equals(mSyncTimezoneWithHost)) {
851             if (mTimezone != null) {
852                 throw new TargetSetupError("Option set-timezone cannot be set when " +
853                                                "sync-timezone-with-host is set to ON",
854                                            device.getDeviceDescriptor(),
855                                            InfraErrorIdentifier.OPTION_CONFIGURATION_ERROR);
856             } else {
857                 mTimezone = TimeZone.getDefault().getID();
858             }
859         }
860 
861         setSettingForBinaryState(
862                 mHideErrorDialogs, mGlobalSettings, "hide_error_dialogs", "1", "0");
863 
864         if (mTimezone != null) {
865             CLog.i("The actual timezone we set here is  %s", mTimezone);
866             mSetProps.put("persist.sys.timezone", mTimezone);
867         }
868 
869         if (mDisableDialing) {
870             mSetProps.put("ro.telephony.disable-call", "true");
871         }
872 
873         if (mDefaultSimData != null) {
874             mGlobalSettings.put("multi_sim_data_call", Integer.toString(mDefaultSimData));
875         }
876 
877         if (mDefaultSimVoice != null) {
878             mGlobalSettings.put("multi_sim_voice_call", Integer.toString(mDefaultSimVoice));
879         }
880 
881         if (mDefaultSimSms != null) {
882             mGlobalSettings.put("multi_sim_sms", Integer.toString(mDefaultSimSms));
883         }
884 
885         if (mDisableAudio) {
886             mSetProps.put("ro.audio.silent", "1");
887         }
888 
889         if (mSetTestHarness) {
890             // set both ro.monkey, ro.test_harness, persist.sys.test_harness, for compatibility with
891             // older platforms
892             mSetProps.put("ro.monkey", "1");
893             mSetProps.put("ro.test_harness", "1");
894             mSetProps.put("persist.sys.test_harness", "1");
895         }
896 
897         if (mDisableDalvikVerifier) {
898             mSetProps.put("dalvik.vm.dexopt-flags", "v=n");
899         }
900 
901         if (mDisableDeviceConfigSync) {
902             mRunCommandBeforeSettings.add("device_config set_sync_disabled_for_tests persistent");
903         }
904     }
905 
906     /**
907      * Change the system properties on the device.
908      *
909      * @param device The {@link ITestDevice}
910      * @throws DeviceNotAvailableException if the device is not available
911      * @throws TargetSetupError if there was a failure setting the system properties
912      */
changeSystemProps(ITestDevice device)913     private void changeSystemProps(ITestDevice device) throws DeviceNotAvailableException,
914             TargetSetupError {
915         if (mForceSkipSystemProps) {
916             CLog.d("Skipping system props due to force-skip-system-props");
917             return;
918         }
919 
920         if (mSetProps.size() > 0 && !device.enableAdbRoot()) {
921             throw new TargetSetupError(
922                     String.format(
923                             "Cannot set system props %s on %s without adb root. Setting "
924                                     + "'force-skip-system-props' or 'enable-root' to avoid error",
925                             mSetProps.toString(), device.getSerialNumber()),
926                     device.getDeviceDescriptor(),
927                     InfraErrorIdentifier.OPTION_CONFIGURATION_ERROR);
928         }
929 
930         boolean needsReboot = false;
931         // Set persistent props and build a map of all the nonpersistent ones
932         Map<String, String> nonpersistentProps = new HashMap<String, String>();
933         for (Map.Entry<String, String> prop : mSetProps.entrySet()) {
934             // MEMTAG_BOOTCTL is essentially a persist property. It triggers an action that
935             // stores the value in the misc partition, and gets applied and restored on
936             // reboot.
937             boolean isPersistProperty =
938                     prop.getKey().startsWith(PERSIST_PREFIX)
939                             || prop.getKey().equals(MEMTAG_BOOTCTL);
940 
941             if (isPersistProperty || mOptimizeNonPersistentSetup) {
942                 device.setProperty(prop.getKey(), prop.getValue());
943             }
944 
945             if (!isPersistProperty) {
946                 nonpersistentProps.put(prop.getKey(), prop.getValue());
947             }
948 
949             if (PROPERTIES_NEEDING_REBOOT.contains(prop.getKey())) {
950                 needsReboot = true;
951             }
952         }
953 
954         // If the reboot optimization is enabled, only set nonpersistent props if
955         // there are changed values from what the device is running.
956         boolean shouldSetProps = true;
957         if (!mOptimizeNonPersistentSetup
958                 && mOptimizedPropertySetting
959                 && !nonpersistentProps.isEmpty()) {
960             boolean allPropsAlreadySet = true;
961             for (Map.Entry<String, String> prop : nonpersistentProps.entrySet()) {
962                 if (!prop.getValue().equals(device.getProperty(prop.getKey()))) {
963                     allPropsAlreadySet = false;
964                     break;
965                 }
966             }
967             if (allPropsAlreadySet) {
968                 shouldSetProps = false;
969                 CLog.i(
970                         "All properties appear to already be set to desired values, skipping"
971                                 + " set stage");
972             }
973         }
974 
975         // Set the nonpersistent properties if needed.
976         if (!nonpersistentProps.isEmpty() && shouldSetProps) {
977             StringBuilder sb = new StringBuilder();
978             for (Map.Entry<String, String> prop : nonpersistentProps.entrySet()) {
979                 sb.append(String.format("%s=%s\n", prop.getKey(), prop.getValue()));
980             }
981 
982             if (mRestoreProperties) {
983                 mPreviousProperties = device.pullFile("/data/local.prop");
984             }
985             CLog.d("Pushing the following properties to /data/local.prop:\n%s", sb.toString());
986             boolean result = device.pushString(sb.toString(), "/data/local.prop");
987             if (!result) {
988                 throw new TargetSetupError(
989                         String.format(
990                                 "Failed to push /data/local.prop to %s", device.getSerialNumber()),
991                         device.getDeviceDescriptor(),
992                         DeviceErrorIdentifier.FAIL_PUSH_FILE);
993             }
994             // Set reasonable permissions for /data/local.prop
995             device.executeShellCommand("chmod 644 /data/local.prop");
996 
997             if (mDisableRamdump) {
998                 device.rebootIntoBootloader();
999                 CLog.i("Disabling ramdump.");
1000                 CommandResult resultRampdump =
1001                         device.executeFastbootCommand("oem", "ramdump", "disable");
1002                 if (!CommandStatus.SUCCESS.equals(resultRampdump.getStatus())) {
1003                     CLog.w(
1004                             "Failed to run ramdump disable: status: %s\nstdout: %s\nstderr: %s",
1005                             resultRampdump.getStatus(),
1006                             resultRampdump.getStdout(),
1007                             resultRampdump.getStderr());
1008                 }
1009             }
1010             if (!mOptimizeNonPersistentSetup) {
1011                 // non-persistent properties do not trigger a reboot in this
1012                 // new setup, if not explicitly set.
1013                 needsReboot = true;
1014             }
1015         }
1016 
1017         if (needsReboot) {
1018             CLog.i("Rebooting %s due to system property change", device.getSerialNumber());
1019             device.reboot();
1020         }
1021 
1022         // Log nonpersistent device properties (that change/lose values after reboot).
1023         String deviceType = device.getClass().getTypeName();
1024         for (Map.Entry<String, String> prop : mSetProps.entrySet()) {
1025             String expected = prop.getValue();
1026             String actual = device.getProperty(prop.getKey());
1027             if ((expected != null && !expected.equals(actual))
1028                     || (expected == null && actual != null)) {
1029                 String entry =
1030                         String.format("%s-%s(%s:%s)", deviceType, prop.getKey(), expected, actual);
1031                 InvocationMetricLogger.addInvocationMetrics(
1032                         InvocationMetricKey.NONPERSISTENT_DEVICE_PROPERTIES, entry);
1033             } else {
1034                 String entry = String.format("%s-%s(%s)", deviceType, prop.getKey(), actual);
1035                 InvocationMetricLogger.addInvocationMetrics(
1036                         InvocationMetricKey.PERSISTENT_DEVICE_PROPERTIES, entry);
1037             }
1038         }
1039     }
1040 
1041     /**
1042      * Handles screen always on settings.
1043      * <p>
1044      * This is done in a dedicated function because special handling is required in case of setting
1045      * screen to always on.
1046      * @throws DeviceNotAvailableException
1047      */
handleScreenAlwaysOnSetting(ITestDevice device)1048     private void handleScreenAlwaysOnSetting(ITestDevice device)
1049             throws DeviceNotAvailableException {
1050         String cmd = "svc power stayon %s";
1051         switch (mScreenAlwaysOn) {
1052             case ON:
1053                 try (CloseableTraceScope ignored =
1054                         new CloseableTraceScope(InvocationMetricKey.screen_on_setup.toString())) {
1055                     CLog.d("Setting screen always on to true");
1056                     String cmdStayOn = String.format(cmd, "true");
1057                     CommandResult stayOn = device.executeShellV2Command(cmdStayOn);
1058                     CLog.d("%s output: %s", cmdStayOn, stayOn);
1059                     if (mDismissViaWm) {
1060                         CommandResult res =
1061                                 device.executeShellV2Command(
1062                                         "wm dismiss-keyguard", 30000L, TimeUnit.MILLISECONDS, 0);
1063                         CLog.d("Output of dismiss-keyguard: %s", res);
1064                     } else {
1065                         // send MENU press in case keyguard needs to be dismissed again
1066                         CommandResult inputKey = device.executeShellV2Command("input keyevent 82");
1067                         CLog.d("Output of input keyevent 82: %s", inputKey);
1068                     }
1069                     // send HOME press in case keyguard was already dismissed, so we bring device
1070                     // back
1071                     // to home screen
1072                     // No need for this on Wear OS, since that causes the launcher to show
1073                     // instead of the home screen
1074                     if ((device instanceof TestDevice)
1075                             && !device.hasFeature("android.hardware.type.watch")) {
1076                         CommandResult inputKey = device.executeShellV2Command("input keyevent 3");
1077                         CLog.d("Output of input keyevent 3: %s", inputKey);
1078                     }
1079                     break;
1080                 }
1081             case OFF:
1082                 CLog.d("Setting screen always on to false");
1083                 device.executeShellCommand(String.format(cmd, "false"));
1084                 break;
1085             case IGNORE:
1086                 break;
1087         }
1088     }
1089 
1090     /**
1091      * Change the settings on the device.
1092      * <p>
1093      * Exposed so children classes may override.
1094      * </p>
1095      *
1096      * @param device The {@link ITestDevice}
1097      * @throws DeviceNotAvailableException if the device is not available
1098      * @throws TargetSetupError if there was a failure setting the settings
1099      */
changeSettings(ITestDevice device)1100     public void changeSettings(ITestDevice device) throws DeviceNotAvailableException,
1101             TargetSetupError {
1102         if (mForceSkipSettings) {
1103             CLog.d("Skipping settings due to force-skip-setttings");
1104             return;
1105         }
1106 
1107         if (mSystemSettings.isEmpty() && mSecureSettings.isEmpty() && mGlobalSettings.isEmpty() &&
1108                 BinaryState.IGNORE.equals(mAirplaneMode)) {
1109             CLog.d("No settings to change");
1110             return;
1111         }
1112 
1113         if (device.getApiLevel() < 22) {
1114             throw new TargetSetupError(String.format("Changing setting not supported on %s, " +
1115                     "must be API 22+", device.getSerialNumber()), device.getDeviceDescriptor());
1116         }
1117 
1118         // Special case airplane mode since it needs to be set before other connectivity settings
1119         // For example, it is possible to enable airplane mode and then turn wifi on
1120         String command = "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state %s";
1121         switch (mAirplaneMode) {
1122             case ON:
1123                 CLog.d("Changing global setting airplane_mode_on to 1");
1124                 device.setSetting("global", "airplane_mode_on", "1");
1125                 if (!mForceSkipRunCommands) {
1126                     device.executeShellCommand(String.format(command, "true"));
1127                 }
1128                 break;
1129             case OFF:
1130                 CLog.d("Changing global setting airplane_mode_on to 0");
1131                 device.setSetting("global", "airplane_mode_on", "0");
1132                 if (!mForceSkipRunCommands) {
1133                     device.executeShellCommand(String.format(command, "false"));
1134                 }
1135                 break;
1136             case IGNORE:
1137                 // No-op
1138                 break;
1139         }
1140 
1141         for (String key : mSystemSettings.keySet()) {
1142             for (String value : mSystemSettings.get(key)) {
1143                 if (mRestoreSettings) {
1144                     String previousSetting = device.getSetting("system", key);
1145                     mPreviousSystemSettings.put(key, previousSetting);
1146                 }
1147                 CLog.d("Changing system setting %s to %s", key, value);
1148                 device.setSetting("system", key, value);
1149             }
1150         }
1151         for (String key : mSecureSettings.keySet()) {
1152             for (String value : mSecureSettings.get(key)) {
1153                 if (mRestoreSettings) {
1154                     String previousSetting = device.getSetting("secure", key);
1155                     mPreviousSecureSettings.put(key, previousSetting);
1156                 }
1157                 CLog.d("Changing secure setting %s to %s", key, value);
1158                 device.setSetting("secure", key, value);
1159             }
1160         }
1161 
1162         for (String key : mGlobalSettings.keySet()) {
1163             for (String value : mGlobalSettings.get(key)) {
1164                 if (mRestoreSettings) {
1165                     String previousSetting = device.getSetting("global", key);
1166                     mPreviousGlobalSettings.put(key, previousSetting);
1167                 }
1168                 CLog.d("Changing global setting %s to %s", key, value);
1169                 device.setSetting("global", key, value);
1170             }
1171         }
1172     }
1173 
1174     /**
1175      * Execute additional commands on the device.
1176      *
1177      * @param device The {@link ITestDevice}
1178      * @param commands The list of commands to run
1179      * @throws DeviceNotAvailableException if the device is not available
1180      * @throws TargetSetupError if there was a failure setting the settings
1181      */
runCommands(ITestDevice device, List<String> commands)1182     private void runCommands(ITestDevice device, List<String> commands)
1183             throws DeviceNotAvailableException, TargetSetupError {
1184         if (mForceSkipRunCommands) {
1185             CLog.d("Skipping run commands due to force-skip-run-commands");
1186             return;
1187         }
1188 
1189         for (String command : commands) {
1190             device.executeShellCommand(command);
1191         }
1192     }
1193 
1194     /**
1195      * Connects device to Wifi if SSID is specified.
1196      *
1197      * @param device The {@link ITestDevice}
1198      * @throws DeviceNotAvailableException if the device is not available
1199      * @throws TargetSetupError if there was a failure setting the settings
1200      */
connectWifi(ITestDevice device)1201     private void connectWifi(ITestDevice device) throws DeviceNotAvailableException,
1202             TargetSetupError {
1203         if (mForceSkipRunCommands) {
1204             CLog.d("Skipping connect wifi due to force-skip-run-commands");
1205             return;
1206         }
1207         if ((mWifiSsid == null || mWifiSsid.isEmpty()) && mWifiSsidToPsk.isEmpty()) {
1208             return;
1209         }
1210         if (mSkipWifi) {
1211             CLog.d("Skipping wifi connection due to skip-wifi-connection");
1212             return;
1213         }
1214 
1215         if (mWifiSsid != null) {
1216             mWifiSsidToPsk.put(mWifiSsid, mWifiPsk);
1217         }
1218         if (device.connectToWifiNetwork(mWifiSsidToPsk)) {
1219             return;
1220         }
1221 
1222         if (mWifiSsid != null || !mWifiSsidToPsk.isEmpty()) {
1223             String network = (mWifiSsid == null) ? mWifiSsidToPsk.toString() : mWifiSsid;
1224             InfraErrorIdentifier errorIdentifier = InfraErrorIdentifier.WIFI_FAILED_CONNECT;
1225             if (device instanceof RemoteAndroidVirtualDevice
1226                     || device instanceof NestedRemoteDevice
1227                     || device instanceof LocalAndroidVirtualDevice) {
1228                 // Error identifier for virtual devices.
1229                 errorIdentifier = InfraErrorIdentifier.VIRTUAL_WIFI_FAILED_CONNECT;
1230             }
1231             throw new TargetSetupError(
1232                     String.format(
1233                             "Failed to connect to wifi network %s on %s",
1234                             network, device.getSerialNumber()),
1235                     device.getDeviceDescriptor(),
1236                     errorIdentifier);
1237         }
1238     }
1239 
1240     /**
1241      * Syncs a set of test data files, specified via local-data-path, to devices external storage.
1242      *
1243      * @param device The {@link ITestDevice}
1244      * @throws DeviceNotAvailableException if the device is not available
1245      * @throws TargetSetupError if data fails to sync
1246      */
syncTestData(ITestDevice device)1247     private void syncTestData(ITestDevice device) throws DeviceNotAvailableException,
1248             TargetSetupError {
1249         if (mLocalDataFile == null) {
1250             return;
1251         }
1252 
1253         if (!mLocalDataFile.exists() || !mLocalDataFile.isDirectory()) {
1254             throw new TargetSetupError(String.format(
1255                     "local-data-path %s is not a directory", mLocalDataFile.getAbsolutePath()),
1256                     device.getDeviceDescriptor());
1257         }
1258         String fullRemotePath = device.getIDevice().getMountPoint(IDevice.MNT_EXTERNAL_STORAGE);
1259         if (fullRemotePath == null) {
1260             throw new TargetSetupError(String.format(
1261                     "failed to get external storage path on device %s", device.getSerialNumber()),
1262                     device.getDeviceDescriptor());
1263         }
1264         if (mRemoteDataPath != null) {
1265             fullRemotePath = String.format("%s/%s", fullRemotePath, mRemoteDataPath);
1266         }
1267         boolean result = device.syncFiles(mLocalDataFile, fullRemotePath);
1268         if (!result) {
1269             // TODO: get exact error code and respond accordingly
1270             throw new TargetSetupError(String.format(
1271                     "failed to sync test data from local-data-path %s to %s on device %s",
1272                     mLocalDataFile.getAbsolutePath(), fullRemotePath, device.getSerialNumber()),
1273                     device.getDeviceDescriptor());
1274         }
1275     }
1276 
1277     /**
1278      * Check that device external store has the required space
1279      *
1280      * @param device The {@link ITestDevice}
1281      * @throws DeviceNotAvailableException if the device is not available or if the device does not
1282      * have the required space
1283      */
checkExternalStoreSpace(ITestDevice device)1284     private void checkExternalStoreSpace(ITestDevice device) throws DeviceNotAvailableException {
1285         if (mMinExternalStorageKb <= 0) {
1286             return;
1287         }
1288         if (!(device instanceof TestDevice)) {
1289             // TODO: instead check that sdcard exists
1290             return;
1291         }
1292         // Wait for device available to ensure the mounting of sdcard
1293         device.waitForDeviceAvailable();
1294         long freeSpace = device.getExternalStoreFreeSpace();
1295         if (freeSpace < mMinExternalStorageKb) {
1296             throw new DeviceNotAvailableException(
1297                     String.format(
1298                             "External store free space %dK is less than required %dK for device %s",
1299                             freeSpace, mMinExternalStorageKb, device.getSerialNumber()),
1300                     device.getSerialNumber(),
1301                     DeviceErrorIdentifier.DEVICE_UNEXPECTED_RESPONSE);
1302         }
1303     }
1304 
dismissSetupWizard(ITestDevice device)1305     private void dismissSetupWizard(ITestDevice device) throws DeviceNotAvailableException {
1306         for (int i = 0; i < mDismissSetupWizardRetry; i++) {
1307             CommandResult cmd1 =
1308                     device.executeShellV2Command(
1309                             "am start -a com.android.setupwizard.FOUR_CORNER_EXIT"); // Android
1310             // UDC+
1311             CommandResult cmd2 =
1312                     device.executeShellV2Command(
1313                             "am start -a com.android.setupwizard.EXIT"); // Android L - T
1314             // if either of the command is successful, count it as success. Otherwise, retry.
1315             if (CommandStatus.SUCCESS.equals(cmd1.getStatus())
1316                     || CommandStatus.SUCCESS.equals(cmd2.getStatus())) {
1317                 break;
1318             }
1319         }
1320         // verify setup wizard is dismissed
1321         CLog.d("Waiting %d ms for setup wizard to be dismissed.", mDismissSetupWizardTimeout);
1322         boolean dismissed = false;
1323         long startTime = System.currentTimeMillis();
1324         while (System.currentTimeMillis() - startTime < mDismissSetupWizardTimeout) {
1325             CommandResult cmdOut =
1326                     device.executeShellV2Command("dumpsys window displays | grep mCurrentFocus");
1327             if (CommandStatus.SUCCESS.equals(cmdOut.getStatus())
1328                     && !cmdOut.getStdout().contains("setupwizard")) {
1329                 CLog.d("Setup wizard is dismissed.");
1330                 dismissed = true;
1331                 break;
1332             } else {
1333                 RunUtil.getDefault().sleep(2 * 1000);
1334             }
1335         }
1336         if (!dismissed) {
1337             CLog.w(
1338                     "Setup wizard was not dismissed within the timeout limit: %d ms.",
1339                     mDismissSetupWizardTimeout);
1340         }
1341     }
1342 
1343     /**
1344      * Helper method to add an ON/OFF setting to a setting map.
1345      *
1346      * @param state The {@link BinaryState}
1347      * @param settingsMap The {@link MultiMap} used to store the settings.
1348      * @param setting The setting key
1349      * @param onValue The value if ON
1350      * @param offValue The value if OFF
1351      */
setSettingForBinaryState(BinaryState state, MultiMap<String, String> settingsMap, String setting, String onValue, String offValue)1352     public static void setSettingForBinaryState(BinaryState state,
1353             MultiMap<String, String> settingsMap, String setting, String onValue, String offValue) {
1354         switch (state) {
1355             case ON:
1356                 settingsMap.put(setting, onValue);
1357                 break;
1358             case OFF:
1359                 settingsMap.put(setting, offValue);
1360                 break;
1361             case IGNORE:
1362                 // Do nothing
1363                 break;
1364         }
1365     }
1366 
1367     /**
1368      * Helper method to add an ON/OFF run command to be executed on the device.
1369      *
1370      * @param state The {@link BinaryState}
1371      * @param commands The list of commands to add the on or off command to.
1372      * @param onCommand The command to run if ON. Ignored if the command is {@code null}
1373      * @param offCommand The command to run if OFF. Ignored if the command is {@code null}
1374      */
setCommandForBinaryState(BinaryState state, List<String> commands, String onCommand, String offCommand)1375     public static void setCommandForBinaryState(BinaryState state, List<String> commands,
1376             String onCommand, String offCommand) {
1377         switch (state) {
1378             case ON:
1379                 if (onCommand != null) {
1380                     commands.add(onCommand);
1381                 }
1382                 break;
1383             case OFF:
1384                 if (offCommand != null) {
1385                     commands.add(offCommand);
1386                 }
1387                 break;
1388             case IGNORE:
1389                 // Do nothing
1390                 break;
1391         }
1392     }
1393 
1394     /** Exposed for unit testing */
setForceSkipSystemProps(boolean force)1395     protected void setForceSkipSystemProps(boolean force) {
1396         mForceSkipSystemProps = force;
1397     }
1398 
setForceRootSetup(boolean force)1399     protected void setForceRootSetup(boolean force) {
1400         mForceRoot = force;
1401     }
1402 
isForceSkipSystemProps()1403     public boolean isForceSkipSystemProps() {
1404         return mForceSkipSystemProps;
1405     }
1406 
1407     /**
1408      * Exposed for unit testing
1409      */
setAirplaneMode(BinaryState airplaneMode)1410     protected void setAirplaneMode(BinaryState airplaneMode) {
1411         mAirplaneMode = airplaneMode;
1412     }
1413 
1414     /* Exposed for unit testing */
1415     @VisibleForTesting
setData(BinaryState data)1416     protected void setData(BinaryState data) {
1417         mData = data;
1418     }
1419 
1420     /* Exposed for unit testing */
1421     @VisibleForTesting
setCell(BinaryState cell)1422     protected void setCell(BinaryState cell) {
1423         mCell = cell;
1424     }
1425 
1426     /* Exposed for unit testing */
1427     @VisibleForTesting
setCellAutoSetting(BinaryState cellAutoSetting)1428     protected void setCellAutoSetting(BinaryState cellAutoSetting) {
1429         mCellAutoSetting = cellAutoSetting;
1430     }
1431 
1432     /**
1433      * Exposed for unit testing
1434      */
setWifi(BinaryState wifi)1435     protected void setWifi(BinaryState wifi) {
1436         mWifi = wifi;
1437     }
1438 
1439     /**
1440      * Exposed for unit testing
1441      */
setWifiNetwork(String wifiNetwork)1442     protected void setWifiNetwork(String wifiNetwork) {
1443         mWifiSsid = wifiNetwork;
1444     }
1445 
1446     /* Exposed for unit testing */
1447     @VisibleForTesting
setWifiPsk(String wifiPsk)1448     protected void setWifiPsk(String wifiPsk) {
1449         mWifiPsk = wifiPsk;
1450     }
1451 
1452     /* Exposed for unit testing */
1453     @VisibleForTesting
setWifiSsidToPsk(Map<String, String> wifiSssidToPsk)1454     protected void setWifiSsidToPsk(Map<String, String> wifiSssidToPsk) {
1455         mWifiSsidToPsk = wifiSssidToPsk;
1456     }
1457 
1458     /**
1459      * Exposed for unit testing
1460      */
setWifiWatchdog(BinaryState wifiWatchdog)1461     protected void setWifiWatchdog(BinaryState wifiWatchdog) {
1462         mWifiWatchdog = wifiWatchdog;
1463     }
1464 
1465     /* Exposed for unit testing */
1466     @VisibleForTesting
setDisableCwWifiMediator(BinaryState disableCwWifiMediator)1467     protected void setDisableCwWifiMediator(BinaryState disableCwWifiMediator) {
1468         mDisableCwWifiMediator = disableCwWifiMediator;
1469     }
1470 
1471     /**
1472      * Exposed for unit testing
1473      */
setWifiScanAlwaysEnabled(BinaryState wifiScanAlwaysEnabled)1474     protected void setWifiScanAlwaysEnabled(BinaryState wifiScanAlwaysEnabled) {
1475         mWifiScanAlwaysEnabled = wifiScanAlwaysEnabled;
1476     }
1477 
1478     /**
1479      * Exposed for unit testing
1480      */
setEthernet(BinaryState ethernet)1481     protected void setEthernet(BinaryState ethernet) {
1482         mEthernet = ethernet;
1483     }
1484 
1485     /**
1486      * Exposed for unit testing
1487      */
setBluetooth(BinaryState bluetooth)1488     protected void setBluetooth(BinaryState bluetooth) {
1489         mBluetooth = bluetooth;
1490     }
1491 
1492     /**
1493      * Exposed for unit testing
1494      */
setNfc(BinaryState nfc)1495     protected void setNfc(BinaryState nfc) {
1496         mNfc = nfc;
1497     }
1498 
1499     /**
1500      * Exposed for unit testing
1501      */
setScreenAdaptiveBrightness(BinaryState screenAdaptiveBrightness)1502     protected void setScreenAdaptiveBrightness(BinaryState screenAdaptiveBrightness) {
1503         mScreenAdaptiveBrightness = screenAdaptiveBrightness;
1504     }
1505 
1506     /**
1507      * Exposed for unit testing
1508      */
setScreenBrightness(Integer screenBrightness)1509     protected void setScreenBrightness(Integer screenBrightness) {
1510         mScreenBrightness = screenBrightness;
1511     }
1512 
1513     /**
1514      * Exposed for unit testing
1515      */
setScreenAlwaysOn(BinaryState screenAlwaysOn)1516     protected void setScreenAlwaysOn(BinaryState screenAlwaysOn) {
1517         mScreenAlwaysOn = screenAlwaysOn;
1518     }
1519 
1520     /**
1521      * Exposed for unit testing
1522      */
setScreenTimeoutSecs(Long screenTimeoutSecs)1523     protected void setScreenTimeoutSecs(Long screenTimeoutSecs) {
1524         mScreenTimeoutSecs = screenTimeoutSecs;
1525     }
1526 
1527     /**
1528      * Exposed for unit testing
1529      */
setScreenAmbientMode(BinaryState screenAmbientMode)1530     protected void setScreenAmbientMode(BinaryState screenAmbientMode) {
1531         mScreenAmbientMode = screenAmbientMode;
1532     }
1533 
1534     /**
1535      * Exposed for unit testing
1536      */
setWakeGesture(BinaryState wakeGesture)1537     protected void setWakeGesture(BinaryState wakeGesture) {
1538         mWakeGesture = wakeGesture;
1539     }
1540 
1541     /**
1542      * Exposed for unit testing
1543      */
setScreenSaver(BinaryState screenSaver)1544     protected void setScreenSaver(BinaryState screenSaver) {
1545         mScreenSaver = screenSaver;
1546     }
1547 
1548     /**
1549      * Exposed for unit testing
1550      */
setNotificationLed(BinaryState notificationLed)1551     protected void setNotificationLed(BinaryState notificationLed) {
1552         mNotificationLed = notificationLed;
1553     }
1554 
1555     /**
1556      * Exposed for unit testing
1557      */
setInstallNonMarketApps(BinaryState installNonMarketApps)1558     protected void setInstallNonMarketApps(BinaryState installNonMarketApps) {
1559         mInstallNonMarketApps = installNonMarketApps;
1560     }
1561 
1562     /**
1563      * Exposed for unit testing
1564      */
setTriggerMediaMounted(boolean triggerMediaMounted)1565     protected void setTriggerMediaMounted(boolean triggerMediaMounted) {
1566         mTriggerMediaMounted = triggerMediaMounted;
1567     }
1568 
1569     /**
1570      * Exposed for unit testing
1571      */
setLocationGps(BinaryState locationGps)1572     protected void setLocationGps(BinaryState locationGps) {
1573         mLocationGps = locationGps;
1574     }
1575 
1576     /**
1577      * Exposed for unit testing
1578      */
setLocationNetwork(BinaryState locationNetwork)1579     protected void setLocationNetwork(BinaryState locationNetwork) {
1580         mLocationNetwork = locationNetwork;
1581     }
1582 
1583     /**
1584      * Exposed for unit testing
1585      */
setAutoRotate(BinaryState autoRotate)1586     protected void setAutoRotate(BinaryState autoRotate) {
1587         mAutoRotate = autoRotate;
1588     }
1589 
1590     /**
1591      * Exposed for unit testing
1592      */
setBatterySaver(BinaryState batterySaver)1593     protected void setBatterySaver(BinaryState batterySaver) {
1594         mBatterySaver = batterySaver;
1595     }
1596 
1597     /**
1598      * Exposed for unit testing
1599      */
setBatterySaverTrigger(Integer batterySaverTrigger)1600     protected void setBatterySaverTrigger(Integer batterySaverTrigger) {
1601         mBatterySaverTrigger = batterySaverTrigger;
1602     }
1603 
1604     /**
1605      * Exposed for unit testing
1606      */
setEnableFullBatteryStatsHistory(boolean enableFullBatteryStatsHistory)1607     protected void setEnableFullBatteryStatsHistory(boolean enableFullBatteryStatsHistory) {
1608         mEnableFullBatteryStatsHistory = enableFullBatteryStatsHistory;
1609     }
1610 
1611     /**
1612      * Exposed for unit testing
1613      */
setDisableDoze(boolean disableDoze)1614     protected void setDisableDoze(boolean disableDoze) {
1615         mDisableDoze = disableDoze;
1616     }
1617 
1618     /**
1619      * Exposed for unit testing
1620      */
setAutoUpdateTime(BinaryState autoUpdateTime)1621     protected void setAutoUpdateTime(BinaryState autoUpdateTime) {
1622         mAutoUpdateTime = autoUpdateTime;
1623     }
1624 
1625     /**
1626      * Exposed for unit testing
1627      */
setAutoUpdateTimezone(BinaryState autoUpdateTimezone)1628     protected void setAutoUpdateTimezone(BinaryState autoUpdateTimezone) {
1629         mAutoUpdateTimezone = autoUpdateTimezone;
1630     }
1631 
1632     /**
1633      * Exposed for unit testing
1634      */
setTimezone(String timezone)1635     protected void setTimezone(String timezone) {
1636         mTimezone = timezone;
1637     }
1638 
1639     /**
1640      * Exposed for unit testing
1641      */
setDisableDialing(boolean disableDialing)1642     protected void setDisableDialing(boolean disableDialing) {
1643         mDisableDialing = disableDialing;
1644     }
1645 
1646     /**
1647      * Exposed for unit testing
1648      */
setDefaultSimData(Integer defaultSimData)1649     protected void setDefaultSimData(Integer defaultSimData) {
1650         mDefaultSimData = defaultSimData;
1651     }
1652 
1653     /**
1654      * Exposed for unit testing
1655      */
setDefaultSimVoice(Integer defaultSimVoice)1656     protected void setDefaultSimVoice(Integer defaultSimVoice) {
1657         mDefaultSimVoice = defaultSimVoice;
1658     }
1659 
1660     /**
1661      * Exposed for unit testing
1662      */
setDefaultSimSms(Integer defaultSimSms)1663     protected void setDefaultSimSms(Integer defaultSimSms) {
1664         mDefaultSimSms = defaultSimSms;
1665     }
1666 
1667     /**
1668      * Exposed for unit testing
1669      */
setDisableAudio(boolean disable)1670     protected void setDisableAudio(boolean disable) {
1671         mDisableAudio = disable;
1672     }
1673 
1674     /**
1675      * Exposed for unit testing
1676      */
setTestHarness(boolean setTestHarness)1677     protected void setTestHarness(boolean setTestHarness) {
1678         mSetTestHarness = setTestHarness;
1679     }
1680 
1681     /**
1682      * Exposed for unit testing
1683      */
setDisableDalvikVerifier(boolean disableDalvikVerifier)1684     protected void setDisableDalvikVerifier(boolean disableDalvikVerifier) {
1685         mDisableDalvikVerifier = disableDalvikVerifier;
1686     }
1687 
1688     /**
1689      * Exposed for unit testing
1690      */
setLocalDataPath(File path)1691     protected void setLocalDataPath(File path) {
1692         mLocalDataFile = path;
1693     }
1694 
1695     /**
1696      * Exposed for unit testing
1697      */
setMinExternalStorageKb(long storageKb)1698     protected void setMinExternalStorageKb(long storageKb) {
1699         mMinExternalStorageKb = storageKb;
1700     }
1701 
1702     /**
1703      * Exposed for unit testing
1704      */
setProperty(String key, String value)1705     protected void setProperty(String key, String value) {
1706         mSetProps.put(key, value);
1707     }
1708 
1709     /** Exposed for unit testing */
setGlobalSetting(String key, String value)1710     public void setGlobalSetting(String key, String value) {
1711         mGlobalSettings.put(key, value);
1712     }
1713 
1714     /** Exposed for unit testing */
setSecureSetting(String key, String value)1715     public void setSecureSetting(String key, String value) {
1716         mSecureSettings.put(key, value);
1717     }
1718 
1719     /** Exposed for unit testing */
setSystemSetting(String key, String value)1720     public void setSystemSetting(String key, String value) {
1721         mSystemSettings.put(key, value);
1722     }
1723 
1724     /** Exposed for unit testing */
setRestoreProperties(boolean restoreProperties)1725     protected void setRestoreProperties(boolean restoreProperties) {
1726         mRestoreProperties = restoreProperties;
1727     }
1728 
1729     /** Exposed for unit testing */
setRestoreSettings(boolean restoreSettings)1730     protected void setRestoreSettings(boolean restoreSettings) {
1731         mRestoreSettings = restoreSettings;
1732     }
1733 
1734     /**
1735      * Exposed for unit testing
1736      * @deprecated use {@link #setMinExternalStorageKb(long)} instead.
1737      */
1738     @Deprecated
setDeprecatedMinExternalStoreSpace(long storeSpace)1739     protected void setDeprecatedMinExternalStoreSpace(long storeSpace) {
1740         mDeprecatedMinExternalStoreSpace = storeSpace;
1741     }
1742 
1743     /**
1744      * Exposed for unit testing
1745      * @deprecated use {@link #setDisableAudio(boolean)} instead.
1746      */
1747     @Deprecated
setDeprecatedAudioSilent(boolean silent)1748     protected void setDeprecatedAudioSilent(boolean silent) {
1749         mDeprecatedSetAudioSilent = silent;
1750     }
1751 
1752     /**
1753      * Exposed for unit testing
1754      * @deprecated use {@link #setProperty(String, String)} instead.
1755      */
1756     @Deprecated
setDeprecatedSetProp(String prop)1757     protected void setDeprecatedSetProp(String prop) {
1758         mDeprecatedSetProps.add(prop);
1759     }
1760 
1761     @Override
getDependencies()1762     public Set<ExternalDependency> getDependencies() {
1763         Set<ExternalDependency> externalDependencies = new LinkedHashSet<>();
1764         // check if we need mobile data
1765         if (BinaryState.ON.equals(mData)) {
1766             externalDependencies.add(new TelephonyDependency());
1767         }
1768         // check if we need wifi
1769         if (!mSkipWifi && !(Strings.isNullOrEmpty(mWifiSsid) && mWifiSsidToPsk.isEmpty())) {
1770             externalDependencies.add(new NetworkDependency());
1771         }
1772         // check if we need ethernet
1773         if (BinaryState.ON.equals(mEthernet)) {
1774             externalDependencies.add(new EthernetDependency());
1775         }
1776         // check if we need bluetooth
1777         if (BinaryState.ON.equals(mBluetooth)) {
1778             externalDependencies.add(new BluetoothDependency());
1779         }
1780         // check if we need location-network
1781         if (BinaryState.ON.equals(mLocationNetwork)) {
1782             externalDependencies.add(new NetworkDependency());
1783         }
1784         return externalDependencies;
1785     }
1786 }
1787