1# Lint as: python2, python3 2# Copyright 2021 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6import dbus 7import logging 8import random 9 10from autotest_lib.client.common_lib import error 11from autotest_lib.client.cros.cellular import hermes_constants 12from autotest_lib.client.cros.networking import hermes_proxy 13from autotest_lib.client.cros.networking import mm1_proxy 14 15# Helper functions 16def connect_to_hermes(): 17 """ 18 Attempts to connect to a DBus object. 19 20 @raise: error.TestFail if connection fails. 21 22 """ 23 hermes_manager = 'None' 24 try: 25 hermes_manager = \ 26 hermes_proxy.HermesManagerProxy().get_hermes_manager() 27 except dbus.DBusException as e: 28 logging.error('get_hermes_manager error:%s', e) 29 raise error.TestFail('Connect to Hermes failed') 30 if hermes_manager is 'None': 31 raise error.TestFail('Could not get connection to Hermes') 32 return hermes_manager 33 34def request_installed_profiles(euicc_path, hermes_manager): 35 """ 36 Check euicc at given path 37 38 @param euicc_path: path of the sim given 39 @return a dict of profiles objects. Returns None if no profile is found 40 @raise: error.TestFail if no euicc matching given path 41 42 """ 43 euicc = hermes_manager.get_euicc(euicc_path) 44 if not euicc: 45 raise error.TestFail('No euicc found at:', euicc_path) 46 47 euicc.request_installed_profiles() 48 installed_profiles = euicc.get_installed_profiles() 49 if not installed_profiles: 50 logging.info('No installed profiles on euicc:%s', euicc_path) 51 return euicc, installed_profiles 52 53def install_profile(euicc_path, hermes_manager, is_prod_ci): 54 """ 55 Install a profile on the euicc at euicc_path. 56 57 @param euicc_path: esim path based on testci/prodci 58 @param hermes_manager: hermes manager object 59 @param is_prod_ci: true if it is prodci test and false for testci 60 @return iccid: iccid of the installed profile or None 61 62 """ 63 if not is_prod_ci: 64 is_smds_test = random.choice([True, False]) 65 logging.info('is_smds_test %s', is_smds_test) 66 if is_smds_test: 67 installed_iccid = install_pending_profile_test( 68 euicc_path, hermes_manager) 69 else: 70 installed_iccid = install_profile_test( 71 euicc_path, hermes_manager) 72 else: 73 installed_iccid = get_profile( 74 euicc_path, hermes_manager, False) 75 return installed_iccid 76 77def uninstall_all_profiles(euicc_path, hermes_manager): 78 """ 79 Uninstalls all installed test profiles 80 81 @param euicc_path: esim path based on testci/prodci 82 @param hermes_manager: hermes manager object 83 @raise error.TestFail if any dbus exception happens 84 85 """ 86 try: 87 euicc, installed_profiles = \ 88 request_installed_profiles(euicc_path, hermes_manager) 89 90 profiles_count = len(installed_profiles) 91 if profiles_count is 0: return 92 93 # also skips iccid 89010000001234567882 - R&S as its TESTING profile 94 for profile in installed_profiles.keys(): 95 if ((hermes_constants.ProfileStateToString( 96 installed_profiles[profile].state) == 'INACTIVE') and 97 (hermes_constants.ProfileClassToString( 98 installed_profiles[profile].profileclass) != 99 'TESTING')): 100 101 logging.info('Uninstalling profile - iccid:%s', 102 installed_profiles[profile].iccid) 103 euicc.uninstall_profile(profile) 104 logging.info('Uninstall done') 105 except dbus.DBusException as e: 106 logging.error('Failed to uninstall a profile error:%s', e) 107 raise error.TestFail('Failed to uninstall profile') 108 109 110def initialize_test(is_prod_ci_test): 111 """ 112 Initialize euicc paths, connect to hermes, set test mode 113 114 @param is_prod_ci_test: true if it is prodci test and false for testci 115 116 """ 117 logging.info('===initialize_test started===') 118 mm_proxy = mm1_proxy.ModemManager1Proxy.get_proxy() 119 120 logging.info('Connect to Hermes') 121 hermes_manager = connect_to_hermes() 122 123 # Always euicc/0 is prod one and euicc/1 is for test esim profiles 124 # we prefer to hardcode euicc/0, since it acts as a check that Hermes 125 # is able to initialize without any error. If hermes encounters an 126 # error, hermes will start exposing objects such as 127 # self.prod_euicc_path = "/org/chromium/Hermes/euicc/22" 128 # self.test_euicc_path = "/org/chromium/Hermes/euicc/23" 129 130 euicc = None 131 euicc_path = None 132 for path in hermes_manager.get_available_euiccs(): 133 logging.info("Found euicc at %s", path) 134 is_prod_euicc = not hermes_manager.get_euicc(path).is_test_euicc() 135 if is_prod_euicc == is_prod_ci_test: 136 euicc_path = path 137 euicc = hermes_manager.get_euicc(euicc_path) 138 break 139 140 if not euicc: 141 raise error.TestFail("Initialize test failed, " + 142 "prod" if is_prod_ci_test else "test" + 143 " euicc not found") 144 145 euicc.use_test_certs(not is_prod_ci_test) 146 147 if not is_prod_ci_test: 148 uninstall_all_profiles(euicc_path, hermes_manager) 149 logging.info('===initialize_test done===\n') 150 return mm_proxy, hermes_manager, euicc_path 151 152def validate_profile_state(euicc_path, hermes_manager, iccid, is_enable): 153 """ 154 Validates given profile(iccid) state 155 156 Check state of changed profile 157 158 @param euicc_path: esim path based on testci/prodci 159 @param hermes_manager: hermes manager object 160 @param iccid: iccid of the profile enabled/disabled 161 @param is_enable: true to enable profile and false to disable 162 @raise error.TestFail if any dbus exception happens 163 164 """ 165 try: 166 target_state = 'ACTIVE' if is_enable else 'INACTIVE' 167 _, installed_profiles = \ 168 request_installed_profiles(euicc_path, hermes_manager) 169 170 # check profile with given iccid has target_state and rest are opposite 171 for profile in installed_profiles.values(): 172 # check for inactive profiles when enabled except enabled one 173 if iccid == profile.iccid: 174 if not (hermes_constants.ProfileStateToString(profile.state) == 175 target_state): 176 logging.error('profile:%s not in %s state', 177 profile.iccid, target_state) 178 raise error.TestFail('validate_profile_state failed') 179 180 logging.info('validate_profile_state succeeded') 181 except dbus.DBusException as e: 182 logging.error('Profile %s error:%s', target_state, e) 183 raise error.TestFail('validate_profile_state failed') 184 185def set_profile_state( 186 is_active, euicc_path=None, hermes_manager=None, iccid=None, profile=None): 187 """ 188 Enable or Disable already enabled/disabled profile 189 190 @param is_active: True to enable, False to disable profile 191 @param euicc_path: esim path based on testci/prodci 192 @param hermes_manager: hermes manager object 193 @param iccid: profile iccid to enable 194 @param profile: profile object to enable/disable 195 @raise error.TestFail if expected error not resulted 196 197 """ 198 logging.info('set_profile_state start') 199 if euicc_path and iccid: 200 euicc = hermes_manager.get_euicc(euicc_path) 201 profile = euicc.get_profile_from_iccid(iccid) 202 203 if is_active: 204 profile.enable() 205 else: 206 profile.disable() 207 logging.info('set_profile_state done') 208 209def get_profile_state(euicc_path, hermes_manager, iccid): 210 """ 211 get profile state 212 213 @param euicc_path: esim path based on testci/prodci 214 @param hermes_manager: hermes manager object 215 @param iccid: profile iccid to find state 216 @return True if profile state is Active and False if state is Inactive 217 218 """ 219 if euicc_path and iccid: 220 euicc = hermes_manager.get_euicc(euicc_path) 221 profile = euicc.get_profile_from_iccid(iccid) 222 223 return True if (hermes_constants.ProfileStateToString(profile.state) == 224 'ACTIVE') else False 225 226def get_profile(euicc_path, hermes_manager, is_active): 227 """ 228 Returns a active/inactive profile on given euicc 229 230 This is to get already enabled or disabled profile. if not found enabled 231 profile, enable an inactive profile and if not found disable profile 232 disable an active profile 233 234 @param euicc_path: esim path based on testci/prodci 235 @param hermes_manager: hermes manager object 236 @param is_active: True to get active profile, False to get inactive profile 237 @return iccid: iccid of the active/inactive profile as requested 238 @raise error.TestFail if any dbus exception happens 239 240 """ 241 try: 242 _, installed_profiles = \ 243 request_installed_profiles(euicc_path, hermes_manager) 244 245 profile_found = False 246 iccid = None 247 profile_needed = 'Enabled' if is_active else 'Disabled' 248 # Find active/inactive profile 249 target_state = 'ACTIVE' if is_active else 'INACTIVE' 250 251 for profile in installed_profiles.values(): 252 # skipping TESTING profiles to prevent install/uninstall operations 253 if (hermes_constants.ProfileClassToString( 254 profile.profileclass) == 'TESTING'): 255 continue 256 257 if not (hermes_constants.ProfileStateToString(profile.state) == 258 target_state): 259 set_profile_state(is_active, profile=profile) 260 261 profile_found = True 262 return profile.iccid 263 264 if not profile_found: 265 logging.error('No installed profile which is %s', profile_needed) 266 return iccid 267 except dbus.DBusException as e: 268 raise error.TestFail('get_profile failed :', repr(e)) 269 270def get_iccid_of_disabled_profile(euicc_path, hermes_manager, is_prod_ci): 271 """ 272 Get profile with disabled status and return its iccid 273 274 For test esim install new profile and return iccid of that profile 275 For prod esim having two profiles is prerequisite, return disabled profile 276 277 @param euicc_path: esim path based on testci/prodci 278 @param hermes_manager: hermes manager object 279 @param is_prod_ci: true if it is prodci test and false for testci 280 @return iccid: iccid of the installed profile or None 281 282 """ 283 if not is_prod_ci: 284 installed_iccid = install_profile_test(euicc_path, hermes_manager) 285 else: 286 # get disabled profile on a prod esim, if not exist then do disable one 287 _, installed_profiles = \ 288 request_installed_profiles(euicc_path, hermes_manager) 289 for profile in installed_profiles.values(): 290 if (hermes_constants.ProfileClassToString(profile.profileclass) == 291 'TESTING'): 292 continue 293 294 if (hermes_constants.ProfileStateToString(profile.state) == 295 'INACTIVE'): 296 return profile.iccid 297 298 installed_iccid = get_profile(euicc_path, hermes_manager, False) 299 300 return installed_iccid 301 302# Test functions 303def enable_or_disable_profile_test( 304 euicc_path, hermes_manager, iccid, is_enable): 305 """ 306 Validates enable/disable profile api DBus call 307 308 @param euicc_path: esim path based on testci/prodci 309 @param hermes_manager: hermes manager object 310 @param iccid: iccid of the profile to be enabled/disabled 311 @param is_enable: true to enable profile and false to disable 312 @raise error.TestFail if any dbus exception happens 313 314 """ 315 try: 316 logging.info('===enable_or_disable_profile_test started===') 317 profile_action = 'Enable' if is_enable else 'Disable' 318 logging.info('%s :', profile_action) 319 euicc, installed_profiles = \ 320 request_installed_profiles(euicc_path, hermes_manager) 321 # Profile objects maybe stale if IsActive is false 322 # Switch to the euicc we are interested in before 323 # performing an op. 324 325 profile_found = False 326 target_state = 'ACTIVE' if is_enable else 'INACTIVE' 327 # Find active or inactive profile to enable/disable 328 for profile in installed_profiles.values(): 329 if not (hermes_constants.ProfileStateToString(profile.state) == 330 target_state): 331 if iccid is None or iccid == profile.iccid: 332 logging.info('Profile to %s:%s', profile_action, 333 profile.iccid) 334 profile_found = True 335 set_profile_state(is_enable, profile=profile) 336 logging.info('===enable_or_disable_profile_test ' 337 'succeeded===\n') 338 break 339 if not profile_found: 340 raise error.TestFail('enable_or_disable_profile_test failed -' 341 'No profile to ' + profile_action) 342 # Check profile state 343 validate_profile_state(euicc_path, hermes_manager, iccid, is_enable) 344 except dbus.DBusException as e: 345 logging.error('Profile %s error:%s', profile_action, e) 346 raise error.TestFail('enable_or_disable_profile_test Failed') 347 348def install_profile_test(euicc_path, hermes_manager): 349 """ 350 Validates InstallProfileFromActivationCode api on test euicc 351 352 use SMDS calls to find iccid, activation code from pending profiles 353 and install those profiles, this requires profiles generated based 354 on EID of test esims in lab devices 355 356 @param euicc_path: esim path based on testci/prodci 357 @param hermes_manager: hermes manager object 358 @return iccid: iccid of the installed profile or None 359 @raise error.TestFail if any dbus exception happens 360 361 """ 362 try: 363 # get all pending profiles which are generated on DUT EID 364 # Read all profiles activation code from pending profile dict 365 # Install a profile from activation code, have iccid and 366 # Check the presence of this profile after installation 367 368 logging.info('===install_profile_test started===') 369 activation_code = None 370 confirmation_code = "" 371 iccid = None 372 euicc = None 373 374 euicc, installed_profiles = \ 375 request_installed_profiles(euicc_path, hermes_manager) 376 377 euicc.request_pending_profiles(dbus.String('prod.smds.rsp.goog')) 378 logging.info('euicc chosen:%s', euicc_path) 379 profiles_pending = euicc.get_pending_profiles() 380 if not profiles_pending: 381 logging.error('install_profile_test: pending profile not found') 382 raise error.TestFail('No pending profile found on euicc:', 383 euicc_path) 384 385 profile_path_to_install, profile_to_install = \ 386 list(profiles_pending.items())[0] 387 logging.debug('First pending profile:%s', profile_path_to_install) 388 389 iccid = profile_to_install.iccid 390 activation_code = profile_to_install.activationcode 391 392 logging.info('Installing iccid:%s act_code:%s conf_code:%s', 393 iccid, activation_code, confirmation_code) 394 # Install 395 euicc.install_profile_from_activation_code( 396 activation_code, confirmation_code) 397 398 # Check if iccid found in installed profiles, installation success 399 installed_profiles = euicc.get_installed_profiles() 400 401 if ((installed_profiles[profile_path_to_install] is None) or 402 (installed_profiles[profile_path_to_install].iccid != 403 profile_to_install.iccid)): 404 logging.error('install_profile_test failed. Test Failed.') 405 raise error.TestFail('No installed profile found on euicc:', 406 euicc_path) 407 408 logging.info('===install_profile_test succeeded===\n') 409 return iccid 410 except dbus.DBusException as e: 411 logging.error('Failed to install a pending profile') 412 raise error.TestFail('install_profile_test failed with ', 413 repr(e)) 414 415def install_pending_profile_test(euicc_path, hermes_manager): 416 """ 417 Validates InstallPendingProfile api on test euicc 418 Find a profile from list of esim pending profiles which is not 419 installed yet and install that profile 420 421 Required to create pending profiles for each EID(euicc sim) in lab dut. 422 create profiles from stork giving lab devices EID. puts profiles in 423 pending state for that euicc when RequestPendingProfiles called. 424 425 @param euicc_path: esim path based on testci/prodci 426 @param hermes_manager: hermes manager object 427 @return iccid: iccid of the installed profile or None 428 @raise error.TestFail if any dbus exception happens 429 430 """ 431 logging.info('===install_pending_profile_test started===') 432 profile_to_install = None 433 434 euicc, installed_profiles = \ 435 request_installed_profiles(euicc_path, hermes_manager) 436 437 euicc.request_pending_profiles(dbus.String('prod.smds.rsp.goog')) 438 profiles_pending = euicc.get_pending_profiles() 439 if not profiles_pending: 440 logging.error( 441 'install_pending_profile_test: pending profile not found') 442 raise error.TestFail('No pending profile found on euicc:', 443 euicc_path) 444 445 profile_path_to_install, profile_to_install = list(profiles_pending.items())[0] 446 iccid = profile_to_install.iccid 447 activation_code = profile_to_install.activationcode 448 449 logging.info('Installing profile:%s iccid:%s act_code:%s', 450 profile_path_to_install, iccid, activation_code) 451 452 try: 453 # Install 454 profile = euicc.install_pending_profile( 455 profile_path_to_install, "") 456 logging.info('Installed pending profile is %s', profile) 457 if not profile: 458 logging.error('No profile object returned after install') 459 return None 460 except dbus.DBusException as e: 461 logging.error('Failed to install pending profile:%s', e) 462 raise error.TestFail('Failed to install pending profile', 463 repr(e)) 464 465 # Find above installed profile, if not exists raise test failure 466 installed_profiles = euicc.get_installed_profiles() 467 if ((installed_profiles[profile_path_to_install] is None) or 468 (installed_profiles[profile_path_to_install].iccid != 469 profile_to_install.iccid)): 470 raise error.TestFail('Install pending profile failed :', 471 profile_path_to_install) 472 473 logging.info('===install_pending_profile_test succeeded===\n') 474 return iccid 475 476def uninstall_profile_test(euicc_path, hermes_manager, iccid): 477 """ 478 Validates UninstallProfile api by uninstalling any randomly 479 selected installed profile 480 481 @param euicc_path: esim path based on testci/prodci 482 @param hermes_manager: hermes manager object 483 @param iccid: iccid of the profile to be uninstalled 484 @raise error.TestFail if any dbus exception happens 485 486 """ 487 logging.info('===uninstall_profile_test started===') 488 # Getinstalled profiles list and randomly uninstall a profile 489 try: 490 euicc, installed_profiles = \ 491 request_installed_profiles(euicc_path, hermes_manager) 492 493 profile_to_uninstall = euicc.get_profile_from_iccid(iccid) 494 if not profile_to_uninstall: 495 raise error.TestFail('No valid profile found at:', euicc_path) 496 497 profile_path = profile_to_uninstall.path 498 uninstalled_profile = None 499 500 # Hermes does not support uninstalling test profiles yet. 501 if hermes_constants.ProfileClassToString( 502 profile_to_uninstall.profileclass) != 'TESTING': 503 logging.info('profile to uninstall is:%s', profile_path) 504 euicc.uninstall_profile(profile_path) 505 uninstalled_profile = profile_path 506 logging.info('uninstall_profile_test succeeded') 507 508 if not uninstalled_profile: 509 raise error.TestFail( 510 'uninstall_profile_test failed - No uninstallable profile') 511 512 # Try to find the uninstalled profile, if exists raise test failure 513 profiles_installed = euicc.get_installed_profiles() 514 for profile in profiles_installed.keys(): 515 if uninstalled_profile in profile: 516 raise error.TestFail('uninstall_profile_test profile Failed') 517 logging.info('===uninstall_profile_test succeeded===\n') 518 except dbus.DBusException as e: 519 raise error.TestFail('Failed to uninstall profile', e) 520