blob: e6b943a67758e1bbd54d7f1387840817d29b69e2 [file] [log] [blame]
tturney1bdf77d2015-12-28 17:46:13 -08001#!/usr/bin/env python3.4
Ang Li73697b32015-12-03 00:41:53 +00002#
tturney1bdf77d2015-12-28 17:46:13 -08003# Copyright 2016 - The Android Open Source Project
Ang Li73697b32015-12-03 00:41:53 +00004#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import base64
18import concurrent.futures
19import datetime
20import json
21import functools
Ang Li60b071d2016-06-29 12:52:42 -070022import logging
Ang Li73697b32015-12-03 00:41:53 +000023import os
24import random
25import re
26import signal
27import string
28import subprocess
29import time
30import traceback
markdrc3028252017-08-22 19:07:20 -070031import zipfile
Ang Li73697b32015-12-03 00:41:53 +000032
markdrddcd7f22017-04-20 11:19:12 -070033from acts.controllers import adb
markdr6607bf12018-01-02 14:45:38 -080034from acts import tracelogger
markdrddcd7f22017-04-20 11:19:12 -070035
Ang Li5e494802016-02-26 18:10:26 -080036# File name length is limited to 255 chars on some OS, so we need to make sure
37# the file names we output fits within the limit.
markdr6607bf12018-01-02 14:45:38 -080038from acts.libs.proc import job
39
Ang Li5e494802016-02-26 18:10:26 -080040MAX_FILENAME_LEN = 255
41
Nathan Harold97d5f822016-03-21 14:00:00 -070042
Ang Li60b071d2016-06-29 12:52:42 -070043class ActsUtilsError(Exception):
44 """Generic error raised for exceptions in ACTS utils."""
45
46
Ang Li73697b32015-12-03 00:41:53 +000047class NexusModelNames:
48 # TODO(angli): This will be fixed later by angli.
49 ONE = 'sprout'
50 N5 = 'hammerhead'
51 N5v2 = 'bullhead'
52 N6 = 'shamu'
53 N6v2 = 'angler'
Nathan Haroldb05c8e62016-05-10 12:02:54 -070054 N6v3 = 'marlin'
55 N5v3 = 'sailfish'
Nathan Harold97d5f822016-03-21 14:00:00 -070056
Joe Brennan8bc4d1b2017-01-23 14:43:07 -080057
Yang Liud2b9ccb2016-05-03 16:04:02 -070058class DozeModeStatus:
59 ACTIVE = "ACTIVE"
60 IDLE = "IDLE"
61
62
Joe Brennan43d15112017-01-26 13:25:26 -080063class CapablityPerDevice:
64 energy_info_models = [
65 "shamu", "volantis", "volantisg", "angler", "bullhead", "ryu",
66 "marlin", "sailfish"
67 ]
68 tdls_models = [
69 "shamu", "hammerhead", "angler", "bullhead", "marlin", "sailfish"
70 ]
71
72
Ang Li73697b32015-12-03 00:41:53 +000073ascii_letters_and_digits = string.ascii_letters + string.digits
74valid_filename_chars = "-_." + ascii_letters_and_digits
75
76models = ("sprout", "occam", "hammerhead", "bullhead", "razor", "razorg",
Nathan Harold97d5f822016-03-21 14:00:00 -070077 "shamu", "angler", "volantis", "volantisg", "mantaray", "fugu",
Nathan Haroldb05c8e62016-05-10 12:02:54 -070078 "ryu", "marlin", "sailfish")
Ang Li73697b32015-12-03 00:41:53 +000079
80manufacture_name_to_model = {
81 "flo": "razor",
82 "flo_lte": "razorg",
83 "flounder": "volantis",
84 "flounder_lte": "volantisg",
85 "dragon": "ryu"
86}
87
88GMT_to_olson = {
Nathan Harold97d5f822016-03-21 14:00:00 -070089 "GMT-9": "America/Anchorage",
90 "GMT-8": "US/Pacific",
91 "GMT-7": "US/Mountain",
92 "GMT-6": "US/Central",
93 "GMT-5": "US/Eastern",
94 "GMT-4": "America/Barbados",
95 "GMT-3": "America/Buenos_Aires",
96 "GMT-2": "Atlantic/South_Georgia",
97 "GMT-1": "Atlantic/Azores",
98 "GMT+0": "Africa/Casablanca",
99 "GMT+1": "Europe/Amsterdam",
100 "GMT+2": "Europe/Athens",
101 "GMT+3": "Europe/Moscow",
102 "GMT+4": "Asia/Baku",
103 "GMT+5": "Asia/Oral",
104 "GMT+6": "Asia/Almaty",
105 "GMT+7": "Asia/Bangkok",
106 "GMT+8": "Asia/Hong_Kong",
107 "GMT+9": "Asia/Tokyo",
Ang Li73697b32015-12-03 00:41:53 +0000108 "GMT+10": "Pacific/Guam",
109 "GMT+11": "Pacific/Noumea",
110 "GMT+12": "Pacific/Fiji",
111 "GMT+13": "Pacific/Tongatapu",
112 "GMT-11": "Pacific/Midway",
113 "GMT-10": "Pacific/Honolulu"
114}
115
Nathan Harold97d5f822016-03-21 14:00:00 -0700116
Ang Li73697b32015-12-03 00:41:53 +0000117def abs_path(path):
118 """Resolve the '.' and '~' in a path to get the absolute path.
119
120 Args:
121 path: The path to expand.
122
123 Returns:
124 The absolute path of the input path.
125 """
126 return os.path.abspath(os.path.expanduser(path))
127
Nathan Harold97d5f822016-03-21 14:00:00 -0700128
Ang Li73697b32015-12-03 00:41:53 +0000129def create_dir(path):
130 """Creates a directory if it does not exist already.
131
132 Args:
133 path: The path of the directory to create.
134 """
135 full_path = abs_path(path)
136 if not os.path.exists(full_path):
137 os.makedirs(full_path)
138
Nathan Harold97d5f822016-03-21 14:00:00 -0700139
Ang Li73697b32015-12-03 00:41:53 +0000140def get_current_epoch_time():
141 """Current epoch time in milliseconds.
142
143 Returns:
144 An integer representing the current epoch time in milliseconds.
145 """
146 return int(round(time.time() * 1000))
147
Nathan Harold97d5f822016-03-21 14:00:00 -0700148
Ang Li73697b32015-12-03 00:41:53 +0000149def get_current_human_time():
150 """Returns the current time in human readable format.
151
152 Returns:
153 The current time stamp in Month-Day-Year Hour:Min:Sec format.
154 """
155 return time.strftime("%m-%d-%Y %H:%M:%S ")
156
Nathan Harold97d5f822016-03-21 14:00:00 -0700157
Ang Li73697b32015-12-03 00:41:53 +0000158def epoch_to_human_time(epoch_time):
159 """Converts an epoch timestamp to human readable time.
160
161 This essentially converts an output of get_current_epoch_time to an output
162 of get_current_human_time
163
164 Args:
165 epoch_time: An integer representing an epoch timestamp in milliseconds.
166
167 Returns:
168 A time string representing the input time.
169 None if input param is invalid.
170 """
171 if isinstance(epoch_time, int):
172 try:
173 d = datetime.datetime.fromtimestamp(epoch_time / 1000)
174 return d.strftime("%m-%d-%Y %H:%M:%S ")
175 except ValueError:
176 return None
177
Nathan Harold97d5f822016-03-21 14:00:00 -0700178
Ang Li73697b32015-12-03 00:41:53 +0000179def get_timezone_olson_id():
180 """Return the Olson ID of the local (non-DST) timezone.
181
182 Returns:
183 A string representing one of the Olson IDs of the local (non-DST)
184 timezone.
185 """
Nathan Harold97d5f822016-03-21 14:00:00 -0700186 tzoffset = int(time.timezone / 3600)
Ang Li73697b32015-12-03 00:41:53 +0000187 gmt = None
188 if tzoffset <= 0:
189 gmt = "GMT+{}".format(-tzoffset)
190 else:
191 gmt = "GMT-{}".format(tzoffset)
192 return GMT_to_olson[gmt]
193
Nathan Harold97d5f822016-03-21 14:00:00 -0700194
Ang Li73697b32015-12-03 00:41:53 +0000195def find_files(paths, file_predicate):
196 """Locate files whose names and extensions match the given predicate in
197 the specified directories.
198
199 Args:
200 paths: A list of directory paths where to find the files.
201 file_predicate: A function that returns True if the file name and
202 extension are desired.
203
204 Returns:
205 A list of files that match the predicate.
206 """
207 file_list = []
Benny Peakeaa238e02016-12-14 16:21:42 -0800208 if not isinstance(paths, list):
209 paths = [paths]
Ang Li73697b32015-12-03 00:41:53 +0000210 for path in paths:
211 p = abs_path(path)
212 for dirPath, subdirList, fileList in os.walk(p):
213 for fname in fileList:
214 name, ext = os.path.splitext(fname)
215 if file_predicate(name, ext):
Nathan Harold97d5f822016-03-21 14:00:00 -0700216 file_list.append((dirPath, name, ext))
Ang Li73697b32015-12-03 00:41:53 +0000217 return file_list
218
Nathan Harold97d5f822016-03-21 14:00:00 -0700219
Ang Li73697b32015-12-03 00:41:53 +0000220def load_config(file_full_path):
221 """Loads a JSON config file.
222
223 Returns:
224 A JSON object.
225 """
226 with open(file_full_path, 'r') as f:
Betty Zhou1e3d0742017-07-17 11:17:18 -0700227 try:
228 conf = json.load(f)
229 except Exception as e:
230 logging.error("Exception error to load %s: %s", f, e)
231 raise
Ang Li73697b32015-12-03 00:41:53 +0000232 return conf
233
Nathan Harold97d5f822016-03-21 14:00:00 -0700234
Ang Li73697b32015-12-03 00:41:53 +0000235def load_file_to_base64_str(f_path):
236 """Loads the content of a file into a base64 string.
237
238 Args:
239 f_path: full path to the file including the file name.
240
241 Returns:
242 A base64 string representing the content of the file in utf-8 encoding.
243 """
244 path = abs_path(f_path)
245 with open(path, 'rb') as f:
Nathan Harold97d5f822016-03-21 14:00:00 -0700246 f_bytes = f.read()
247 base64_str = base64.b64encode(f_bytes).decode("utf-8")
248 return base64_str
249
Ang Li73697b32015-12-03 00:41:53 +0000250
Jack Hed0b3bf72017-01-24 22:36:09 -0800251def dump_string_to_file(content, file_path, mode='w'):
252 """ Dump content of a string to
253
254 Args:
255 content: content to be dumped to file
256 file_path: full path to the file including the file name.
257 mode: file open mode, 'w' (truncating file) by default
258 :return:
259 """
260 full_path = abs_path(file_path)
261 with open(full_path, mode) as f:
262 f.write(content)
263
264
Ang Li73697b32015-12-03 00:41:53 +0000265def find_field(item_list, cond, comparator, target_field):
266 """Finds the value of a field in a dict object that satisfies certain
267 conditions.
268
269 Args:
270 item_list: A list of dict objects.
271 cond: A param that defines the condition.
272 comparator: A function that checks if an dict satisfies the condition.
273 target_field: Name of the field whose value to be returned if an item
274 satisfies the condition.
275
276 Returns:
277 Target value or None if no item satisfies the condition.
278 """
279 for item in item_list:
Nathan Harold97d5f822016-03-21 14:00:00 -0700280 if comparator(item, cond) and target_field in item:
281 return item[target_field]
Ang Li73697b32015-12-03 00:41:53 +0000282 return None
283
Nathan Harold97d5f822016-03-21 14:00:00 -0700284
Ang Li73697b32015-12-03 00:41:53 +0000285def rand_ascii_str(length):
286 """Generates a random string of specified length, composed of ascii letters
287 and digits.
288
289 Args:
290 length: The number of characters in the string.
291
292 Returns:
293 The random string generated.
294 """
295 letters = [random.choice(ascii_letters_and_digits) for i in range(length)]
296 return ''.join(letters)
297
Nathan Harold97d5f822016-03-21 14:00:00 -0700298
Ang Li73697b32015-12-03 00:41:53 +0000299# Thead/Process related functions.
300def concurrent_exec(func, param_list):
301 """Executes a function with different parameters pseudo-concurrently.
302
303 This is basically a map function. Each element (should be an iterable) in
304 the param_list is unpacked and passed into the function. Due to Python's
305 GIL, there's no true concurrency. This is suited for IO-bound tasks.
306
307 Args:
308 func: The function that parforms a task.
309 param_list: A list of iterables, each being a set of params to be
310 passed into the function.
Ang Li8d3e18e2015-12-04 17:41:38 -0800311
312 Returns:
313 A list of return values from each function execution. If an execution
314 caused an exception, the exception object will be the corresponding
315 result.
Ang Li73697b32015-12-03 00:41:53 +0000316 """
317 with concurrent.futures.ThreadPoolExecutor(max_workers=30) as executor:
318 # Start the load operations and mark each future with its params
319 future_to_params = {executor.submit(func, *p): p for p in param_list}
Ang Li8d3e18e2015-12-04 17:41:38 -0800320 return_vals = []
Ang Li73697b32015-12-03 00:41:53 +0000321 for future in concurrent.futures.as_completed(future_to_params):
322 params = future_to_params[future]
323 try:
Ang Li8d3e18e2015-12-04 17:41:38 -0800324 return_vals.append(future.result())
Ang Li73697b32015-12-03 00:41:53 +0000325 except Exception as exc:
Nathan Harold97d5f822016-03-21 14:00:00 -0700326 print("{} generated an exception: {}".format(
327 params, traceback.format_exc()))
Ang Li8d3e18e2015-12-04 17:41:38 -0800328 return_vals.append(exc)
329 return return_vals
Ang Li73697b32015-12-03 00:41:53 +0000330
Nathan Harold97d5f822016-03-21 14:00:00 -0700331
Ang Li73697b32015-12-03 00:41:53 +0000332def exe_cmd(*cmds):
333 """Executes commands in a new shell.
334
335 Args:
336 cmds: A sequence of commands and arguments.
337
338 Returns:
339 The output of the command run.
340
341 Raises:
Ang Lid18a3a52016-04-06 10:36:46 -0700342 OSError is raised if an error occurred during the command execution.
Ang Li73697b32015-12-03 00:41:53 +0000343 """
344 cmd = ' '.join(cmds)
tturneybd2cedf2016-09-26 12:44:58 -0700345 proc = subprocess.Popen(
346 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
Ang Li73697b32015-12-03 00:41:53 +0000347 (out, err) = proc.communicate()
348 if not err:
349 return out
Ang Lid18a3a52016-04-06 10:36:46 -0700350 raise OSError(err)
Ang Li73697b32015-12-03 00:41:53 +0000351
Nathan Harold97d5f822016-03-21 14:00:00 -0700352
Ang Li73697b32015-12-03 00:41:53 +0000353def require_sl4a(android_devices):
354 """Makes sure sl4a connection is established on the given AndroidDevice
355 objects.
356
357 Args:
358 android_devices: A list of AndroidDevice objects.
359
360 Raises:
361 AssertionError is raised if any given android device does not have SL4A
362 connection established.
363 """
364 for ad in android_devices:
365 msg = "SL4A connection not established properly on %s." % ad.serial
366 assert ad.droid, msg
367
Nathan Harold97d5f822016-03-21 14:00:00 -0700368
Ang Li60b071d2016-06-29 12:52:42 -0700369def _assert_subprocess_running(proc):
370 """Checks if a subprocess has terminated on its own.
Ang Li73697b32015-12-03 00:41:53 +0000371
372 Args:
Ang Li60b071d2016-06-29 12:52:42 -0700373 proc: A subprocess returned by subprocess.Popen.
374
375 Raises:
376 ActsUtilsError is raised if the subprocess has stopped.
377 """
378 ret = proc.poll()
379 if ret is not None:
380 out, err = proc.communicate()
381 raise ActsUtilsError("Process %d has terminated. ret: %d, stderr: %s,"
382 " stdout: %s" % (proc.pid, ret, err, out))
383
384
385def start_standing_subprocess(cmd, check_health_delay=0):
386 """Starts a long-running subprocess.
387
388 This is not a blocking call and the subprocess started by it should be
389 explicitly terminated with stop_standing_subprocess.
390
391 For short-running commands, you should use exe_cmd, which blocks.
392
393 You can specify a health check after the subprocess is started to make sure
394 it did not stop prematurely.
395
396 Args:
397 cmd: string, the command to start the subprocess with.
398 check_health_delay: float, the number of seconds to wait after the
399 subprocess starts to check its health. Default is 0,
400 which means no check.
Ang Li73697b32015-12-03 00:41:53 +0000401
402 Returns:
403 The subprocess that got started.
404 """
tturneybd2cedf2016-09-26 12:44:58 -0700405 proc = subprocess.Popen(
406 cmd,
407 stdout=subprocess.PIPE,
408 stderr=subprocess.PIPE,
409 shell=True,
410 preexec_fn=os.setpgrp)
Ang Li60b071d2016-06-29 12:52:42 -0700411 logging.debug("Start standing subprocess with cmd: %s", cmd)
412 if check_health_delay > 0:
413 time.sleep(check_health_delay)
414 _assert_subprocess_running(proc)
415 return proc
Ang Li73697b32015-12-03 00:41:53 +0000416
Nathan Harold97d5f822016-03-21 14:00:00 -0700417
Ang Li60b071d2016-06-29 12:52:42 -0700418def stop_standing_subprocess(proc, kill_signal=signal.SIGTERM):
Ang Li73697b32015-12-03 00:41:53 +0000419 """Stops a subprocess started by start_standing_subprocess.
420
Ang Li60b071d2016-06-29 12:52:42 -0700421 Before killing the process, we check if the process is running, if it has
422 terminated, ActsUtilsError is raised.
423
Ang Li73697b32015-12-03 00:41:53 +0000424 Catches and ignores the PermissionError which only happens on Macs.
425
426 Args:
Ang Li60b071d2016-06-29 12:52:42 -0700427 proc: Subprocess to terminate.
Ang Li73697b32015-12-03 00:41:53 +0000428 """
Ang Li60b071d2016-06-29 12:52:42 -0700429 pid = proc.pid
430 logging.debug("Stop standing subprocess %d", pid)
431 _assert_subprocess_running(proc)
Ang Li73697b32015-12-03 00:41:53 +0000432 try:
Ang Li60b071d2016-06-29 12:52:42 -0700433 os.killpg(pid, kill_signal)
Ang Li73697b32015-12-03 00:41:53 +0000434 except PermissionError:
435 pass
436
Nathan Harold97d5f822016-03-21 14:00:00 -0700437
Ang Li60b071d2016-06-29 12:52:42 -0700438def wait_for_standing_subprocess(proc, timeout=None):
Etan Cohen568faa62016-02-26 11:03:59 -0800439 """Waits for a subprocess started by start_standing_subprocess to finish
440 or times out.
441
442 Propagates the exception raised by the subprocess.wait(.) function.
443 The subprocess.TimeoutExpired exception is raised if the process timed-out
444 rather then terminating.
445
446 If no exception is raised: the subprocess terminated on its own. No need
447 to call stop_standing_subprocess() to kill it.
448
449 If an exception is raised: the subprocess is still alive - it did not
450 terminate. Either call stop_standing_subprocess() to kill it, or call
451 wait_for_standing_subprocess() to keep waiting for it to terminate on its
452 own.
453
454 Args:
455 p: Subprocess to wait for.
456 timeout: An integer number of seconds to wait before timing out.
457 """
Ang Li60b071d2016-06-29 12:52:42 -0700458 proc.wait(timeout)
Etan Cohen568faa62016-02-26 11:03:59 -0800459
Nathan Harold97d5f822016-03-21 14:00:00 -0700460
Ang Li73697b32015-12-03 00:41:53 +0000461def sync_device_time(ad):
462 """Sync the time of an android device with the current system time.
463
464 Both epoch time and the timezone will be synced.
465
466 Args:
467 ad: The android device to sync time on.
468 """
469 droid = ad.droid
470 droid.setTimeZone(get_timezone_olson_id())
471 droid.setTime(get_current_epoch_time())
472
Nathan Harold97d5f822016-03-21 14:00:00 -0700473
Ang Li73697b32015-12-03 00:41:53 +0000474# Timeout decorator block
475class TimeoutError(Exception):
476 """Exception for timeout decorator related errors.
477 """
478 pass
479
Nathan Harold97d5f822016-03-21 14:00:00 -0700480
Ang Li73697b32015-12-03 00:41:53 +0000481def _timeout_handler(signum, frame):
482 """Handler function used by signal to terminate a timed out function.
483 """
484 raise TimeoutError()
485
Nathan Harold97d5f822016-03-21 14:00:00 -0700486
Ang Li73697b32015-12-03 00:41:53 +0000487def timeout(sec):
488 """A decorator used to add time out check to a function.
489
Ang Li30822402016-06-17 16:38:35 -0700490 This only works in main thread due to its dependency on signal module.
491 Do NOT use it if the decorated funtion does not run in the Main thread.
492
Ang Li73697b32015-12-03 00:41:53 +0000493 Args:
494 sec: Number of seconds to wait before the function times out.
495 No timeout if set to 0
496
497 Returns:
498 What the decorated function returns.
499
500 Raises:
501 TimeoutError is raised when time out happens.
502 """
Nathan Harold97d5f822016-03-21 14:00:00 -0700503
Ang Li73697b32015-12-03 00:41:53 +0000504 def decorator(func):
505 @functools.wraps(func)
506 def wrapper(*args, **kwargs):
507 if sec:
508 signal.signal(signal.SIGALRM, _timeout_handler)
509 signal.alarm(sec)
510 try:
511 return func(*args, **kwargs)
512 except TimeoutError:
513 raise TimeoutError(("Function {} timed out after {} "
Nathan Harold97d5f822016-03-21 14:00:00 -0700514 "seconds.").format(func.__name__, sec))
Ang Li73697b32015-12-03 00:41:53 +0000515 finally:
516 signal.alarm(0)
Nathan Harold97d5f822016-03-21 14:00:00 -0700517
Ang Li73697b32015-12-03 00:41:53 +0000518 return wrapper
Nathan Harold97d5f822016-03-21 14:00:00 -0700519
Ang Li73697b32015-12-03 00:41:53 +0000520 return decorator
521
Nathan Harold97d5f822016-03-21 14:00:00 -0700522
Ang Li73697b32015-12-03 00:41:53 +0000523def trim_model_name(model):
524 """Trim any prefix and postfix and return the android designation of the
525 model name.
526
527 e.g. "m_shamu" will be trimmed to "shamu".
528
529 Args:
530 model: model name to be trimmed.
531
532 Returns
533 Trimmed model name if one of the known model names is found.
534 None otherwise.
535 """
536 # Directly look up first.
537 if model in models:
538 return model
539 if model in manufacture_name_to_model:
540 return manufacture_name_to_model[model]
541 # If not found, try trimming off prefix/postfix and look up again.
542 tokens = re.split("_|-", model)
543 for t in tokens:
544 if t in models:
545 return t
546 if t in manufacture_name_to_model:
547 return manufacture_name_to_model[t]
548 return None
549
Nathan Harold97d5f822016-03-21 14:00:00 -0700550
Yang Liu29a70d52016-01-15 17:38:46 -0800551def force_airplane_mode(ad, new_state, timeout_value=60):
552 """Force the device to set airplane mode on or off by adb shell command.
553
554 Args:
555 ad: android device object.
556 new_state: Turn on airplane mode if True.
557 Turn off airplane mode if False.
558 timeout_value: max wait time for 'adb wait-for-device'
559
560 Returns:
561 True if success.
562 False if timeout.
563 """
564 # Using timeout decorator.
565 # Wait for device with timeout. If after <timeout_value> seconds, adb
566 # is still waiting for device, throw TimeoutError exception.
567 @timeout(timeout_value)
568 def wait_for_device_with_timeout(ad):
569 ad.adb.wait_for_device()
570
571 try:
572 wait_for_device_with_timeout(ad)
Nathan Harold97d5f822016-03-21 14:00:00 -0700573 ad.adb.shell("settings put global airplane_mode_on {}".format(
574 1 if new_state else 0))
Bindu Mahadev102ed532017-02-16 16:56:11 -0800575 ad.adb.shell("am broadcast -a android.intent.action.AIRPLANE_MODE")
Yang Liu29a70d52016-01-15 17:38:46 -0800576 except TimeoutError:
577 # adb wait for device timeout
578 return False
579 return True
580
Nathan Harold97d5f822016-03-21 14:00:00 -0700581
Ang Li73697b32015-12-03 00:41:53 +0000582def enable_doze(ad):
583 """Force the device into doze mode.
584
585 Args:
586 ad: android device object.
587
588 Returns:
589 True if device is in doze mode.
590 False otherwise.
591 """
592 ad.adb.shell("dumpsys battery unplug")
593 ad.adb.shell("dumpsys deviceidle enable")
Yang Liud2b9ccb2016-05-03 16:04:02 -0700594 ad.adb.shell("dumpsys deviceidle force-idle")
Ang Li73697b32015-12-03 00:41:53 +0000595 ad.droid.goToSleepNow()
596 time.sleep(5)
Girish Moturu05598772017-05-11 11:10:45 +0530597 adb_shell_result = ad.adb.shell("dumpsys deviceidle get deep")
Yang Liud2b9ccb2016-05-03 16:04:02 -0700598 if not adb_shell_result.startswith(DozeModeStatus.IDLE):
599 info = ("dumpsys deviceidle get deep: {}".format(adb_shell_result))
Ang Li73697b32015-12-03 00:41:53 +0000600 print(info)
601 return False
602 return True
603
Nathan Harold97d5f822016-03-21 14:00:00 -0700604
Ang Li73697b32015-12-03 00:41:53 +0000605def disable_doze(ad):
606 """Force the device not in doze mode.
607
608 Args:
609 ad: android device object.
610
611 Returns:
612 True if device is not in doze mode.
613 False otherwise.
614 """
615 ad.adb.shell("dumpsys deviceidle disable")
616 ad.adb.shell("dumpsys battery reset")
Girish Moturu05598772017-05-11 11:10:45 +0530617 adb_shell_result = ad.adb.shell("dumpsys deviceidle get deep")
Yang Liud2b9ccb2016-05-03 16:04:02 -0700618 if not adb_shell_result.startswith(DozeModeStatus.ACTIVE):
619 info = ("dumpsys deviceidle get deep: {}".format(adb_shell_result))
620 print(info)
621 return False
622 return True
623
624
625def enable_doze_light(ad):
626 """Force the device into doze light mode.
627
628 Args:
629 ad: android device object.
630
631 Returns:
632 True if device is in doze light mode.
633 False otherwise.
634 """
635 ad.adb.shell("dumpsys battery unplug")
636 ad.droid.goToSleepNow()
637 time.sleep(5)
638 ad.adb.shell("cmd deviceidle enable light")
639 ad.adb.shell("cmd deviceidle step light")
Girish Moturu05598772017-05-11 11:10:45 +0530640 adb_shell_result = ad.adb.shell("dumpsys deviceidle get light")
Yang Liud2b9ccb2016-05-03 16:04:02 -0700641 if not adb_shell_result.startswith(DozeModeStatus.IDLE):
642 info = ("dumpsys deviceidle get light: {}".format(adb_shell_result))
643 print(info)
644 return False
645 return True
646
647
648def disable_doze_light(ad):
649 """Force the device not in doze light mode.
650
651 Args:
652 ad: android device object.
653
654 Returns:
655 True if device is not in doze light mode.
656 False otherwise.
657 """
658 ad.adb.shell("dumpsys battery reset")
659 ad.adb.shell("cmd deviceidle disable light")
Girish Moturu05598772017-05-11 11:10:45 +0530660 adb_shell_result = ad.adb.shell("dumpsys deviceidle get light")
Yang Liud2b9ccb2016-05-03 16:04:02 -0700661 if not adb_shell_result.startswith(DozeModeStatus.ACTIVE):
662 info = ("dumpsys deviceidle get light: {}".format(adb_shell_result))
Ang Li73697b32015-12-03 00:41:53 +0000663 print(info)
664 return False
665 return True
666
Nathan Harold97d5f822016-03-21 14:00:00 -0700667
Ang Li73697b32015-12-03 00:41:53 +0000668def set_ambient_display(ad, new_state):
669 """Set "Ambient Display" in Settings->Display
670
671 Args:
672 ad: android device object.
673 new_state: new state for "Ambient Display". True or False.
674 """
Jack Hed0b3bf72017-01-24 22:36:09 -0800675 ad.adb.shell(
676 "settings put secure doze_enabled {}".format(1 if new_state else 0))
Nathan Harold97d5f822016-03-21 14:00:00 -0700677
Ang Li73697b32015-12-03 00:41:53 +0000678
679def set_adaptive_brightness(ad, new_state):
680 """Set "Adaptive Brightness" in Settings->Display
681
682 Args:
683 ad: android device object.
684 new_state: new state for "Adaptive Brightness". True or False.
685 """
Nathan Harold97d5f822016-03-21 14:00:00 -0700686 ad.adb.shell("settings put system screen_brightness_mode {}".format(
687 1 if new_state else 0))
688
Ang Li73697b32015-12-03 00:41:53 +0000689
690def set_auto_rotate(ad, new_state):
691 """Set "Auto-rotate" in QuickSetting
692
693 Args:
694 ad: android device object.
695 new_state: new state for "Auto-rotate". True or False.
696 """
Nathan Harold97d5f822016-03-21 14:00:00 -0700697 ad.adb.shell("settings put system accelerometer_rotation {}".format(
698 1 if new_state else 0))
699
Ang Li73697b32015-12-03 00:41:53 +0000700
701def set_location_service(ad, new_state):
702 """Set Location service on/off in Settings->Location
703
704 Args:
705 ad: android device object.
706 new_state: new state for "Location service".
707 If new_state is False, turn off location service.
708 If new_state if True, set location service to "High accuracy".
709 """
Etan Cohen171c3752018-05-25 07:54:27 -0700710 ad.adb.shell("content insert --uri "
Etan Cohen126f6fc2018-05-09 12:49:58 -0700711 " content://com.google.settings/partner --bind "
712 "name:s:network_location_opt_in --bind value:s:1")
Etan Cohen171c3752018-05-25 07:54:27 -0700713 ad.adb.shell("content insert --uri "
Etan Cohen126f6fc2018-05-09 12:49:58 -0700714 " content://com.google.settings/partner --bind "
715 "name:s:use_location_for_services --bind value:s:1")
Ang Li73697b32015-12-03 00:41:53 +0000716 if new_state:
717 ad.adb.shell("settings put secure location_providers_allowed +gps")
718 ad.adb.shell("settings put secure location_providers_allowed +network")
719 else:
720 ad.adb.shell("settings put secure location_providers_allowed -gps")
721 ad.adb.shell("settings put secure location_providers_allowed -network")
Yang Liu963caef2016-01-15 16:27:43 -0800722
Nathan Harold97d5f822016-03-21 14:00:00 -0700723
Yang Liu963caef2016-01-15 16:27:43 -0800724def set_mobile_data_always_on(ad, new_state):
725 """Set Mobile_Data_Always_On feature bit
726
727 Args:
728 ad: android device object.
729 new_state: new state for "mobile_data_always_on"
730 if new_state is False, set mobile_data_always_on disabled.
731 if new_state if True, set mobile_data_always_on enabled.
732 """
Nathan Harold97d5f822016-03-21 14:00:00 -0700733 ad.adb.shell("settings put global mobile_data_always_on {}".format(
734 1 if new_state else 0))
tturneybd2cedf2016-09-26 12:44:58 -0700735
736
markdrddcd7f22017-04-20 11:19:12 -0700737def bypass_setup_wizard(ad, bypass_wait_time=3):
tturneybd2cedf2016-09-26 12:44:58 -0700738 """Bypass the setup wizard on an input Android device
739
740 Args:
741 ad: android device object.
markdrddcd7f22017-04-20 11:19:12 -0700742 bypass_wait_time: Do not set this variable. Only modified for framework
743 tests.
tturneybd2cedf2016-09-26 12:44:58 -0700744
745 Returns:
746 True if Andorid device successfully bypassed the setup wizard.
747 False if failed.
748 """
markdrddcd7f22017-04-20 11:19:12 -0700749 try:
750 ad.adb.shell("am start -n \"com.google.android.setupwizard/"
751 ".SetupWizardExitActivity\"")
markdr8db34ee2017-04-25 17:50:51 -0700752 logging.debug("No error during default bypass call.")
markdrddcd7f22017-04-20 11:19:12 -0700753 except adb.AdbError as adb_error:
754 if adb_error.stdout == "ADB_CMD_OUTPUT:0":
755 if adb_error.stderr and \
756 not adb_error.stderr.startswith("Error type 3\n"):
markdr8db34ee2017-04-25 17:50:51 -0700757 logging.error(
758 "ADB_CMD_OUTPUT:0, but error is %s " % adb_error.stderr)
markdrddcd7f22017-04-20 11:19:12 -0700759 raise adb_error
markdr8db34ee2017-04-25 17:50:51 -0700760 logging.debug("Bypass wizard call received harmless error 3: "
761 "No setup to bypass.")
markdrddcd7f22017-04-20 11:19:12 -0700762 elif adb_error.stdout == "ADB_CMD_OUTPUT:255":
763 # Run it again as root.
764 ad.adb.root_adb()
markdr8db34ee2017-04-25 17:50:51 -0700765 logging.debug("Need root access to bypass setup wizard.")
markdrddcd7f22017-04-20 11:19:12 -0700766 try:
767 ad.adb.shell("am start -n \"com.google.android.setupwizard/"
768 ".SetupWizardExitActivity\"")
markdr8db34ee2017-04-25 17:50:51 -0700769 logging.debug("No error during rooted bypass call.")
markdrddcd7f22017-04-20 11:19:12 -0700770 except adb.AdbError as adb_error:
771 if adb_error.stdout == "ADB_CMD_OUTPUT:0":
772 if adb_error.stderr and \
markdr8db34ee2017-04-25 17:50:51 -0700773 not adb_error.stderr.startswith("Error type 3\n"):
774 logging.error("Rooted ADB_CMD_OUTPUT:0, but error is "
775 "%s " % adb_error.stderr)
markdrddcd7f22017-04-20 11:19:12 -0700776 raise adb_error
markdr8db34ee2017-04-25 17:50:51 -0700777 logging.debug(
778 "Rooted bypass wizard call received harmless "
779 "error 3: No setup to bypass.")
markdrddcd7f22017-04-20 11:19:12 -0700780
tturneybd2cedf2016-09-26 12:44:58 -0700781 # magical sleep to wait for the gservices override broadcast to complete
markdrddcd7f22017-04-20 11:19:12 -0700782 time.sleep(bypass_wait_time)
783
tturneybd2cedf2016-09-26 12:44:58 -0700784 provisioned_state = int(
785 ad.adb.shell("settings get global device_provisioned"))
markdrddcd7f22017-04-20 11:19:12 -0700786 if provisioned_state != 1:
tturneybd2cedf2016-09-26 12:44:58 -0700787 logging.error("Failed to bypass setup wizard.")
788 return False
markdr8db34ee2017-04-25 17:50:51 -0700789 logging.debug("Setup wizard successfully bypassed.")
tturneybd2cedf2016-09-26 12:44:58 -0700790 return True
Jaineeld0083ba2017-04-03 10:58:23 -0700791
markdrddcd7f22017-04-20 11:19:12 -0700792
Jaineeld0083ba2017-04-03 10:58:23 -0700793def parse_ping_ouput(ad, count, out, loss_tolerance=20):
794 """Ping Parsing util.
795
796 Args:
797 ad: Android Device Object.
798 count: Number of ICMP packets sent
799 out: shell output text of ping operation
800 loss_tolerance: Threshold after which flag test as false
801 Returns:
802 False: if packet loss is more than loss_tolerance%
803 True: if all good
804 """
805 out = out.split('\n')[-3:]
806 stats = out[1].split(',')
807 # For failure case, line of interest becomes the last line
808 if len(stats) != 4:
809 stats = out[2].split(',')
810 packet_loss = float(stats[2].split('%')[0])
811 packet_xmit = int(stats[0].split()[0])
812 packet_rcvd = int(stats[1].split()[0])
813 min_packet_xmit_rcvd = (100 - loss_tolerance) * 0.01
814
Sean Sutherland473661d2017-10-27 17:43:12 -0700815 if (packet_loss >= loss_tolerance
816 or packet_xmit < count * min_packet_xmit_rcvd
817 or packet_rcvd < count * min_packet_xmit_rcvd):
Jaineeld0083ba2017-04-03 10:58:23 -0700818 ad.log.error(
819 "More than %d %% packet loss seen, Expected Packet_count %d \
820 Packet loss %.2f%% Packets_xmitted %d Packets_rcvd %d",
821 loss_tolerance, count, packet_loss, packet_xmit, packet_rcvd)
822 return False
823 ad.log.info("Pkt_count %d Pkt_loss %.2f%% Pkt_xmit %d Pkt_rcvd %d", count,
824 packet_loss, packet_xmit, packet_rcvd)
825 return True
826
827
markdrddcd7f22017-04-20 11:19:12 -0700828def adb_shell_ping(ad,
829 count=120,
830 dest_ip="www.google.com",
831 timeout=200,
Jaineeld0083ba2017-04-03 10:58:23 -0700832 loss_tolerance=20):
833 """Ping utility using adb shell.
834
835 Args:
836 ad: Android Device Object.
837 count: Number of ICMP packets to send
838 dest_ip: hostname or IP address
839 default www.google.com
840 timeout: timeout for icmp pings to complete.
841 """
842 ping_cmd = "ping -W 1"
843 if count:
844 ping_cmd += " -c %d" % count
845 if dest_ip:
846 ping_cmd += " %s | tee /data/ping.txt" % dest_ip
847 try:
848 ad.log.info("Starting ping test to %s using adb command %s", dest_ip,
849 ping_cmd)
850 out = ad.adb.shell(ping_cmd, timeout=timeout)
851 if not parse_ping_ouput(ad, count, out, loss_tolerance):
852 return False
853 return True
854 except Exception as e:
Jaineele133ca32017-07-18 14:49:47 -0700855 ad.log.warning("Ping Test to %s failed with exception %s", dest_ip, e)
Jaineeld0083ba2017-04-03 10:58:23 -0700856 return False
857 finally:
858 ad.adb.shell("rm /data/ping.txt", timeout=10, ignore_status=True)
markdrc3028252017-08-22 19:07:20 -0700859
860
861def unzip_maintain_permissions(zip_path, extract_location):
862 """Unzip a .zip file while maintaining permissions.
863
864 Args:
865 zip_path: The path to the zipped file.
866 extract_location: the directory to extract to.
867 """
868 with zipfile.ZipFile(zip_path, 'r') as zip_file:
869 for info in zip_file.infolist():
870 _extract_file(zip_file, info, extract_location)
871
872
873def _extract_file(zip_file, zip_info, extract_location):
874 """Extracts a single entry from a ZipFile while maintaining permissions.
875
876 Args:
877 zip_file: A zipfile.ZipFile.
878 zip_info: A ZipInfo object from zip_file.
879 extract_location: The directory to extract to.
880 """
881 out_path = zip_file.extract(zip_info.filename, path=extract_location)
882 perm = zip_info.external_attr >> 16
883 os.chmod(out_path, perm)
Sean Sutherland473661d2017-10-27 17:43:12 -0700884
885
886def get_directory_size(path):
887 """Computes the total size of the files in a directory, including subdirectories.
888
889 Args:
890 path: The path of the directory.
891 Returns:
892 The size of the provided directory.
893 """
894 total = 0
895 for dirpath, dirnames, filenames in os.walk(path):
896 for filename in filenames:
897 total += os.path.getsize(os.path.join(dirpath, filename))
markdr6607bf12018-01-02 14:45:38 -0800898 return total
899
900
901def get_process_uptime(process):
902 """Returns the runtime in [[dd-]hh:]mm:ss, or '' if not running."""
903 pid = job.run('pidof %s' % process, ignore_status=True).stdout
904 runtime = ''
905 if pid:
906 runtime = job.run('ps -o etime= -p "%s"' % pid).stdout
907 return runtime
908
909
910def get_device_process_uptime(adb, process):
911 """Returns the uptime of a device process."""
912 pid = adb.shell('pidof %s' % process, ignore_status=True)
913 runtime = ''
914 if pid:
915 runtime = adb.shell('ps -o etime= -p "%s"' % pid)
916 return runtime