intermittently occuring appium unknown server-side error - appium

I seem to get this issue intermittently. no error if i were to un plug and replug the devices and re-excuite the test, however this is only temperory fix till the time it shows up again.
i have 2 instances of appium server for 2 devices-
server1
Host-0.0.0.0
server port-5050
Bootstrap port- 4734
allow session overrides- ticked
server2
Host-0.0.0.0
server port-4723
Bootstrap port- 4724
allow session overrides- ticked
appium version- 1.15.1
List of devices attached-
52002dd24392b5a1 device
5200472dec01a4a9 device
error on appium server console-
W3C] Encountered internal error running command: Error: Cannot start the 'com.XYZ.XYZ' application. Visit https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/android/activity-startup.md for troubleshooting. Original error: Error executing adbExec. Original error: 'Command ''C:\\Users\\XYZ\\AppData\\Local\\Android\\Sdk\\platform-tools\\adb.exe' -P 5037 -s 5200472dec01a4a9 shell am start -W -n com.XYZ.XYZ/host.exp.exponent.LauncherActivity -S' timed out after 120000ms'. Try to increase the 120000ms adb execution timeout represented by 'adbExecTimeout' capability
[W3C] at ADB.startApp (C:\Program Files\Appium\resources\app\node_modules\appium\node_modules\appium-adb\lib\tools\apk-utils.js:153:11)
[HTTP] <-- POST /wd/hub/session 500 132866 ms - 1782
[HTTP]
[Instrumentation] .
[Instrumentation] Time: 123.085
[Instrumentation]
[Instrumentation] OK (1 test)
[Instrumentation] The process has exited with code 0
error on IDE-
org.openqa.selenium.SessionNotCreatedException: Unable to create a new remote session. Please check the server log for more details. Original error: An unknown server-side error occurred while processing the command. Original error: Cannot start the 'com.XYZ.XYZ' application. Visit https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/android/activity-startup.md for troubleshooting. Original error: Error executing adbExec. Original error: 'Command ''C:\\Users\\XYZ\\AppData\\Local\\Android\\Sdk\\platform-tools\\adb.exe' -P 5037 -s 5200472dec01a4a9 shell am start -W -n com.XYZ.XYZ/host.exp.exponent.LauncherActivity -S' timed out after 120000ms'. Try to increase the 120000ms adb execution timeout represented by 'adbExecTimeout' capability
Driver info: driver.version: AndroidDriver
remote stacktrace: UnknownError: An unknown server-side error occurred while processing the command. Original error: Cannot start the 'com.XYZ.XYZ' application. Visit https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/android/activity-startup.md for troubleshooting. Original error: Error executing adbExec. Original error: 'Command ''C:\\Users\\XYZ\\AppData\\Local\\Android\\Sdk\\platform-tools\\adb.exe' -P 5037 -s 5200472dec01a4a9 shell am start -W -n com.XYZ.XYZ/host.exp.exponent.LauncherActivity -S' timed out after 120000ms'. Try to increase the 120000ms adb execution timeout represented by 'adbExecTimeout' capability
desired capabilities for device1-
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("deviceName", "Tab");
capabilities.setCapability("platformVersion", "8.0.0");
capabilities.setCapability("platformName", "ANDROID");
capabilities.setCapability("udid", "5200472dec01a4a9");
capabilities.setCapability("noReset", "true");
capabilities.setCapability("appPackage", "com.xyz.xyz");
capabilities.setCapability("appActivity", "host.exp.exponent.LauncherActivity");
capabilities.setCapability("adbExecTimeout", 120000);
capabilities.setCapability("newCommandTimeout", 3000);
capabilities.setCapability("noSign", "true");
URL mobileURL = new URL("http://0.0.0.0:5050/wd/hub");
mobileParent = new AndroidDriver(mobileURL, capabilities);
mobile capabilities for device2-
capabilities.setCapability("deviceName", "Tab");
capabilities.setCapability("platformVersion", "8.0.0");
capabilities.setCapability("platformName", "ANDROID");
capabilities.setCapability("udid", "52002dd24392b5a1");
capabilities.setCapability("noReset", "true");
capabilities.setCapability("appPackage", "com.coachhire.kura");
capabilities.setCapability("appActivity", "host.exp.exponent.LauncherActivity");
capabilities.setCapability(AndroidMobileCapabilityType.AUTO_GRANT_PERMISSIONS, true);
capabilities.setCapability("adbExecTimeout", 120000);
capabilities.setCapability("newCommandTimeout", 3000);
capabilities.setCapability("noSign", "true");
URL mobileURL = new URL("http://0.0.0.0:4723/wd/hub");
mobileDriver = new AndroidDriver(mobileURL, capabilities);
any help greatly appretiated!

Looking at the details you have provided, there should not be any errors. It should work fine.
Unknown error usually accrues when Appium not able to figure out the list of capabilities provided for the run. I would suggest double check appPackage and appActivity details are right.
Also, try to run tests on one devices and see any errors. see the consistency of test run. if not errors than you can add one more device and see how it goes.
Add below in the capabilities,
"automationName": "UiAutomator2"
If you are running test on local machine use http://127.0.0.1:<Port>/wd/hubinstead 0.0.0.0
If you are running tests remotely make sure in above, local IP 127.0.0.0 replaced with the machine IP where Appium server is running.
As you are running Android devices only, I hope prerequisites softwares are installed as per Android test setup.
For mac device setup, follow steps here - https://www.swtestacademy.com/how-to-install-appium-on-mac/
Try below steps,
Check you can install the app manually by running below command.
adb -P <port> -s <device id > shell pm install -t -g <apk path>
If this could work, it indicates that there are no issues with the device. Else, you should reset the device and re test it.
Check Appium server is running with no issues. and access this link and see you get 200 response on appium server console.
http://<appium server ip>:4723/wd/hub/sessions
If you see an error there, it indicates that there is an issue with Appium. Un-install and install stable version of Appium and re test.
You should know that sometimes Node.js could not talk to 127.0.0.1:4723, So use 0.0.0.0:4723 instead and visa versa. Remember to change this in both code (DesiredCapabilities) and appium setting.
Try "udid" parametre is required for desired capabilities -
Example, caps.setCapability("udid", "ce0217124184c72505"); //DeviceId from "adb devices" command

Related

I need to use Appium 2 for CodeceptJS mobile tests

Because latest iOS is making problems with Appium 1.x, Appium team is no longer supporting it,
and Appium 2 is working ok - I need to use v2...
CodeceptJS 3.3.x is using Appium 1.x now and because of the '/wd/path' used by default in CodeceptJS Appium helper, I can't set it to use Appium 2.
When starting a test, CodeceptJS fails with:
Error: Failed to create session.
The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource
Please make sure Selenium Server is running and accessible
Error: Can't connect to WebDriver.
Error: Failed to create session.
The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource
Please make sure Selenium Server is running and accessible
And Appium 2 fails with:
[debug] [HTTP] No route found for /wd/hub/session
[HTTP] <-- POST /wd/hub/session 404 1 ms - 211
Is there a way to change the default '/wd/path' to just '/' ?
Or is there another helper or something for Appium 2?
Part of the codecept.config.js file:
Appium: {
host: process.env.CLOUD_HOST || 'localhost',
port: parseInt(process.env.CLOUD_PORT) || 4723,
app: (PLATFORM_NAME === 'iOS') ? APP_PATH_IOS || 'com.mobile.beta' : APP_PATH_ANDROID,
desiredCapabilities: {
platformName: process.env.PLATFORM_NAME || 'iOS',
platformVersion: process.env.PLATFORM_VERSION || '16.0',
automationName: process.env.AUTOMATION_NAME || 'xcuitest',
deviceName: process.env.DEVICE_NAME || 'iPhone Xs',
appPackage: process.env.APP_PACKAGE,
appActivity: process.env.APP_ACTIVITY,
xcodeOrgId: process.env.XCODE_ORG_ID,
// udid: process.env.UDID, // used only for real iOS device
},
},
=========================================
UPDATE:
I started Appium with --base-path /wd/hub (appium --base-path /wd/hub) but now another error appears:
Error: Can't connect to WebDriver.
Error: Failed to create session.
All non-standard capabilities should have a vendor prefix. The following capabilities did not have one: platformVersion,automationName,deviceName,appPackage,appActivity,xcodeOrgId,app
Please make sure Selenium Server is running and accessible
I tried updating the capabilities in codecept.conf.js with appium: prefix, but either I do it wrong or it doesn't work:
The Update:
Appium: {
host: process.env.CLOUD_HOST || 'localhost',
port: parseInt(process.env.CLOUD_PORT) || 4723,
app: (PLATFORM_NAME === 'iOS') ? APP_PATH_IOS || 'com.mobile.beta' : APP_PATH_ANDROID,
desiredCapabilities: {
'appium:platformName': process.env.PLATFORM_NAME || 'iOS',
'appium:platformVersion': process.env.PLATFORM_VERSION || '16.0',
'appium:automationName': process.env.AUTOMATION_NAME || 'xcuitest',
'appium:deviceName': process.env.DEVICE_NAME || 'iPhone Xs',
'appium:appPackage': process.env.APP_PACKAGE, // not needed for iOS
'appium:appActivity': process.env.APP_ACTIVITY, // not needed for iOS
'xcodeOrgId': process.env.XCODE_ORG_ID, // not needed for Android
udid: process.env.UDID, // used only for real iOS device testing
},
},
Error:
Error: Can't connect to WebDriver.
Error: Invalid or unsupported WebDriver capabilities found ("platformVersion", "deviceName", "appPackage", "appActivity", "xcodeOrgId", "app", "tunnelIdentifier"). Ensure to only use valid W3C WebDriver capabilities (see https://w3c.github.io/webdriver/#capabilities).If you run your tests on a remote vendor, like Sauce Labs or BrowserStack, make sure that you put them into vendor specific capabilities, e.g. "sauce:options" or "bstack:options". Please reach out to to your vendor support team if you have further questions.
Please make sure Selenium Server is running and accessible
Error#2:
Error: Can't connect to WebDriver.
Error: Invalid or unsupported WebDriver capabilities found ("deviceName", "app", "tunnelIdentifier"). Ensure to only use valid W3C WebDriver capabilities (see https://w3c.github.io/webdriver/#capabilities).If you run your tests on a remote vendor, like Sauce Labs or BrowserStack, make sure that you put them into vendor specific capabilities, e.g. "sauce:options" or "bstack:options". Please reach out to to your vendor support team if you have further questions.
Please make sure Selenium Server is running and accessible
If you are starting Appium server using the CLI you can use something like appium -p 4726 --base-path /wd/hub which will make it accept requests on the old endpoint
Update:
With Appium 2.0, the Appium server will enforce strict compability with the W3C WebDriver specification when it comes to Capabilities. They can be found here. Anything else that you provide for Appium should have the appium: prefix (like appium:platformVersion, appium:appPackage and so on)
This is most probably a problem with the CodeceptJS's Appium helper and needs to be updated to follow the Appium's more strict compability with the W3C WebDriver specification.
The update needs a support for prefixes like:
...
appium:platformName: 'iOS'
appium:automationName: 'xcuitest'
...
or
...
browserstack:platformName: 'Android'
saucelabs:automationName: 'uiautomator2'
...
for now, we can't configure variables with prefixes.
There's also no way I know of, to contact the CodeceptJS maintainers for making this update.

Appium inspector resets permissions for app on Android

When I start Appium inspector on Windows 10 for Android 10, my App shuts down and restarts with reset permissions, that is it asks me if the app needs access to camera, video, location, etc though these permissions were already given. It also removes the license file from my app. Any idea why this happens?
Following are my Desired capabilities:
{
"platformName": "Android",
"platformVersion": "10.0",
"deviceName": "R52N20ALVDN",
"udid": "R52N20ALVDN",
"appPackage": "Nap.CP.Android",
"automationName": "uiautomator2",
"appActivity": "crc6429e3927486beccbc.ActivityLicenseBrowse"
}
The uiautomatorviewer works fine. I tried changing the activity from:
"appActivity": "crc6429e3927486beccbc.ActivityLicenseBrowse"
To:
"appActivity": "crc6429e3927486beccbc.ActivityLogin"
And got the following error:
Error
An unknown server-side error occurred while processing the command. Original error: Cannot start the 'Nap.CP.Android' application. Visit https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/android/activity-startup.md for troubleshooting. Original error: Error executing adbExec. Original error: 'Command 'C:\Users\skhandekar\AppData\Local\Android\Sdk\platform-tools\adb.exe
-P 5037 -s R52N20ALVDN shell am start -W -n Nap.CP.Android/crc6429e3927486beccbc.ActivityLogin -S' exited with code 255'; Stderr: 'Security exception: Permission Denial: starting Intent { flg=0x10000000 cmp=Nap.CP.Android/crc6429e3927486beccbc.ActivityLogin } from null (pid=31202, uid=2000) not exported from uid 10286 java.lang.SecurityException: Permission Denial: starting Intent { flg=0x10000000 cmp=Nap.CP.Android/crc6429e3927486beccbc.ActivityLogin } from null (pid=31202, uid=2000) not exported from uid 10286 at com.android.server.wm.ActivityStackSupervisor.checkStartAnyActivityPermission(ActivityStackSupervisor.java:1447) at com.android.server.wm.ActivityStarter.startActivity(ActivityStarter.java:978) at com.android.server.wm.ActivityStarter.startActivity(ActivityStarter.java:732) at com.android.server.wm.ActivityStarter.startActivityMayWait(ActivityStarter.java:2072) at com.android.server.wm.ActivityStarter.execute(ActivityStarter.java:646) at com.android.server.wm.ActivityTaskManagerService.startActivityAndWait(ActivityTaskManagerService.java:1893) at com.android.server.am.ActivityManagerService.startActivityAndWait(ActivityManagerService.java:4269) at com.android.server.am.ActivityManagerShellCommand.runStartActivity(ActivityManagerShellCommand.java:513) at com.android.server.am.ActivityManagerShellCommand.onCommand(ActivityManagerShellCommand.java:172) at android.os.ShellCommand.exec(ShellCommand.java:104) at com.android.server.am.ActivityManagerService.onShellCommand(ActivityManagerService.java:12424) at android.os.Binder.shellCommand(Binder.java:916) at android.os.Binder.onTransact(Binder.java:790) at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:5475) at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3426) at android.os.Binder.execTransactInternal(Binder.java:1056) at android.os.Binder.execTransact(Binder.java:1029)'; Code: '255'
And it again resets the permissions as before in addition to giving above error.
Inspector was working fine when I tried it at my workplace but failure showed up when I tried the same from home, though other than the network ie wi-fi at home, nothing has changed. The Desired capabilities were also the same.
You can use this in your capabilities to allow permissions at app start :
autoGrantPermissions:true
noReset:False
It will allow all permission to the app and will not reset the app at startup.

Composer rest server can't connect to ca.org1.example.com

I followed this tutorial to setup myorg/composer-rest-server and everything was working fine till I import card but when I make a GET request to /api/system/ping it returns 500 Error:
{"error":{"statusCode":500,"name":"Error","message":"Error trying login and get user Context. Error: Error trying to enroll user or load channel configuration. Error: Calling enrollment endpoint failed with error [Error: connect ECONNREFUSED 127.0.0.1:7054]","stack":"Error: Error trying login and get user Context. Error: Error trying to enroll user or load channel configuration. Error: Calling enrollment endpoint failed with error [Error: connect ECONNREFUSED 127.0.0.1:7054]\n at client.getUserContext.then.then.catch (/home/composer/.npm-global/lib/node_modules/composer-rest-server/node_modules/composer-connector-hlfv1/lib/hlfconnection.js:393:34)\n at <anonymous>\n at process._tickDomainCallback (internal/process/next_tick.js:228:7)"}}
So I checked the logs for rest container, it can't seem to find 127.0.0.1:7054. Here is the error log.
Unhandled error for request GET /api/system/ping: Error: Error trying login and get user Context. Error: Error trying to enroll user or load channel configuration. Error: Calling enrollment endpoint failed with error [Error: connect ECONNREFUSED 127.0.0.1:7054]
at client.getUserContext.then.then.catch (/home/composer/.npm-global/lib/node_modules/composer-rest-server/node_modules/composer-connector-hlfv1/lib/hlfconnection.js:393:34)
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:228:7)
So I checked the logs for the container ca.org1.example.com, and it is listening to port 7054
2018/04/01 09:57:25 [DEBUG] CA initialization successful
2018/04/01 09:57:25 [INFO] Home directory for default CA: /etc/hyperledger/fabric-ca-server
2018/04/01 09:57:25 [DEBUG] 1 CA instance(s) running on server
2018/04/01 09:57:25 [INFO] Listening on http://0.0.0.0:7054
I think i need to change 127.0.0.1 to 0.0.0.0 but not sure how to do it the right way. Could also be a firewall issue?
Here's my .composer/cards/restadmin#myserver/connection.json
{"name":"hlfv1","x-type":"hlfv1","x-commitTimeout":300,"version":"1.0.0","client":{"organization":"Org1","connection":{"timeout":{"peer":{"endorser":"300","eventHub":"300","eventReg":"300"},"orderer":"300"}}},"channels":{"composerchannel":{"orderers":["orderer.example.com"],"peers":{"peer0.org1.example.com":{}}}},"organizations":{"Org1":{"mspid":"Org1MSP","peers":["peer0.org1.example.com"],"certificateAuthorities":["ca.org1.example.com"]}},"orderers":{"orderer.example.com":{"url":"grpc://orderer.example.com:7050"}},"peers":{"peer0.org1.example.com":{"url":"grpc://peer0.org1.example.com:7051","eventUrl":"grpc://peer0.org1.example.com:7053"}},"certificateAuthorities":{"ca.org1.example.com":{"url":"http://ca.org1.example.com:7054","caName":"ca.org1.example.com"}}}
I'm using AWS EC2
OS: Ubuntu 16.04.3 LTS,
Docker: 17.12.0-ce,
Composer: v0.19.0
Fabric: v1.1
Which card have you imported? If it is the restadmin card, I think you may have imported a Card containing an expired One-Time secret. After the rest admin card was used to start the REST server (in the container) the secret was replaced with certificates - so if you export the restadmin card again with a different name composer card export -c restadmin#trade-network -f restadmin-cert.card you will see that it is a larger file because of the certificates. You should be able to import and use this new .card file.
(If you were using a different card e.g. jdoe - did you run the sed command for this card to correct the addresses?)

A new session could not be created exception occurs

i will try to open chrome browser & automate the facebook (try to open any site so that i can check my appium configuration is ok or not)
After execution of following code got exception as,
#Test
public void test1() throws MalformedURLException
{
DesiredCapabilities capabilities=DesiredCapabilities.android();
capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, BrowserType.CHROME);
capabilities.setCapability(MobileCapabilityType.PLATFORM, Platform.ANDROID);
capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "my phone");
capabilities.setCapability(MobileCapabilityType.VERSION, "6.0.1");
URL url=new URL("http://127.0.0.1:4727/wd/hub");
//java.net.URL url=new URL("http://127.0.0.1:4727/wd/hub");
//URL url=new URL("http://127.0.0.1:4727/wd/hub");
WebDriver driver=new AndroidDriver(url, capabilities);
//WebDriver driver = new RemoteWebDriver(new URL( http://****:4723/wd/hub), capabilities);
// WebDriver driver = new RemoteWebDriver(new URL("http://****:4723/wd/hub", capabilities);
//driver.get("http://www.yahoo.com");
driver.get("https://www.facebook.com/");
System.out.println(driver.getTitle());
exception as,
#BeforeTest setUp org.openqa.selenium.SessionNotCreatedException: A new session could not be created. (Original error: An unknown server-side error occurred while processing the command. (Original error: unknown error: Device 4e89be7c is not online (Driver info: chromedriver=2.18.343845 (73dd713ba7fbfb73cbb514e62641d8c96a94682a),platform=Windows NT 10.0 x86_64))) (WARNING: The server did not provide any stacktrace information) Command duration or timeout: 9.05 seconds Build info: version: '2.53.0', revision: '35ae25b', time: '2016-03-15 16:57:40'
please help
I see in the stacktrace that "Device 4e89be7c is not online"
I'm pretty sure that you avd is open and running so please time in terminal "adb devices"
if your avd is shown and its status is "offline" then type:
"adb kill-server"
IMPORTANT: Close any instances of adb in the Task Manager
now type in terminal "adb start-server" and "adb devices" -> if the status of the adb has changed, you are good to go.

Configuration of appium server

Checking if an update is available
Update not available
Launching Appium server with command: C:\Program Files\Appium\node.exe lib\server\main.js --address 127.0.0.1 --port 4723 --app --app-pkg --platform-name Android --platform-version 16 --automation-name Appium --log-no-color
usage: main.js [-h] [-v] [--shell]
[--localizable-strings-dir LOCALIZABLESTRINGSDIR] [--app APP]
[--ipa IPA] [-U UDID] [-a ADDRESS] [-p PORT]
[-ca CALLBACKADDRESS] [-cp CALLBACKPORT] [-bp BOOTSTRAPPORT]
[-k] [-r BACKENDRETRIES] [--session-override] [--full-reset]
[--no-reset] [-l] [-lt LAUNCHTIMEOUT] [-g LOG]
[--log-level {info,info:debug,info:info,info:warn,info:error,warn,warn:debug,warn:info,warn:warn,warn:error,error,error:debug,error:info,error:warn,error:error,debug,debug:debug,debug:info,debug:warn,debug:error}]
[--log-timestamp] [--local-timezone] [--log-no-colors]
[-G WEBHOOK] [--native-instruments-lib]
[--app-pkg ANDROIDPACKAGE] [--app-activity ANDROIDACTIVITY]
[--app-wait-package ANDROIDWAITPACKAGE]
[--app-wait-activity ANDROIDWAITACTIVITY]
[--android-coverage ANDROIDCOVERAGE] [--avd AVD]
[--avd-args AVDARGS]
[--device-ready-timeout ANDROIDDEVICEREADYTIMEOUT] [--safari]
[--device-name DEVICENAME] [--platform-name PLATFORMNAME]
[--platform-version PLATFORMVERSION]
[--automation-name AUTOMATIONNAME] [--browser-name BROWSERNAME]
[--default-device] [--force-iphone] [--force-ipad]
[--language LANGUAGE] [--locale LOCALE]
[--calendar-format CALENDARFORMAT] [--orientation ORIENTATION]
[--tracetemplate AUTOMATIONTRACETEMPLATEPATH]
[--instruments INSTRUMENTSPATH] [--show-sim-log]
[--show-ios-log] [--nodeconfig NODECONFIG] [-ra ROBOTADDRESS]
[-rp ROBOTPORT] [--selendroid-port SELENDROIDPORT]
[--chromedriver-port CHROMEDRIVERPORT]
[--chromedriver-executable CHROMEDRIVEREXECUTABLE]
[--use-keystore] [--keystore-path KEYSTOREPATH]
[--keystore-password KEYSTOREPASSWORD] [--key-alias KEYALIAS]
[--key-password KEYPASSWORD] [--show-config] [--no-perms-check]
[--command-timeout DEFAULTCOMMANDTIMEOUT] [--keep-keychains]
[--strict-caps] [--isolate-sim-device] [--tmp TMPDIR]
[--trace-dir TRACEDIR] [--intent-action INTENTACTION]
[--intent-category INTENTCATEGORY] [--intent-flags INTENTFLAGS]
[--intent-args OPTIONALINTENTARGUMENTS]
[--dont-stop-app-on-reset] [--debug-log-spacing]
[--suppress-adb-kill-server] [--async-trace]
main.js: error: argument "--app": Expected one argument. null
Appium server process ended
suggest solution
Your Appium application's Android settings have App Path and Package ticked enabled, but their values are empty.
You need to enable only one of these strategies:
App Path takes your apk file. This is seen in the log as --app APP.apk
Package takes your application's package name (must be pre-installed to the device). This is seen in the log as --app-pkg ANDROIDPACKAGE
Launch Activity takes your application's launch activity (required when launching with package name). This is seen in the log as --app-activity ANDROIDACTIVITY

Resources