1*b7c941bbSAndroid Build Coastguard Worker# Copyright 2024 The Android Open Source Project 2*b7c941bbSAndroid Build Coastguard Worker# 3*b7c941bbSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*b7c941bbSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*b7c941bbSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*b7c941bbSAndroid Build Coastguard Worker# 7*b7c941bbSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*b7c941bbSAndroid Build Coastguard Worker# 9*b7c941bbSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*b7c941bbSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*b7c941bbSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*b7c941bbSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*b7c941bbSAndroid Build Coastguard Worker# limitations under the License. 14*b7c941bbSAndroid Build Coastguard Worker"""Utility functions for interacting with a device via the UI.""" 15*b7c941bbSAndroid Build Coastguard Worker 16*b7c941bbSAndroid Build Coastguard Workerimport datetime 17*b7c941bbSAndroid Build Coastguard Workerimport logging 18*b7c941bbSAndroid Build Coastguard Workerimport re 19*b7c941bbSAndroid Build Coastguard Workerimport time 20*b7c941bbSAndroid Build Coastguard Workerimport types 21*b7c941bbSAndroid Build Coastguard Workerimport xml.etree.ElementTree as et 22*b7c941bbSAndroid Build Coastguard Worker 23*b7c941bbSAndroid Build Coastguard Workerimport camera_properties_utils 24*b7c941bbSAndroid Build Coastguard Workerimport its_device_utils 25*b7c941bbSAndroid Build Coastguard Worker 26*b7c941bbSAndroid Build Coastguard Worker_DIR_EXISTS_TXT = 'Directory exists' 27*b7c941bbSAndroid Build Coastguard Worker_PERMISSIONS_LIST = ('CAMERA', 'RECORD_AUDIO', 'ACCESS_FINE_LOCATION', 28*b7c941bbSAndroid Build Coastguard Worker 'ACCESS_COARSE_LOCATION') 29*b7c941bbSAndroid Build Coastguard Worker 30*b7c941bbSAndroid Build Coastguard WorkerACTION_ITS_DO_JCA_CAPTURE = ( 31*b7c941bbSAndroid Build Coastguard Worker 'com.android.cts.verifier.camera.its.ACTION_ITS_DO_JCA_CAPTURE' 32*b7c941bbSAndroid Build Coastguard Worker) 33*b7c941bbSAndroid Build Coastguard WorkerACTION_ITS_DO_JCA_VIDEO_CAPTURE = ( 34*b7c941bbSAndroid Build Coastguard Worker 'com.android.cts.verifier.camera.its.ACTION_ITS_DO_JCA_VIDEO_CAPTURE' 35*b7c941bbSAndroid Build Coastguard Worker) 36*b7c941bbSAndroid Build Coastguard WorkerACTIVITY_WAIT_TIME_SECONDS = 5 37*b7c941bbSAndroid Build Coastguard WorkerAGREE_BUTTON = 'Agree' 38*b7c941bbSAndroid Build Coastguard WorkerAGREE_AND_CONTINUE_BUTTON = 'Agree and continue' 39*b7c941bbSAndroid Build Coastguard WorkerCANCEL_BUTTON_TXT = 'Cancel' 40*b7c941bbSAndroid Build Coastguard WorkerCAMERA_FILES_PATHS = ('/sdcard/DCIM/Camera', 41*b7c941bbSAndroid Build Coastguard Worker '/storage/emulated/0/Pictures') 42*b7c941bbSAndroid Build Coastguard WorkerCAPTURE_BUTTON_RESOURCE_ID = 'CaptureButton' 43*b7c941bbSAndroid Build Coastguard WorkerDONE_BUTTON_TXT = 'Done' 44*b7c941bbSAndroid Build Coastguard WorkerFLASH_MODE_TO_CLICKS = types.MappingProxyType({ 45*b7c941bbSAndroid Build Coastguard Worker 'OFF': 3, 46*b7c941bbSAndroid Build Coastguard Worker 'AUTO': 2 47*b7c941bbSAndroid Build Coastguard Worker}) 48*b7c941bbSAndroid Build Coastguard WorkerIMG_CAPTURE_CMD = 'am start -a android.media.action.IMAGE_CAPTURE' 49*b7c941bbSAndroid Build Coastguard WorkerITS_ACTIVITY_TEXT = 'Camera ITS Test' 50*b7c941bbSAndroid Build Coastguard WorkerJPG_FORMAT_STR = '.jpg' 51*b7c941bbSAndroid Build Coastguard WorkerOK_BUTTON_TXT = 'OK' 52*b7c941bbSAndroid Build Coastguard WorkerTAKE_PHOTO_CMD = 'input keyevent KEYCODE_CAMERA' 53*b7c941bbSAndroid Build Coastguard WorkerQUICK_SETTINGS_RESOURCE_ID = 'QuickSettingsDropDown' 54*b7c941bbSAndroid Build Coastguard WorkerQUICK_SET_FLASH_RESOURCE_ID = 'QuickSettingsFlashButton' 55*b7c941bbSAndroid Build Coastguard WorkerQUICK_SET_FLIP_CAMERA_RESOURCE_ID = 'QuickSettingsFlipCameraButton' 56*b7c941bbSAndroid Build Coastguard WorkerQUICK_SET_RATIO_RESOURCE_ID = 'QuickSettingsRatioButton' 57*b7c941bbSAndroid Build Coastguard WorkerRATIO_TO_UI_DESCRIPTION = { 58*b7c941bbSAndroid Build Coastguard Worker '1 to 1 aspect ratio': 'QuickSettingsRatio1:1Button', 59*b7c941bbSAndroid Build Coastguard Worker '3 to 4 aspect ratio': 'QuickSettingsRatio3:4Button', 60*b7c941bbSAndroid Build Coastguard Worker '9 to 16 aspect ratio': 'QuickSettingsRatio9:16Button' 61*b7c941bbSAndroid Build Coastguard Worker} 62*b7c941bbSAndroid Build Coastguard WorkerREMOVE_CAMERA_FILES_CMD = 'rm ' 63*b7c941bbSAndroid Build Coastguard WorkerUI_DESCRIPTION_BACK_CAMERA = 'Back Camera' 64*b7c941bbSAndroid Build Coastguard WorkerUI_DESCRIPTION_FRONT_CAMERA = 'Front Camera' 65*b7c941bbSAndroid Build Coastguard WorkerUI_OBJECT_WAIT_TIME_SECONDS = datetime.timedelta(seconds=3) 66*b7c941bbSAndroid Build Coastguard WorkerVIEWFINDER_NOT_VISIBLE_PREFIX = 'viewfinder_not_visible' 67*b7c941bbSAndroid Build Coastguard WorkerVIEWFINDER_VISIBLE_PREFIX = 'viewfinder_visible' 68*b7c941bbSAndroid Build Coastguard WorkerWAIT_INTERVAL_FIVE_SECONDS = datetime.timedelta(seconds=5) 69*b7c941bbSAndroid Build Coastguard Worker 70*b7c941bbSAndroid Build Coastguard Worker 71*b7c941bbSAndroid Build Coastguard Workerdef _find_ui_object_else_click(object_to_await, object_to_click): 72*b7c941bbSAndroid Build Coastguard Worker """Waits for a UI object to be visible. If not, clicks another UI object. 73*b7c941bbSAndroid Build Coastguard Worker 74*b7c941bbSAndroid Build Coastguard Worker Args: 75*b7c941bbSAndroid Build Coastguard Worker object_to_await: A snippet-uiautomator selector object to be awaited. 76*b7c941bbSAndroid Build Coastguard Worker object_to_click: A snippet-uiautomator selector object to be clicked. 77*b7c941bbSAndroid Build Coastguard Worker """ 78*b7c941bbSAndroid Build Coastguard Worker if not object_to_await.wait.exists(UI_OBJECT_WAIT_TIME_SECONDS): 79*b7c941bbSAndroid Build Coastguard Worker object_to_click.click() 80*b7c941bbSAndroid Build Coastguard Worker 81*b7c941bbSAndroid Build Coastguard Worker 82*b7c941bbSAndroid Build Coastguard Workerdef verify_ui_object_visible(ui_object, call_on_fail=None): 83*b7c941bbSAndroid Build Coastguard Worker """Verifies that a UI object is visible. 84*b7c941bbSAndroid Build Coastguard Worker 85*b7c941bbSAndroid Build Coastguard Worker Args: 86*b7c941bbSAndroid Build Coastguard Worker ui_object: A snippet-uiautomator selector object. 87*b7c941bbSAndroid Build Coastguard Worker call_on_fail: [Optional] Callable; method to call on failure. 88*b7c941bbSAndroid Build Coastguard Worker """ 89*b7c941bbSAndroid Build Coastguard Worker ui_object_visible = ui_object.wait.exists(UI_OBJECT_WAIT_TIME_SECONDS) 90*b7c941bbSAndroid Build Coastguard Worker if not ui_object_visible: 91*b7c941bbSAndroid Build Coastguard Worker if call_on_fail is not None: 92*b7c941bbSAndroid Build Coastguard Worker call_on_fail() 93*b7c941bbSAndroid Build Coastguard Worker raise AssertionError('UI object was not visible!') 94*b7c941bbSAndroid Build Coastguard Worker 95*b7c941bbSAndroid Build Coastguard Worker 96*b7c941bbSAndroid Build Coastguard Workerdef open_jca_viewfinder(dut, log_path, request_video_capture=False): 97*b7c941bbSAndroid Build Coastguard Worker """Sends an intent to JCA and open its viewfinder. 98*b7c941bbSAndroid Build Coastguard Worker 99*b7c941bbSAndroid Build Coastguard Worker Args: 100*b7c941bbSAndroid Build Coastguard Worker dut: An Android controller device object. 101*b7c941bbSAndroid Build Coastguard Worker log_path: str; Log path to save screenshots. 102*b7c941bbSAndroid Build Coastguard Worker request_video_capture: boolean; True if requesting video capture. 103*b7c941bbSAndroid Build Coastguard Worker Raises: 104*b7c941bbSAndroid Build Coastguard Worker AssertionError: If JCA viewfinder is not visible. 105*b7c941bbSAndroid Build Coastguard Worker """ 106*b7c941bbSAndroid Build Coastguard Worker its_device_utils.start_its_test_activity(dut.serial) 107*b7c941bbSAndroid Build Coastguard Worker call_on_fail = lambda: dut.take_screenshot(log_path, prefix='its_not_found') 108*b7c941bbSAndroid Build Coastguard Worker verify_ui_object_visible( 109*b7c941bbSAndroid Build Coastguard Worker dut.ui(text=ITS_ACTIVITY_TEXT), 110*b7c941bbSAndroid Build Coastguard Worker call_on_fail=call_on_fail 111*b7c941bbSAndroid Build Coastguard Worker ) 112*b7c941bbSAndroid Build Coastguard Worker 113*b7c941bbSAndroid Build Coastguard Worker # Send intent to ItsTestActivity, which will start the correct JCA activity. 114*b7c941bbSAndroid Build Coastguard Worker if request_video_capture: 115*b7c941bbSAndroid Build Coastguard Worker its_device_utils.run( 116*b7c941bbSAndroid Build Coastguard Worker f'adb -s {dut.serial} shell am broadcast -a' 117*b7c941bbSAndroid Build Coastguard Worker f'{ACTION_ITS_DO_JCA_VIDEO_CAPTURE}' 118*b7c941bbSAndroid Build Coastguard Worker ) 119*b7c941bbSAndroid Build Coastguard Worker else: 120*b7c941bbSAndroid Build Coastguard Worker its_device_utils.run( 121*b7c941bbSAndroid Build Coastguard Worker f'adb -s {dut.serial} shell am broadcast -a' 122*b7c941bbSAndroid Build Coastguard Worker f'{ACTION_ITS_DO_JCA_CAPTURE}' 123*b7c941bbSAndroid Build Coastguard Worker ) 124*b7c941bbSAndroid Build Coastguard Worker jca_capture_button_visible = dut.ui( 125*b7c941bbSAndroid Build Coastguard Worker res=CAPTURE_BUTTON_RESOURCE_ID).wait.exists( 126*b7c941bbSAndroid Build Coastguard Worker UI_OBJECT_WAIT_TIME_SECONDS) 127*b7c941bbSAndroid Build Coastguard Worker if not jca_capture_button_visible: 128*b7c941bbSAndroid Build Coastguard Worker dut.take_screenshot(log_path, prefix=VIEWFINDER_NOT_VISIBLE_PREFIX) 129*b7c941bbSAndroid Build Coastguard Worker logging.debug('Current UI dump: %s', dut.ui.dump()) 130*b7c941bbSAndroid Build Coastguard Worker raise AssertionError('JCA was not started successfully!') 131*b7c941bbSAndroid Build Coastguard Worker dut.take_screenshot(log_path, prefix=VIEWFINDER_VISIBLE_PREFIX) 132*b7c941bbSAndroid Build Coastguard Worker 133*b7c941bbSAndroid Build Coastguard Worker 134*b7c941bbSAndroid Build Coastguard Workerdef switch_jca_camera(dut, log_path, facing): 135*b7c941bbSAndroid Build Coastguard Worker """Interacts with JCA UI to switch camera if necessary. 136*b7c941bbSAndroid Build Coastguard Worker 137*b7c941bbSAndroid Build Coastguard Worker Args: 138*b7c941bbSAndroid Build Coastguard Worker dut: An Android controller device object. 139*b7c941bbSAndroid Build Coastguard Worker log_path: str; log path to save screenshots. 140*b7c941bbSAndroid Build Coastguard Worker facing: str; constant describing the direction the camera lens faces. 141*b7c941bbSAndroid Build Coastguard Worker Raises: 142*b7c941bbSAndroid Build Coastguard Worker AssertionError: If JCA does not report that camera has been switched. 143*b7c941bbSAndroid Build Coastguard Worker """ 144*b7c941bbSAndroid Build Coastguard Worker if facing == camera_properties_utils.LENS_FACING['BACK']: 145*b7c941bbSAndroid Build Coastguard Worker ui_facing_description = UI_DESCRIPTION_BACK_CAMERA 146*b7c941bbSAndroid Build Coastguard Worker elif facing == camera_properties_utils.LENS_FACING['FRONT']: 147*b7c941bbSAndroid Build Coastguard Worker ui_facing_description = UI_DESCRIPTION_FRONT_CAMERA 148*b7c941bbSAndroid Build Coastguard Worker else: 149*b7c941bbSAndroid Build Coastguard Worker raise ValueError(f'Unknown facing: {facing}') 150*b7c941bbSAndroid Build Coastguard Worker dut.ui(res=QUICK_SETTINGS_RESOURCE_ID).click() 151*b7c941bbSAndroid Build Coastguard Worker _find_ui_object_else_click(dut.ui(desc=ui_facing_description), 152*b7c941bbSAndroid Build Coastguard Worker dut.ui(res=QUICK_SET_FLIP_CAMERA_RESOURCE_ID)) 153*b7c941bbSAndroid Build Coastguard Worker if not dut.ui(desc=ui_facing_description).wait.exists( 154*b7c941bbSAndroid Build Coastguard Worker UI_OBJECT_WAIT_TIME_SECONDS): 155*b7c941bbSAndroid Build Coastguard Worker dut.take_screenshot(log_path, prefix='failed_to_switch_camera') 156*b7c941bbSAndroid Build Coastguard Worker logging.debug('JCA UI dump: %s', dut.ui.dump()) 157*b7c941bbSAndroid Build Coastguard Worker raise AssertionError(f'Failed to switch to {ui_facing_description}!') 158*b7c941bbSAndroid Build Coastguard Worker dut.take_screenshot( 159*b7c941bbSAndroid Build Coastguard Worker log_path, prefix=f"switched_to_{ui_facing_description.replace(' ', '_')}" 160*b7c941bbSAndroid Build Coastguard Worker ) 161*b7c941bbSAndroid Build Coastguard Worker dut.ui(res=QUICK_SETTINGS_RESOURCE_ID).click() 162*b7c941bbSAndroid Build Coastguard Worker 163*b7c941bbSAndroid Build Coastguard Worker 164*b7c941bbSAndroid Build Coastguard Workerdef change_jca_aspect_ratio(dut, log_path, aspect_ratio): 165*b7c941bbSAndroid Build Coastguard Worker """Interacts with JCA UI to change aspect ratio if necessary. 166*b7c941bbSAndroid Build Coastguard Worker 167*b7c941bbSAndroid Build Coastguard Worker Args: 168*b7c941bbSAndroid Build Coastguard Worker dut: An Android controller device object. 169*b7c941bbSAndroid Build Coastguard Worker log_path: str; log path to save screenshots. 170*b7c941bbSAndroid Build Coastguard Worker aspect_ratio: str; Aspect ratio that JCA supports. 171*b7c941bbSAndroid Build Coastguard Worker Acceptable values: _RATIO_TO_UI_DESCRIPTION 172*b7c941bbSAndroid Build Coastguard Worker Raises: 173*b7c941bbSAndroid Build Coastguard Worker ValueError: If ratio is not supported in JCA. 174*b7c941bbSAndroid Build Coastguard Worker AssertionError: If JCA does not find the requested ratio. 175*b7c941bbSAndroid Build Coastguard Worker """ 176*b7c941bbSAndroid Build Coastguard Worker if aspect_ratio not in RATIO_TO_UI_DESCRIPTION: 177*b7c941bbSAndroid Build Coastguard Worker raise ValueError(f'Testing ratio {aspect_ratio} not supported in JCA!') 178*b7c941bbSAndroid Build Coastguard Worker dut.ui(res=QUICK_SETTINGS_RESOURCE_ID).click() 179*b7c941bbSAndroid Build Coastguard Worker # Change aspect ratio in ratio switching menu if needed 180*b7c941bbSAndroid Build Coastguard Worker if not dut.ui(desc=aspect_ratio).wait.exists(UI_OBJECT_WAIT_TIME_SECONDS): 181*b7c941bbSAndroid Build Coastguard Worker dut.ui(res=QUICK_SET_RATIO_RESOURCE_ID).click() 182*b7c941bbSAndroid Build Coastguard Worker try: 183*b7c941bbSAndroid Build Coastguard Worker dut.ui(res=RATIO_TO_UI_DESCRIPTION[aspect_ratio]).click() 184*b7c941bbSAndroid Build Coastguard Worker except Exception as e: 185*b7c941bbSAndroid Build Coastguard Worker dut.take_screenshot( 186*b7c941bbSAndroid Build Coastguard Worker log_path, prefix=f'failed_to_find{aspect_ratio.replace(" ", "_")}' 187*b7c941bbSAndroid Build Coastguard Worker ) 188*b7c941bbSAndroid Build Coastguard Worker raise AssertionError( 189*b7c941bbSAndroid Build Coastguard Worker f'Testing ratio {aspect_ratio} not found in JCA app UI!') from e 190*b7c941bbSAndroid Build Coastguard Worker dut.ui(res=QUICK_SETTINGS_RESOURCE_ID).click() 191*b7c941bbSAndroid Build Coastguard Worker 192*b7c941bbSAndroid Build Coastguard Worker 193*b7c941bbSAndroid Build Coastguard Workerdef do_jca_video_setup(dut, log_path, facing, aspect_ratio): 194*b7c941bbSAndroid Build Coastguard Worker """Change video capture settings using the UI. 195*b7c941bbSAndroid Build Coastguard Worker 196*b7c941bbSAndroid Build Coastguard Worker Selects UI elements to modify settings. 197*b7c941bbSAndroid Build Coastguard Worker 198*b7c941bbSAndroid Build Coastguard Worker Args: 199*b7c941bbSAndroid Build Coastguard Worker dut: An Android controller device object. 200*b7c941bbSAndroid Build Coastguard Worker log_path: str; log path to save screenshots. 201*b7c941bbSAndroid Build Coastguard Worker facing: str; constant describing the direction the camera lens faces. 202*b7c941bbSAndroid Build Coastguard Worker Acceptable values: camera_properties_utils.LENS_FACING[BACK, FRONT] 203*b7c941bbSAndroid Build Coastguard Worker aspect_ratio: str; Aspect ratios that JCA supports. 204*b7c941bbSAndroid Build Coastguard Worker Acceptable values: _RATIO_TO_UI_DESCRIPTION 205*b7c941bbSAndroid Build Coastguard Worker """ 206*b7c941bbSAndroid Build Coastguard Worker open_jca_viewfinder(dut, log_path, request_video_capture=True) 207*b7c941bbSAndroid Build Coastguard Worker switch_jca_camera(dut, log_path, facing) 208*b7c941bbSAndroid Build Coastguard Worker change_jca_aspect_ratio(dut, log_path, aspect_ratio) 209*b7c941bbSAndroid Build Coastguard Worker 210*b7c941bbSAndroid Build Coastguard Worker 211*b7c941bbSAndroid Build Coastguard Workerdef default_camera_app_setup(device_id, pkg_name): 212*b7c941bbSAndroid Build Coastguard Worker """Setup Camera app by providing required permissions. 213*b7c941bbSAndroid Build Coastguard Worker 214*b7c941bbSAndroid Build Coastguard Worker Args: 215*b7c941bbSAndroid Build Coastguard Worker device_id: serial id of device. 216*b7c941bbSAndroid Build Coastguard Worker pkg_name: pkg name of the app to setup. 217*b7c941bbSAndroid Build Coastguard Worker Returns: 218*b7c941bbSAndroid Build Coastguard Worker Runtime exception from called function or None. 219*b7c941bbSAndroid Build Coastguard Worker """ 220*b7c941bbSAndroid Build Coastguard Worker logging.debug('Setting up the app with permission.') 221*b7c941bbSAndroid Build Coastguard Worker for permission in _PERMISSIONS_LIST: 222*b7c941bbSAndroid Build Coastguard Worker cmd = f'pm grant {pkg_name} android.permission.{permission}' 223*b7c941bbSAndroid Build Coastguard Worker its_device_utils.run_adb_shell_command(device_id, cmd) 224*b7c941bbSAndroid Build Coastguard Worker allow_manage_storage_cmd = ( 225*b7c941bbSAndroid Build Coastguard Worker f'appops set {pkg_name} MANAGE_EXTERNAL_STORAGE allow' 226*b7c941bbSAndroid Build Coastguard Worker ) 227*b7c941bbSAndroid Build Coastguard Worker its_device_utils.run_adb_shell_command(device_id, allow_manage_storage_cmd) 228*b7c941bbSAndroid Build Coastguard Worker 229*b7c941bbSAndroid Build Coastguard Worker 230*b7c941bbSAndroid Build Coastguard Workerdef switch_default_camera(dut, facing, log_path): 231*b7c941bbSAndroid Build Coastguard Worker """Interacts with default camera app UI to switch camera. 232*b7c941bbSAndroid Build Coastguard Worker 233*b7c941bbSAndroid Build Coastguard Worker Args: 234*b7c941bbSAndroid Build Coastguard Worker dut: An Android controller device object. 235*b7c941bbSAndroid Build Coastguard Worker facing: str; constant describing the direction the camera lens faces. 236*b7c941bbSAndroid Build Coastguard Worker log_path: str; log path to save screenshots. 237*b7c941bbSAndroid Build Coastguard Worker Raises: 238*b7c941bbSAndroid Build Coastguard Worker AssertionError: If default camera app does not report that 239*b7c941bbSAndroid Build Coastguard Worker camera has been switched. 240*b7c941bbSAndroid Build Coastguard Worker """ 241*b7c941bbSAndroid Build Coastguard Worker flip_camera_pattern = ( 242*b7c941bbSAndroid Build Coastguard Worker r'(switch to|flip camera|switch camera|camera switch|switch)' 243*b7c941bbSAndroid Build Coastguard Worker ) 244*b7c941bbSAndroid Build Coastguard Worker default_ui_dump = dut.ui.dump() 245*b7c941bbSAndroid Build Coastguard Worker logging.debug('Default camera UI dump: %s', default_ui_dump) 246*b7c941bbSAndroid Build Coastguard Worker root = et.fromstring(default_ui_dump) 247*b7c941bbSAndroid Build Coastguard Worker camera_flip_res = False 248*b7c941bbSAndroid Build Coastguard Worker for node in root.iter('node'): 249*b7c941bbSAndroid Build Coastguard Worker resource_id = node.get('resource-id') 250*b7c941bbSAndroid Build Coastguard Worker content_desc = node.get('content-desc') 251*b7c941bbSAndroid Build Coastguard Worker if re.search( 252*b7c941bbSAndroid Build Coastguard Worker flip_camera_pattern, content_desc, re.IGNORECASE 253*b7c941bbSAndroid Build Coastguard Worker ): 254*b7c941bbSAndroid Build Coastguard Worker logging.debug('Pattern matches') 255*b7c941bbSAndroid Build Coastguard Worker logging.debug('Resource id: %s', resource_id) 256*b7c941bbSAndroid Build Coastguard Worker logging.debug('Flip camera content-desc: %s', content_desc) 257*b7c941bbSAndroid Build Coastguard Worker camera_flip_res = True 258*b7c941bbSAndroid Build Coastguard Worker break 259*b7c941bbSAndroid Build Coastguard Worker if content_desc and resource_id: 260*b7c941bbSAndroid Build Coastguard Worker if facing == 'front' and camera_flip_res: 261*b7c941bbSAndroid Build Coastguard Worker if ('rear' in content_desc.lower() or 'rear' in resource_id.lower() 262*b7c941bbSAndroid Build Coastguard Worker or 'back' in content_desc.lower() or 'back' in resource_id.lower() 263*b7c941bbSAndroid Build Coastguard Worker ): 264*b7c941bbSAndroid Build Coastguard Worker logging.debug('Pattern found but camera is already switched.') 265*b7c941bbSAndroid Build Coastguard Worker else: 266*b7c941bbSAndroid Build Coastguard Worker dut.ui(desc=content_desc).click.wait() 267*b7c941bbSAndroid Build Coastguard Worker elif facing == 'rear' and camera_flip_res: 268*b7c941bbSAndroid Build Coastguard Worker if 'front' in content_desc.lower() or 'front' in resource_id.lower(): 269*b7c941bbSAndroid Build Coastguard Worker logging.debug('Pattern found but camera is already switched.') 270*b7c941bbSAndroid Build Coastguard Worker else: 271*b7c941bbSAndroid Build Coastguard Worker dut.ui(desc=content_desc).click.wait() 272*b7c941bbSAndroid Build Coastguard Worker else: 273*b7c941bbSAndroid Build Coastguard Worker raise ValueError(f'Unknown facing: {facing}') 274*b7c941bbSAndroid Build Coastguard Worker 275*b7c941bbSAndroid Build Coastguard Worker dut.take_screenshot( 276*b7c941bbSAndroid Build Coastguard Worker log_path, prefix=f'switched_to_{facing}_default_camera' 277*b7c941bbSAndroid Build Coastguard Worker ) 278*b7c941bbSAndroid Build Coastguard Worker 279*b7c941bbSAndroid Build Coastguard Worker if not camera_flip_res: 280*b7c941bbSAndroid Build Coastguard Worker raise AssertionError('Flip camera resource not found.') 281*b7c941bbSAndroid Build Coastguard Worker 282*b7c941bbSAndroid Build Coastguard Worker 283*b7c941bbSAndroid Build Coastguard Workerdef pull_img_files(device_id, input_path, output_path): 284*b7c941bbSAndroid Build Coastguard Worker """Pulls files from the input_path on the device to output_path. 285*b7c941bbSAndroid Build Coastguard Worker 286*b7c941bbSAndroid Build Coastguard Worker Args: 287*b7c941bbSAndroid Build Coastguard Worker device_id: serial id of device. 288*b7c941bbSAndroid Build Coastguard Worker input_path: File location on device. 289*b7c941bbSAndroid Build Coastguard Worker output_path: Location to save the file on the host. 290*b7c941bbSAndroid Build Coastguard Worker """ 291*b7c941bbSAndroid Build Coastguard Worker logging.debug('Pulling files from the device') 292*b7c941bbSAndroid Build Coastguard Worker pull_cmd = f'adb -s {device_id} pull {input_path} {output_path}' 293*b7c941bbSAndroid Build Coastguard Worker its_device_utils.run(pull_cmd) 294*b7c941bbSAndroid Build Coastguard Worker 295*b7c941bbSAndroid Build Coastguard Worker 296*b7c941bbSAndroid Build Coastguard Workerdef launch_and_take_capture(dut, pkg_name, camera_facing, log_path): 297*b7c941bbSAndroid Build Coastguard Worker """Launches the camera app and takes still capture. 298*b7c941bbSAndroid Build Coastguard Worker 299*b7c941bbSAndroid Build Coastguard Worker Args: 300*b7c941bbSAndroid Build Coastguard Worker dut: An Android controller device object. 301*b7c941bbSAndroid Build Coastguard Worker pkg_name: pkg_name of the default camera app to 302*b7c941bbSAndroid Build Coastguard Worker be used for captures. 303*b7c941bbSAndroid Build Coastguard Worker camera_facing: camera lens facing orientation 304*b7c941bbSAndroid Build Coastguard Worker log_path: str; log path to save screenshots. 305*b7c941bbSAndroid Build Coastguard Worker 306*b7c941bbSAndroid Build Coastguard Worker Returns: 307*b7c941bbSAndroid Build Coastguard Worker img_path_on_dut: Path of the captured image on the device 308*b7c941bbSAndroid Build Coastguard Worker """ 309*b7c941bbSAndroid Build Coastguard Worker device_id = dut.serial 310*b7c941bbSAndroid Build Coastguard Worker try: 311*b7c941bbSAndroid Build Coastguard Worker logging.debug('Launching app: %s', pkg_name) 312*b7c941bbSAndroid Build Coastguard Worker launch_cmd = f'monkey -p {pkg_name} 1' 313*b7c941bbSAndroid Build Coastguard Worker its_device_utils.run_adb_shell_command(device_id, launch_cmd) 314*b7c941bbSAndroid Build Coastguard Worker 315*b7c941bbSAndroid Build Coastguard Worker # Click OK/Done button on initial pop up windows 316*b7c941bbSAndroid Build Coastguard Worker if dut.ui(text=AGREE_BUTTON).wait.exists( 317*b7c941bbSAndroid Build Coastguard Worker timeout=WAIT_INTERVAL_FIVE_SECONDS): 318*b7c941bbSAndroid Build Coastguard Worker dut.ui(text=AGREE_BUTTON).click.wait() 319*b7c941bbSAndroid Build Coastguard Worker if dut.ui(text=AGREE_AND_CONTINUE_BUTTON).wait.exists( 320*b7c941bbSAndroid Build Coastguard Worker timeout=WAIT_INTERVAL_FIVE_SECONDS): 321*b7c941bbSAndroid Build Coastguard Worker dut.ui(text=AGREE_AND_CONTINUE_BUTTON).click.wait() 322*b7c941bbSAndroid Build Coastguard Worker if dut.ui(text=OK_BUTTON_TXT).wait.exists( 323*b7c941bbSAndroid Build Coastguard Worker timeout=WAIT_INTERVAL_FIVE_SECONDS): 324*b7c941bbSAndroid Build Coastguard Worker dut.ui(text=OK_BUTTON_TXT).click.wait() 325*b7c941bbSAndroid Build Coastguard Worker if dut.ui(text=DONE_BUTTON_TXT).wait.exists( 326*b7c941bbSAndroid Build Coastguard Worker timeout=WAIT_INTERVAL_FIVE_SECONDS): 327*b7c941bbSAndroid Build Coastguard Worker dut.ui(text=DONE_BUTTON_TXT).click.wait() 328*b7c941bbSAndroid Build Coastguard Worker if dut.ui(text=CANCEL_BUTTON_TXT).wait.exists( 329*b7c941bbSAndroid Build Coastguard Worker timeout=WAIT_INTERVAL_FIVE_SECONDS): 330*b7c941bbSAndroid Build Coastguard Worker dut.ui(text=CANCEL_BUTTON_TXT).click.wait() 331*b7c941bbSAndroid Build Coastguard Worker switch_default_camera(dut, camera_facing, log_path) 332*b7c941bbSAndroid Build Coastguard Worker time.sleep(ACTIVITY_WAIT_TIME_SECONDS) 333*b7c941bbSAndroid Build Coastguard Worker logging.debug('Taking photo') 334*b7c941bbSAndroid Build Coastguard Worker its_device_utils.run_adb_shell_command(device_id, TAKE_PHOTO_CMD) 335*b7c941bbSAndroid Build Coastguard Worker time.sleep(ACTIVITY_WAIT_TIME_SECONDS) 336*b7c941bbSAndroid Build Coastguard Worker img_path_on_dut = '' 337*b7c941bbSAndroid Build Coastguard Worker photo_storage_path = '' 338*b7c941bbSAndroid Build Coastguard Worker for path in CAMERA_FILES_PATHS: 339*b7c941bbSAndroid Build Coastguard Worker check_path_cmd = ( 340*b7c941bbSAndroid Build Coastguard Worker f'ls {path} && echo "Directory exists" || ' 341*b7c941bbSAndroid Build Coastguard Worker 'echo "Directory does not exist"' 342*b7c941bbSAndroid Build Coastguard Worker ) 343*b7c941bbSAndroid Build Coastguard Worker cmd_output = dut.adb.shell(check_path_cmd).decode('utf-8').strip() 344*b7c941bbSAndroid Build Coastguard Worker if _DIR_EXISTS_TXT in cmd_output: 345*b7c941bbSAndroid Build Coastguard Worker photo_storage_path = path 346*b7c941bbSAndroid Build Coastguard Worker break 347*b7c941bbSAndroid Build Coastguard Worker find_file_path = ( 348*b7c941bbSAndroid Build Coastguard Worker f'find {photo_storage_path} ! -empty -a ! -name \'.pending*\'' 349*b7c941bbSAndroid Build Coastguard Worker ' -a -type f -iname "*.jpg" -o -iname "*.jpeg"' 350*b7c941bbSAndroid Build Coastguard Worker ) 351*b7c941bbSAndroid Build Coastguard Worker img_path_on_dut = ( 352*b7c941bbSAndroid Build Coastguard Worker dut.adb.shell(find_file_path).decode('utf-8').strip().lower() 353*b7c941bbSAndroid Build Coastguard Worker ) 354*b7c941bbSAndroid Build Coastguard Worker logging.debug('Image path on DUT: %s', img_path_on_dut) 355*b7c941bbSAndroid Build Coastguard Worker if JPG_FORMAT_STR not in img_path_on_dut: 356*b7c941bbSAndroid Build Coastguard Worker raise AssertionError('Failed to find jpg files!') 357*b7c941bbSAndroid Build Coastguard Worker finally: 358*b7c941bbSAndroid Build Coastguard Worker force_stop_app(dut, pkg_name) 359*b7c941bbSAndroid Build Coastguard Worker return img_path_on_dut 360*b7c941bbSAndroid Build Coastguard Worker 361*b7c941bbSAndroid Build Coastguard Worker 362*b7c941bbSAndroid Build Coastguard Workerdef force_stop_app(dut, pkg_name): 363*b7c941bbSAndroid Build Coastguard Worker """Force stops an app with given pkg_name. 364*b7c941bbSAndroid Build Coastguard Worker 365*b7c941bbSAndroid Build Coastguard Worker Args: 366*b7c941bbSAndroid Build Coastguard Worker dut: An Android controller device object. 367*b7c941bbSAndroid Build Coastguard Worker pkg_name: pkg_name of the app to be stopped. 368*b7c941bbSAndroid Build Coastguard Worker """ 369*b7c941bbSAndroid Build Coastguard Worker logging.debug('Closing app: %s', pkg_name) 370*b7c941bbSAndroid Build Coastguard Worker force_stop_cmd = f'am force-stop {pkg_name}' 371*b7c941bbSAndroid Build Coastguard Worker dut.adb.shell(force_stop_cmd) 372*b7c941bbSAndroid Build Coastguard Worker 373*b7c941bbSAndroid Build Coastguard Worker 374*b7c941bbSAndroid Build Coastguard Workerdef default_camera_app_dut_setup(device_id, pkg_name): 375*b7c941bbSAndroid Build Coastguard Worker """Setup the device for testing default camera app. 376*b7c941bbSAndroid Build Coastguard Worker 377*b7c941bbSAndroid Build Coastguard Worker Args: 378*b7c941bbSAndroid Build Coastguard Worker device_id: serial id of device. 379*b7c941bbSAndroid Build Coastguard Worker pkg_name: pkg_name of the app. 380*b7c941bbSAndroid Build Coastguard Worker Returns: 381*b7c941bbSAndroid Build Coastguard Worker Runtime exception from called function or None. 382*b7c941bbSAndroid Build Coastguard Worker """ 383*b7c941bbSAndroid Build Coastguard Worker default_camera_app_setup(device_id, pkg_name) 384*b7c941bbSAndroid Build Coastguard Worker for path in CAMERA_FILES_PATHS: 385*b7c941bbSAndroid Build Coastguard Worker its_device_utils.run_adb_shell_command( 386*b7c941bbSAndroid Build Coastguard Worker device_id, f'{REMOVE_CAMERA_FILES_CMD}{path}/*') 387