├── .trivyignore
├── .base_version
├── js
├── tests
│ ├── unit
│ │ ├── .gitignore
│ │ ├── .babelrc.json
│ │ ├── package.json
│ │ ├── eslint.config.mjs
│ │ └── run-tests.sh
│ ├── e2e
│ │ ├── .prettierignore
│ │ ├── tests
│ │ │ ├── visual.spec.js-snapshots
│ │ │ │ ├── long-press-home.png
│ │ │ │ ├── status-bar-swipe.png
│ │ │ │ └── app-drawer-settings.png
│ │ │ ├── join.spec.js
│ │ │ ├── shared.js
│ │ │ └── visual.spec.js
│ │ ├── .gitignore
│ │ ├── run-tests.sh
│ │ ├── eslint.config.mjs
│ │ ├── fixtures
│ │ │ ├── anbox-test.js
│ │ │ ├── constants.cjs
│ │ │ └── coverage.js
│ │ ├── package.json
│ │ ├── global-teardown.js
│ │ ├── index.html
│ │ ├── playwright.config.js
│ │ └── global-setup.js
│ └── combined-coverage-report.sh
├── run-tests.sh
└── README.md
├── .gitignore
├── CODEOWNERS
├── android
└── anbox_streaming_sdk
│ ├── consumer-rules.pro
│ ├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── canonical
│ │ └── anbox
│ │ └── streaming_sdk
│ │ ├── IMEJSInterface.java
│ │ └── IMEService.java
│ ├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── .gitignore
│ ├── proguard-rules.pro
│ ├── README.md
│ ├── gradle.properties
│ ├── build.gradle
│ └── gradlew.bat
├── examples
├── android
│ ├── out_of_band_v2
│ │ ├── app
│ │ │ ├── .gitignore
│ │ │ ├── src
│ │ │ │ └── main
│ │ │ │ │ ├── res
│ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-anydpi-v26
│ │ │ │ │ │ ├── ic_launcher.xml
│ │ │ │ │ │ └── ic_launcher_round.xml
│ │ │ │ │ ├── values
│ │ │ │ │ │ ├── strings.xml
│ │ │ │ │ │ ├── colors.xml
│ │ │ │ │ │ └── themes.xml
│ │ │ │ │ ├── values-night
│ │ │ │ │ │ └── themes.xml
│ │ │ │ │ ├── drawable-v24
│ │ │ │ │ │ └── ic_launcher_foreground.xml
│ │ │ │ │ └── layout
│ │ │ │ │ │ └── activity_main.xml
│ │ │ │ │ ├── AndroidManifest.xml
│ │ │ │ │ └── java
│ │ │ │ │ └── com
│ │ │ │ │ └── canonical
│ │ │ │ │ └── anboxcloud
│ │ │ │ │ └── outofbandappv2
│ │ │ │ │ └── DataReadTask.java
│ │ │ ├── proguard-rules.pro
│ │ │ └── build.gradle
│ │ ├── settings.gradle
│ │ ├── gradle
│ │ │ └── wrapper
│ │ │ │ ├── gradle-wrapper.jar
│ │ │ │ └── gradle-wrapper.properties
│ │ ├── build.gradle
│ │ ├── README.md
│ │ ├── gradle.properties
│ │ ├── gradlew.bat
│ │ └── gradlew
│ ├── webview_streaming
│ │ ├── app
│ │ │ ├── .gitignore
│ │ │ ├── src
│ │ │ │ └── main
│ │ │ │ │ ├── assets
│ │ │ │ │ ├── css
│ │ │ │ │ │ └── style.css
│ │ │ │ │ └── index.html
│ │ │ │ │ ├── res
│ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── values
│ │ │ │ │ │ ├── colors.xml
│ │ │ │ │ │ ├── strings.xml
│ │ │ │ │ │ └── styles.xml
│ │ │ │ │ ├── mipmap-anydpi-v26
│ │ │ │ │ │ ├── ic_launcher.xml
│ │ │ │ │ │ └── ic_launcher_round.xml
│ │ │ │ │ ├── layout
│ │ │ │ │ │ ├── activity_stream.xml
│ │ │ │ │ │ └── activity_main.xml
│ │ │ │ │ └── drawable-v24
│ │ │ │ │ │ └── ic_launcher_foreground.xml
│ │ │ │ │ ├── AndroidManifest.xml
│ │ │ │ │ └── java
│ │ │ │ │ └── com
│ │ │ │ │ └── canonical
│ │ │ │ │ └── anbox
│ │ │ │ │ └── streaming
│ │ │ │ │ └── sdk
│ │ │ │ │ └── webview_example
│ │ │ │ │ └── MainActivity.java
│ │ │ ├── proguard-rules.pro
│ │ │ └── build.gradle
│ │ ├── settings.gradle
│ │ ├── gradle
│ │ │ └── wrapper
│ │ │ │ ├── gradle-wrapper.jar
│ │ │ │ └── gradle-wrapper.properties
│ │ ├── .gitignore
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradlew.bat
│ │ └── gradlew
│ └── enhanced_webview_streaming
│ │ ├── app
│ │ ├── .gitignore
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── assets
│ │ │ │ ├── css
│ │ │ │ │ └── style.css
│ │ │ │ └── index.html
│ │ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── values
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── mipmap-anydpi-v26
│ │ │ │ │ ├── ic_launcher.xml
│ │ │ │ │ └── ic_launcher_round.xml
│ │ │ │ ├── layout
│ │ │ │ │ ├── activity_stream.xml
│ │ │ │ │ └── activity_main.xml
│ │ │ │ └── drawable-v24
│ │ │ │ │ └── ic_launcher_foreground.xml
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── canonical
│ │ │ │ └── anbox
│ │ │ │ └── streaming_sdk
│ │ │ │ └── enhanced_webview_example
│ │ │ │ └── MainActivity.java
│ │ ├── proguard-rules.pro
│ │ └── build.gradle
│ │ ├── settings.gradle
│ │ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ │ ├── .gitignore
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradlew.bat
│ │ └── README.md
└── js
│ ├── Dockerfile
│ ├── README.md
│ ├── run.sh
│ ├── serve.py
│ └── example.html
├── trivy.yaml
├── .github
├── CODEOWNERS
├── pull_request_template.md
├── workflows
│ ├── cla_check.yaml
│ ├── trivy-update.yaml
│ ├── security-scan.yaml
│ ├── release.yaml
│ ├── cache-images.yaml
│ ├── branch.yaml
│ ├── tics.yaml
│ └── pr.yaml
├── renovate.json
└── actions
│ └── setup-trivy
│ └── action.yaml
├── scripts
├── run-tests.sh
├── setup-gh-ssh.sh
├── compare_kev_vulnerabilities.sh
├── gen-version.sh
├── clean-build.sh
├── validate.sh
├── build.sh
└── build-with-docker.sh
├── SECURITY.md
└── README.md
/.trivyignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.base_version:
--------------------------------------------------------------------------------
1 | 1.29
2 |
--------------------------------------------------------------------------------
/js/tests/unit/.gitignore:
--------------------------------------------------------------------------------
1 | coverage
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .npm
2 | node_modules
3 | .idea
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @canonical/anbox
--------------------------------------------------------------------------------
/js/tests/e2e/.prettierignore:
--------------------------------------------------------------------------------
1 | **/node_modules
--------------------------------------------------------------------------------
/android/anbox_streaming_sdk/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | rootProject.name = "Out of Band Data v2"
3 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/assets/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color:#000000
3 | }
--------------------------------------------------------------------------------
/examples/android/webview_streaming/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name='WebViewStreaming'
2 | include ':app'
3 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/assets/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color:#000000
3 | }
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name='EnhanceWebViewStreaming'
2 | include ':app'
3 |
--------------------------------------------------------------------------------
/android/anbox_streaming_sdk/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/trivy.yaml:
--------------------------------------------------------------------------------
1 | format: table
2 | exit-code: 1
3 | severity:
4 | - MEDIUM
5 | - HIGH
6 | - CRITICAL
7 | scan:
8 | scanners:
9 | - vuln
10 | - secret
11 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # The Anbox team owns all for now
2 | * @canonical/anbox
3 |
4 | # The UI team should be included for all JS changes
5 | /js @canonical/anbox-ui
6 |
7 |
--------------------------------------------------------------------------------
/android/anbox_streaming_sdk/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/android/anbox_streaming_sdk/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/js/tests/e2e/tests/visual.spec.js-snapshots/long-press-home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/js/tests/e2e/tests/visual.spec.js-snapshots/long-press-home.png
--------------------------------------------------------------------------------
/js/tests/e2e/tests/visual.spec.js-snapshots/status-bar-swipe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/js/tests/e2e/tests/visual.spec.js-snapshots/status-bar-swipe.png
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/out_of_band_v2/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/examples/android/webview_streaming/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/webview_streaming/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/js/tests/e2e/tests/visual.spec.js-snapshots/app-drawer-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/js/tests/e2e/tests/visual.spec.js-snapshots/app-drawer-settings.png
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/out_of_band_v2/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/out_of_band_v2/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/enhanced_webview_streaming/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/out_of_band_v2/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/out_of_band_v2/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/out_of_band_v2/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/webview_streaming/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/webview_streaming/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/webview_streaming/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/webview_streaming/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/js/tests/e2e/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | /test-results/
3 | /playwright-report/
4 | /blob-report/
5 | /playwright/.cache/
6 | .cache/
7 | coverage/
8 |
9 | .env.local
10 | anbox-cloud.crt
11 | anbox-cloud.key
12 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/out_of_band_v2/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/out_of_band_v2/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/out_of_band_v2/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/out_of_band_v2/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/webview_streaming/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/out_of_band_v2/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/webview_streaming/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/webview_streaming/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/webview_streaming/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/webview_streaming/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/webview_streaming/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canonical/anbox-streaming-sdk/main/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/js/tests/unit/.babelrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",{
5 | "targets":{
6 | "node":"current"
7 | }
8 | }
9 | ]
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
7 |
--------------------------------------------------------------------------------
/android/anbox_streaming_sdk/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | local.properties
4 | .idea/caches
5 | .idea/libraries
6 | .idea/modules.xml
7 | .idea/workspace.xml
8 | .idea/navEditor.xml
9 | .idea/assetWizardSettings.xml
10 | .DS_Store
11 | build
12 | captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
7 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | local.properties
4 | .idea/caches
5 | .idea/libraries
6 | .idea/modules.xml
7 | .idea/workspace.xml
8 | .idea/navEditor.xml
9 | .idea/assetWizardSettings.xml
10 | .DS_Store
11 | build
12 | captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jul 09 12:37:45 SBT 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
7 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Jun 22 18:40:28 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
7 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Jun 22 18:40:28 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
7 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Done
2 |
3 | [Summary of work items]
4 |
5 | ## QA
6 |
7 | [Steps for how to test the changes]
8 |
9 | ## JIRA / Launchpad bug
10 |
11 | Fixes #
12 |
13 | ## Documentation
14 |
15 | Does this impact the team's internal or public documentation? If yes, has the relevant documentation been updated?
--------------------------------------------------------------------------------
/android/anbox_streaming_sdk/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/examples/js/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04
2 |
3 | RUN apt update && apt upgrade -y && apt install -y python3
4 |
5 | RUN mkdir -p /app/demos
6 |
7 | COPY example.html /app/demos
8 | COPY sgsr.html /app/demos
9 | COPY anime4K.html /app/demos
10 | COPY anbox-stream-sdk.js /app
11 | COPY serve.py /app
12 |
13 | EXPOSE 8000
14 | CMD cd /app && python3 ./serve.py
15 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.github/workflows/cla_check.yaml:
--------------------------------------------------------------------------------
1 | name: CLA check
2 |
3 | on:
4 | pull_request:
5 | branches: [master]
6 |
7 | jobs:
8 | cla-check:
9 | runs-on: ubuntu-24.04
10 | steps:
11 | - name: Check if Canonical's Contributor License Agreement has been signed
12 | uses: canonical/has-signed-canonical-cla@1c20438ad54b4d37105e777000545881d2293ba4 # 2.2.0
13 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Out of band v2
3 | Connect
4 | Send
5 | Channel name
6 | Received data
7 | Text to be sent
8 |
9 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | WebViewStreaming
3 | StreamActivity
4 | URL of the gateway
5 | Gateway API token
6 | Stream
7 | Name of the application to stream
8 |
9 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | EnhancedWebViewStreaming
3 | StreamActivity
4 | URL of the gateway
5 | Gateway API token
6 | Stream
7 | Name of the application to stream
8 |
9 |
--------------------------------------------------------------------------------
/examples/js/README.md:
--------------------------------------------------------------------------------
1 | # Anbox Streaming SDK Example
2 |
3 | This directory contains the bare minimum to start a stream on the Anbox Streaming Stack.
4 |
5 | ## Prerequisites
6 | For this example you'll need the following:
7 |
8 | - An Anbox Streaming Stack deployment
9 | - A Stream Gateway API token
10 | - At least one registered application on AMS
11 |
12 | ## Running the example
13 | You'll need a webserver serving the content for the example. A simple server can be created
14 | with the following command in the example directory:
15 |
16 | python3 -m http.server 8080
17 |
18 | And open your web browser to `127.0.0.1:8080`.
19 |
20 |
21 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:8.7.2'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | google()
18 | jcenter()
19 | }
20 | }
21 |
22 | task clean(type: Delete) {
23 | delete rootProject.buildDir
24 | }
25 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/layout/activity_stream.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:recommended",
5 | ":disableDependencyDashboard",
6 | "schedule:earlyMondays",
7 | ":combinePatchMinorReleases",
8 | ":ignoreUnstable",
9 | "helpers:pinGitHubActionDigestsToSemver",
10 | "group:allNonMajor",
11 | "group:allDigest"
12 | ],
13 | "packageRules": [
14 | {
15 | "groupName": "GitHub actions",
16 | "matchManagers": [
17 | "github-actions"
18 | ],
19 | "matchUpdateTypes": [
20 | "digest",
21 | "patch",
22 | "minor",
23 | "major"
24 | ]
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 |
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:8.7.2'
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 |
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/js/run-tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 | #
3 | # Copyright 2021 Canonical Ltd.
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 | cd tests/unit
18 | $PWD/run-tests.sh $@
19 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 |
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:8.7.2'
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 |
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/.github/workflows/trivy-update.yaml:
--------------------------------------------------------------------------------
1 | name: Update Trivy cache
2 | on:
3 | workflow_dispatch:
4 | # Run daily after midnight UTC
5 | schedule:
6 | - cron: '0 1 * * *'
7 |
8 | jobs:
9 | trivy-update:
10 | strategy:
11 | fail-fast: false
12 | matrix:
13 | arch: [arm64, amd64]
14 | runs-on: [self-hosted, linux, "${{ matrix.arch == 'amd64' && 'X64' || 'ARM64' }}", jammy, large]
15 | steps:
16 | - name: Check out repository
17 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
18 | with:
19 | fetch-depth: 0
20 | - name: Setup Trivy to warm up the cache
21 | uses: ./.github/actions/setup-trivy
22 | with:
23 | arch: ${{ matrix.arch }}
24 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/layout/activity_stream.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/scripts/run-tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -ex
2 | #
3 | # Copyright 2020 Canonical Ltd.
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 | for component in js ; do
18 | ( cd "$component"; ./run-tests.sh $@ )
19 | done
20 |
--------------------------------------------------------------------------------
/scripts/setup-gh-ssh.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -ex
2 | # Copyright 2024 Canonical Ltd. All rights reserved.
3 |
4 | # The key is provided through a secret and we need to write it to
5 | # disk in order to load it into the SSH agent
6 | mkdir -p "$HOME"/.ssh
7 | echo "$ANBOX_CLOUD_CI_BOT_SSH_KEY" > "$HOME"/.ssh/id_bot
8 | chmod 0600 "$HOME"/.ssh/id_bot
9 |
10 | # Setup a host alias we can use with git push
11 | cat << EOF > "$HOME"/.ssh/config
12 | Host github-anbox-streaming-sdk
13 | Hostname github.com
14 | IdentityFile=$HOME/.ssh/id_bot
15 | EOF
16 |
17 | # We need to trust the SSH host key from GitHub
18 | ssh-keyscan github.com > "$HOME"/.ssh/known_hosts
19 |
20 | # And now we can finally start the agent and load our key
21 | eval "$(ssh-agent -s)"
22 | ssh-add "$HOME"/.ssh/id_bot
23 |
--------------------------------------------------------------------------------
/js/tests/combined-coverage-report.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -ex
2 | #
3 | # Copyright 2024 Canonical Ltd.
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 | cd unit && sh run-tests.sh && cd ../e2e && sh run-tests.sh && mkdir -p coverage/combined-report && npm run test-report-coverage
18 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/README.md:
--------------------------------------------------------------------------------
1 | # Anbox Cloud - Out of Band Data v2
2 |
3 | An Android application that demonstrates how to use [out of band data v2](https://documentation.ubuntu.com/anbox-cloud/en/latest/howto/stream/exchange-oob-data/#version-2) feature to exchange data between an Android application running within an Android container and a WebRTC client.
4 |
5 | **NOTE**: After building the application, the resulting APK must be installed and running as a system app in the Android container to communicate with [org.anbox.webrtc.IDataProxyService](https://documentation.ubuntu.com/anbox-cloud/en/latest/howto/stream/exchange-oob-data/#anbox-webrtc-data-proxy) system service for data exchange. See the guide on [how to install an APK as a system app](https://documentation.ubuntu.com/anbox-cloud/en/latest/howto/port/install-apk-system-app) for more information.
6 |
--------------------------------------------------------------------------------
/android/anbox_streaming_sdk/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/js/tests/unit/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "anbox-stream-sdk-unit-tests",
3 | "version": "1.13.0",
4 | "description": "unit testing for anbox stream sdk (JS only)",
5 | "scripts": {
6 | "test": "jest",
7 | "test:coverage": "jest --coverage --coverageDirectory=coverage --coverageProvider=v8"
8 | },
9 | "author": "indore-team@launchpad.net",
10 | "license": "Proprietary",
11 | "jest": {
12 | "testEnvironment": "jsdom"
13 | },
14 | "devDependencies": {
15 | "@babel/core": "^7.26.0",
16 | "@babel/preset-env": "^7.26.0",
17 | "@eslint/js": "9.17.0",
18 | "@eslint/eslintrc": "3.2.0",
19 | "eslint": "^9.17.0",
20 | "eslint-config-prettier": "^9.1.0",
21 | "eslint-plugin-prettier": "^5.2.1",
22 | "globals": "15.14.0",
23 | "jest": "^29.7.0",
24 | "jest-environment-jsdom": "^29.7.0",
25 | "prettier": "3.4.2"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/js/tests/unit/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import globals from "globals";
2 | import path from "node:path";
3 | import { fileURLToPath } from "node:url";
4 | import js from "@eslint/js";
5 | import { FlatCompat } from "@eslint/eslintrc";
6 |
7 | const __filename = fileURLToPath(import.meta.url);
8 | const __dirname = path.dirname(__filename);
9 | const compat = new FlatCompat({
10 | baseDirectory: __dirname,
11 | recommendedConfig: js.configs.recommended,
12 | allConfig: js.configs.all,
13 | });
14 |
15 | export default [
16 | ...compat.extends("eslint:recommended", "plugin:prettier/recommended"),
17 | {
18 | languageOptions: {
19 | globals: {
20 | ...globals.browser,
21 | ...globals.node,
22 | ...globals.jest,
23 | ...globals.jasmine,
24 | },
25 |
26 | ecmaVersion: "latest",
27 | sourceType: "module",
28 | },
29 | },
30 | ];
31 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/.github/workflows/security-scan.yaml:
--------------------------------------------------------------------------------
1 | name: Run security scan
2 | on:
3 | push:
4 | branches:
5 | - main
6 | - stable-*
7 | pull_request:
8 |
9 | concurrency:
10 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
11 | cancel-in-progress: true
12 |
13 | jobs:
14 | build:
15 | name: Run Trivy security scan
16 | runs-on: [self-hosted, linux, X64, jammy, large]
17 | steps:
18 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
19 | - name: Setup Trivy
20 | uses: ./.github/actions/setup-trivy
21 | - name: Run Trivy vulnerability scanner
22 | run: |
23 | trivy repository "$GITHUB_WORKSPACE" \
24 | -c trivy.yaml \
25 | --ignorefile .trivyignore \
26 | --show-suppressed \
27 | --cache-dir="$GITHUB_WORKSPACE"/.cache/trivy
28 | - name: Compare Trivy results with KEV list
29 | run: bash ./scripts/compare_kev_vulnerabilities.sh
30 |
--------------------------------------------------------------------------------
/js/tests/e2e/tests/join.spec.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Anbox Cloud Streaming SDK
3 | *
4 | * Copyright 2024 Canonical Ltd.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | import { test } from "../fixtures/anbox-test";
20 | import { joinSession, disconnectStream } from "./shared";
21 |
22 | test("join session and disconnect stream", async ({ page }) => {
23 | await joinSession(page, process.env.AOSP_SESSION_ID);
24 | await disconnectStream(page);
25 | });
26 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
17 |
18 |
22 |
23 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
17 |
18 |
22 |
23 |
--------------------------------------------------------------------------------
/js/tests/e2e/run-tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -ex
2 | #
3 | # Copyright 2024 Canonical Ltd.
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 | ANBOX_STREAMING_SDK_DIR="$PWD/../../"
18 |
19 | docker run --rm \
20 | -v "$ANBOX_STREAMING_SDK_DIR:/anbox-streaming-sdk-js" \
21 | node:22 \
22 | bash -c "cd /anbox-streaming-sdk-js/tests/e2e && \
23 | npm ci && \
24 | npx playwright install --with-deps && \
25 | npm run test:coverage && \
26 | chown -R $(id -u ${USER}):$(id -g ${USER}) coverage"
27 |
--------------------------------------------------------------------------------
/android/anbox_streaming_sdk/README.md:
--------------------------------------------------------------------------------
1 | # Anbox Streaming SDK
2 |
3 | The Anbox Streaming SDK enables developers to build a hybrid mobile application
4 | that can integrate the features that Anbox Cloud provides. It comes with an
5 | Android library that offers easy-to-use native components like AnboxWebView,
6 | which extends the AOSP WebView. It provides better handling of text input for
7 | the hybrid application that loads the Anbox Streaming JavaScript SDK with an
8 | embedded WebView for video streaming.
9 |
10 | ## Build the AAR library
11 |
12 | 1. Import the project into Android studio
13 | 2. Go to menu bar and click `Build` -> `Make Module 'anbox_streaming_sdk'`
14 |
15 | After the compilation is done, you'll get a piece of an aar file for all build
16 | flavors in the build/outputs/aar/ directory of the project.
17 |
18 | ## Integrate the AAR library into your project
19 |
20 | See the [development documentation](https://documentation.ubuntu.com/anbox-cloud/en/latest/howto/stream/integrate-virtual-keyboard/)
21 | for how to integrate the AAR library into your project.
22 |
--------------------------------------------------------------------------------
/js/tests/e2e/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Anbox Cloud Streaming SDK
3 | *
4 | * Copyright 2024 Canonical Ltd.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | import globals from "globals";
20 | import pluginJs from "@eslint/js";
21 | import eslintConfigPrettier from "eslint-config-prettier";
22 |
23 | export default [
24 | { languageOptions: { globals: { ...globals.browser, ...globals.node } } },
25 | { ignores: [".cache/"] },
26 | pluginJs.configs.recommended,
27 | eslintConfigPrettier,
28 | ];
29 |
--------------------------------------------------------------------------------
/android/anbox_streaming_sdk/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/examples/js/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -ex
2 | #
3 | # This file is part of Anbox Cloud Streaming SDK
4 | #
5 | # Copyright 2021 Canonical Ltd.
6 | #
7 | # Licensed under the Apache License, Version 2.0 (the "License");
8 | # you may not use this file except in compliance with the License.
9 | # You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing, software
14 | # distributed under the License is distributed on an "AS IS" BASIS,
15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | # See the License for the specific language governing permissions and
17 | # limitations under the License.
18 |
19 | if [ -e .last_docker_id ]; then
20 | id=$(cat .last_docker_id)
21 | docker stop $id || true
22 | rm .last_docker_id
23 | fi
24 |
25 | echo "INFO: Building container image ..."
26 | docker build . -t anbox-stream-sdk-example:latest
27 | id=$(docker run -d --rm -p 8000:8000 anbox-stream-sdk-example:latest)
28 | echo "$id" > .last_docker_id
29 |
30 | echo "INFO: Container up and running, open http://localhost:8000/demos in your browser ..."
31 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | namespace "com.canonical.anbox.streaming.sdk.webview_example"
5 | compileSdkVersion 30
6 |
7 | defaultConfig {
8 | applicationId "com.canonical.anbox.streaming.sdk.webview_example"
9 | minSdkVersion 26
10 | targetSdkVersion 30
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: 'libs', include: ['*.jar'])
28 |
29 | implementation 'androidx.appcompat:appcompat:1.1.0'
30 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
31 | implementation 'androidx.webkit:webkit:1.2.0'
32 | testImplementation 'junit:junit:4.12'
33 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
34 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
35 | }
36 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/scripts/compare_kev_vulnerabilities.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -ex
2 | #
3 | # Copyright 2025 Canonical Ltd.
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 | trivy fs "$GITHUB_WORKSPACE" --format json --output trivy-full-report.json
18 |
19 | kev_cves="$(jq -r '.vulnerabilities[].cveID' kev.json | sort -u)"
20 |
21 | found_cves="$(jq -r '.Results[] | select(.Vulnerabilities != null) | .Vulnerabilities[].VulnerabilityID' trivy-full-report.json | sort -u)"
22 |
23 | matches="$(echo "$found_cves" | grep -F -f <(echo "$kev_cves") || true)"
24 |
25 | if [ -n "$matches" ]; then
26 | echo "KEV listed vulnerabilities found."
27 | echo "$matches"
28 | exit 1
29 | fi
30 |
31 | echo "No KEV listed vulnerabilities found."
32 |
--------------------------------------------------------------------------------
/examples/js/serve.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # encoding: utf-8
3 | #
4 | # This file is part of Anbox Cloud Streaming SDK
5 | #
6 | # Copyright 2021 Canonical Ltd.
7 | #
8 | # Licensed under the Apache License, Version 2.0 (the "License");
9 | # you may not use this file except in compliance with the License.
10 | # You may obtain a copy of the License at
11 | #
12 | # http://www.apache.org/licenses/LICENSE-2.0
13 | #
14 | # Unless required by applicable law or agreed to in writing, software
15 | # distributed under the License is distributed on an "AS IS" BASIS,
16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | # See the License for the specific language governing permissions and
18 | # limitations under the License.
19 |
20 | from http.server import HTTPServer, SimpleHTTPRequestHandler
21 |
22 | class CORSRequestHandler(SimpleHTTPRequestHandler):
23 | def end_headers(self):
24 | self.send_header('Access-Control-Allow-Origin', '*')
25 | self.send_header('Access-Control-Allow-Methods', 'GET')
26 | self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate')
27 | return super(CORSRequestHandler, self).end_headers()
28 |
29 | httpd = HTTPServer(('', 8000), CORSRequestHandler)
30 | httpd.serve_forever()
31 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | }
4 |
5 | android {
6 | namespace "com.canonical.anboxcloud.outofbandappv2"
7 | compileSdkVersion 30
8 |
9 | defaultConfig {
10 | applicationId "com.canonical.anboxcloud.outofbandappv2"
11 | minSdkVersion 16
12 | targetSdkVersion 30
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 | compileOptions {
26 | sourceCompatibility JavaVersion.VERSION_1_8
27 | targetCompatibility JavaVersion.VERSION_1_8
28 | }
29 | }
30 |
31 | dependencies {
32 |
33 | implementation 'androidx.appcompat:appcompat:1.2.0'
34 | implementation 'com.google.android.material:material:1.1.0'
35 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
36 | testImplementation 'junit:junit:4.+'
37 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
38 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
39 | }
40 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | namespace "com.canonical.anbox.streaming_sdk.enhanced_webview_example"
5 | compileSdkVersion 30
6 |
7 | defaultConfig {
8 | applicationId "com.canonical.anbox.streaming_sdk.enhanced_webview_example"
9 | minSdkVersion 26
10 | targetSdkVersion 30
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: 'libs', include: ['*.jar'])
28 | implementation fileTree(dir: 'libs', include: ['*.aar'])
29 |
30 | implementation 'androidx.appcompat:appcompat:1.1.0'
31 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
32 | implementation 'androidx.webkit:webkit:1.2.0'
33 | testImplementation 'junit:junit:4.12'
34 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
35 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
36 | }
37 |
--------------------------------------------------------------------------------
/js/tests/e2e/fixtures/anbox-test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Anbox Cloud Streaming SDK
3 | *
4 | * Copyright 2024 Canonical Ltd.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | import { test as base } from "@playwright/test";
20 | import { finishCoverage, startCoverage } from "./coverage.js";
21 |
22 | export const test = base.extend({
23 | runCoverage: [
24 | async ({ page, browserName }, use) => {
25 | const supportsCoverage = browserName === "chromium";
26 | if (supportsCoverage) {
27 | await startCoverage(page);
28 | }
29 | await use(page);
30 | if (supportsCoverage) {
31 | await finishCoverage(page);
32 | }
33 | },
34 | { auto: true },
35 | ],
36 | });
37 |
38 | export const expect = test.expect;
39 |
--------------------------------------------------------------------------------
/android/anbox_streaming_sdk/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | jcenter()
4 | google()
5 | }
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:8.7.3'
8 | }
9 | }
10 |
11 | allprojects {
12 | repositories {
13 | google()
14 | jcenter()
15 | }
16 | }
17 |
18 | apply plugin: 'com.android.library'
19 |
20 | android {
21 | namespace 'com.canonical.anbox.streaming_sdk'
22 | compileSdkVersion 30
23 |
24 | defaultConfig {
25 | minSdkVersion 23
26 | targetSdkVersion 30
27 | versionCode 1
28 | versionName "1.0"
29 |
30 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
31 | consumerProguardFiles "consumer-rules.pro"
32 | }
33 |
34 | buildTypes {
35 | release {
36 | minifyEnabled false
37 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
38 | }
39 | }
40 | }
41 |
42 | dependencies {
43 | implementation fileTree(dir: "libs", include: ["*.jar"])
44 | implementation 'androidx.appcompat:appcompat:1.7.0'
45 | testImplementation 'junit:junit:4.13.2'
46 | androidTestImplementation 'androidx.test.ext:junit:1.2.1'
47 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/js/tests/e2e/fixtures/constants.cjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Anbox Cloud Streaming SDK
3 | *
4 | * Copyright 2024 Canonical Ltd.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | const AOSP_APP_NAME = "streaming-sdk-e2e-tests_AOSP";
20 | const AAOS_APP_NAME = "streaming-sdk-e2e-tests_AAOS";
21 | const DEFAULT_ARCH = "amd64";
22 | const SERVER_PORT = 2999;
23 | const BASE_URL = `http://127.0.0.1:${SERVER_PORT}`;
24 |
25 | const SHARED_BROWSER_OPTIONS = {
26 | hasTouch: true,
27 | viewport: { width: 1280, height: 720 },
28 | };
29 |
30 | exports.AOSP_APP_NAME = AOSP_APP_NAME;
31 | exports.AAOS_APP_NAME = AAOS_APP_NAME;
32 | exports.DEFAULT_ARCH = DEFAULT_ARCH;
33 | exports.SERVER_PORT = SERVER_PORT;
34 | exports.BASE_URL = BASE_URL;
35 | exports.SHARED_BROWSER_OPTIONS = SHARED_BROWSER_OPTIONS;
36 |
--------------------------------------------------------------------------------
/js/tests/e2e/tests/shared.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Anbox Cloud Streaming SDK
3 | *
4 | * Copyright 2024 Canonical Ltd.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | import { expect } from "../fixtures/anbox-test";
20 |
21 | export const joinSession = async (page, sessionId) => {
22 | await page.goto(`/?sessionId=${sessionId}`);
23 |
24 | await expect(page.locator("#anbox-stream").locator("video")).toHaveCount(1);
25 | await expect(page.locator("#anbox-stream").locator("audio")).toHaveCount(1);
26 | await page.waitForFunction(() => globalThis.isReady !== undefined, null, {
27 | timeout: 20_000,
28 | });
29 | };
30 |
31 | export const disconnectStream = async (page) => {
32 | await page.evaluate(() => globalThis.stream.disconnect());
33 | await page.waitForFunction(() => globalThis.isClosed !== undefined, null, {
34 | timeout: 20_000,
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | tag_name:
7 | description: 'Tag name for the release'
8 | required: true
9 | target_commitish:
10 | description: |
11 | Target commitish (see GH API) for tag creation.
12 | Can be empty - it will default to the head of the branch it is run from.
13 | required: false
14 |
15 | # To publish assets to on GitHub release pages
16 | permissions:
17 | contents: write
18 |
19 | jobs:
20 | build:
21 | runs-on: ubuntu-latest
22 | environment: stable
23 |
24 | steps:
25 | - name: Checkout repository
26 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
27 | with:
28 | fetch-depth: 0
29 |
30 | - name: Build
31 | run: |
32 | ./scripts/build.sh --create-tarball --version=${{ inputs.tag_name }}
33 |
34 | - name: Run tests
35 | run: |
36 | ./scripts/run-tests.sh
37 |
38 | - name: Release
39 | env:
40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
41 | run: |
42 | gh release create "${{ inputs.tag_name }}" \
43 | anbox-streaming-sdk_${{ inputs.tag_name }}.zip \
44 | --target "${{ inputs.target_commitish || github.ref }}" \
45 | --title "${{ inputs.tag_name }}" \
46 | --notes "See https://documentation.ubuntu.com/anbox-cloud/reference/release-notes/${{ inputs.tag_name }}/ for more information."
47 |
--------------------------------------------------------------------------------
/.github/workflows/cache-images.yaml:
--------------------------------------------------------------------------------
1 | name: Cache Anbox images
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: '0 10 * * *'
7 |
8 | jobs:
9 | cache-images:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
13 | - name: Download images
14 | env:
15 | IMAGE_SERVER_BUCKET: nightly
16 | run: |
17 | base_version="$(cat .base_version)"
18 | channel="$base_version"/edge
19 | image_server_url=https://"${{ secrets.IMAGE_SERVER_AUTH }}"@images.anbox-cloud.io/"$IMAGE_SERVER_BUCKET"/"$channel"
20 | item_type=image
21 |
22 | mkdir images
23 | for product in android14 aaos15 ; do
24 | image_name=jammy:"$product":amd64
25 | image_path="$(curl -s "$image_server_url"/streams/v1/images.json | \
26 | jq -r "last(.products.\"$image_name\".versions[] | select(.items.\"${item_type}\" != null)).items.\"${item_type}\".path")"
27 | image_url="$image_server_url"/"$image_path"
28 | curl -s "$image_url" -o images/"$product"_amd64.tar.xz
29 | done
30 | - name: Generate cache key
31 | id: cache-key
32 | run: |
33 | echo "value=anbox-images-amd64-$(/bin/date -u "+%Y%m%d")" >> $GITHUB_OUTPUT
34 | shell: bash
35 | - name: Cache all images
36 | uses: actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
37 | with:
38 | path: images
39 | key: ${{ steps.cache-key.outputs.value }}
40 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Anbox Cloud security policy
2 |
3 | Learn about our [release and support policy](https://documentation.ubuntu.com/anbox-cloud/en/latest/reference/release-notes/release-notes/#release-and-support-policy) for the nature of our releases and versions.
4 |
5 | ## Reporting a vulnerability
6 |
7 | If you discover a security vulnerability, follow the steps outlined below to report it:
8 |
9 | 1. Do not publicly disclose the vulnerability before discussing it with us.
10 | 2. Report a bug at https://bugs.launchpad.net/anbox-cloud
11 |
12 | **Important**: Remember to set the information type to *Private Security*. You will see a field with the text *This bug contains information that is:*
13 | 3. Provide detailed information about the vulnerability, including:
14 | - A description of the vulnerability
15 | - Steps to reproduce the issue
16 | - Potential impact and affected versions
17 | - Suggested mitigation, if possible
18 |
19 | The [Ubuntu Security disclosure and embargo policy](https://ubuntu.com/security/disclosure-policy) contains more information about what you can expect when you contact us and what we expect from you.
20 |
21 | The Anbox Cloud team will be notified of the issue and review the vulnerability. We may reach out to you for further information or clarification if needed.
22 | If the issue is confirmed as a valid security vulnerability, we will assign a CVE and coordinate the release of the fix. We also document them as [security notices](https://documentation.ubuntu.com/anbox-cloud/en/latest/reference/security-notices/).
23 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
15 |
22 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/js/tests/e2e/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "anbox-stream-sdk-e2e-tests",
3 | "version": "1.22.0",
4 | "description": "e2e tests for anbox stream sdk",
5 | "scripts": {
6 | "start": "node server.js",
7 | "test:coverage": "rm -rf coverage ; PW_TEST_HTML_REPORT_OPEN='never' npx playwright test",
8 | "test-report-coverage": "cp ../unit/coverage/coverage-final.json coverage/ && sed -i 's|/anbox-streaming-sdk-unit-test/||g' coverage/coverage-final.json && cd ../.. && BASE_PATH=$(pwd) && sed -i \"s|anbox-stream-sdk.js|$BASE_PATH/anbox-stream-sdk.js|g\" tests/e2e/coverage/*.json && nyc report --reporter html --reporter cobertura --reporter text-summary --temp-dir $BASE_PATH/tests/e2e/coverage --report-dir $BASE_PATH/tests/e2e/coverage/combined-report/js ; mv tests/e2e/coverage/combined-report/js/cobertura-coverage.xml tests/e2e/coverage/combined-report/cobertura-coverage-js.xml"
9 | },
10 | "author": "indore-team@launchpad.net",
11 | "license": "Proprietary",
12 | "devDependencies": {
13 | "@eslint/js": "9.17.0",
14 | "@playwright/test": "1.49.1",
15 | "@types/convert-source-map": "2.0.3",
16 | "@types/node": "22.10.5",
17 | "convert-source-map": "2.0.0",
18 | "eslint": "9.37.0",
19 | "eslint-config-prettier": "9.1.0",
20 | "eslint-plugin-prettier": "5.2.1",
21 | "globals": "15.14.0",
22 | "nyc": "17.1.0",
23 | "prettier": "3.4.2",
24 | "v8-to-istanbul": "9.3.0"
25 | },
26 | "dependencies": {
27 | "axios": "1.12.2",
28 | "compressing": "1.10.1",
29 | "dotenv": "16.4.7",
30 | "express": "4.21.2",
31 | "express-rate-limit": "7.5.0",
32 | "yaml": "2.7.0"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
19 |
21 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/scripts/gen-version.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 | #
3 | # Copyright 2020 Canonical Ltd.
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 | if [ -z "$BUILD_ID" ]; then
18 | BUILD_ID=0
19 | fi
20 |
21 | # We follow https://semver.org/; this will generate us the next patch version (we don't
22 | # take any assumption if the next release will be a minor or major version) and append
23 | # a pre-release qualifier to always sort lower than the final released version. To
24 | # have incrementing pre-release version we take the build id from Jenkins.
25 | # Example: 1.5.0 (old) -> 1.5.1-alpha.43
26 | current_version=$(git describe --tags `git rev-list --tags --max-count=1`)
27 | version_age=$(git log --oneline $(git describe --tags --abbrev=0 @)..@ | wc -l)
28 | if [ "$version_age" != 0 ]; then
29 | gitr=$(git rev-parse --short HEAD)
30 | base_version="$(cat .base_version)"
31 | mmp=$(echo "$current_version" | cut -d'-' -f1)
32 | if echo "$mmp" | grep -q "$base_version" ; then
33 | base_version="${mmp%.*}.$((${mmp##*.}+1))"
34 | else
35 | base_version="$base_version".0
36 | fi
37 | version="$base_version"-alpha."$BUILD_ID"+git"$gitr"
38 | else
39 | # When the current commit is having the tag we can just use the current version
40 | version="$current_version"
41 | fi
42 |
43 | echo "$version"
44 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/js/tests/e2e/global-teardown.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Anbox Cloud Streaming SDK
3 | *
4 | * Copyright 2024 Canonical Ltd.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | import { expect } from "./fixtures/anbox-test";
20 | import {
21 | AOSP_APP_NAME,
22 | AAOS_APP_NAME,
23 | BASE_URL,
24 | } from "./fixtures/constants.cjs";
25 | require("dotenv").config({ path: ".env.local" });
26 |
27 | const deleteTestInstance = async (sessionId) => {
28 | if (!sessionId) {
29 | return;
30 | }
31 | const deleteInstanceResponse = await fetch(
32 | `${BASE_URL}/instance?sessionId=${sessionId}`,
33 | {
34 | method: "DELETE",
35 | },
36 | );
37 | expect(deleteInstanceResponse.status).toBe(200);
38 | };
39 |
40 | const deleteTestApplication = async (appName) => {
41 | const deleteAppResponse = await fetch(
42 | `${BASE_URL}/application?name=${appName}`,
43 | {
44 | method: "DELETE",
45 | },
46 | );
47 | expect(deleteAppResponse.status).toBe(200);
48 | };
49 |
50 | async function globalTeardown() {
51 | await deleteTestInstance(process.env.AOSP_SESSION_ID);
52 | await deleteTestApplication(AOSP_APP_NAME);
53 | await deleteTestInstance(process.env.AAOS_SESSION_ID);
54 | await deleteTestApplication(AAOS_APP_NAME);
55 | }
56 |
57 | export default globalTeardown;
58 |
--------------------------------------------------------------------------------
/android/anbox_streaming_sdk/src/main/java/com/canonical/anbox/streaming_sdk/IMEJSInterface.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Anbox Cloud Streaming SDK
3 | *
4 | * Copyright 2022 Canonical Ltd.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.canonical.anbox.streaming_sdk;
20 |
21 | import android.webkit.JavascriptInterface;
22 |
23 | /**
24 | * IMEInterface is a bridge that provides access from JavaScript to the Android Java
25 | * layer specifically for ime related messages.
26 | * */
27 | public class IMEJSInterface {
28 | public interface ActionListener {
29 | void onOpenVirtualKeyboard();
30 | void onHideVirtualKeyboard();
31 | }
32 | private IMEJSInterface.ActionListener mActionListener;
33 |
34 | IMEJSInterface(ActionListener listener) {
35 | mActionListener = listener;
36 | }
37 |
38 | /** Show virtual keyboard after receiving the message from Anbox */
39 | @JavascriptInterface
40 | public void openVirtualKeyboard() {
41 | if (mActionListener != null) {
42 | mActionListener.onOpenVirtualKeyboard();
43 | }
44 | }
45 |
46 | /** Hide virtual keyboard after receiving the message from Anbox */
47 | @JavascriptInterface
48 | public void hideVirtualKeyboard() {
49 | if (mActionListener != null) {
50 | mActionListener.onHideVirtualKeyboard();
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/scripts/clean-build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -ex
2 | #
3 | # Copyright 2020 Canonical Ltd.
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 | VERSION=unknown
18 |
19 | for p in "$@"
20 | do
21 | case $p in
22 | --version=*)
23 | VERSION=${p#*=}
24 | ;;
25 | esac
26 | done
27 |
28 | sudo apt-get update -qq
29 | sudo apt-get clean
30 | cd /work/src/examples
31 |
32 | # For Anbox Streaming SDK library(AAR)
33 | (
34 | cd android/anbox_streaming_sdk
35 | cat << EOF >> local.properties
36 | sdk.dir="$ANDROID_HOME"
37 | EOF
38 | ./gradlew assembleDebug
39 | find ./ -name *.aar -exec mv {} /work/com.canonical.anbox.streaming_sdk.aar \;
40 | )
41 |
42 | # For enhanced webview streaming example
43 | (
44 | # Use the aar file just built out from the above step
45 | mkdir -p android/enhanced_webview_streaming/app/libs
46 | cp /work/com.canonical.anbox.streaming_sdk.aar android/enhanced_webview_streaming/app/libs/
47 |
48 | cd android/enhanced_webview_streaming
49 | cat << EOF >> local.properties
50 | sdk.dir="$ANDROID_HOME"
51 | EOF
52 | ./gradlew assembleDebug
53 | find ./ -name *.apk -exec mv {} /work/com.canonical.anbox.streaming_sdk.enhanced_webview_example_"$VERSION".apk \;
54 | )
55 |
56 | # For android out of band v2 example
57 | (
58 | cd android/out_of_band_v2
59 | cat << EOF >> local.properties
60 | sdk.dir="$ANDROID_HOME"
61 | anbox-stream-sdk.dir=/work/src/sdk
62 | EOF
63 | ./gradlew assembleDebug
64 | find ./ -name *.apk -exec mv {} /work/com.canonical.anboxcloud.outofbandappv2_"$VERSION".apk \;
65 | )
66 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/assets/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Anbox Streaming SDK Example
7 |
8 |
9 |
10 |
11 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/examples/js/example.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Anbox Streaming SDK Example
7 |
8 |
9 |
10 |
58 | Anbox Streaming Stack example
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/js/tests/unit/run-tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -ex
2 | #
3 | # Copyright 2021 Canonical Ltd.
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 | ANBOX_STREAMING_SDK_DIR=$PWD/../../
18 | ESLINT_ARGS=
19 | FIX_MODE=false
20 | while [ -n "$1" ]; do
21 | case "$1" in
22 | --anbox-streaming-sdk-dir=*)
23 | ANBOX_STREAMING_SDK_DIR=${1#*=}
24 | shift
25 | ;;
26 | --fix)
27 | ESLINT_ARGS="$ESLINT_ARGS --fix"
28 | FIX_MODE=true
29 | shift
30 | ;;
31 | *)
32 | shift
33 | ;;
34 | esac
35 | done
36 |
37 | if [ -z "$ANBOX_STREAMING_SDK_DIR" ]; then
38 | echo "--anbox-streaming-sdk-dir is missing"
39 | exit 1
40 | fi
41 |
42 | # Copy the JS SDK to the root dir of jtest so that
43 | # unit tests can be executed properly.
44 | cp $ANBOX_STREAMING_SDK_DIR/*.js ./
45 |
46 | cleanup() {
47 | if [ "$FIX_MODE" = true ]; then
48 | # In fix mode, copy the fixed sdk js files back to the original directory
49 | cp anbox-stream-sdk.js "$ANBOX_STREAMING_SDK_DIR"/
50 | fi
51 | # Always remove the copied files from test directory
52 | rm -f anbox-stream-sdk.js
53 | }
54 | trap cleanup EXIT INT TERM
55 |
56 | extra_args=
57 | if ! docker -v | grep -q podman ; then
58 | # If we're running with podman passing in user mappings is not
59 | # going to work
60 | extra_args="-u $(id -u ${USER}):$(id -g ${USER})"
61 | fi
62 |
63 | # 1. Run ESLint for the sanity checks and static analysis'
64 | # 2. Run unit test
65 | docker run --rm \
66 | -v $PWD:/anbox-streaming-sdk-unit-test \
67 | -e HOME=/anbox-streaming-sdk-unit-test \
68 | $extra_args \
69 | node:22 \
70 | bash -c "cd /anbox-streaming-sdk-unit-test && \
71 | npm install --include=dev && \
72 | ./node_modules/.bin/eslint $ESLINT_ARGS *.js && \
73 | npm run test:coverage"
74 |
--------------------------------------------------------------------------------
/js/tests/e2e/fixtures/coverage.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Anbox Cloud Streaming SDK
3 | *
4 | * Copyright 2024 Canonical Ltd.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | import fs from "fs";
20 | import crypto from "crypto";
21 | import { fromSource, removeMapFileComments } from "convert-source-map";
22 | import v8ToIstanbul from "v8-to-istanbul";
23 |
24 | export const startCoverage = async (page) => {
25 | await page.coverage.startJSCoverage({
26 | reportAnonymousScripts: true,
27 | resetOnNavigation: false,
28 | });
29 | };
30 |
31 | export const finishCoverage = async (page) => {
32 | const coverage = await page.coverage.stopJSCoverage();
33 | for (const entry of coverage) {
34 | const fileMatcher = entry.url.match(/http(s)*:\/\/.*:2999\/(?.*)/);
35 | if (
36 | !fileMatcher?.groups ||
37 | fileMatcher.groups.file !== "anbox-stream-sdk.js"
38 | ) {
39 | continue;
40 | }
41 | const source = removeMapFileComments(entry.source ?? "");
42 | const sourceMap = fromSource(entry.source ?? "");
43 |
44 | const converter = v8ToIstanbul(fileMatcher.groups.file, 0, {
45 | source,
46 | sourceMap,
47 | });
48 | await converter.load();
49 | converter.applyCoverage(entry.functions);
50 | const istanbulCoverage = converter.toIstanbul();
51 |
52 | // a unique name for this report
53 | const uuid = crypto.randomBytes(16).toString("hex");
54 |
55 | // _coverageSchema is mandatory for nyc to parse the report
56 | Object.entries(istanbulCoverage).forEach(([key]) => {
57 | istanbulCoverage[key]["_coverageSchema"] = uuid;
58 | });
59 |
60 | const outDir = "coverage";
61 | if (!fs.existsSync(outDir)) {
62 | fs.mkdirSync(outDir, { recursive: true });
63 | }
64 | fs.writeFileSync(
65 | `${outDir}/playwright_coverage_${uuid}.json`,
66 | JSON.stringify(istanbulCoverage),
67 | );
68 | }
69 | };
70 |
--------------------------------------------------------------------------------
/.github/workflows/branch.yaml:
--------------------------------------------------------------------------------
1 | name: Branch for stable series
2 | on:
3 | workflow_dispatch:
4 | inputs:
5 | ref:
6 | description: |
7 | Commit hash or other reference to use when branching. If not given
8 | defaults to 'main'.
9 | required: false
10 | default: 'main'
11 |
12 | permissions:
13 | contents: write
14 | pull-requests: write
15 |
16 | jobs:
17 | branch:
18 | runs-on: ubuntu-latest
19 | if: ${{ github.repository_owner == 'canonical' }}
20 | environment: stable
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
24 | with:
25 | ref: ${{ inputs.ref }}
26 | fetch-depth: 0
27 | - name: Create branch and bump version
28 | env:
29 | AUTHOR_NAME: "Anbox Bot"
30 | AUTHOR_EMAIL: "anbox-bot@canonical.com"
31 | ANBOX_CLOUD_CI_BOT_SSH_KEY: ${{ secrets.ANBOX_CLOUD_CI_BOT_SSH_KEY }}
32 | GH_TOKEN: ${{ github.token }}
33 | run: |
34 | # Setup SSH key based access so we can write a branch
35 | . "$GITHUB_WORKSPACE"/scripts/setup-gh-ssh.sh
36 |
37 | git config --global user.name "$AUTHOR_NAME"
38 | git config --global user.email "$AUTHOR_EMAIL"
39 |
40 | # First we create the new stable branch
41 | series="$(cat .base_version | cut -d. -f1,2)"
42 | minor="$(echo "$series" | cut -d. -f2)"
43 | next_minor=$((minor+1))
44 | next_version="1.$next_minor"
45 | branch_name=stable-"$series"
46 | git checkout -b "$branch_name"
47 | git push git@github-anbox-streaming-sdk:canonical/anbox-streaming-sdk "$branch_name"
48 |
49 | # Next we have to update main to include the new version number
50 | branch=bump-version-to-"$next_version"
51 | # If we already have a branch and PR we can stop here
52 | if git branch -a | grep -q "$branch" ; then
53 | exit 0
54 | fi
55 | git checkout -b "$branch"
56 | sed "s/${series}/${next_version}/g" -i .base_version js/anbox-stream-sdk.js
57 | git add .base_version js/anbox-stream-sdk.js
58 | git commit -m "chore: bump version to $next_version"
59 | git push git@github-anbox-streaming-sdk:canonical/anbox-streaming-sdk "$branch"
60 | gh pr create -B main -H "$branch" \
61 | --title "Bump version to $next_version" \
62 | --body "Automatically requested by workflow. Please close and reopen this PR to trigger workflows!"
63 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/assets/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Anbox Streaming SDK Example
7 |
8 |
9 |
10 |
11 |
61 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
25 |
26 |
35 |
36 |
37 |
38 |
46 |
47 |
55 |
56 |
57 |
58 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
25 |
26 |
35 |
36 |
37 |
38 |
46 |
47 |
55 |
56 |
57 |
58 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/js/tests/e2e/index.html:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 | Anbox Streaming SDK e2e tests
24 |
25 |
34 |
35 |
36 |
37 |
38 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/android/anbox_streaming_sdk/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/js/tests/e2e/playwright.config.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Anbox Cloud Streaming SDK
3 | *
4 | * Copyright 2024 Canonical Ltd.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | import { defineConfig, devices } from "@playwright/test";
20 | import { BASE_URL, SHARED_BROWSER_OPTIONS } from "./fixtures/constants.cjs";
21 | require("dotenv").config({ path: ".env.local" });
22 |
23 | export default defineConfig({
24 | globalSetup: require.resolve("./global-setup"),
25 | globalTeardown: require.resolve("./global-teardown"),
26 | testDir: "./tests",
27 | snapshotPathTemplate:
28 | "{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{ext}",
29 | /* Maximum time one test can run for. */
30 | timeout: 60_000,
31 | expect: {
32 | /**
33 | * Maximum time expect() should wait for the condition to be met.
34 | * For example in `await expect(locator).toHaveText();`
35 | */
36 | timeout: 5_000,
37 | },
38 | /* Run tests in files in parallel */
39 | fullyParallel: true,
40 | /* Fail the build on CI if you accidentally left test.only in the source code. */
41 | forbidOnly: !!process.env.CI,
42 | /* Retry on CI only */
43 | retries: process.env.CI ? 2 : 0,
44 | /* Opt out of parallel tests on CI. */
45 | workers: 1,
46 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */
47 | reporter: process.env.CI ? "html" : "line",
48 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
49 | use: {
50 | baseURL: BASE_URL,
51 | ignoreHTTPSErrors: true,
52 | /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
53 | actionTimeout: 0,
54 | video: "retain-on-failure",
55 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
56 | trace: "on-first-retry",
57 | },
58 |
59 | /* Configure projects for major browsers */
60 | projects: [
61 | {
62 | name: "chromium",
63 | use: {
64 | ...devices["Desktop Chrome"],
65 | ...SHARED_BROWSER_OPTIONS,
66 | },
67 | },
68 |
69 | {
70 | name: "firefox",
71 | use: {
72 | ...devices["Desktop Firefox"],
73 | ...SHARED_BROWSER_OPTIONS,
74 | // on Firefox, the picture-in-picture video toggle overlaps the video
75 | // element, so we need to disable it to avoid it getting in the way
76 | launchOptions: {
77 | firefoxUserPrefs: {
78 | "media.videocontrols.picture-in-picture.video-toggle.enabled": false,
79 | },
80 | },
81 | },
82 | },
83 | ],
84 |
85 | /* Run your local dev server before starting the tests */
86 | webServer: {
87 | command: "npm run start",
88 | url: BASE_URL,
89 | reuseExistingServer: !process.env.CI,
90 | },
91 | });
92 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/app/src/main/java/com/canonical/anboxcloud/outofbandappv2/DataReadTask.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Anbox Cloud Streaming SDK
3 | *
4 | * Copyright 2022 Canonical Ltd.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.canonical.anboxcloud.outofbandappv2;
20 |
21 |
22 | import android.util.Log;
23 |
24 | import android.os.AsyncTask;
25 | import android.os.ParcelFileDescriptor;
26 |
27 | import java.io.IOException;
28 | import java.io.InputStream;
29 | import java.util.Arrays;
30 |
31 |
32 | /**
33 | * DataReadTask is a background task which processes the data reading from the data channel.
34 | * */
35 |
36 | public class DataReadTask extends AsyncTask {
37 | private static final String TAG = "DataReadTask";
38 |
39 | private final ParcelFileDescriptor mFd;
40 | private final DataReadListener mListener;
41 |
42 | public interface DataReadListener {
43 | void onDataRead(byte[] readBytes);
44 | }
45 |
46 | public DataReadTask(ParcelFileDescriptor fd, DataReadListener listener) {
47 | mFd = fd;
48 | mListener = listener;
49 | }
50 |
51 | @Override
52 | protected Void doInBackground(Void... parameters) {
53 | try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(mFd)) {
54 | byte[] data = new byte[1024];
55 | while (!isCancelled()) {
56 | int read_size = in.read(data);
57 | if (read_size < 0) {
58 | Log.e(TAG, "Failed to read data");
59 | break;
60 | } else if (read_size == 0) {
61 | // EOF reached
62 | break;
63 | }
64 |
65 | byte [] readBytes = Arrays.copyOfRange(data, 0, read_size);
66 | mListener.onDataRead(readBytes);
67 | }
68 | } catch (IOException ex) {
69 | // Do not log errors out if the IO interruption occurred
70 | // just because we terminate the stream.
71 | if (!isCancelled())
72 | Log.e(TAG, "Failed to read data: " + ex);
73 | }
74 |
75 | return null;
76 | }
77 |
78 | @Override
79 | protected void onCancelled() {
80 | onPostExecute(null);
81 | }
82 |
83 | public void terminate() {
84 | cancel(true);
85 | // Interrupt the InputStream.read and causes thread
86 | // exit properly without hanging at the block operation.
87 | closeQuietly();
88 | }
89 |
90 | @Override
91 | protected void onPostExecute(Void v) {
92 | super.onPostExecute(v);
93 | closeQuietly();
94 | }
95 |
96 | private void closeQuietly() {
97 | if (mFd != null) {
98 | try {
99 | mFd.close();
100 | } catch (Exception ex) {
101 | Log.e(TAG, "Error closing " + mFd, ex);
102 | }
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/scripts/validate.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 | #
3 | # Copyright 2022 Canonical Ltd.
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 | ALLOWLIST="./sdk-files.allowlist"
18 | SDK_ZIP_TARBALL="./anbox-streaming-sdk.zip"
19 | SDK_PATH=
20 |
21 | show_help() {
22 | cat <<'EOF'
23 | Usage: validate.sh [OPTIONS]
24 |
25 | Validates Anbox Streaming SDK by checking that all included source files are in the allowlist
26 |
27 | optional arguments:
28 | --allowlist= Path to the file holding the list of allowed files into the sdk (default: ./sdk-files.allowlist)
29 | --sdk-zip-tarball= Path to the streaming sdk zip tarball (default: ./anbox-streaming-sdk.zip)
30 | --sdk-path= Path to the folder path that include the Anbox Streaming SDK
31 | EOF
32 | }
33 |
34 | while [ -n "$1" ]; do
35 | case "$1" in
36 | --help)
37 | show_help
38 | exit
39 | ;;
40 | --allowlist=*)
41 | ALLOWLIST=${1#*=}
42 | shift
43 | ;;
44 | --sdk-path*)
45 | SDK_PATH=${1#*=}
46 | shift
47 | ;;
48 | --sdk-zip-tarball=*)
49 | SDK_ZIP_TARBALL=${1#*=}
50 | shift
51 | ;;
52 | *)
53 | echo "Unknown command: $1"
54 | exit 1
55 | ;;
56 | esac
57 | done
58 |
59 | if [ ! -f "$SDK_ZIP_TARBALL" ] ; then
60 | if [ ! -d "$SDK_PATH" ] ; then
61 | echo "Anbox Streaming SDK is missing"
62 | exit 1
63 | fi
64 | elif [ -d "$SDK_PATH" ] ; then
65 | echo "parameter '--sdk-path' and '--sdk-zip-tarbll' are mutually exclusive"
66 | exit 1
67 | fi
68 |
69 | if [ ! -f "$ALLOWLIST" ]; then
70 | echo "File allowlist is missing"
71 | exit 1
72 | fi
73 |
74 | remove() {
75 | local file_pattern=$1
76 | local sdk_version=$2
77 |
78 | # To match the versionized file like apk
79 | local path_to_remove="$(printf $1 $2)"
80 | if [ -f "$path_to_remove" ]; then
81 | rm "$path_to_remove"
82 | fi
83 |
84 | # Remove the current dir once it's empty
85 | # NOTE: Ignore the top folder.
86 | local dir_path="$(dirname $path_to_remove)"
87 | while [ -z "$(ls -A $dir_path)" ] &&
88 | [ "$dir_path" != "." ]; do
89 | rmdir $dir_path
90 | dir_path="$(dirname $dir_path)"
91 | done
92 | }
93 |
94 | search_for_remaining_files() {
95 | local dir_path=$1
96 | output=$(find "$dir_path" -type f)
97 | echo "$output"
98 | }
99 |
100 | (
101 | tmpfolder=$(mktemp -d)
102 | cleanup() {
103 | rm -rf "$tmpfolder"
104 | }
105 | trap cleanup EXIT INT
106 |
107 | if [ -d "$SDK_PATH" ]; then
108 | cp -ra "$SDK_PATH" "$tmpfolder"
109 | else
110 | unzip -qq "$SDK_ZIP_TARBALL" -d "$tmpfolder"
111 | fi
112 | cd "$tmpfolder"/anbox-streaming-sdk_*
113 | sdk_version="$(pwd | cut -d_ -f2)"
114 |
115 | # Read allowlist and search for every file into the tmp folder.
116 | # Once it is found, delete it.
117 | while IFS='' read -r line || [[ -n "$line" ]]; do
118 | remove "$line" "$sdk_version"
119 | done < "$ALLOWLIST"
120 |
121 | # There should remain no file after processing all the allowlist
122 | remaining_contents="$(search_for_remaining_files ./)"
123 | if [ -n "$remaining_contents" ]; then
124 | cat <
2 |
8 |
9 |
10 |
15 |
16 |
17 |
31 |
32 |
33 |
42 |
43 |
44 |
56 |
57 |
58 |
70 |
71 |
72 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/js/README.md:
--------------------------------------------------------------------------------
1 | # Anbox Streaming SDK
2 |
3 | The Anbox Streaming SDK is a javascript library you can plug in your website
4 | to easily establish a video stream of your Anbox instances.
5 |
6 | ## Run the Example
7 |
8 | To run the included example client you need to have docker installed. See
9 | https://docs.docker.com/engine/install/ubuntu/ for more details.
10 |
11 | Once docker is ready, you can modify example.html to point to your server
12 | and insert a valid authentication token. Afterwards simply launch the example
13 | client via:
14 |
15 | $ ./run.sh
16 |
17 | ### Usage
18 |
19 | Include the script
20 |
21 | ```html
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | ```
32 |
33 |
34 | Create a node element with an ID
35 |
36 | ```html
37 |
38 | ```
39 |
40 | create a stream gateway connector.
41 |
42 | ```javascript
43 | /**
44 | * AnboxStreamGatewayConnector enables communication between the client and the
45 | * gateway. They can be customized to add an additional layer between
46 | * the client and the gateway in order to add more features (user management,
47 | * limits, analytics, etc).
48 | * For instance, to add user management, you would create a connector that
49 | * would communicate with a service you own, which in turn would talk to the
50 | * stream gateway to create an actual streaming session.
51 | * The connector would pass that session information to the SDK which takes
52 | * care of the rest.
53 | */
54 | const connector = new AnboxStreamGatewayConnector({
55 | url: '',
56 | authToken: '',
57 | session: {
58 | app: '',
59 | },
60 | screen: {
61 | width: 1280,
62 | height: 720,
63 | fps: 25,
64 | }
65 | });
66 | ```
67 |
68 | create a stream instance with the initialized connector, which is used to connect the stream gateway to start streaming.
69 |
70 | ```javascript
71 | /**
72 | * AnboxStream creates a connection between your client and an Android instance and
73 | * displays its video & audio feed in an HTML5 player
74 | *
75 | * @param options: {object} {
76 | * connector: WebRTC Stream connector.
77 | * targetElement: ID of the DOM element to attach the video to. (required)
78 | * url: Address of the service. (required)
79 | * authToken: Authentication token acquired through /1.0/login (required)
80 | * stunServers: List ICE servers (default: [{"urls": ['stun:stun.l.google.com:19302'], username: "", password: ""}])
81 | * controls: {
82 | * keyboard: true or false, send keypress events to the Android instance. (default: true)
83 | * mouse: true or false, send mouse and touch events to the Android instance. (default: true)
84 | * gamepad: true or false, send gamepad events to the Android instance. (default: true)
85 | * },
86 | * callbacks: {
87 | * ready: function, called when the video and audio stream are ready to be inserted. (default: none)
88 | * error: function, called on stream error with the message as parameter. (default: none)
89 | * done: function, called when the stream is closed. (default: none)
90 | * },
91 | * experimental: {
92 | * disableBrowserBlock: don't throw an error if an unsupported browser is detected. (default: false)
93 | * }
94 | * }
95 | */
96 |
97 | let stream = new AnboxStream({
98 | connector: connector,
99 | targetElement: "anbox-stream",
100 | url: config.backendAddress,
101 | authToken: "abc123",
102 | callbacks: {
103 | ready: () => { console.log('video stream is ready') },
104 | error: (e) => { console.log('an error occurred:', e) },
105 | done: () => { console.log('stream has been closed') },
106 | },
107 | });
108 |
109 | stream.connect();
110 | ```
111 |
--------------------------------------------------------------------------------
/.github/actions/setup-trivy/action.yaml:
--------------------------------------------------------------------------------
1 | name: Setup Trivy for security scanning and SBOM generation
2 | description: |
3 | The action sets up Trivy, its database, and the KEV list, in an ideal case
4 | from the GitHub cache to avoid making any requests to upstream repositories.
5 | In case that no cached database, debian package, or KEV list is found, they
6 | will be downloaded and cached.
7 |
8 | inputs:
9 | arch:
10 | description: |
11 | Architecture to cache trivy for. Defaults to "amd64".
12 | require: false
13 | default: "amd64"
14 |
15 | runs:
16 | using: composite
17 | steps:
18 | - name: Calculate cache keys
19 | shell: bash
20 | id: cache_keys
21 | run: |
22 | date="$(date +'%Y-%m-%d')"
23 | echo "db=trivy-db-${{ inputs.arch }}-${date}" >> $GITHUB_OUTPUT
24 | echo "deb=trivy-deb-${{ inputs.arch }}-${date}" >> $GITHUB_OUTPUT
25 | echo "kev=kev-list-${date}" >> $GITHUB_OUTPUT
26 |
27 | - name: Restore trivy deb from cache
28 | id: cache_deb
29 | uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
30 | with:
31 | key: ${{ steps.cache_keys.outputs.deb }}
32 | path: ${{ github.workspace }}/trivy.deb
33 | restore-keys:
34 | trivy-deb-${{ inputs.arch }}-
35 |
36 | - name: Fetch debian package for Trivy
37 | if: ${{ steps.cache_deb.outputs.cache-hit != 'true' }}
38 | env:
39 | TRIVY_VERSION: "0.57.0"
40 | TRIVY_ARCH: ${{ inputs.arch == 'amd64' && '64bit' || 'ARM64' }}
41 | TRIVY_SHA256: ${{ inputs.arch == 'amd64' && '0ef038ae7078449b89af6dcdd1cdecd744f65b8b50432797cda78846448c62dd' || '8ae7a057a32d98818c8504c2484017598437e117b9c96858d5749942c99cf1dd' }}
42 | shell: bash
43 | run: |
44 | curl -L -o trivy.deb \
45 | https://github.com/aquasecurity/trivy/releases/download/v"$TRIVY_VERSION"/trivy_"$TRIVY_VERSION"_Linux-"$TRIVY_ARCH".deb
46 | echo "$TRIVY_SHA256 trivy.deb" | sha256sum --check --status
47 |
48 | - name: Install trivy debian package
49 | shell: bash
50 | run: |
51 | sudo apt install -y ./trivy.deb
52 |
53 | - name: Restore trivy db from cache
54 | id: cache_db
55 | uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
56 | with:
57 | key: ${{ steps.cache_keys.outputs.db }}
58 | path: ${{ github.workspace }}/.cache/trivy
59 | restore-keys:
60 | trivy-db-${{ inputs.arch }}-
61 |
62 | - name: Set up oras
63 | if: ${{ steps.cache_db.outputs.cache-hit != 'true' }}
64 | shell: bash
65 | env:
66 | ORAS_VERSION: "1.2.0"
67 | ORAS_SHA256: ${{ inputs.arch == 'amd64' && '5b3f1cbb86d869eee68120b9b45b9be983f3738442f87ee5f06b00edd0bab336' || '27df680a39fc2fcedc549cb737891623bc696c9a92a03fd341e9356a35836bae' }}
68 | run: |
69 | curl -L -o oras.tar.gz \
70 | https://github.com/oras-project/oras/releases/download/v"${ORAS_VERSION}"/oras_"${ORAS_VERSION}"_linux_${{ inputs.arch }}.tar.gz
71 | echo "$ORAS_SHA256 oras.tar.gz" | sha256sum --check --status
72 | tar xf oras.tar.gz oras
73 | chmod +x ./oras
74 | sudo mv oras /usr/local/bin
75 |
76 | - name: Download and extract the vulnerability DB
77 | if: ${{ steps.cache_db.outputs.cache-hit != 'true' }}
78 | shell: bash
79 | run: |
80 | mkdir -p "$GITHUB_WORKSPACE"/.cache/trivy/db
81 | oras pull ghcr.io/aquasecurity/trivy-db:2
82 | tar -xzf db.tar.gz -C "$GITHUB_WORKSPACE"/.cache/trivy/db
83 | rm db.tar.gz
84 |
85 | mkdir -p "$GITHUB_WORKSPACE"/.cache/trivy/java-db
86 | oras pull ghcr.io/aquasecurity/trivy-java-db:1
87 | tar -xzf javadb.tar.gz -C "$GITHUB_WORKSPACE"/.cache/trivy/java-db
88 | rm javadb.tar.gz
89 |
90 | - name: Restore KEV list from cache
91 | id: cache_kev
92 | uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
93 | with:
94 | key: ${{ steps.cache_keys.outputs.kev }}
95 | path: ${{ github.workspace }}/kev.json
96 | restore-keys:
97 | kev-list-
98 |
99 | - name: Fetch KEV list from CISA
100 | if: ${{ steps.cache_kev.outputs.cache-hit != 'true' }}
101 | shell: bash
102 | run: |
103 | curl -s -o kev.json https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json
104 |
--------------------------------------------------------------------------------
/scripts/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -xe
2 | #
3 | # Copyright 2019 Canonical Ltd.
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 | PROXY=
18 | CREATE_TARBALL=false
19 | VERSION=$(scripts/gen-version.sh)
20 |
21 | for p in "$@"
22 | do
23 | case $p in
24 | --proxy=*)
25 | PROXY=${p#*=}
26 | ;;
27 | --version=*)
28 | VERSION=${p#*=}
29 | ;;
30 | --create-tarball)
31 | CREATE_TARBALL=true
32 | ;;
33 | *)
34 | echo "unrecognized option $p"
35 | esac
36 | done
37 |
38 | mkdir_cp() {
39 | local dest="${!#}"
40 | mkdir -p "$dest" && cp -av "${@:1:${#}-1}" "$dest"
41 | }
42 |
43 | builddir=$(mktemp -d -p $PWD .buildXXXXXX)
44 | topdir="$PWD"
45 | trap "rm -rf ${builddir}" INT EXIT
46 |
47 | sdkname=anbox-streaming-sdk_${VERSION}
48 | sdkdir="$builddir"/"$sdkname"
49 |
50 | mkdir_cp LICENSE "$sdkdir"
51 |
52 | for f in examples js android; do
53 | mkdir -p "$sdkdir"/"$f"
54 | done
55 |
56 | # Modify the JS SDK version
57 | sed -i "s/@VERSION@/${VERSION}/" js/anbox-stream-sdk.js
58 |
59 | # Only copy the JS SDK and markdown file to the js folder
60 | cp js/README.md js/anbox-stream-sdk.js "$sdkdir"/js/
61 | for f in android js ; do
62 | cp -r examples/"$f" "$sdkdir"/examples
63 | done
64 |
65 | # Copy the streaming sdk to the android folder
66 | cp -av android/anbox_streaming_sdk "$sdkdir"/android/
67 |
68 | # Copy JS file to the each example folder of the sdk folder to avoid
69 | # a bunch of copy of js sdk in the source tree.
70 | for d in `find $sdkdir/examples -name js -type d`; do
71 | cp js/anbox-stream-sdk.js $d
72 | done
73 |
74 | for d in `find $sdkdir/examples -name assets -type d`; do
75 | mkdir_cp js/anbox-stream-sdk.js "$d"/js
76 | done
77 |
78 | if [ "$CREATE_TARBALL" = true ]; then
79 | (cd "$builddir" ; zip -r "$topdir"/"$sdkname".zip *)
80 | fi
81 |
82 | # Do a test build of our examples with the generated SDK package
83 | (
84 | # Copy the anbox-stream-sdk.js file to examples folders
85 | for d in `find $topdir/examples -name assets -type d`; do
86 | mkdir_cp js/anbox-stream-sdk.js "$d"/js
87 | done
88 |
89 | # Create a symbol link for anbox_streaming_sdk to example/android folder so that we
90 | # can create the sdk library alongside with Android example when building the APKs.
91 | cp -av "$topdir"/android/anbox_streaming_sdk "$topdir"/examples/android/anbox_streaming_sdk;
92 | "$topdir"/scripts/build-with-docker.sh --proxy="${PROXY}" \
93 | --version="${VERSION}" \
94 | --anbox-stream-sdk="$builddir"
95 |
96 | # To repack zip taball which includes APKs file later
97 | mkdir_cp assets/*.apk "$sdkname"/examples/android/apks;
98 | # To repack zip taball which includes JAR/AAR files built during the docker runtime
99 | mkdir_cp assets/*.aar "$sdkname"/android/libs
100 | mkdir_cp assets/*.aar "$sdkname"/examples/android/enhanced_webview_streaming/app/libs
101 |
102 | if [ "$CREATE_TARBALL" = true ]; then
103 | zip -r "$topdir"/"$sdkname".zip "$sdkname"/examples/android/apks/*.apk "$sdkname"/android/libs/*.aar \
104 | "$sdkname"/examples/android/enhanced_webview_streaming/app/libs/*.aar
105 |
106 | # Validate the streaming sdk to ensure we don't accidentally leak unwanted files.
107 | "$topdir"/scripts/validate.sh --sdk-zip-tarball="$topdir"/"$sdkname".zip \
108 | --allowlist="$topdir"/scripts/streaming-sdk-files.allowlist
109 | else
110 | mkdir_cp "$sdkname"/examples/android/apks/*.apk "$sdkdir"/examples/android/apks/
111 | mkdir_cp "$sdkname"/android/libs/*.aar "$sdkdir"/android/libs/
112 | mkdir_cp "$sdkname"/examples/android/enhanced_webview_streaming/app/libs/*.aar \
113 | "$sdkdir"/examples/android/enhanced_webview_streaming/app/libs/
114 |
115 | "$topdir"/scripts/validate.sh --sdk-path="$sdkdir" \
116 | --allowlist="$topdir"/scripts/streaming-sdk-files.allowlist
117 |
118 | mv "$sdkdir" "$topdir"/results
119 | fi
120 | )
121 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/README.md:
--------------------------------------------------------------------------------
1 | # Anbox Streaming SDK
2 |
3 | Anbox Streaming SDK is a Android library([AAR](https://developer.android.com/studio/projects/android-library)) which provides a set of convenient Android native components that can be integrated into a Android application project built with [WebView](https://developer.android.com/reference/android/webkit/WebView) + Anbox Streaming JS SDK for better client user experience, such as text input from the built-in virtual keyboard on client side to the remote application running in Android container.
4 |
5 | ## Import the AAR library
6 |
7 | Check out the [Anbox Streaming SDK](https://github.com/anbox-cloud/anbox-streaming-sdk) from Github
8 |
9 | ```
10 | $ git clone https://github.com/anbox-cloud/anbox-streaming-sdk.git
11 | ```
12 |
13 | Under the `android/libs` folder, you can find the AAR file `anbox_streaming_sdk.aar`, please refer to the official [documentation](https://developer.android.com/studio/projects/android-library) on how to import an Android library into an Android application project.
14 |
15 | ## Integrate components
16 |
17 | The Android library(`anbox_streaming_sdk.aar`) provides an AnboxWebView component that extends the AOSP [WebView](https://developer.android.com/reference/android/webkit/WebView) and provides better handling of the text input for an application that loads the Anbox Stream JS SDK with an embedded webview for video streaming. The feature enables developers to capture the text input from the built-in virtual keyboard on the client side and send them to the application running in the Android container.
18 |
19 | To use the AnboxWebView in your project after importing the AAR file.
20 | 1. Adjust the layout of the activity xml file that you want to use the enhanced webview component, E.g:
21 |
22 | ```
23 |
27 | ```
28 |
29 |
30 | 2. Set virtual keyboard listener to the activity that wants to capture the text input from the virtual keyboard and monitor its state(visibility) change during streaming.
31 |
32 | ```
33 | import com.canonical.anbox.streaming_sdk.AnboxWebView;
34 | ...
35 | ...
36 | ...
37 | webview = (AnboxWebView) findViewById(R.id.webview);
38 | webview.setVirtualKeyboardListener(this);
39 | ```
40 |
41 | When a request of opening virtual keyboard was sent from Android container via Anbox Streaming JS SDK through Anbox Stream specific protocol, people must call the AnboxWebView.openVirtualKeyboard() function to pop up virtual keyboard on the client side.
42 | To receive the message sending from Javascript through the protocol, it can be done via adding [javascript interface](https://developer.android.com/guide/webapps/webview#BindingJavaScript).
43 |
44 |
45 | Override the interface method `onVirtualKeyboardTextChanged` so when captured texts are changed,
46 | the following callback function will be invoked, a developer must send them to Android container via Anbox Streaming JS SDK through Anbox Stream specific protocol. This could be done via [executing javascript](https://developer.android.com/reference/android/webkit/WebView#evaluateJavascript) functions in WebView.
47 |
48 | ```
49 | @Override
50 | public void onVirtualKeyboardTextChanged(String text) {
51 | Log.i(TAG, "virtual keyboard text changed: " + text);
52 | }
53 | ```
54 |
55 | So that the EditText or TextInput widget placed in an application running in the Android container can receive and display them in the UI via Anbox correspondingly.
56 |
57 | Override the interface method `onVirtualKeyboardStateChanged` to invoke the callback function
58 | when the state of virtual keyboard is changed.
59 |
60 | ```
61 | @Override
62 | public void onVirtualKeyboardStateChanged(boolean visible) {
63 | Log.i(TAG, "virtual keyboard visibility state changed: " + visible);
64 | }
65 | ```
66 |
67 | NOTE: there are two application scenarios that the above function got triggered
68 | when virtual keyboard is hidden(visible -> false).
69 | - Call AnboxWebView.hideVirtualKeyboard() when receiving the request of hiding virtual keyboard sendingfrom Android container via Anbox Streaming JS SDK.
70 | - Hide the virtual keyboard on the client side by clicking the pop-down button.
71 |
72 | For case(2), when this callback function will be triggered, people must notify the Android container to hide the virtual keyboard via Anbox Streaming SDK through the protocol, otherwise the virtual keyboard may misbehave on both ends.
73 |
--------------------------------------------------------------------------------
/scripts/build-with-docker.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -ex
2 | #
3 | # Copyright 2020 Canonical Ltd.
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 | DOCKER_IMAGE_NAME=anbox-stream-sdk
18 | DOCKER_IMAGE_TAG=latest
19 | UBUNTU_VERSION=24.04
20 | BASE_DOCKER_IMAGE="ubuntu:$UBUNTU_VERSION"
21 | ANDROID_NDK_VERSION=21.0.6113669
22 | ANDROID_HOME=/usr/local/android-sdk
23 | ANDROID_PLATFORM_VERSION=30
24 | ANDROID_BUILD_TOOLS_VERSION=34.0.0
25 | SDK_URL="https://dl.google.com/android/repository/commandlinetools-linux-6858069_latest.zip"
26 | CMAKE_VERSION=3.10.2.4988404
27 |
28 | sdk=
29 | proxy=
30 | version=
31 |
32 | while [ -n "$1" ]; do
33 | case "$1" in
34 | --anbox-stream-sdk=*)
35 | sdk=${1#*=}
36 | shift
37 | ;;
38 | --proxy=*)
39 | proxy=${1#*=}
40 | shift
41 | ;;
42 | --version=*)
43 | version=${1#*=}
44 | shift
45 | ;;
46 | *)
47 | echo "ERROR: Unknown argument $1"
48 | exit 1
49 | ;;
50 | esac
51 | done
52 |
53 | if [ ! -e "$sdk" ]; then
54 | echo "ERROR: Missing Anbox Streaming SDK"
55 | exit 1
56 | fi
57 |
58 | # Build the docker container. If the container is already up to date this
59 | # will be a no-op
60 | workdir=$(pwd)
61 | cd $(mktemp -d)
62 | cat << EOF > Dockerfile
63 | FROM $BASE_DOCKER_IMAGE
64 |
65 | ENV DEBIAN_FRONTEND=noninteractive \
66 | ANDROID_HOME=/usr/local/android-sdk
67 |
68 | RUN apt update -qq && apt install -qq -y \
69 | sudo \
70 | wget \
71 | unzip \
72 | zip \
73 | curl
74 |
75 | # The jenkins user must be setup so that the work directory which is owned
76 | # by jenkins is accessible
77 | RUN useradd -u $(id -u) -U jenkins
78 | RUN echo "jenkins ALL = NOPASSWD: ALL" >> /etc/sudoers
79 | RUN mkdir /work && chown jenkins:jenkins /work
80 | RUN mkdir /home/jenkins && chown -R jenkins:jenkins /home/jenkins
81 |
82 | RUN apt install -qq -y \
83 | openjdk-17-jdk-headless \
84 | gradle
85 |
86 | # Download Android SDK
87 | RUN mkdir ${ANDROID_HOME} \
88 | && chmod a+w ${ANDROID_HOME} \
89 | && cd ${ANDROID_HOME} \
90 | && curl -o sdk.zip $SDK_URL \
91 | && unzip sdk.zip \
92 | && rm sdk.zip \
93 | && mkdir -p "${ANDROID_HOME}/licenses" \
94 | && echo "24333f8a63b6825ea9c5514f83c2829b004d1fee" > "${ANDROID_HOME}/licenses/android-sdk-license"
95 |
96 | # Install Android Build Tool, NDK and cmake
97 | RUN ${ANDROID_HOME}/cmdline-tools/bin/sdkmanager --sdk_root="${ANDROID_HOME}" --update
98 | RUN ${ANDROID_HOME}/cmdline-tools/bin/sdkmanager --sdk_root="${ANDROID_HOME}" \
99 | "build-tools;${ANDROID_BUILD_TOOLS_VERSION}" \
100 | "platforms;android-${ANDROID_PLATFORM_VERSION}" \
101 | "ndk;${ANDROID_NDK_VERSION}" \
102 | "cmake;${CMAKE_VERSION}" \
103 | "platform-tools"
104 | EOF
105 |
106 | if [ -n "$proxy" ]; then
107 | proxy_host=$(echo "$proxy" | awk -F '[/:]' '{print $4}')
108 | proxy_port=$(echo "$proxy" | awk -F '[/:]' '{print $5}')
109 |
110 | cat << EOF > gradle.properties
111 | systemProp.http.proxyHost=$proxy_host
112 | systemProp.http.proxyPort=$proxy_port
113 | systemProp.https.proxyHost=$proxy_host
114 | systemProp.https.proxyPort=$proxy_port
115 | EOF
116 |
117 | cat << EOF >> Dockerfile
118 | RUN mkdir /home/jenkins/.gradle && chown -R jenkins:jenkins /home/jenkins/.gradle
119 | COPY gradle.properties /home/jenkins/.gradle
120 | EOF
121 | fi
122 |
123 | docker build -t "$DOCKER_IMAGE_NAME":"$DOCKER_IMAGE_TAG" .
124 | cd "$workdir" && mkdir -p assets
125 | builddir=$(mktemp -p "$PWD" -d .build.XXXXXX)
126 | cleanup() {
127 | rm -rf "$builddir"
128 | }
129 | trap cleanup INT EXIT TERM
130 |
131 | cp -ra "$sdk" examples scripts "$builddir"
132 | docker run --rm \
133 | --network host \
134 | -v "$PWD"/assets:/work \
135 | -v $builddir:/work/src \
136 | -u $(id -u $USER) \
137 | "$DOCKER_IMAGE_NAME":"$DOCKER_IMAGE_TAG" \
138 | /work/src/scripts/clean-build.sh --version="${version}" $@
139 |
--------------------------------------------------------------------------------
/.github/workflows/tics.yaml:
--------------------------------------------------------------------------------
1 | name: Run code quality scan
2 | on:
3 | workflow_dispatch:
4 | # FIXME put on hold until infrastructure limits are sorted. Until
5 | # then we only run weekly on Sundays.
6 | # push:
7 | # branches:
8 | # - main
9 | schedule:
10 | # Run every Sunday on 8am
11 | - cron: "0 8 * * 0"
12 |
13 |
14 | concurrency:
15 | group: tics
16 | cancel-in-progress: false
17 |
18 | jobs:
19 | tics-scan:
20 | name: Run TICS quality scan
21 | runs-on: [self-hosted, linux, amd64, tiobe, jammy]
22 | steps:
23 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
24 | - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
25 | with:
26 | node-version: 24
27 |
28 | - name: Determine test configuration
29 | id: config
30 | run: |
31 | echo "base_version=$(cat .base_version)" >> "$GITHUB_OUTPUT"
32 |
33 | - name: Setup Anbox Cloud
34 | uses: canonical/anbox-cloud-github-action@ac73782f4b581a69aa12650bda5eccc865cd038d
35 | with:
36 | channel: ${{ steps.config.outputs.base_version }}/edge
37 |
38 | - name: Tune installation
39 | run: |
40 | amc config set container.security_updates false
41 |
42 | - name: Restore cached images
43 | uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
44 | with:
45 | path: images
46 | key: anbox-images-amd64
47 | restore-keys: |
48 | anbox-images-amd64-
49 |
50 | - name: Import cached images
51 | run: |
52 | for name in android14 aaos15 ; do
53 | amc image add jammy:"$name":amd64 "$GITHUB_WORKSPACE"/images/"$name"_amd64.tar.xz
54 | done
55 |
56 | - name: Register trust certificate with AMS
57 | working-directory: js/tests/e2e
58 | run: |
59 | openssl req -x509 -newkey rsa:4096 -keyout anbox-cloud.key \
60 | -out anbox-cloud.crt -days 365 -nodes \
61 | -subj "/C=GB/ST=London/L=London/O=Global Security/OU=IT Department/CN=example.com"
62 | amc config trust add ./anbox-cloud.crt
63 |
64 | - name: Configure env variables
65 | working-directory: js/tests/e2e
66 | run: |
67 | echo "CI=true" > .env.local
68 | echo "AMS_API_CERTIFICATE=anbox-cloud.crt" >> .env.local
69 | echo "AMS_API_CERTIFICATE_KEY=anbox-cloud.key" >> .env.local
70 | echo "AMS_API_URL=$(sudo cat /var/snap/anbox-cloud-appliance/common/dashboard/config.yaml | grep AMS_API_URL | cut -d ' ' -f2)" >> .env.local
71 | echo "ASG_API_URL=$(sudo cat /var/snap/anbox-cloud-appliance/common/dashboard/config.yaml | grep ASG_API_URL | cut -d ' ' -f2)" >> .env.local
72 | echo "ASG_API_TOKEN=$(sudo cat /var/snap/anbox-cloud-appliance/common/dashboard/config.yaml | grep ASG_API_TOKEN | cut -d ' ' -f2)" >> .env.local
73 |
74 | - name: Run tests, generate coverage report
75 | working-directory: js/tests
76 | run: |
77 | ./combined-coverage-report.sh
78 |
79 | - name: Dump logs on failure
80 | if: failure()
81 | run: |
82 | sudo snap logs -n all anbox-cloud-appliance > appliance.log
83 | sudo anbox-cloud-appliance.buginfo > appliance.buginfo
84 | for id in $(amc ls --format=csv | cut -d',' -f1) ; do
85 | status=$(amc show "$id" --format=json | jq -r .status)
86 | if [ "$status" = error ] ; then
87 | for log in $(amc show "$id" --format=json | jq -r '.stored_logs[]' | xargs) ; do
88 | amc show-log "$id" "$log" |& tee -a "$id"_"$log"
89 | done
90 | elif [ "$status" = started ] || [ "$status" = running ]; then
91 | for name in android anbox ; do
92 | timeout 30s amc logs "$id" -t "$name" |& tee -a "$id"_"$name".log
93 | done
94 | fi
95 | done
96 |
97 | - name: Upload test results and coverage data
98 | if: always()
99 | uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
100 | with:
101 | name: tics-test-reports
102 | path: |
103 | js/tests/e2e/test-results
104 | js/tests/e2e/coverage
105 | # Keep for a bit longer to allow investigation on older workflow runs
106 | retention-days: 7
107 |
108 | - name: Run TICS scan
109 | env:
110 | TICSAUTHTOKEN: ${{ secrets.TICSAUTHTOKEN }}
111 | run: |
112 | set -x
113 | source ~/.profile
114 | TICSQServer -project anbox-streaming-sdk -tmpdir /tmp/tics
115 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/app/src/main/java/com/canonical/anbox/streaming/sdk/webview_example/MainActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Anbox Cloud Streaming SDK
3 | *
4 | * Copyright 2022 Canonical Ltd.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.canonical.anbox.streaming.sdk.webview_example;
20 |
21 | import android.content.Intent;
22 | import android.os.Build;
23 | import android.os.Bundle;
24 | import android.util.Log;
25 | import android.view.View;
26 | import android.widget.EditText;
27 | import android.widget.Switch;
28 | import android.widget.Toast;
29 |
30 | import androidx.annotation.RequiresApi;
31 | import androidx.appcompat.app.AppCompatActivity;
32 |
33 | import org.json.JSONArray;
34 | import org.json.JSONException;
35 | import org.json.JSONObject;
36 |
37 | import java.io.IOException;
38 | import java.util.ArrayList;
39 | import java.util.List;
40 |
41 |
42 | public class MainActivity extends AppCompatActivity {
43 | private static final String LOG_TAG = MainActivity.class.getSimpleName();
44 |
45 | public static final String EXTRA_SIGNALING_URL
46 | = "com.canonical.anbox.streaming.sdk.webview_example.EXTRA_SIGNALING_URL";
47 | public static final String EXTRA_API_TOKEN
48 | = "com.canonical.anbox.streaming.sdk.webview_example.EXTRA_API_TOKEN";
49 | public static final String EXTRA_APP_NAME
50 | = "com.canonical.anbox.streaming.sdk.webview_example.EXTRA_APP_NAME";
51 |
52 | @Override
53 | protected void onCreate(Bundle savedInstanceState) {
54 | super.onCreate(savedInstanceState);
55 | setContentView(R.layout.activity_main);
56 |
57 | Intent launchIntent = getIntent();
58 | String apiToken = launchIntent.getStringExtra("api_token");
59 | String gatewayURL = launchIntent.getStringExtra("gateway_url");
60 | String appName = launchIntent.getStringExtra("app_name");
61 | boolean useInsecureTLS = launchIntent.getBooleanExtra("use_insecure_tls", false);
62 |
63 | if (apiToken != null && apiToken.length() > 0) {
64 | final EditText apiTokenBox = findViewById(R.id.api_token);
65 | apiTokenBox.setText(apiToken);
66 | }
67 | if (gatewayURL != null && gatewayURL.length() > 0) {
68 | final EditText gatewayURLBox = findViewById(R.id.gateway_url);
69 | gatewayURLBox.setText(gatewayURL);
70 | }
71 | if (appName != null && appName.length() > 0) {
72 | final EditText appNameBox = findViewById(R.id.app_name);
73 | appNameBox.setText(appName);
74 | }
75 | }
76 |
77 | @RequiresApi(api = Build.VERSION_CODES.KITKAT)
78 | public void startStreaming(View view) {
79 | final EditText apiTokenBox = findViewById(R.id.api_token);
80 | final EditText gatewayURLBox = findViewById(R.id.gateway_url);
81 | final EditText appNameBox = findViewById(R.id.app_name);
82 |
83 | String apiToken = apiTokenBox.getText().toString();
84 | String gatewayURL = gatewayURLBox.getText().toString();
85 | String appName = appNameBox.getText().toString();
86 |
87 | if (apiToken.length() == 0 || gatewayURL.length() == 0 || appName.length() == 0) {
88 | Toast.makeText(this, "Missing gateway URL, API token or application name", Toast.LENGTH_SHORT).show();
89 | return;
90 | }
91 |
92 | // In case of the given URL contains a trailing slash, we get rid of it
93 | // since it potentially causes IOException when talking to stream gateway.
94 | if (gatewayURL.charAt(gatewayURL.length() - 1) == '/') {
95 | gatewayURL = gatewayURL.substring(0, gatewayURL.length() - 1);
96 | }
97 |
98 | Intent intent = new Intent(MainActivity.this, StreamActivity.class);
99 | intent.putExtra(EXTRA_SIGNALING_URL, gatewayURL);
100 | intent.putExtra(EXTRA_API_TOKEN, apiToken);
101 | intent.putExtra(EXTRA_APP_NAME, appName);
102 | startActivity(intent);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/js/tests/e2e/tests/visual.spec.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Anbox Cloud Streaming SDK
3 | *
4 | * Copyright 2024 Canonical Ltd.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | import { expect, test } from "../fixtures/anbox-test";
20 | import { joinSession, disconnectStream } from "./shared";
21 |
22 | const OPTIONS = {
23 | clip: {
24 | x: 438,
25 | y: 30, // crop the status bar, which has changing content (date/time)
26 | width: 405,
27 | height: 690,
28 | },
29 | maxDiffPixels: 25, // allowing some leeway for the EditText caret
30 | };
31 |
32 | const swipeCoordinates = {
33 | openStatusBar: {
34 | x: 640,
35 | startY: 10,
36 | endY: 300,
37 | },
38 | openAppDrawer: {
39 | x: 640,
40 | startY: 400,
41 | endY: 50,
42 | },
43 | }
44 |
45 | const tapHomeButton = async (page) => {
46 | await page.mouse.click(640, 700);
47 | // Wait 2s (home screen animation)
48 | await page.waitForTimeout(2000);
49 | };
50 |
51 | const swipeVertically = async (page, action) => {
52 | if(Object.keys(swipeCoordinates).includes(action)){
53 | await page.mouse.move(swipeCoordinates[action].x, swipeCoordinates[action].startY);
54 | await page.mouse.down();
55 | await page.mouse.move(swipeCoordinates[action].x, swipeCoordinates[action].endY);
56 | await page.mouse.up();
57 | // Wait for action animation
58 | await page.waitForTimeout(1000);
59 | } else {
60 | test.fail(true, "Action does not exist in swipeCoordinates");
61 | }
62 | }
63 |
64 | const openSearchBar = async (page) => {
65 | await page.mouse.click(525, 125);
66 | // Wait 1s (search screen animation)
67 | await page.waitForTimeout(1000);
68 | };
69 |
70 | const typeSearch = async (page, text) => {
71 | await page.keyboard.type(text);
72 | // Wait 1s (text input)
73 | await page.waitForTimeout(1000);
74 | await expect(page).toHaveScreenshot("app-drawer-settings.png", OPTIONS);
75 | };
76 |
77 | const longPressHome = async (page) => {
78 | await page.locator("#anbox-stream").click({
79 | position: { x: 640, y: 350 },
80 | delay: 1000,
81 | });
82 | await expect(page).toHaveScreenshot("long-press-home.png", OPTIONS);
83 | };
84 |
85 | test("AOSP visual tests: touch and keyboard input", async ({ browser }) => {
86 | // Since Playwright does not support mobile gestures like the swipe one, we
87 | // need to give up touch for this test, and use regular mouse events instead
88 | const context = await browser.newContext({ hasTouch: false });
89 | const page = await context.newPage();
90 |
91 | await joinSession(page, process.env.AOSP_SESSION_ID);
92 | await tapHomeButton(page);
93 |
94 | await swipeVertically(page, "openAppDrawer");
95 | await openSearchBar(page);
96 | await typeSearch(page, "Settings");
97 |
98 | await tapHomeButton(page);
99 | await disconnectStream(page);
100 | });
101 |
102 | test("AOSP visual tests: long press input", async ({ browser }) => {
103 | // Since Playwright does not support long press using the tap() method, we
104 | // need to give up touch for this test, and use regular click events instead
105 | const context = await browser.newContext({ hasTouch: false });
106 | const page = await context.newPage();
107 |
108 | await joinSession(page, process.env.AOSP_SESSION_ID);
109 | await longPressHome(page);
110 | await disconnectStream(page);
111 | });
112 |
113 | test("AOSP visual tests: swipe", async ({ browser }) => {
114 | // Since Playwright does not support mobile gestures like the swipe one, we
115 | // need to give up touch for this test, and use regular mouse events instead
116 | const context = await browser.newContext({ hasTouch: false });
117 | const page = await context.newPage();
118 |
119 | await joinSession(page, process.env.AOSP_SESSION_ID);
120 | await swipeVertically(page, "openStatusBar");
121 | // Wait 1s (system UI expand animation)
122 | await page.waitForTimeout(1000);
123 | await expect(page).toHaveScreenshot("status-bar-swipe.png", OPTIONS);
124 | await disconnectStream(page);
125 | });
126 |
--------------------------------------------------------------------------------
/examples/android/enhanced_webview_streaming/app/src/main/java/com/canonical/anbox/streaming_sdk/enhanced_webview_example/MainActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Anbox Cloud Streaming SDK
3 | *
4 | * Copyright 2022 Canonical Ltd.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.canonical.anbox.streaming_sdk.enhanced_webview_example;
20 |
21 | import android.content.Intent;
22 | import android.os.Build;
23 | import android.os.Bundle;
24 | import android.util.Log;
25 | import android.view.View;
26 | import android.widget.EditText;
27 | import android.widget.Switch;
28 | import android.widget.Toast;
29 |
30 | import androidx.annotation.RequiresApi;
31 | import androidx.appcompat.app.AppCompatActivity;
32 |
33 | import org.json.JSONArray;
34 | import org.json.JSONException;
35 | import org.json.JSONObject;
36 |
37 | import java.io.IOException;
38 | import java.util.ArrayList;
39 | import java.util.List;
40 |
41 |
42 | public class MainActivity extends AppCompatActivity {
43 | private static final String LOG_TAG = MainActivity.class.getSimpleName();
44 |
45 | public static final String EXTRA_SIGNALING_URL
46 | = "com.canonical.anbox.streaming.sdk.webview_example.EXTRA_SIGNALING_URL";
47 | public static final String EXTRA_API_TOKEN
48 | = "com.canonical.anbox.streaming.sdk.webview_example.EXTRA_API_TOKEN";
49 | public static final String EXTRA_APP_NAME
50 | = "com.canonical.anbox.streaming.sdk.webview_example.EXTRA_APP_NAME";
51 |
52 | @Override
53 | protected void onCreate(Bundle savedInstanceState) {
54 | super.onCreate(savedInstanceState);
55 | setContentView(R.layout.activity_main);
56 |
57 | Intent launchIntent = getIntent();
58 | String apiToken = launchIntent.getStringExtra("api_token");
59 | String gatewayURL = launchIntent.getStringExtra("gateway_url");
60 | String appName = launchIntent.getStringExtra("app_name");
61 | boolean useInsecureTLS = launchIntent.getBooleanExtra("use_insecure_tls", false);
62 |
63 | if (apiToken != null && apiToken.length() > 0) {
64 | final EditText apiTokenBox = findViewById(R.id.api_token);
65 | apiTokenBox.setText(apiToken);
66 | }
67 | if (gatewayURL != null && gatewayURL.length() > 0) {
68 | final EditText gatewayURLBox = findViewById(R.id.gateway_url);
69 | gatewayURLBox.setText(gatewayURL);
70 | }
71 | if (appName != null && appName.length() > 0) {
72 | final EditText appNameBox = findViewById(R.id.app_name);
73 | appNameBox.setText(appName);
74 | }
75 | }
76 |
77 | @RequiresApi(api = Build.VERSION_CODES.KITKAT)
78 | public void startStreaming(View view) {
79 | final EditText apiTokenBox = findViewById(R.id.api_token);
80 | final EditText gatewayURLBox = findViewById(R.id.gateway_url);
81 | final EditText appNameBox = findViewById(R.id.app_name);
82 |
83 | String apiToken = apiTokenBox.getText().toString();
84 | String gatewayURL = gatewayURLBox.getText().toString();
85 | String appName = appNameBox.getText().toString();
86 |
87 | if (apiToken.length() == 0 || gatewayURL.length() == 0 || appName.length() == 0) {
88 | Toast.makeText(this, "Missing gateway URL, API token or application name", Toast.LENGTH_SHORT).show();
89 | return;
90 | }
91 |
92 | // In case of the given URL contains a trailing slash, we get rid of it
93 | // since it potentially causes IOException when talking to stream gateway.
94 | if (gatewayURL.charAt(gatewayURL.length() - 1) == '/') {
95 | gatewayURL = gatewayURL.substring(0, gatewayURL.length() - 1);
96 | }
97 |
98 | Intent intent = new Intent(MainActivity.this, StreamActivity.class);
99 | intent.putExtra(EXTRA_SIGNALING_URL, gatewayURL);
100 | intent.putExtra(EXTRA_API_TOKEN, apiToken);
101 | intent.putExtra(EXTRA_APP_NAME, appName);
102 | startActivity(intent);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Anbox Streaming SDK
2 |
3 | ## Description
4 |
5 | The Anbox Streaming SDK empowers developers to create personalized streaming clients that interface with [Anbox Cloud](https://anbox-cloud.io/).
6 |
7 | ## Usage
8 |
9 | ### [Javascript Library](js)
10 |
11 | The JS SDK library manages all streaming facets, from the intricate WebRTC protocol to the seamless integration of controls, gamepads, speakers, and screen resolutions. Additionally, it incorporates an Android library that provides user-friendly native components for Android application integration.
12 |
13 | ### [Android Library](android/anbox_streaming_sdk)
14 |
15 | The Android library offers user-friendly native components, including AnboxWebView, an extension of the AOSP WebView. This library enhances text input handling for hybrid applications loading the Anbox Streaming JavaScript SDK within an embedded WebView for video streaming.
16 |
17 | Please navigate to each library folder for instructions on compiling the Javascript or Android libraries and integrating it into your project.
18 |
19 | ## Examples
20 |
21 | Refer to the following examples to explore advanced usage scenarios and best practices:
22 |
23 | * [Simple Android Webview Streaming](examples/android/webview_streaming) - An Android example that embeds WebView integrated with the JS SDK for video streaming.
24 | * [Enhanced Android Webview Streaming](examples/android/enhanced_webview_streaming) - An Android example that embeds AnboxWebView for text input handling enhancement for a hybrid application.
25 | * [Out of Band](examples/android/out_of_band_v2) - An Android example demonstrating the usage of [out of band data](https://documentation.ubuntu.com/anbox-cloud/en/latest/howto/stream/exchange-oob-data/#oob-v2) feature which exchanges data between the Android application running in the Anbox container and a streaming client.
26 | * [JavaScript-based Streaming Example](examples/js) - A web application utilizes the JS SDK for video streaming.
27 |
28 | ## Testing
29 |
30 | The JS SDK library comes with two test suites: one for unit tests and another for end-to-end tests. Unit tests use [Jest](https://jestjs.io), and end-to-end tests use [Playwright](https://playwright.dev).
31 |
32 | - Unit tests are located in `js/tests/unit`.
33 | - End-to-end tests are located in `js/tests/e2e`.
34 |
35 | Both these folders contain a `run-tests.sh` script, which runs the tests inside a Docker container. Check how to install Docker [here](https://docs.docker.com/engine/install/).
36 |
37 | If you want to run end-to-end tests, you need an Anbox Cloud deployment. The easiest way to set up one is by using the [Anbox Cloud Appliance](https://documentation.ubuntu.com/anbox-cloud/en/latest/tutorial/installing-appliance/).
38 |
39 | Once the appliance is ready, follow these steps to run the end-to-end tests:
40 |
41 | - In the `js/tests/e2e` folder, create a `.env.local` file.
42 | - Add the following environment variables in the `.env.local` file:
43 | - `ASG_API_TOKEN=my-asg-api-token`
44 | Token to access the stream gateway API. See reference [here](https://documentation.ubuntu.com/anbox-cloud/en/latest/howto/stream/access-stream-gateway/#creating-a-token).
45 | - `AMS_API_CERTIFICATE=./path-to-ams-cert.crt`
46 | A certificate that is trusted by AMS. See reference [here](https://documentation.ubuntu.com/anbox-cloud/en/latest/howto/anbox/control-ams-remotely/#install-a-trusted-certificate).
47 | - `AMS_API_CERTIFICATE_KEY=./path-to-ams-cert.key`
48 | The key for the certificate above.
49 | - `AMS_API_URL=https://my-ams-url:8444`
50 | URL (including port) of the AMS back-end.
51 | - `ASG_API_URL=https://my-asg-url:9031`
52 | URL (including port) of the ASG back-end.
53 | - Run the `run-tests.sh` script in the `js/tests/e2e` folder.
54 |
55 | When you run tests with the `run-tests.sh` script, coverage data will be generated in the following folders:
56 |
57 | - `js/tests/unit/coverage` for unit tests.
58 | - `js/tests/e2e/coverage` for end-to-end tests.
59 |
60 | To combine the coverage of the unit tests with that of the end-to-end tests, and generate a report using [nyc](https://github.com/istanbuljs/nyc), you can run the `test-report-coverage` script:
61 |
62 | - First, execute the `run-tests.sh` scripts in both the `js/tests/unit` and the `js/tests/e2e` folders. This will run the tests, and produce coverage data for both suites.
63 | - In the `js/tests/e2e` folder, execute `npm run test-report-coverage`. The report will be stored in the `js/tests/e2e/coverage/combined-report` folder.
64 |
65 | ## Contributing
66 |
67 | We welcome contributions from the community to enhance the Anbox Streaming SDK. To contribute, please follow these guidelines:
68 |
69 | 1. Fork the repository and create your fork from the `main` branch.
70 | 2. Ensure that your code adheres to the established [coding standards and practices](https://ubuntu.com/community/ethos/code-of-conduct).
71 | 3. Test your changes to ensure that automated tests pass with the modifications you made.
72 | 4. Provide relevant documentation updates if needed.
73 | 5. Sign the [contributor agreement](https://ubuntu.com/legal/contributors), submit a pull request, outlining the purpose and scope of your changes.
74 |
75 | Our team will review your contribution and collaborate with you to integrate it into the SDK.
76 |
77 | ## Bug report
78 |
79 | To report issues, please submit a [bug](https://bugs.launchpad.net/anbox-cloud/+filebug) to Anbox Cloud project on launchpad.
80 |
81 | ## License
82 |
83 | The Anbox Streaming SDK is licensed under the Apache License 2.0. For more details, refer to the [LICENSE](LICENSE) file for details.
84 |
--------------------------------------------------------------------------------
/android/anbox_streaming_sdk/src/main/java/com/canonical/anbox/streaming_sdk/IMEService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Anbox Cloud Streaming SDK
3 | *
4 | * Copyright 2022 Canonical Ltd.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.canonical.anbox.streaming_sdk;
20 |
21 | import android.app.Activity;
22 | import android.content.Context;
23 | import android.graphics.Point;
24 | import android.graphics.Rect;
25 | import android.os.Handler;
26 | import android.os.Looper;
27 | import android.util.Log;
28 | import android.view.Display;
29 | import android.view.View;
30 | import android.view.ViewTreeObserver;
31 | import android.view.Window;
32 | import android.view.inputmethod.InputMethodManager;
33 |
34 |
35 | public class IMEService implements Runnable, ViewTreeObserver.OnGlobalLayoutListener {
36 |
37 | private static final String TAG = IMEService.class.getSimpleName();
38 |
39 | private static final int INTERVAL_MS = 100;
40 |
41 | private Window mWindow;
42 | private View mContentView;
43 |
44 | private Context mContext;
45 | private View mTargetView;
46 | private Handler mHandler;
47 |
48 | private boolean mIsKeyboardVisible = false;
49 | public interface KeyboardStateListener {
50 | void onKeyboardStateChanged(boolean isShow, double displayRatio);
51 | }
52 | private KeyboardStateListener mKeyboardStateListener;
53 |
54 | public IMEService(Context context, View targetView) {
55 | mContext = context;
56 | mTargetView = targetView;
57 | mHandler = new Handler(Looper.getMainLooper());
58 | mKeyboardStateListener = (KeyboardStateListener) mTargetView;
59 |
60 | mContentView = ((Activity) context).findViewById(android.R.id.content);
61 | mWindow = ((Activity) context).getWindow();
62 | if (mContentView != null && mWindow != null) {
63 | mContentView.getViewTreeObserver().addOnGlobalLayoutListener(this);
64 | }
65 | }
66 |
67 | @Override
68 | public void run() {
69 | if (mContext == null || mTargetView == null) {
70 | return;
71 | }
72 |
73 | // NOTE: if for some reasons the virtual keyboard doesn't popup from the very first time,
74 | // we will request the focus again by retrying via post method.
75 | // This ensure virtual keyboard popup everything on request
76 | InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
77 | if (!mTargetView.isFocusable() || !mTargetView.isFocusableInTouchMode()) {
78 | Log.d(TAG,"focusable = " + mTargetView.isFocusable() + ", focusableInTouchMode = " + mTargetView.isFocusableInTouchMode());
79 | return;
80 | } else if (!mTargetView.requestFocus()) {
81 | Log.d(TAG,"Cannot focus on view");
82 | post();
83 | } else if (!imm.showSoftInput(mTargetView, InputMethodManager.SHOW_FORCED)) {
84 | // NOTE: use InputMethodManager.SHOW_FORCED here rather than InputMethodManager.SHOW_IMPLICIT
85 | // to force display IME when screen orientation is in landscape node.
86 | Log.d(TAG,"Unable to show virtual keyboard");
87 | post();
88 | }
89 | }
90 |
91 | public void show() {
92 | mHandler.post(this);
93 | }
94 |
95 | public void hide() {
96 | if (mContext == null || mTargetView == null) {
97 | return;
98 | }
99 |
100 | InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
101 | imm.hideSoftInputFromWindow(mTargetView.getWindowToken(), 0);
102 | }
103 |
104 | protected void post() {
105 | mHandler.postDelayed(this, INTERVAL_MS);
106 | }
107 |
108 | @Override
109 | public void onGlobalLayout() {
110 | if (mWindow == null || mContentView == null || mContentView.getHeight() == 0) {
111 | return;
112 | }
113 |
114 | Point p = new Point();
115 | Display defaultDisplay = mWindow.getWindowManager().getDefaultDisplay();
116 | defaultDisplay.getRealSize(p);
117 | int screenHeight = p.y;
118 |
119 | Rect rect = new Rect();
120 | mWindow.getDecorView().getWindowVisibleDisplayFrame(rect);
121 | int windowBottom = rect.bottom;
122 | int keyboardHeight = screenHeight - windowBottom;
123 |
124 | // Do not use the fixed height here to avoid error when
125 | // changing screen orientation
126 | boolean isVisible = keyboardHeight > screenHeight / 4.0;
127 | if (mIsKeyboardVisible != isVisible) {
128 | mIsKeyboardVisible = isVisible;
129 |
130 | if (mKeyboardStateListener != null) {
131 | double displayRatio = keyboardHeight / (double)screenHeight;
132 | mKeyboardStateListener.onKeyboardStateChanged(mIsKeyboardVisible, displayRatio);
133 | }
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/.github/workflows/pr.yaml:
--------------------------------------------------------------------------------
1 | name: Build and run tests for a PR
2 |
3 | on:
4 | pull_request:
5 |
6 | concurrency:
7 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
8 | cancel-in-progress: true
9 |
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout repository
15 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
16 | with:
17 | # to avoid shadow clone which would impact the version generation
18 | # See `scripts/gen-version.sh`
19 | fetch-depth: "0"
20 |
21 | - name: Get version
22 | id: get-version
23 | run: |
24 | echo "version=$(./scripts/gen-version.sh)" >> $GITHUB_OUTPUT
25 |
26 | - name: Build
27 | run: |
28 | ./scripts/build.sh
29 |
30 | - name: Run tests
31 | run: |
32 | ./scripts/run-tests.sh
33 |
34 | - name: Collect artifacts
35 | uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
36 | with:
37 | name: anbox-streaming-sdk_${{ steps.get-version.outputs.version }}
38 | if-no-files-found: error
39 | path: results/
40 |
41 | - name: Setup Trivy
42 | if: ${{ inputs.generate-sbom == 'true' }}
43 | uses: ./.github/actions/setup-trivy
44 | with:
45 | arch: ${{ matrix.arch }}
46 |
47 | - name: Generate SBOM for source code
48 | if: ${{ inputs.generate-sbom == 'true' }}
49 | run: |
50 | trivy repository "$GITHUB_WORKSPACE" \
51 | --format=spdx-json \
52 | --skip-dirs=packaging \
53 | --output=source-spdx.json \
54 | --cache-dir=./.cache/trivy
55 |
56 | run-e2e-tests:
57 | needs: [build]
58 | name: Run e2e tests (appliance, amd64)
59 | runs-on: [self-hosted, linux, amd64, noble, large]
60 | steps:
61 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
62 |
63 | - name: Store current date and time in output
64 | id: datetime
65 | run: echo "datetime=$(date -u +'%Y-%m-%dT%H.%M.%SZ')" >> $GITHUB_OUTPUT
66 |
67 | - name: Determine test configuration
68 | id: config
69 | run: |
70 | echo "base_version=$(cat .base_version)" >> "$GITHUB_OUTPUT"
71 |
72 | - name: Setup Anbox Cloud
73 | uses: canonical/anbox-cloud-github-action@ac73782f4b581a69aa12650bda5eccc865cd038d
74 | with:
75 | channel: ${{ steps.config.outputs.base_version }}/edge
76 |
77 | - name: Tune installation
78 | run: |
79 | amc config set container.security_updates false
80 |
81 | - name: Restore cached images
82 | uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
83 | with:
84 | path: images
85 | key: anbox-images-amd64
86 | restore-keys: |
87 | anbox-images-amd64-
88 |
89 | - name: Import cached images
90 | run: |
91 | for name in android14 aaos15 ; do
92 | amc image add jammy:"$name":amd64 "$GITHUB_WORKSPACE"/images/"$name"_amd64.tar.xz
93 | done
94 |
95 | - name: Register trust certificate with AMS
96 | working-directory: js/tests/e2e
97 | run: |
98 | openssl req -x509 -newkey rsa:4096 -keyout anbox-cloud.key \
99 | -out anbox-cloud.crt -days 365 -nodes \
100 | -subj "/C=GB/ST=London/L=London/O=Global Security/OU=IT Department/CN=example.com"
101 | amc config trust add ./anbox-cloud.crt
102 |
103 | - name: Configure env variables
104 | working-directory: js/tests/e2e
105 | run: |
106 | echo "CI=true" > .env.local
107 | echo "AMS_API_CERTIFICATE=anbox-cloud.crt" >> .env.local
108 | echo "AMS_API_CERTIFICATE_KEY=anbox-cloud.key" >> .env.local
109 | echo "AMS_API_URL=$(sudo cat /var/snap/anbox-cloud-appliance/common/dashboard/config.yaml | grep AMS_API_URL | cut -d ' ' -f2)" >> .env.local
110 | echo "ASG_API_URL=$(sudo cat /var/snap/anbox-cloud-appliance/common/dashboard/config.yaml | grep ASG_API_URL | cut -d ' ' -f2)" >> .env.local
111 | echo "ASG_API_TOKEN=$(sudo cat /var/snap/anbox-cloud-appliance/common/dashboard/config.yaml | grep ASG_API_TOKEN | cut -d ' ' -f2)" >> .env.local
112 |
113 | - name: Run tests
114 | working-directory: js/tests/e2e
115 | run: |
116 | ./run-tests.sh
117 |
118 | - name: Dump logs
119 | if: failure()
120 | run: |
121 | sudo snap logs -n all anbox-cloud-appliance > appliance.log
122 | sudo anbox-cloud-appliance.buginfo > appliance.buginfo
123 | for id in $(amc ls --format=csv | cut -d',' -f1) ; do
124 | status=$(amc show "$id" --format=json | jq -r .status)
125 | if [ "$status" = error ] ; then
126 | for log in $(amc show "$id" --format=json | jq -r '.stored_logs[]' | xargs) ; do
127 | amc show-log "$id" "$log" |& tee -a "$id"_"$log"
128 | done
129 | elif [ "$status" = started ] || [ "$status" = running ]; then
130 | for name in android anbox ; do
131 | timeout 30s amc logs "$id" -t "$name" |& tee -a "$id"_"$name".log
132 | done
133 | fi
134 | done
135 |
136 | - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
137 | if: always()
138 | with:
139 | name: test-data-${{ steps.datetime.outputs.datetime }}
140 | path: |
141 | js/tests/e2e/playwright-report/
142 | js/tests/e2e/*.log
143 | appliance.buginfo
144 | *.log
145 | retention-days: 30
146 |
--------------------------------------------------------------------------------
/js/tests/e2e/global-setup.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Anbox Cloud Streaming SDK
3 | *
4 | * Copyright 2024 Canonical Ltd.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | import { chromium } from "@playwright/test";
20 | import { expect } from "./fixtures/anbox-test";
21 | import {
22 | AOSP_APP_NAME,
23 | AAOS_APP_NAME,
24 | BASE_URL,
25 | } from "./fixtures/constants.cjs";
26 | require("dotenv").config({ path: ".env.local" });
27 |
28 | const APP_RETRY_LIMIT = 300;
29 | const INSTANCE_RETRY_LIMIT = 100;
30 | const ANDROID_BOOT_DELAY = 20_000;
31 |
32 | const isTestAppAvailable = async (appName) => {
33 | const response = await fetch(`${BASE_URL}/asgApplications`);
34 | const applications = await response.json();
35 | return applications.find((application) => application.name === appName);
36 | };
37 |
38 | const hasTestInstance = async (appName) => {
39 | const response = await fetch(`${BASE_URL}/instances`);
40 | const instances = await response.json();
41 | return instances.find((instance) => instance.app_name === appName);
42 | };
43 |
44 | const getRunningInstance = async (appName) => {
45 | const response = await fetch(`${BASE_URL}/instances`);
46 | const instances = await response.json();
47 | const instance = instances.find(
48 | (instance) =>
49 | instance.app_name === appName &&
50 | instance.status === "running" &&
51 | instance.tags.some((tag) => tag.startsWith("session=")),
52 | );
53 | return instance;
54 | };
55 |
56 | const waitForInstanceRunning = async (page, appName) => {
57 | let retryCount = 0;
58 | while (retryCount < INSTANCE_RETRY_LIMIT) {
59 | const instance = await getRunningInstance(appName);
60 | if (instance) return instance;
61 | await page.waitForTimeout(3_000);
62 | retryCount++;
63 | }
64 | return false;
65 | };
66 |
67 | const createTestInstance = async (appName) => {
68 | const createInstanceResponse = await fetch(
69 | `${BASE_URL}/instance?appName=${appName}`,
70 | {
71 | method: "POST",
72 | },
73 | );
74 | expect(createInstanceResponse.status).toBe(200);
75 | };
76 |
77 | const hasTestApplication = async (appName) => {
78 | const response = await fetch(`${BASE_URL}/applications`);
79 | const applications = await response.json();
80 | return applications.some((application) => application.name === appName);
81 | };
82 |
83 | const hasReadyTestApplication = async (appName) => {
84 | const response = await fetch(`${BASE_URL}/applications`);
85 | const applications = await response.json();
86 | const app = applications.find((application) => application.name === appName);
87 | if (app && app.status === "error") {
88 | throw new Error("Application creation failed");
89 | }
90 | return applications.some(
91 | (application) =>
92 | application.name === appName && application.status === "ready",
93 | );
94 | };
95 |
96 | const waitForAppReady = async (page, appName) => {
97 | let retryCount = 0;
98 | // 1) check that status becomes ready
99 | // we allow many retries as we need to give it time to download the image
100 | while (retryCount < APP_RETRY_LIMIT) {
101 | const isReady = await hasReadyTestApplication(appName);
102 | if (isReady) break;
103 | await page.waitForTimeout(3_000);
104 | retryCount++;
105 | }
106 | // 2) check that application is available on ASG
107 | while (retryCount < APP_RETRY_LIMIT) {
108 | const isAvailable = await isTestAppAvailable(appName);
109 | if (isAvailable) return true;
110 | await page.waitForTimeout(3_000);
111 | retryCount++;
112 | }
113 | return false;
114 | };
115 |
116 | const createTestApplication = async (appName) => {
117 | const createAppResponse = await fetch(
118 | `${BASE_URL}/application?name=${appName}`,
119 | {
120 | method: "POST",
121 | },
122 | );
123 | expect(createAppResponse.status).toBe(200);
124 | };
125 |
126 | const setupSessionFor = async (page, appName) => {
127 | const hasApp = await hasTestApplication(appName);
128 | if (!hasApp) {
129 | await createTestApplication(appName);
130 | }
131 | const isAppReady = await waitForAppReady(page, appName);
132 | if (!isAppReady) {
133 | throw new Error("Application did not become ready");
134 | }
135 | const hasInstance = await hasTestInstance(appName);
136 | if (!hasInstance) {
137 | await createTestInstance(appName);
138 | }
139 | const instance = await waitForInstanceRunning(page, appName);
140 | if (!instance) {
141 | throw new Error(
142 | `No running instance for the target test application: ${appName}`,
143 | );
144 | }
145 | const sessionId = instance.tags
146 | .find((tag) => tag.startsWith("session="))
147 | .split("session=")[1];
148 | // wait a few more seconds to let Android boot up
149 | await page.waitForTimeout(ANDROID_BOOT_DELAY);
150 | return sessionId;
151 | };
152 |
153 | async function globalSetup() {
154 | const browser = await chromium.launch();
155 | const page = await browser.newPage();
156 | const sessionIds = [];
157 | for (const appName of [AOSP_APP_NAME, AAOS_APP_NAME]) {
158 | const id = await setupSessionFor(page, appName);
159 | sessionIds.push(id);
160 | }
161 | process.env.AOSP_SESSION_ID = sessionIds[0];
162 | process.env.AAOS_SESSION_ID = sessionIds[1];
163 | await browser.close();
164 | }
165 |
166 | export default globalSetup;
167 |
--------------------------------------------------------------------------------
/examples/android/out_of_band_v2/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/examples/android/webview_streaming/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------