1#!/usr/bin/env python 2# 3# Copyright (C) 2020 The Android Open Source Project 4# 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# 17 18import logging 19import subprocess 20import time 21 22from threading import Timer 23 24from vts.testcases.vndk import utils 25 26SYSPROP_DEV_BOOTCOMPLETE = "dev.bootcomplete" 27SYSPROP_SYS_BOOT_COMPLETED = "sys.boot_completed" 28 29 30class Shell(object): 31 """This class to wrap adb shell command.""" 32 33 def __init__(self, serial_number): 34 self._serial_number = serial_number 35 36 def Execute(self, *args): 37 """Executes a command. 38 39 Args: 40 args: Strings, the arguments. 41 42 Returns: 43 Stdout as a string, stderr as a string, and return code as an 44 integer. 45 """ 46 cmd = ["adb", "-s", self._serial_number, "shell"] 47 cmd.extend(args) 48 return RunCommand(cmd) 49 50 51class ADB(object): 52 """This class to wrap adb command.""" 53 54 def __init__(self, serial_number): 55 self._serial_number = serial_number 56 57 def Execute(self, cmd_list, timeout=None): 58 """Executes a command. 59 60 Args: 61 args: Strings, the arguments. 62 63 Returns: 64 Stdout as a string, stderr as a string, and return code as an 65 integer. 66 """ 67 cmd = ["adb", "-s", self._serial_number] 68 cmd.extend(cmd_list) 69 return RunCommand(cmd, timeout) 70 71class AndroidDevice(utils.AndroidDevice): 72 """This class controls the device via adb commands.""" 73 74 def __init__(self, serial_number): 75 super(AndroidDevice, self).__init__(serial_number) 76 self._serial_number = serial_number 77 self.shell = Shell(serial_number) 78 self.adb = ADB(serial_number) 79 80 def GetPermission(self, filepath): 81 """Get file permission.""" 82 out, err, r_code = self.shell.Execute('stat -c %%a %s' % filepath) 83 if r_code != 0 or err.strip(): 84 raise IOError("`stat -c %%a '%s'` stdout: %s\nstderr: %s" % 85 (filepath, out, err)) 86 return out.strip() 87 88 def WaitForBootCompletion(self, timeout=None): 89 """Get file permission.""" 90 start = time.time() 91 cmd = ['wait-for-device'] 92 self.adb.Execute(cmd, timeout) 93 while not self.isBootCompleted(): 94 if time.time() - start >= timeout: 95 logging.error("Timeout while waiting for boot completion.") 96 return False 97 time.sleep(1) 98 return True 99 100 def isBootCompleted(self): 101 """Checks whether the device has booted. 102 103 Returns: 104 True if booted, False otherwise. 105 """ 106 try: 107 if (self._GetProp(SYSPROP_SYS_BOOT_COMPLETED) == '1' and 108 self._GetProp(SYSPROP_DEV_BOOTCOMPLETE) == '1'): 109 return True 110 except Exception as e: 111 # adb shell calls may fail during certain period of booting 112 # process, which is normal. Ignoring these errors. 113 pass 114 return False 115 116 def IsShutdown(self, timeout=0): 117 """Checks whether the device has booted. 118 119 Returns: 120 True if booted, False otherwise. 121 """ 122 start = time.time() 123 while (time.time() - start) <= timeout: 124 if not self.isBootCompleted(): 125 return True 126 time.sleep(1) 127 return self.isBootCompleted() 128 129 def Root(self): 130 try: 131 self.adb.Execute(["root"]) 132 RETRIES = 3 133 for i in range(RETRIES): 134 self.adb.Execute(["wait-for-device"]) 135 # Verify that we haven't raced with the exit of the old, 136 # non-root adbd 137 out, err, r_code = self.shell.Execute("id -un") 138 if r_code == 0 and not err.strip() and out.strip() == "root": 139 return True 140 time.sleep(1) 141 except subprocess.CalledProcessError as e: 142 logging.exception(e) 143 return False 144 145 def ReadFileContent(self, filepath): 146 """Read the content of a file and perform assertions. 147 148 Args: 149 filepath: string, path to file 150 151 Returns: 152 string, content of file 153 """ 154 cmd = "cat %s" % filepath 155 out, err, r_code = self.shell.Execute(cmd) 156 157 # checks the exit code 158 if r_code != 0 or err.strip(): 159 raise IOError("%s: Error happened while reading the file due to %s." 160 % (filepath, err)) 161 return out 162 163 164def RunCommand(cmd, timeout=None): 165 kill = lambda process:process.kill() 166 proc = subprocess.Popen(args=cmd, 167 stderr=subprocess.PIPE, 168 stdout=subprocess.PIPE) 169 _timer = Timer(timeout, kill, [proc]) 170 try: 171 _timer.start() 172 (out, err) = proc.communicate() 173 finally: 174 _timer.cancel() 175 176 try: 177 return out.decode('UTF-8'), err.decode('UTF-8'), proc.returncode 178 except UnicodeDecodeError: 179 # ProcUidCpuPowerTimeInStateTest, ProcUidCpuPowerConcurrentActiveTimeTest, 180 # and ProcUidCpuPowerConcurrentPolicyTimeTest output could not be decode 181 # to UTF-8. 182 return out.decode('ISO-8859-1'), err.decode('ISO-8859-1'), proc.returncode 183