1#!/usr/bin/env python 2# 3# Copyright (C) 2018 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 time 20 21from vts.proto import ComponentSpecificationMessage_pb2 as CompSpecMsg 22from vts.runners.host import asserts 23from vts.runners.host import base_test 24from vts.runners.host import const 25from vts.runners.host import test_runner 26from vts.utils.python.file import target_file_utils 27 28 29class VtsCodelabHidlHandleTest(base_test.BaseTestClass): 30 """This class tests hidl_handle API calls from host side. 31 32 Attributes: 33 TEST_DIR_PATH: string, directory where the test file will be created. 34 TEST_FILE_PATH: string, path to the file that host side uses. 35 SERVICE_NAME: string, dumpstate HAL full service name. 36 START_COMMAND: string, command to start dumpstate HAL service 37 since it is a lazy HAL. 38 CHECK_COMMAND: string, command to check if dumpstate HAL service 39 is started. 40 MAX_RETRY: int, number of times to check if dumpstate HAL service 41 is started. 42 _created: bool, whether we created TEST_DIR_PATH. 43 _writer: ResourceHidlHandleMirror, writer who writes to TEST_FILE_PATH. 44 _reader: ResourceHidlHandleMirror, reader who reads from TEST_FILE_PATH. 45 _dumpstate: dumpstate HAL server instance. 46 _permission: SELinux permission, either enforcing or permissive. 47 This needs to set to permissive on the target side 48 when HAL server writes to user-defined destination. 49 This class will restore the permission level after 50 the test finishes. 51 """ 52 53 TEST_DIR_PATH = "/data/local/tmp/vts_codelab_tmp/" 54 TEST_FILE_PATH = TEST_DIR_PATH + "test.txt" 55 SERVICE_NAME = "android.hardware.dumpstate@1.0::IDumpstateDevice/default" 56 START_COMMAND = "setprop ctl.interface_start " + SERVICE_NAME 57 CHECK_COMMAND = "lshal --types=b | grep \"" + SERVICE_NAME + "\"" 58 MAX_RETRY = 3 59 60 def setUpClass(self): 61 """Necessary setup for the test environment. 62 63 We need to start dumpstate HAL service manually because it is a lazy 64 HAL service, then load it in our target-side driver. 65 Create a tmp directory in /data to create test files in it if the tmp 66 directory doesn't exist. 67 We also need to set SELinux permission to permissive because 68 HAL server and host side are communicating via a file. 69 We will recover SELinux permission during tearDown of this class. 70 """ 71 self.dut = self.android_devices[0] 72 # Execute shell command to start dumpstate HAL service because 73 # it is a lazy HAL service. 74 self.dut.shell.Execute(self.START_COMMAND) 75 76 start_hal_success = False 77 # Wait until service is started. 78 # Retry at most three times. 79 for _ in range(self.MAX_RETRY): 80 result = self.dut.shell.Execute(self.CHECK_COMMAND) 81 if result[const.STDOUT][0] != "": 82 start_hal_success = True # setup successful 83 break 84 time.sleep(1) # wait one second. 85 # Dumpstate HAL service is still not started after waiting for 86 # self.MAX_RETRY times, stop the testcase. 87 if not start_hal_success: 88 logging.error("Failed to start dumpstate HAL service.") 89 return False 90 91 # Initialize a hal driver to start all managers on the target side, 92 # not used for other purposes. 93 self.dut.hal.InitHidlHal( 94 target_type="dumpstate", 95 target_basepaths=self.dut.libPaths, 96 target_version_major=1, 97 target_version_minor=0, 98 target_package="android.hardware.dumpstate", 99 target_component_name="IDumpstateDevice", 100 bits=int(self.abi_bitness)) 101 # Make a shortcut name for the dumpstate HAL server. 102 self._dumpstate = self.dut.hal.dumpstate 103 104 # In order for dumpstate service to write to file, need to set 105 # SELinux to permissive. 106 permission_result = self.dut.shell.Execute("getenforce") 107 self._permission = permission_result[const.STDOUT][0].strip() 108 if self._permission == "Enforcing": 109 self.dut.shell.Execute("setenforce permissive") 110 111 # Check if a tmp directory under /data exists. 112 self._created = False 113 if not target_file_utils.Exists(self.TEST_DIR_PATH, self.dut.shell): 114 # Create a tmp directory under /data. 115 self.dut.shell.Execute("mkdir " + self.TEST_DIR_PATH) 116 # Verify it succeeds. Stop test if it fails. 117 if not target_file_utils.Exists(self.TEST_DIR_PATH, 118 self.dut.shell): 119 logging.error("Failed to create " + self.TEST_DIR_PATH + 120 " directory. Stopping test.") 121 return False 122 # Successfully created the directory. 123 logging.info("Manually created " + self.TEST_DIR_PATH + 124 " for the test.") 125 self._created = True 126 127 def setUp(self): 128 """Initialize a writer and a reader for each test case. 129 130 We open the file with w+ in every test case, which will create the file 131 if it doesn't exist, or truncate the file if it exists, because some 132 test case won't fully read the content of the file, causing dependency 133 between test cases. 134 """ 135 self._writer = self.dut.resource.InitHidlHandleForSingleFile( 136 self.TEST_FILE_PATH, 137 "w+", 138 client=self.dut.hal.GetTcpClient("dumpstate")) 139 self._reader = self.dut.resource.InitHidlHandleForSingleFile( 140 self.TEST_FILE_PATH, 141 "r", 142 client=self.dut.hal.GetTcpClient("dumpstate")) 143 asserts.assertTrue(self._writer is not None, 144 "Writer should be initialized successfully.") 145 asserts.assertTrue(self._reader is not None, 146 "Reader should be initialized successfully.") 147 asserts.assertNotEqual(self._writer.handleId, -1) 148 asserts.assertNotEqual(self._reader.handleId, -1) 149 150 def tearDown(self): 151 """Cleanup for each test case. 152 153 This method closes all open file descriptors on target side. 154 """ 155 # Close all file descriptors by calling CleanUp(). 156 self.dut.resource.CleanUp() 157 158 def tearDownClass(self): 159 """Cleanup after all tests. 160 161 Remove self.TEST_FILE_DIR directory if we created it at the beginning. 162 If SELinux permission level is changed, restore it here. 163 """ 164 # Delete self.TEST_DIR_PATH if we created it. 165 if self._created: 166 logging.info("Deleting " + self.TEST_DIR_PATH + 167 " directory created for this test.") 168 self.dut.shell.Execute("rm -rf " + self.TEST_DIR_PATH) 169 170 # Restore SELinux permission level. 171 if self._permission == "Enforcing": 172 self.dut.shell.Execute("setenforce enforcing") 173 174 def testInvalidWrite(self): 175 """Test writing to a file with no write permission should fail. """ 176 read_data = self._reader.writeFile("abc", 3) 177 asserts.assertTrue( 178 read_data is None, 179 "Reader should fail because it doesn't have write permission to the file." 180 ) 181 182 def testOpenNonexistingFile(self): 183 """Test opening a nonexisting file with read-only flag should fail. 184 185 This test case first checks if the file exists. If it exists, it skips 186 this test case. If it doesn't, it will try to open the non-existing file 187 with 'r' flag. 188 """ 189 if not target_file_utils.Exists(self.TEST_DIR_PATH + "abc.txt", 190 self.dut.shell): 191 logging.info("Test opening a non-existing file with 'r' flag.") 192 failed_reader = self.dut.resource.InitHidlHandleForSingleFile( 193 self.TEST_DIR_PATH + "abc.txt", 194 "r", 195 client=self.dut.hal.GetTcpClient("dumpstate")) 196 asserts.assertTrue( 197 failed_reader is None, 198 "Open a non-existing file with 'r' flag should fail.") 199 200 def testSimpleReadWrite(self): 201 """Test a simple read/write interaction between reader and writer. """ 202 write_data = "Hello World!" 203 asserts.assertEqual( 204 len(write_data), self._writer.writeFile(write_data, 205 len(write_data))) 206 read_data = self._reader.readFile(len(write_data)) 207 asserts.assertEqual(write_data, read_data) 208 209 def testLargeReadWrite(self): 210 """Test consecutive reads/writes between reader and writer. """ 211 write_data = "Android VTS" 212 213 for i in range(10): 214 asserts.assertEqual( 215 len(write_data), 216 self._writer.writeFile(write_data, len(write_data))) 217 curr_read_data = self._reader.readFile(len(write_data)) 218 asserts.assertEqual(curr_read_data, write_data) 219 220 def testHidlHandleArgument(self): 221 """Test calling APIs in dumpstate HAL server. 222 223 Host side specifies a handle object in resource_manager, ans pass 224 it to dumpstate HAL server to write debug message into it. 225 Host side then reads part of the debug message. 226 """ 227 # Prepare a VariableSpecificationMessage to specify the handle object 228 # that will be passed into the HAL service. 229 var_msg = CompSpecMsg.VariableSpecificationMessage() 230 var_msg.type = CompSpecMsg.TYPE_HANDLE 231 var_msg.handle_value.handle_id = self._writer.handleId 232 233 self._dumpstate.dumpstateBoard(var_msg) 234 # Read 1000 bytes to retrieve part of debug message. 235 debug_msg = self._reader.readFile(1000) 236 logging.info("Below are part of result from dumpstate: ") 237 logging.info(debug_msg) 238 asserts.assertNotEqual(debug_msg, "") 239 240 241if __name__ == "__main__": 242 test_runner.main() 243