├── .circleci ├── config.yml └── deploy.sh ├── .gitignore ├── LICENSE ├── README.md ├── RELEASE_PROCESS ├── build.gradle ├── gradle.properties ├── gradle ├── gradle-mvn-push.gradle ├── jacoco-android.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── mcumgr-ble ├── .gitignore ├── build.gradle ├── gradle.properties ├── mcumgr-ble-proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── io │ │ │ └── runtime │ │ │ └── mcumgr │ │ │ └── ble │ │ │ ├── McuMgrBleTransport.java │ │ │ ├── callback │ │ │ ├── SmpMerger.java │ │ │ ├── SmpProtocolSession.kt │ │ │ └── SmpTransaction.kt │ │ │ └── util │ │ │ ├── ResultCondition.kt │ │ │ └── RotatingCounter.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── io │ └── runtime │ └── mcumgr │ └── transport │ └── ble │ └── SmpProtocolSessionTest.kt ├── mcumgr-core ├── .gitignore ├── build.gradle ├── gradle.properties ├── mcumgr-core-proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── io │ │ └── runtime │ │ └── mcumgr │ │ ├── McuManager.java │ │ ├── McuMgrCallback.java │ │ ├── McuMgrErrorCode.java │ │ ├── McuMgrHeader.java │ │ ├── McuMgrScheme.java │ │ ├── McuMgrTransport.java │ │ ├── crash │ │ ├── CoreDump.java │ │ ├── CoreDumpHeader.java │ │ ├── CoreDumpTlv.java │ │ └── CoreDumpTlvEntry.java │ │ ├── dfu │ │ ├── FirmwareUpgradeCallback.java │ │ ├── FirmwareUpgradeController.java │ │ └── FirmwareUpgradeManager.java │ │ ├── exception │ │ ├── InsufficientMtuException.java │ │ ├── McuMgrCoapException.java │ │ ├── McuMgrErrorException.java │ │ ├── McuMgrException.java │ │ └── McuMgrTimeoutException.java │ │ ├── image │ │ ├── McuMgrImage.java │ │ ├── McuMgrImageHeader.java │ │ ├── McuMgrImageVersion.java │ │ └── tlv │ │ │ ├── McuMgrImageTlv.java │ │ │ ├── McuMgrImageTlvInfo.java │ │ │ └── McuMgrImageTlvTrailerEntry.java │ │ ├── managers │ │ ├── ConfigManager.java │ │ ├── CrashManager.java │ │ ├── DefaultManager.java │ │ ├── FsManager.java │ │ ├── ImageManager.java │ │ ├── LogManager.java │ │ ├── StatsManager.java │ │ └── meta │ │ │ └── StatisticsCollector.kt │ │ ├── response │ │ ├── DownloadResponse.java │ │ ├── McuMgrResponse.java │ │ ├── UploadResponse.java │ │ ├── config │ │ │ └── McuMgrConfigReadResponse.java │ │ ├── dflt │ │ │ ├── McuMgrEchoResponse.java │ │ │ ├── McuMgrMpStatResponse.java │ │ │ ├── McuMgrReadDateTimeResponse.java │ │ │ └── McuMgrTaskStatResponse.java │ │ ├── fs │ │ │ ├── McuMgrFsDownloadResponse.java │ │ │ └── McuMgrFsUploadResponse.java │ │ ├── img │ │ │ ├── McuMgrCoreLoadResponse.java │ │ │ ├── McuMgrImageStateResponse.java │ │ │ └── McuMgrImageUploadResponse.java │ │ ├── log │ │ │ ├── McuMgrLevelListResponse.java │ │ │ ├── McuMgrLogListResponse.java │ │ │ ├── McuMgrLogResponse.java │ │ │ └── McuMgrModuleListResponse.java │ │ └── stat │ │ │ ├── McuMgrStatListResponse.java │ │ │ └── McuMgrStatResponse.java │ │ ├── transfer │ │ ├── Download.java │ │ ├── DownloadCallback.java │ │ ├── ImageUploader.kt │ │ ├── Transfer.java │ │ ├── TransferCallable.java │ │ ├── TransferCallback.java │ │ ├── TransferController.java │ │ ├── TransferManager.java │ │ ├── Upload.java │ │ ├── UploadCallback.java │ │ ├── UploadResult.kt │ │ └── Uploader.kt │ │ └── util │ │ ├── ByteUtil.java │ │ ├── CBOR.java │ │ └── Endian.java │ └── test │ ├── java │ └── io │ │ └── runtime │ │ └── mcumgr │ │ ├── McuMgrImageTest.kt │ │ ├── StatisticsCollectorTest.kt │ │ ├── mock │ │ ├── McuMgrGroup.kt │ │ ├── McuMgrHandler.kt │ │ ├── McuMgrOperation.kt │ │ ├── MockMcuMgrResponse.kt │ │ ├── MockMcuMgrTransport.kt │ │ └── handlers │ │ │ └── MockStatsHandler.kt │ │ └── response │ │ └── McuMgrResponseTest.java │ └── resources │ ├── slinky-no-prot-tlv.img │ └── slinky-prot-tlv.img ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── io │ │ └── runtime │ │ └── mcumgr │ │ └── sample │ │ ├── MainActivity.java │ │ ├── ScannerActivity.java │ │ ├── adapter │ │ ├── DeviceDiffCallback.java │ │ ├── DevicesAdapter.java │ │ └── DiscoveredBluetoothDevice.java │ │ ├── application │ │ └── Dagger2Application.java │ │ ├── di │ │ ├── AppInjector.java │ │ ├── Injectable.java │ │ ├── McuMgrScope.java │ │ ├── component │ │ │ ├── ApplicationComponent.java │ │ │ ├── McuMgrSubComponent.java │ │ │ ├── McuMgrViewModelSubComponent.java │ │ │ └── ViewModelSubComponent.java │ │ └── module │ │ │ ├── ActivitiesModule.java │ │ │ ├── ContextModule.java │ │ │ ├── McuMgrActivitiesModule.java │ │ │ ├── McuMgrFragmentBuildersModule.java │ │ │ ├── McuMgrManagerModule.java │ │ │ ├── McuMgrModule.java │ │ │ ├── McuMgrTransportModule.java │ │ │ ├── McuMgrViewModelModule.java │ │ │ └── ViewModelModule.java │ │ ├── dialog │ │ ├── FirmwareUpgradeModeDialogFragment.java │ │ ├── GenerateFileDialogFragment.java │ │ ├── HelpDialogFragment.java │ │ └── PartitionDialogFragment.java │ │ ├── fragment │ │ ├── DeviceFragment.java │ │ ├── FilesFragment.java │ │ ├── ImageFragment.java │ │ ├── LogsStatsFragment.java │ │ └── mcumgr │ │ │ ├── DeviceStatusFragment.java │ │ │ ├── EchoFragment.java │ │ │ ├── FileBrowserFragment.java │ │ │ ├── FilesDownloadFragment.java │ │ │ ├── FilesUploadFragment.java │ │ │ ├── ImageControlFragment.java │ │ │ ├── ImageUpgradeFragment.java │ │ │ ├── ImageUploadFragment.java │ │ │ ├── ResetFragment.java │ │ │ └── StatsFragment.java │ │ ├── observable │ │ ├── BondingState.java │ │ ├── ConnectionState.java │ │ └── ObservableMcuMgrBleTransport.java │ │ ├── utils │ │ ├── FilterUtils.java │ │ ├── FsUtils.java │ │ ├── StringUtils.java │ │ └── Utils.java │ │ └── viewmodel │ │ ├── DevicesLiveData.java │ │ ├── MainViewModel.java │ │ ├── ScannerStateLiveData.java │ │ ├── ScannerViewModel.java │ │ ├── SingleLiveEvent.java │ │ ├── ViewModelFactory.java │ │ └── mcumgr │ │ ├── DeviceStatusViewModel.java │ │ ├── EchoViewModel.java │ │ ├── FilesDownloadViewModel.java │ │ ├── FilesUploadViewModel.java │ │ ├── ImageControlViewModel.java │ │ ├── ImageUpgradeViewModel.java │ │ ├── ImageUploadViewModel.java │ │ ├── McuMgrViewModel.java │ │ ├── McuMgrViewModelFactory.java │ │ ├── ResetViewModel.java │ │ └── StatsViewModel.java │ └── res │ ├── color │ └── button_destructive.xml │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable-xxhdpi │ ├── ic_device_mynewt.png │ ├── ic_device_other.png │ └── ic_device_zephyr.png │ ├── drawable │ ├── echo_error.xml │ ├── echo_request.xml │ ├── echo_response.xml │ ├── ic_bluetooth_disabled.xml │ ├── ic_bluetooth_searching.xml │ ├── ic_device_bg.xml │ ├── ic_filter.xml │ ├── ic_help.xml │ ├── ic_history.xml │ ├── ic_launcher_background.xml │ ├── ic_location_off.xml │ ├── ic_nav_fs.xml │ ├── ic_nav_general.xml │ ├── ic_nav_image.xml │ ├── ic_nav_stats.xml │ ├── ic_rssi_0_bar.xml │ ├── ic_rssi_1_bar.xml │ ├── ic_rssi_2_bars.xml │ ├── ic_rssi_3_bars.xml │ ├── ic_rssi_bar.xml │ └── ic_settings.xml │ ├── layout │ ├── activity_main.xml │ ├── activity_scanner.xml │ ├── device_item.xml │ ├── dialog_files_settings.xml │ ├── dialog_generate_file.xml │ ├── fragment_card_device_status.xml │ ├── fragment_card_echo.xml │ ├── fragment_card_files_download.xml │ ├── fragment_card_files_upload.xml │ ├── fragment_card_image_control.xml │ ├── fragment_card_image_upgrade.xml │ ├── fragment_card_image_upload.xml │ ├── fragment_card_reset.xml │ ├── fragment_card_stats.xml │ ├── fragment_device.xml │ ├── fragment_fs.xml │ ├── fragment_image.xml │ ├── fragment_logs_stats.xml │ ├── info_no_bluetooth.xml │ ├── info_no_devices.xml │ └── info_no_permission.xml │ ├── menu │ ├── bottom_navigation.xml │ ├── filter.xml │ ├── help.xml │ ├── image_mode.xml │ └── settings.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── 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-night │ ├── colors.xml │ └── styles.xml │ ├── values-w380dp │ ├── strings_files_upload.xml │ └── styles.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── colors_common.xml │ ├── colors_nordic.xml │ ├── dimens.xml │ ├── strings.xml │ ├── strings_device_status.xml │ ├── strings_echo.xml │ ├── strings_file_loader.xml │ ├── strings_files.xml │ ├── strings_files_download.xml │ ├── strings_files_upload.xml │ ├── strings_image.xml │ ├── strings_image_control.xml │ ├── strings_image_upgrade.xml │ ├── strings_image_upload.xml │ ├── strings_navigation.xml │ ├── strings_reset.xml │ ├── strings_stats.xml │ └── styles.xml └── settings.gradle /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | working_directory: ~/mcumgr-android 5 | docker: 6 | - image: circleci/android:api-29 7 | steps: 8 | - checkout 9 | - run: 10 | name: Configure Android SDK Licenses 11 | command: | 12 | mkdir "$ANDROID_HOME/licenses" || true 13 | echo "8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license" 14 | echo "d56f5187479451eabf01fb78af6dfcb131a6481e" >> "$ANDROID_HOME/licenses/android-sdk-license" 15 | - restore_cache: 16 | key: gradle-{{ checksum "settings.gradle" }}-{{ checksum "build.gradle" }}-{{ checksum "mcumgr-core/build.gradle" }}-{{ checksum "mcumgr-ble/build.gradle" }} 17 | - run: 18 | name: Android Assemble Debug 19 | command: > 20 | ./gradlew 21 | -PdisablePreDex 22 | --max-workers 2 23 | --no-daemon 24 | --stacktrace 25 | assembleDebug 26 | - run: 27 | name: Import GPG Keys 28 | command: echo "$SIGNING_SECRET_KEY" | base64 --decode > ~/mcumgr-private.asc && gpg --batch --import ~/mcumgr-private.asc && echo "$SIGNING_PASSWORD" > ~/mcumgr-private-passphrase && gpg --pinentry-mode loopback --passphrase-file ~/mcumgr-private-passphrase --keyring secring.gpg --export-secret-keys > ~/.gnupg/secring.gpg 29 | - run: 30 | name: Deploy 31 | command: .circleci/deploy.sh 32 | - save_cache: 33 | paths: 34 | - ~/.gradle 35 | key: gradle-{{ checksum "settings.gradle" }}-{{ checksum "build.gradle" }}-{{ checksum "mcumgr-core/build.gradle" }}-{{ checksum "mcumgr-ble/build.gradle" }} 36 | - run: 37 | name: Test 38 | command: ./gradlew test 39 | - run: 40 | name: JaCoCo Code Coverage Report 41 | command: ./gradlew jacocoTestDebugUnitTestReport 42 | - run: 43 | name: Codecov 44 | command: bash <(curl -s https://codecov.io/bash) 45 | -------------------------------------------------------------------------------- /.circleci/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo. 4 | # 5 | # Adapted from https://raw.githubusercontent.com/JakeWharton/retrofit2-kotlinx-serialization-converter/670dc5002a61726afe8feaef982c92f451495bee/.buildscript/deploy_snapshot.sh 6 | 7 | REPOSITORY_URL="git@github.com:JuulLabs-OSS/mcumgr-android.git" 8 | BRANCH="master" 9 | 10 | set -e 11 | 12 | if [ "$CIRCLE_REPOSITORY_URL" != "$REPOSITORY_URL" ]; then 13 | echo "Skipping snapshot deployment: wrong repository. Expected '$REPOSITORY_URL' but was '$CIRCLE_REPOSITORY_URL'." 14 | elif [ ! -z "$CIRCLE_PULL_REQUEST" ]; then 15 | echo "Skipping snapshot deployment: was pull request." 16 | elif [ "$CIRCLE_BRANCH" != "$BRANCH" ]; then 17 | echo "Skipping snapshot deployment: wrong branch. Expected '$BRANCH' but was '$CIRCLE_BRANCH'." 18 | else 19 | echo "Deploying..." 20 | ./gradlew uploadArchives \ 21 | -Psigning.keyId="$SIGNING_KEY_ID" \ 22 | -Psigning.password="$SIGNING_PASSWORD" \ 23 | -Psigning.secretKeyRingFile="$HOME/.gnupg/secring.gpg" 24 | echo "Deployed!" 25 | fi 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | build/* 3 | 4 | # Crashlytics configuations 5 | com_crashlytics_export_strings.xml 6 | 7 | # Local configuration file (sdk path, etc) 8 | local.properties 9 | 10 | # Gradle generated files 11 | .gradle/ 12 | 13 | # Signing files 14 | .signing/ 15 | 16 | # User-specific configurations 17 | .idea/libraries/ 18 | .idea/* 19 | *.iml 20 | 21 | # OS-specific files 22 | .DS_Store 23 | .DS_Store? 24 | ._* 25 | .Spotlight-V100 26 | .Trashes 27 | ehthumbs.db 28 | Thumbs.db 29 | -------------------------------------------------------------------------------- /RELEASE_PROCESS: -------------------------------------------------------------------------------- 1 | 1. Update gradle.properties with new release version 2 | 2. Run ./gradlew uploadArchives 3 | 3. Log into https://oss.sonatype.org/ and check staging repository artifacts 4 | 4. Close the staging repository 5 | 5. Perform any tests or validation using staging artifact from 'https://oss.sonatype.org/content/repositories/staging' 6 | 7. Update README with new version 7 | 8. Update gradle.properties version with -SNAPSHOT 8 | 9. Push release commit to master 9 | 10. Create release on github w/ release notes 10 | 11. In sonatype nexus, Release the closed staging repository artifact 11 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Runtime Inc. 3 | * Copyright (c) Intellinium SAS, 2014-present 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | */ 7 | 8 | buildscript { 9 | ext.kotlin_version = '1.3.72' 10 | repositories { 11 | google() 12 | jcenter() 13 | maven { 14 | url "https://plugins.gradle.org/m2/" 15 | } 16 | } 17 | dependencies { 18 | classpath 'com.android.tools.build:gradle:4.0.0' 19 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 20 | classpath 'com.hiya:jacoco-android:0.2' 21 | } 22 | } 23 | 24 | allprojects { 25 | repositories { 26 | google() 27 | jcenter() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | VERSION_NAME=0.12.0-SNAPSHOT 2 | GROUP=io.runtime.mcumgr 3 | 4 | POM_DESCRIPTION=A mobile management library for devices running Apache Mynewt and Zephyr (DFU, logs, stats, config, etc.) 5 | POM_URL=https://github.com/runtimeco/mcumgr-android 6 | POM_SCM_URL=https://github.com/runtimeco/mcumgr-android 7 | POM_SCM_CONNECTION=scm:git@github.com:runtimeco/mcumgr-android.git 8 | POM_SCM_DEV_CONNECTION=scm:git@github.com:runtimeco/mcumgr-android.git 9 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 10 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 11 | POM_LICENCE_DIST=repo 12 | POM_DEVELOPER_ID=runtimeco 13 | POM_DEVELOPER_NAME=Runtime, Inc. 14 | 15 | android.useAndroidX=true 16 | android.enableJetifier=true 17 | -------------------------------------------------------------------------------- /gradle/jacoco-android.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.hiya.jacoco-android' 2 | 3 | jacoco { 4 | toolVersion = "0.8.5" 5 | } 6 | 7 | jacocoAndroidUnitTestReport { 8 | csv.enabled false 9 | html.enabled true 10 | xml.enabled true 11 | } 12 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jun 09 13:34:11 CEST 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-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 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 Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /mcumgr-ble/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /mcumgr-ble/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply from: rootProject.file('gradle/jacoco-android.gradle') 4 | 5 | android { 6 | compileSdkVersion 29 7 | 8 | defaultConfig { 9 | minSdkVersion 18 10 | targetSdkVersion 29 11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 12 | } 13 | 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | consumerProguardFiles 'mcumgr-ble-proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | // Import the BLE Library 24 | // TODO need to get new release with fixes. 25 | api 'no.nordicsemi.android:ble:2.2.3' 26 | 27 | // Logging 28 | implementation 'org.slf4j:slf4j-api:1.7.30' 29 | 30 | // Import mcumgr-core 31 | api project(':mcumgr-core') 32 | 33 | // Kotlin 34 | implementation "org.jetbrains.kotlin:kotlin-stdlib" 35 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8" 36 | 37 | testImplementation 'junit:junit:4.13' 38 | testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" 39 | } 40 | 41 | apply from: rootProject.file('gradle/gradle-mvn-push.gradle') 42 | -------------------------------------------------------------------------------- /mcumgr-ble/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=mcumgr-ble 2 | POM_NAME=McuManager Ble 3 | POM_PACKAGING=aar 4 | -------------------------------------------------------------------------------- /mcumgr-ble/mcumgr-ble-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 | -------------------------------------------------------------------------------- /mcumgr-ble/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /mcumgr-ble/src/main/java/io/runtime/mcumgr/ble/callback/SmpMerger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.ble.callback; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.annotation.Nullable; 11 | 12 | import java.io.IOException; 13 | 14 | import io.runtime.mcumgr.McuMgrScheme; 15 | import io.runtime.mcumgr.response.McuMgrResponse; 16 | import no.nordicsemi.android.ble.data.DataMerger; 17 | import no.nordicsemi.android.ble.data.DataStream; 18 | 19 | public class SmpMerger implements DataMerger { 20 | private Integer mExpectedLength; 21 | 22 | @Override 23 | public boolean merge(@NonNull final DataStream output, @Nullable final byte[] lastPacket, final int index) { 24 | // Add the new packet to the output stream 25 | output.write(lastPacket); 26 | 27 | // If it's a first or a single packet of this message, try to read the expected length 28 | if (index == 0) { 29 | try { 30 | // This should never happen, but let's not crash if it did 31 | if (lastPacket == null) { 32 | return true; 33 | } 34 | // Read the expected length from the header 35 | mExpectedLength = McuMgrResponse.getExpectedLength(McuMgrScheme.BLE, lastPacket); 36 | } catch (final IOException e) { 37 | return true; 38 | } 39 | } 40 | // The message is complete when the stream size is equal to expected length 41 | return output.size() == mExpectedLength; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /mcumgr-ble/src/main/java/io/runtime/mcumgr/ble/callback/SmpTransaction.kt: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.ble.callback 2 | 3 | import java.util.concurrent.TimeoutException 4 | 5 | class TransactionTimeoutException internal constructor( 6 | val id: Int 7 | ) : TimeoutException("Transaction $id timed out without receiving a response") 8 | 9 | class TransactionOverwriteException internal constructor( 10 | val id: Int 11 | ) : Exception("Transaction $id has been overwritten") 12 | 13 | internal interface SmpTransaction { 14 | fun send(data: ByteArray) 15 | fun onResponse(data: ByteArray) 16 | fun onFailure(e: Throwable) 17 | } 18 | -------------------------------------------------------------------------------- /mcumgr-ble/src/main/java/io/runtime/mcumgr/ble/util/ResultCondition.kt: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.ble.util 2 | 3 | import android.os.ConditionVariable 4 | import io.runtime.mcumgr.exception.McuMgrException 5 | import java.lang.IllegalStateException 6 | import java.util.concurrent.TimeoutException 7 | 8 | internal class ResultCondition(state: Boolean) { 9 | 10 | private var result: T? = null 11 | private var exception: McuMgrException? = null 12 | private val lock: ConditionVariable = ConditionVariable(state) 13 | 14 | @Throws(McuMgrException::class) 15 | fun block(): T { 16 | lock.block() 17 | val exception = this.exception 18 | val result = this.result 19 | when { 20 | exception != null -> throw exception 21 | result == null -> throw IllegalStateException("Condition result must not be null.") 22 | else -> return result 23 | } 24 | } 25 | 26 | @Throws(McuMgrException::class, TimeoutException::class) 27 | fun block(timeout: Long): T? { 28 | return if (lock.block(timeout)) { 29 | result 30 | } else { 31 | throw TimeoutException("Condition timed out!") 32 | } 33 | } 34 | 35 | fun close() { 36 | lock.close() 37 | } 38 | 39 | fun open(result: T) { 40 | this.result = result 41 | lock.open() 42 | } 43 | 44 | fun openExceptionally(exception: McuMgrException) { 45 | this.exception = exception 46 | lock.open() 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /mcumgr-ble/src/main/java/io/runtime/mcumgr/ble/util/RotatingCounter.kt: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.ble.util 2 | 3 | /** 4 | * Helper class for managing a counter which rotates between 0 and a max value. 5 | * Equivalent to unsigned int overflow. This class is not thread safe. 6 | */ 7 | internal class RotatingCounter(private val max: Int) { 8 | 9 | private var value = 0 10 | 11 | fun getAndRotate(): Int { 12 | val tmp = value 13 | value = value.rotate() 14 | return tmp 15 | } 16 | 17 | private fun Int.rotate(): Int { 18 | return if (this == max) { 19 | 0 20 | } else { 21 | this + 1 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /mcumgr-ble/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | mcumgr-transport-ble 3 | 4 | -------------------------------------------------------------------------------- /mcumgr-core/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /mcumgr-core/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Runtime Inc. 3 | * Copyright (c) Intellinium SAS, 2014-present 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | */ 7 | 8 | apply plugin: 'com.android.library' 9 | apply plugin: 'kotlin-android' 10 | apply from: rootProject.file('gradle/jacoco-android.gradle') 11 | 12 | android { 13 | compileSdkVersion 29 14 | 15 | defaultConfig { 16 | minSdkVersion 18 17 | targetSdkVersion 29 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | consumerProguardFiles 'mcumgr-core-proguard-rules.pro' 24 | } 25 | } 26 | } 27 | 28 | dependencies { 29 | 30 | // Kotlin 31 | implementation "org.jetbrains.kotlin:kotlin-stdlib" 32 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8" 33 | 34 | // Annotations 35 | implementation 'org.jetbrains:annotations:16.0.1' 36 | 37 | // Logging 38 | implementation 'org.slf4j:slf4j-api:1.7.30' 39 | 40 | // Import CBOR parser 41 | implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.11.0' 42 | implementation 'com.fasterxml.jackson.core:jackson-core:2.11.0' 43 | implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.0' 44 | 45 | // Test 46 | testImplementation 'junit:junit:4.13' 47 | testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" 48 | } 49 | 50 | apply from: rootProject.file('gradle/gradle-mvn-push.gradle') 51 | -------------------------------------------------------------------------------- /mcumgr-core/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=mcumgr-core 2 | POM_NAME=McuManager Core 3 | POM_PACKAGING=aar -------------------------------------------------------------------------------- /mcumgr-core/mcumgr-core-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 | 23 | ############################################################ 24 | ## jackson 25 | ############################################################ 26 | -dontwarn com.fasterxml.** 27 | 28 | -keepattributes *Annotation*,EnclosingMethod,Signature 29 | -keepnames class com.fasterxml.jackson.** { *; } 30 | -dontwarn com.fasterxml.jackson.databind.** 31 | -keep class org.codehaus.** { *; } 32 | -keepclassmembers public final enum org.codehaus.jackson.annotate.JsonAutoDetect$Visibility { 33 | public static final org.codehaus.jackson.annotate.JsonAutoDetect$Visibility *; } 34 | -keepclassmembers class * { 35 | @com.fasterxml.jackson.annotation.JsonCreator *; 36 | @com.fasterxml.jackson.annotation.JsonProperty *; 37 | } 38 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/McuMgrCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Runtime Inc. 3 | * Copyright (c) Intellinium SAS, 2014-present 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | */ 7 | 8 | package io.runtime.mcumgr; 9 | 10 | 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import io.runtime.mcumgr.exception.McuMgrException; 14 | import io.runtime.mcumgr.response.McuMgrResponse; 15 | 16 | /** 17 | * Callback for asynchronous Mcu Manager commands. 18 | */ 19 | public interface McuMgrCallback { 20 | /** 21 | * Mcu Manager has received a response. 22 | * 23 | * @param response the response. 24 | */ 25 | void onResponse(@NotNull T response); 26 | 27 | /** 28 | * Mcu Manager has encountered a transport error while sending the command. 29 | * 30 | * @param error the error. 31 | */ 32 | void onError(@NotNull McuMgrException error); 33 | } 34 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/McuMgrScheme.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Intellinium SAS, 2014-present 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr; 8 | 9 | /** 10 | * The MCU Manager protocol can be used over a variety of transports and schemes. On android phones, 11 | * the only applicable schemes are {@link McuMgrScheme#BLE}, {@link McuMgrScheme#COAP_BLE}, and 12 | * {@link McuMgrScheme#COAP_UDP}. Besides the obvious transport differences, the scheme primarily 13 | * determines the format of the MCU Manager packet. For example, CoAP schemes put the 8-byte MCU 14 | * Manager header as a key-value pair in the CBOR payload while non-CoAP schemes simply append the 15 | * CBOR payload after the 8-byte header. 16 | */ 17 | public enum McuMgrScheme { 18 | BLE, 19 | COAP_BLE, 20 | COAP_UDP; 21 | 22 | public boolean isCoap() { 23 | return this == COAP_BLE || this == COAP_UDP; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/crash/CoreDump.java: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.crash; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * A core dump collected from a device. Use the {@link #fromBytes(byte[])} method to parse the file 10 | * into this object. 11 | */ 12 | @SuppressWarnings({"unused", "WeakerAccess"}) 13 | public class CoreDump { 14 | 15 | protected static final int MAGIC = 0x690c47c3; 16 | 17 | protected static final int TLV_TYPE_IMAGE = 1; 18 | protected static final int TLV_TYPE_MEM = 2; 19 | protected static final int TLV_TYPE_REG = 3; 20 | 21 | private CoreDumpHeader mHeader; 22 | private CoreDumpTlv mTlv; 23 | 24 | public CoreDump(@NotNull CoreDumpHeader header, @NotNull CoreDumpTlv tlv) { 25 | mHeader = header; 26 | mTlv = tlv; 27 | } 28 | 29 | /** 30 | * Parse a core dump file from a byte array. 31 | * 32 | * @param data The core dump. 33 | * @return the parsed core dump. 34 | * @throws IOException if the core dump is invalid. 35 | */ 36 | @NotNull 37 | public static CoreDump fromBytes(@NotNull byte[] data) throws IOException { 38 | CoreDumpHeader header = CoreDumpHeader.fromBytes(data); 39 | CoreDumpTlv tlv = CoreDumpTlv.fromBytes(data); 40 | return new CoreDump(header, tlv); 41 | } 42 | 43 | /** 44 | * Get the image hash from the core dump TLV. 45 | * 46 | * @return the image hash, or null if not found in the TLV. 47 | */ 48 | @Nullable 49 | public byte[] getImageHash() { 50 | CoreDumpTlvEntry entry = mTlv.getEntryOfType(TLV_TYPE_IMAGE); 51 | if (entry == null) { 52 | return null; 53 | } 54 | return entry.getValue(); 55 | } 56 | 57 | /** 58 | * Get the registers from the core dump TLV. 59 | * 60 | * @return the registers, or null if not found in the TLV. 61 | */ 62 | @Nullable 63 | public byte[] getRegisters() { 64 | CoreDumpTlvEntry entry = mTlv.getEntryOfType(TLV_TYPE_REG); 65 | if (entry == null) { 66 | return null; 67 | } 68 | return entry.getValue(); 69 | } 70 | 71 | @NotNull 72 | public CoreDumpHeader getHeader() { 73 | return mHeader; 74 | } 75 | 76 | @NotNull 77 | public CoreDumpTlv getTlv() { 78 | return mTlv; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/crash/CoreDumpHeader.java: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.crash; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.io.IOException; 6 | 7 | import io.runtime.mcumgr.util.ByteUtil; 8 | import io.runtime.mcumgr.util.Endian; 9 | 10 | /** 11 | * The header of the core dump file. Contains the magic number and the core dump size for 12 | * validation. 13 | */ 14 | @SuppressWarnings({"unused", "WeakerAccess"}) 15 | public class CoreDumpHeader { 16 | 17 | private static final int OFFSET = 0; 18 | 19 | private int mMagic; 20 | private int mSize; 21 | 22 | public CoreDumpHeader(int magic, int size) { 23 | mMagic = magic; 24 | mSize = size; 25 | } 26 | 27 | /** 28 | * Parse the core dump header from the default offset of 0. 29 | * @param data entire core dump in bytes 30 | * @return The core dump header. 31 | * @throws IOException If the magic number was invalid. 32 | */ 33 | @NotNull 34 | public static CoreDumpHeader fromBytes(@NotNull byte[] data) throws IOException { 35 | return fromBytes(data, OFFSET); 36 | } 37 | 38 | /** 39 | * Parse the core dump header from a given offset. 40 | * @param data data containing the core dump 41 | * @param offset the offset to start parsing the header from. 42 | * @return The core dump header. 43 | * @throws IOException If the magic number was invalid. 44 | */ 45 | @NotNull 46 | public static CoreDumpHeader fromBytes(@NotNull byte[] data, int offset) throws IOException { 47 | int magic = ByteUtil.byteArrayToUnsignedInt(data, offset, Endian.LITTLE, 4); 48 | if (magic != CoreDump.MAGIC) { 49 | throw new IOException("Illegal magic number: actual=" + 50 | String.format("0x%x", magic) + ", expected=" + 51 | String.format("0x%x", CoreDump.MAGIC)); 52 | } 53 | int size = ByteUtil.byteArrayToUnsignedInt(data, offset + 4, Endian.LITTLE, 4); 54 | return new CoreDumpHeader(magic, size); 55 | } 56 | 57 | public int getMagic() { 58 | return mMagic; 59 | } 60 | 61 | public int getSize() { 62 | return mSize; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/dfu/FirmwareUpgradeCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Runtime Inc. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.dfu; 8 | 9 | import io.runtime.mcumgr.exception.McuMgrException; 10 | 11 | /** 12 | * Callbacks for firmware upgrades started using the {@link FirmwareUpgradeManager}. 13 | */ 14 | public interface FirmwareUpgradeCallback { 15 | 16 | /** 17 | * Called when the {@link FirmwareUpgradeManager} has started. 18 | *

19 | * This callback is used to pass the upgrade controller which can pause/resume/cancel 20 | * an upgrade to a controller which may not have access to the original object. 21 | * 22 | * @param controller the upgrade controller. 23 | */ 24 | void onUpgradeStarted(FirmwareUpgradeController controller); 25 | 26 | /** 27 | * Called when the firmware upgrade changes state. 28 | * 29 | * @param prevState previous state. 30 | * @param newState new state. 31 | * @see FirmwareUpgradeManager.State 32 | */ 33 | void onStateChanged(FirmwareUpgradeManager.State prevState, 34 | FirmwareUpgradeManager.State newState); 35 | 36 | /** 37 | * Called when the firmware upgrade has succeeded. 38 | */ 39 | void onUpgradeCompleted(); 40 | 41 | /** 42 | * Called when the firmware upgrade has failed. 43 | * 44 | * @param state the state the upgrade failed in. 45 | * @param error the error. 46 | */ 47 | void onUpgradeFailed(FirmwareUpgradeManager.State state, McuMgrException error); 48 | 49 | /** 50 | * Called when the firmware upgrade has been canceled using the 51 | * {@link FirmwareUpgradeManager#cancel()} method. The upgrade may be cancelled only during 52 | * uploading the image. When the image is uploaded, the test and/or confirm commands will be 53 | * sent depending on the {@link FirmwareUpgradeManager#setMode(FirmwareUpgradeManager.Mode)}. 54 | * 55 | * @param state the state the upgrade was cancelled in. 56 | */ 57 | void onUpgradeCanceled(FirmwareUpgradeManager.State state); 58 | 59 | /** 60 | * Called when the {@link FirmwareUpgradeManager.State#UPLOAD} state progress has changed. 61 | * 62 | * @param bytesSent the number of bytes sent so far. 63 | * @param imageSize the total number of bytes to send. 64 | * @param timestamp the time that the successful response packet for the progress was received. 65 | */ 66 | void onUploadProgressChanged(int bytesSent, int imageSize, long timestamp); 67 | } 68 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/dfu/FirmwareUpgradeController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Runtime Inc. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.dfu; 8 | 9 | @SuppressWarnings("unused") 10 | public interface FirmwareUpgradeController { 11 | 12 | /** 13 | * Pause the firmware upgrade. 14 | */ 15 | void pause(); 16 | 17 | /** 18 | * Resume a paused firmware upgrade. 19 | */ 20 | void resume(); 21 | 22 | /** 23 | * Cancel the firmware upgrade. 24 | * The firmware may be cancelled in 25 | * {@link FirmwareUpgradeManager.State#VALIDATE} or 26 | * {@link FirmwareUpgradeManager.State#UPLOAD} state. 27 | * The manager does not try to recover the original firmware after the test or confirm commands 28 | * have been sent. To undo the upload, confirm the image that have been moved to slot 1 during 29 | * swap. 30 | */ 31 | void cancel(); 32 | 33 | /** 34 | * Determine whether the firmware upgrade is paused. 35 | * 36 | * @return True if the firmware upgrade is paused, false otherwise. 37 | */ 38 | boolean isPaused(); 39 | 40 | /** 41 | * Determine whether the firmware upgrade is in progress. 42 | * 43 | * @return True if the firmware upgrade is in progress, false otherwise. 44 | */ 45 | boolean isInProgress(); 46 | } 47 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/exception/InsufficientMtuException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Runtime Inc. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.exception; 8 | 9 | import io.runtime.mcumgr.dfu.FirmwareUpgradeManager; 10 | 11 | /** 12 | * InsufficientMtuExceptions should be thrown by the transporter when the packet from the manager 13 | * is too large to be sent or (potentially) fragmented. The exception includes the transporter's 14 | * MTU. This is used in practice by the {@link FirmwareUpgradeManager} to resize it's packets to 15 | * fit within the transporter's MTU. 16 | */ 17 | @SuppressWarnings("unused") 18 | public class InsufficientMtuException extends McuMgrException { 19 | private int mMtu; 20 | private int mDataLength; 21 | 22 | public InsufficientMtuException(int payloadLength, int mtu) { 23 | super("Payload (" + payloadLength + " bytes) too long for MTU: " + mtu); 24 | mDataLength = payloadLength; 25 | mMtu = mtu; 26 | } 27 | 28 | public int getMtu() { 29 | return mMtu; 30 | } 31 | 32 | public int getDataLength() { 33 | return mDataLength; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/exception/McuMgrCoapException.java: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.exception; 2 | 3 | import io.runtime.mcumgr.McuMgrCallback; 4 | 5 | /** 6 | * A McuMgrException caused by a CoAP response error. For CoAP transport scheme McuMgr requests 7 | * which result in a CoAP response error, this exception will be thrown (for 8 | * synchronous requests) or the {@link McuMgrCallback#onError(McuMgrException)} will be called with 9 | * this exception (for asynchronous requests). 10 | * 11 | * This exception holds all the necessary information to determine the error code and reconstruct 12 | * the CoAP response. 13 | */ 14 | @SuppressWarnings("unused") 15 | public class McuMgrCoapException extends McuMgrException { 16 | private byte[] mBytes; 17 | private int mCodeClass; 18 | private int mCodeDetail; 19 | 20 | public McuMgrCoapException(byte[] bytes, int codeClass, int codeDetail) { 21 | super("McuManager CoAP request resulted in an error response: " + codeClass + ".0" + codeDetail); 22 | mBytes = bytes; 23 | mCodeClass = codeClass; 24 | mCodeDetail = codeDetail; 25 | } 26 | 27 | /** 28 | * Get the raw bytes of the response which caused this exception. 29 | * 30 | * @return The bytes of the response. 31 | */ 32 | public byte[] getBytes() { 33 | return mBytes; 34 | } 35 | 36 | /** 37 | * Get the response's code class. 38 | * 39 | * @return The response code class. 40 | */ 41 | public int getCodeClass() { 42 | return mCodeClass; 43 | } 44 | 45 | /** 46 | * Get the response's code detail. 47 | * 48 | * @return The response code detail. 49 | */ 50 | public int getCodeDetail() { 51 | return mCodeDetail; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/exception/McuMgrErrorException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Runtime Inc. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.exception; 8 | 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import io.runtime.mcumgr.McuMgrErrorCode; 12 | import io.runtime.mcumgr.dfu.FirmwareUpgradeManager; 13 | import io.runtime.mcumgr.response.McuMgrResponse; 14 | 15 | /** 16 | * Used to convey errors caused by an {@link McuMgrErrorCode} within a response. This is used in 17 | * practice by {@link FirmwareUpgradeManager} to send a failure callback with the 18 | * {@link McuMgrErrorCode} that caused the failure. 19 | */ 20 | @SuppressWarnings("unused") 21 | public class McuMgrErrorException extends McuMgrException { 22 | @NotNull 23 | private McuMgrErrorCode mCode; 24 | 25 | public McuMgrErrorException(@NotNull McuMgrErrorCode code) { 26 | super("Mcu Mgr Error: " + code); 27 | mCode = code; 28 | } 29 | 30 | public McuMgrErrorException(@NotNull McuMgrResponse response) { 31 | this(McuMgrErrorCode.valueOf(response.rc)); 32 | } 33 | 34 | /** 35 | * Get the code which caused this exception to be thrown. 36 | * 37 | * @return The McuManager response code which caused this exception to be thrown. 38 | */ 39 | @NotNull 40 | public McuMgrErrorCode getCode() { 41 | return mCode; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/exception/McuMgrException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Runtime Inc. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package io.runtime.mcumgr.exception; 7 | 8 | /** 9 | * Essentially a renamed {@link Exception}. 10 | */ 11 | public class McuMgrException extends Exception { 12 | 13 | /** 14 | * Constructs a new exception with {@code null} as its detail message. 15 | * The cause is not initialized, and may subsequently be initialized by a 16 | * call to {@link #initCause}. 17 | */ 18 | public McuMgrException() { 19 | super(); 20 | } 21 | 22 | /** 23 | * Constructs a new exception with the specified detail message. The 24 | * cause is not initialized, and may subsequently be initialized by 25 | * a call to {@link #initCause}. 26 | * 27 | * @param message the detail message. The detail message is saved for 28 | * later retrieval by the {@link #getMessage()} method. 29 | */ 30 | public McuMgrException(String message) { 31 | super(message); 32 | } 33 | 34 | /** 35 | * Constructs a new exception with the specified detail message and 36 | * cause.

Note that the detail message associated with 37 | * {@code cause} is not automatically incorporated in 38 | * this exception's detail message. 39 | * 40 | * @param message the detail message (which is saved for later retrieval 41 | * by the {@link #getMessage()} method). 42 | * @param cause the cause (which is saved for later retrieval by the 43 | * {@link #getCause()} method). (A null value is 44 | * permitted, and indicates that the cause is nonexistent or 45 | * unknown.) 46 | * @since 1.4 47 | */ 48 | public McuMgrException(String message, Throwable cause) { 49 | super(message, cause); 50 | } 51 | 52 | /** 53 | * Constructs a new exception with the specified cause and a detail 54 | * message of (cause==null ? null : cause.toString()) (which 55 | * typically contains the class and detail message of cause). 56 | * This constructor is useful for exceptions that are little more than 57 | * wrappers for other throwables (for example, {@link 58 | * java.security.PrivilegedActionException}). 59 | * 60 | * @param cause the cause (which is saved for later retrieval by the 61 | * {@link #getCause()} method). (A null value is 62 | * permitted, and indicates that the cause is nonexistent or 63 | * unknown.) 64 | * @since 1.4 65 | */ 66 | public McuMgrException(Throwable cause) { 67 | super(cause); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/exception/McuMgrTimeoutException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Runtime Inc. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.exception; 8 | 9 | import io.runtime.mcumgr.McuMgrErrorCode; 10 | import io.runtime.mcumgr.dfu.FirmwareUpgradeManager; 11 | 12 | /** 13 | * Used to convey errors caused by an {@link McuMgrErrorCode} within a response. This is used in 14 | * practice by {@link FirmwareUpgradeManager} to send a failure callback with the 15 | * {@link McuMgrErrorCode} that caused the failure. 16 | */ 17 | @SuppressWarnings("unused") 18 | public class McuMgrTimeoutException extends McuMgrException { 19 | } 20 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/image/McuMgrImageVersion.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Intellinium SAS, 2014-present 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.image; 8 | 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import io.runtime.mcumgr.exception.McuMgrException; 12 | import io.runtime.mcumgr.util.ByteUtil; 13 | import io.runtime.mcumgr.util.Endian; 14 | 15 | /** 16 | * Represents a firmware image version for devices using McuBoot or the legacy Apache Mynewt 17 | * bootloader. 18 | *

19 | * For more info about McuBoot and image format see: 20 | * https://juullabs-oss.github.io/mcuboot/design.html 21 | */ 22 | @SuppressWarnings("unused") 23 | public class McuMgrImageVersion { 24 | 25 | private byte mMajor; 26 | private byte mMinor; 27 | private short mRevision; 28 | private int mBuildNum; 29 | 30 | private McuMgrImageVersion(byte major, byte minor, short revision, int buildNum) { 31 | mMajor = major; 32 | mMinor = minor; 33 | mRevision = revision; 34 | mBuildNum = buildNum; 35 | } 36 | 37 | @NotNull 38 | public static McuMgrImageVersion fromBytes(@NotNull byte[] b) throws McuMgrException { 39 | return fromBytes(b, 0); 40 | } 41 | 42 | @NotNull 43 | public static McuMgrImageVersion fromBytes(@NotNull byte[] b, int offset) throws McuMgrException { 44 | if (b.length - offset < getSize()) { 45 | throw new McuMgrException("The byte array is too short to be a McuMgrImageVersion"); 46 | } 47 | 48 | byte major = b[offset++]; 49 | byte minor = b[offset++]; 50 | short revision = (short) ByteUtil.byteArrayToUnsignedInt(b, offset, Endian.LITTLE, 2); 51 | int buildNum = ByteUtil.byteArrayToUnsignedInt(b, offset + 2, Endian.LITTLE, 4); 52 | 53 | return new McuMgrImageVersion(major, minor, revision, buildNum); 54 | } 55 | 56 | public static int getSize() { 57 | return 1 + 1 + 2 + 4; 58 | } 59 | 60 | public byte getMajor() { 61 | return mMajor; 62 | } 63 | 64 | public byte getMinor() { 65 | return mMinor; 66 | } 67 | 68 | public short getRevision() { 69 | return mRevision; 70 | } 71 | 72 | public int getBuildNum() { 73 | return mBuildNum; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/image/tlv/McuMgrImageTlvInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Intellinium SAS, 2014-present 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.image.tlv; 8 | 9 | import io.runtime.mcumgr.exception.McuMgrException; 10 | import io.runtime.mcumgr.util.ByteUtil; 11 | import io.runtime.mcumgr.util.Endian; 12 | 13 | /** 14 | * Represents a type-length-value info table for firmware images using McuBoot or the legacy Apache 15 | * Mynewt bootloader. 16 | *

17 | * For more info about McuBoot and image format see: 18 | * https://juullabs-oss.github.io/mcuboot/design.html 19 | */ 20 | @SuppressWarnings("unused") 21 | public class McuMgrImageTlvInfo { 22 | private short mMagic; 23 | private short mTotal; 24 | 25 | private McuMgrImageTlvInfo() { 26 | // empty private constructor 27 | } 28 | 29 | public static McuMgrImageTlvInfo fromBytes(byte[] b) throws McuMgrException { 30 | return fromBytes(b, 0); 31 | } 32 | 33 | public static McuMgrImageTlvInfo fromBytes(byte[] b, int offset) throws McuMgrException { 34 | if (b.length - offset < getSize()) 35 | throw new McuMgrException("The byte array is too short to be a McuMgrImageTlvInfo: " + 36 | "length= " + b.length + ", offset= " + offset + ", min size= " + getSize()); 37 | 38 | McuMgrImageTlvInfo info = new McuMgrImageTlvInfo(); 39 | info.mMagic = (short) ByteUtil.byteArrayToUnsignedInt(b, offset, Endian.LITTLE, 2); 40 | info.mTotal = (short) ByteUtil.byteArrayToUnsignedInt(b, offset + 2, Endian.LITTLE, 2); 41 | 42 | if (info.mMagic != McuMgrImageTlv.IMG_TLV_INFO_MAGIC && 43 | info.mMagic != McuMgrImageTlv.IMG_TLV_PROTECTED_INFO_MAGIC) { 44 | throw new McuMgrException("Wrong magic number, magic=" + info.mMagic); 45 | } 46 | return info; 47 | } 48 | 49 | public static int getSize() { 50 | return 4; 51 | } 52 | 53 | public short getMagic() { 54 | return mMagic; 55 | } 56 | 57 | public short getTotal() { 58 | return mTotal; 59 | } 60 | 61 | public boolean isProtected() { 62 | return mMagic == McuMgrImageTlv.IMG_TLV_PROTECTED_INFO_MAGIC; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/image/tlv/McuMgrImageTlvTrailerEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Runtime Inc. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.image.tlv; 8 | 9 | import java.util.Arrays; 10 | 11 | import io.runtime.mcumgr.exception.McuMgrException; 12 | import io.runtime.mcumgr.util.ByteUtil; 13 | import io.runtime.mcumgr.util.Endian; 14 | 15 | /** 16 | * Represents a type-length-value trailer entry for firmware images using McuBoot or the legacy 17 | * Apache Mynewt bootloader. 18 | *

19 | * For more info about McuBoot and image format see: 20 | * https://juullabs-oss.github.io/mcuboot/design.html 21 | */ 22 | public class McuMgrImageTlvTrailerEntry { 23 | public final byte type; 24 | public final short length; 25 | public final byte[] value; 26 | 27 | private McuMgrImageTlvTrailerEntry(byte type, short length, byte[] value) { 28 | this.type = type; 29 | this.length = length; 30 | this.value = value; 31 | } 32 | 33 | public int getEntryLength() { 34 | return getMinSize() + length; 35 | } 36 | 37 | public static McuMgrImageTlvTrailerEntry fromBytes(byte[] b, int offset) throws McuMgrException { 38 | if (offset + getMinSize() > b.length) { 39 | throw new McuMgrException("The byte array is too short to be a McuMgrImageTlvTrailerEntry"); 40 | } 41 | // Get type 42 | byte t = b[offset++]; 43 | offset++; // Account for byte padding 44 | // Get length 45 | short l = (short) ByteUtil.byteArrayToUnsignedInt(b, offset, Endian.LITTLE, 2); 46 | offset += 2; // Move past length 47 | // Get value 48 | byte[] v = Arrays.copyOfRange(b, offset, offset + l); 49 | return new McuMgrImageTlvTrailerEntry(t, l, v); 50 | } 51 | 52 | public static int getMinSize() { 53 | return 4; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/managers/CrashManager.java: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.managers; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.HashMap; 6 | 7 | import io.runtime.mcumgr.McuManager; 8 | import io.runtime.mcumgr.McuMgrCallback; 9 | import io.runtime.mcumgr.McuMgrTransport; 10 | import io.runtime.mcumgr.exception.McuMgrException; 11 | import io.runtime.mcumgr.response.McuMgrResponse; 12 | import io.runtime.mcumgr.response.config.McuMgrConfigReadResponse; 13 | 14 | @SuppressWarnings({"unused", "WeakerAccess"}) 15 | public class CrashManager extends McuManager { 16 | 17 | /** 18 | * Crash test command ID. 19 | */ 20 | private final static int ID_CRASH_TEST = 0; 21 | 22 | /** 23 | * Type of crash to test on the device. 24 | */ 25 | public enum Test { 26 | DIV_0("div0"), 27 | JUMP_0("jump0"), 28 | REF_0("ref0"), 29 | ASSERT("assert"), 30 | WDOG("wdog"); 31 | private final String value; 32 | Test(String value) { 33 | this.value = value; 34 | } 35 | @Override 36 | public String toString() { 37 | return value; 38 | } 39 | } 40 | 41 | /** 42 | * Construct a McuManager instance. 43 | * 44 | * @param transporter the transporter to use to send commands. 45 | */ 46 | public CrashManager(@NotNull McuMgrTransport transporter) { 47 | super(McuManager.GROUP_CRASH, transporter); 48 | } 49 | 50 | /** 51 | * Trigger a crash test. 52 | * @param test The type of crash test. 53 | * @return The response 54 | * @throws McuMgrException on failure. 55 | */ 56 | @NotNull 57 | public McuMgrResponse test(@NotNull Test test) throws McuMgrException { 58 | HashMap payloadMap = new HashMap<>(); 59 | payloadMap.put("t", test.toString()); 60 | return send(OP_WRITE, ID_CRASH_TEST, payloadMap, McuMgrConfigReadResponse.class); 61 | } 62 | 63 | /** 64 | * Trigger a crash test. 65 | * @param test The type of crash test. 66 | * @param callback The response callback. 67 | */ 68 | public void test(@NotNull Test test, @NotNull McuMgrCallback callback) { 69 | HashMap payloadMap = new HashMap<>(); 70 | payloadMap.put("t", test.toString()); 71 | send(OP_WRITE, ID_CRASH_TEST, payloadMap, McuMgrResponse.class, callback); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/DownloadResponse.java: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | public class DownloadResponse extends McuMgrResponse { 7 | /** The offset of the {@link #data}. */ 8 | @JsonProperty("off") 9 | public int off; 10 | /** The total length of the data. Only sent in the initial packet. */ 11 | @JsonProperty("len") 12 | public int len; 13 | /** The data. */ 14 | @JsonProperty("data") 15 | public byte[] data; 16 | 17 | @JsonCreator 18 | public DownloadResponse() {} 19 | } 20 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/UploadResponse.java: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | public class UploadResponse extends McuMgrResponse { 7 | /** The offset. Number of bytes that were received. */ 8 | @JsonProperty("off") 9 | public int off; 10 | 11 | @JsonCreator 12 | public UploadResponse() {} 13 | } 14 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/config/McuMgrConfigReadResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Runtime Inc. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.response.config; 8 | 9 | import com.fasterxml.jackson.annotation.JsonCreator; 10 | import com.fasterxml.jackson.annotation.JsonProperty; 11 | 12 | import io.runtime.mcumgr.response.McuMgrResponse; 13 | 14 | public class McuMgrConfigReadResponse extends McuMgrResponse { 15 | /** The value of the config variable. */ 16 | @JsonProperty("val") 17 | public String val; 18 | 19 | @JsonCreator 20 | public McuMgrConfigReadResponse() {} 21 | } 22 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/dflt/McuMgrEchoResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Intellinium SAS, 2014-present 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.response.dflt; 8 | 9 | import com.fasterxml.jackson.annotation.JsonCreator; 10 | import com.fasterxml.jackson.annotation.JsonProperty; 11 | 12 | import io.runtime.mcumgr.response.McuMgrResponse; 13 | 14 | public class McuMgrEchoResponse extends McuMgrResponse { 15 | /** The echo response. */ 16 | @JsonProperty("r") 17 | public String r; 18 | 19 | @JsonCreator 20 | public McuMgrEchoResponse() {} 21 | } 22 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/dflt/McuMgrMpStatResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Intellinium SAS, 2014-present 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.response.dflt; 8 | 9 | 10 | import com.fasterxml.jackson.annotation.JsonCreator; 11 | import com.fasterxml.jackson.annotation.JsonProperty; 12 | 13 | import java.util.Map; 14 | 15 | import io.runtime.mcumgr.response.McuMgrResponse; 16 | 17 | @SuppressWarnings("unused") 18 | public class McuMgrMpStatResponse extends McuMgrResponse { 19 | // For Mynewt see: 20 | // https://github.com/apache/mynewt-core/blob/master/kernel/os/include/os/os_mempool.h 21 | 22 | /** 23 | * Memory pool information. The keys of this map are the names of the memory pools. 24 | */ 25 | @JsonProperty("mpools") 26 | public Map mpools; 27 | 28 | @JsonCreator 29 | public McuMgrMpStatResponse() {} 30 | 31 | /** 32 | * Information describing a memory pool, used to return OS information 33 | * to the management layer. 34 | */ 35 | public static class MpStat { 36 | /** Size of the memory blocks in the pool. */ 37 | @JsonProperty("blksiz") 38 | public int blksiz; 39 | /** Number of memory blocks in the pool. */ 40 | @JsonProperty("nblks") 41 | public int nblks; 42 | /** Number of free memory blocks. */ 43 | @JsonProperty("nfree") 44 | public int nfree; 45 | /** Minimum number of free memory blocks ever. */ 46 | @JsonProperty("min") 47 | public int min; 48 | 49 | @JsonCreator 50 | public MpStat() {} 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/dflt/McuMgrReadDateTimeResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Intellinium SAS, 2014-present 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.response.dflt; 8 | 9 | import com.fasterxml.jackson.annotation.JsonCreator; 10 | import com.fasterxml.jackson.annotation.JsonProperty; 11 | 12 | import io.runtime.mcumgr.response.McuMgrResponse; 13 | 14 | public class McuMgrReadDateTimeResponse extends McuMgrResponse { 15 | /** 16 | * Date & time in yyyy-MM-dd'T'HH:mm:ss.SSSSSS format. 17 | */ 18 | @JsonProperty("datetime") 19 | public String datetime; 20 | 21 | @JsonCreator 22 | public McuMgrReadDateTimeResponse() {} 23 | } 24 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/fs/McuMgrFsDownloadResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Runtime Inc. 3 | * Copyright (c) Intellinium SAS, 2014-present 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | */ 7 | 8 | package io.runtime.mcumgr.response.fs; 9 | 10 | import com.fasterxml.jackson.annotation.JsonCreator; 11 | import com.fasterxml.jackson.annotation.JsonProperty; 12 | 13 | import io.runtime.mcumgr.response.DownloadResponse; 14 | import io.runtime.mcumgr.response.McuMgrResponse; 15 | 16 | public class McuMgrFsDownloadResponse extends DownloadResponse { 17 | @JsonCreator 18 | public McuMgrFsDownloadResponse() {} 19 | } 20 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/fs/McuMgrFsUploadResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Runtime Inc. 3 | * Copyright (c) Intellinium SAS, 2014-present 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 6 | */ 7 | 8 | package io.runtime.mcumgr.response.fs; 9 | 10 | import com.fasterxml.jackson.annotation.JsonCreator; 11 | import com.fasterxml.jackson.annotation.JsonProperty; 12 | 13 | import io.runtime.mcumgr.response.McuMgrResponse; 14 | import io.runtime.mcumgr.response.UploadResponse; 15 | 16 | public class McuMgrFsUploadResponse extends UploadResponse { 17 | @JsonCreator 18 | public McuMgrFsUploadResponse() {} 19 | } 20 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/img/McuMgrCoreLoadResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | * 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 12 | * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this 13 | * software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 17 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 18 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 20 | * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 21 | */ 22 | 23 | package io.runtime.mcumgr.response.img; 24 | 25 | import com.fasterxml.jackson.annotation.JsonCreator; 26 | import com.fasterxml.jackson.annotation.JsonProperty; 27 | 28 | import io.runtime.mcumgr.response.DownloadResponse; 29 | import io.runtime.mcumgr.response.McuMgrResponse; 30 | 31 | public class McuMgrCoreLoadResponse extends DownloadResponse { 32 | @JsonCreator 33 | public McuMgrCoreLoadResponse() {} 34 | } 35 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/img/McuMgrImageStateResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Intellinium SAS, 2014-present 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package io.runtime.mcumgr.response.img; 7 | 8 | import com.fasterxml.jackson.annotation.JsonCreator; 9 | import com.fasterxml.jackson.annotation.JsonProperty; 10 | 11 | import io.runtime.mcumgr.response.McuMgrResponse; 12 | 13 | @SuppressWarnings("unused") 14 | public class McuMgrImageStateResponse extends McuMgrResponse { 15 | // For Mynewt see: 16 | // https://github.com/apache/mynewt-core/blob/master/boot/split/include/split/split.h 17 | public static final int SPLIT_STATUS_INVALID = 0; 18 | public static final int SPLIT_STATUS_NOT_MATCHING = 1; 19 | public static final int SPLIT_STATUS_MATCHING = 2; 20 | 21 | /** Image slot information. */ 22 | @JsonProperty("images") 23 | public ImageSlot[] images; 24 | /** 25 | * The split status. For Zephyr implementation this is always 0. 26 | * For Mynewt, see SPLIT_STATUS_* constants. 27 | */ 28 | @JsonProperty("splitStatus") 29 | public int splitStatus; 30 | 31 | @JsonCreator 32 | public McuMgrImageStateResponse() {} 33 | 34 | /** 35 | * The single image slot data structure. 36 | */ 37 | public static class ImageSlot { 38 | /** The slot number: 0 or 1. */ 39 | @JsonProperty("slot") 40 | public int slot; 41 | /** The image version string. */ 42 | @JsonProperty("version") 43 | public String version; 44 | /** The image hash. */ 45 | @JsonProperty("hash") 46 | public byte[] hash; 47 | /** An image is bootable when the Boot Loader verified that the image is valid. */ 48 | @JsonProperty("bootable") 49 | public boolean bootable; 50 | /** 51 | * An image is pending when it was scheduled to be swapped to slot 0. 52 | * That is, after the test or confirm commands were sent, but before 53 | * the device was reset. 54 | */ 55 | @JsonProperty("pending") 56 | public boolean pending; 57 | /** An image is confirmed when it managed to boot on slot 0. */ 58 | @JsonProperty("confirmed") 59 | public boolean confirmed; 60 | /** An image is active when it is running on slot 0. */ 61 | @JsonProperty("active") 62 | public boolean active; 63 | /** An image is permanent after it was confirmed using confirm command. */ 64 | @JsonProperty("permanent") 65 | public boolean permanent; 66 | 67 | @JsonCreator 68 | public ImageSlot() {} 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/img/McuMgrImageUploadResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Intellinium SAS, 2014-present 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.response.img; 8 | 9 | import com.fasterxml.jackson.annotation.JsonCreator; 10 | import com.fasterxml.jackson.annotation.JsonProperty; 11 | 12 | import io.runtime.mcumgr.response.McuMgrResponse; 13 | import io.runtime.mcumgr.response.UploadResponse; 14 | 15 | public class McuMgrImageUploadResponse extends UploadResponse { 16 | @JsonCreator 17 | public McuMgrImageUploadResponse() {} 18 | } 19 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/log/McuMgrLevelListResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Intellinium SAS, 2014-present 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.response.log; 8 | 9 | 10 | import com.fasterxml.jackson.annotation.JsonCreator; 11 | import com.fasterxml.jackson.annotation.JsonProperty; 12 | 13 | import io.runtime.mcumgr.response.McuMgrResponse; 14 | 15 | public class McuMgrLevelListResponse extends McuMgrResponse { 16 | @JsonProperty("level_map") 17 | public String[] level_map; 18 | 19 | @JsonCreator 20 | public McuMgrLevelListResponse() {} 21 | } 22 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/log/McuMgrLogListResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Intellinium SAS, 2014-present 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.response.log; 8 | 9 | import com.fasterxml.jackson.annotation.JsonCreator; 10 | import com.fasterxml.jackson.annotation.JsonProperty; 11 | 12 | import io.runtime.mcumgr.response.McuMgrResponse; 13 | 14 | public class McuMgrLogListResponse extends McuMgrResponse { 15 | @JsonProperty("log_list") 16 | public String[] log_list; 17 | 18 | @JsonCreator 19 | public McuMgrLogListResponse() {} 20 | } 21 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/log/McuMgrModuleListResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Intellinium SAS, 2014-present 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.response.log; 8 | 9 | import com.fasterxml.jackson.annotation.JsonCreator; 10 | import com.fasterxml.jackson.annotation.JsonProperty; 11 | 12 | import java.util.Map; 13 | 14 | import io.runtime.mcumgr.response.McuMgrResponse; 15 | 16 | public class McuMgrModuleListResponse extends McuMgrResponse { 17 | @JsonProperty("module_map") 18 | public Map module_map; 19 | 20 | @JsonCreator 21 | public McuMgrModuleListResponse() {} 22 | } 23 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/stat/McuMgrStatListResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Intellinium SAS, 2014-present 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.response.stat; 8 | 9 | import com.fasterxml.jackson.annotation.JsonCreator; 10 | import com.fasterxml.jackson.annotation.JsonProperty; 11 | 12 | import io.runtime.mcumgr.response.McuMgrResponse; 13 | 14 | public class McuMgrStatListResponse extends McuMgrResponse { 15 | /** A list of modules. */ 16 | @JsonProperty("stat_list") 17 | public String[] stat_list; 18 | 19 | @JsonCreator 20 | public McuMgrStatListResponse() {} 21 | } 22 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/response/stat/McuMgrStatResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Intellinium SAS, 2014-present 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.response.stat; 8 | 9 | import com.fasterxml.jackson.annotation.JsonCreator; 10 | import com.fasterxml.jackson.annotation.JsonProperty; 11 | 12 | import java.util.Map; 13 | 14 | import io.runtime.mcumgr.response.McuMgrResponse; 15 | 16 | public class McuMgrStatResponse extends McuMgrResponse { 17 | /** Module name. */ 18 | @JsonProperty("name") 19 | public String name; 20 | /** A map of key-value pairs for the given module. */ 21 | @JsonProperty("fields") 22 | public Map fields; 23 | 24 | @JsonCreator 25 | public McuMgrStatResponse() {} 26 | } 27 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/transfer/DownloadCallback.java: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.transfer; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import io.runtime.mcumgr.exception.McuMgrException; 7 | 8 | public interface DownloadCallback { 9 | /** 10 | * Called when a response has been received successfully. 11 | * 12 | * @param current the number of bytes downloaded so far. 13 | * @param total the total size of the download in bytes. 14 | * @param timestamp the timestamp of when the response was received. 15 | */ 16 | void onDownloadProgressChanged(int current, int total, long timestamp); 17 | 18 | /** 19 | * Called when the download has failed. 20 | * 21 | * @param error the error. See the cause for more info. 22 | */ 23 | void onDownloadFailed(@NotNull McuMgrException error); 24 | 25 | /** 26 | * Called when the download has been canceled. 27 | */ 28 | void onDownloadCanceled(); 29 | 30 | /** 31 | * Called when the download has finished successfully. 32 | * 33 | * @param data downloaded data. 34 | */ 35 | void onDownloadCompleted(@NotNull byte[] data); 36 | } 37 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/transfer/Transfer.java: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.transfer; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import io.runtime.mcumgr.exception.McuMgrException; 6 | import io.runtime.mcumgr.response.McuMgrResponse; 7 | 8 | @SuppressWarnings({"WeakerAccess", "unused"}) 9 | public abstract class Transfer implements TransferCallback { 10 | 11 | @Nullable 12 | byte[] mData; 13 | int mOffset; 14 | 15 | Transfer() { 16 | mData = null; 17 | mOffset = 0; 18 | } 19 | 20 | Transfer(int offset) { 21 | mData = null; 22 | mOffset = offset; 23 | } 24 | 25 | Transfer(@Nullable byte[] data) { 26 | mData = data; 27 | mOffset = 0; 28 | } 29 | 30 | Transfer(@Nullable byte[] data, int offset) { 31 | mData = data; 32 | mOffset = offset; 33 | } 34 | 35 | /** 36 | * Resets the transfer status parameters. 37 | */ 38 | public abstract void reset(); 39 | 40 | /** 41 | * Synchronously sends or requests the part of data from given offset. 42 | * 43 | * @param offset the offset, from which data will be transferred. 44 | * @return the response received. 45 | * @throws McuMgrException a reason of a failure. 46 | */ 47 | public abstract McuMgrResponse send(int offset) throws McuMgrException; 48 | 49 | /** 50 | * Synchronously sends or requests the next part of data. 51 | * 52 | * @return the response received. 53 | * @throws McuMgrException a reason of a failure. 54 | */ 55 | @SuppressWarnings("UnusedReturnValue") 56 | public McuMgrResponse sendNext() throws McuMgrException { 57 | return send(mOffset); 58 | } 59 | 60 | /** 61 | * Returns the data associated with this object. For incoming transfers the data are available 62 | * only when the transfer is complete. 63 | * 64 | * @return the data. 65 | */ 66 | @Nullable 67 | public byte[] getData() { 68 | return mData; 69 | } 70 | 71 | /** 72 | * Returns the current offset. 73 | * 74 | * @return the offset. 75 | */ 76 | public int getOffset() { 77 | return mOffset; 78 | } 79 | 80 | /** 81 | * Returns true if transfer is complete. 82 | */ 83 | public boolean isFinished() { 84 | return mData != null && mOffset == mData.length; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/transfer/TransferCallback.java: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.transfer; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import io.runtime.mcumgr.exception.McuMgrException; 6 | 7 | public interface TransferCallback { 8 | /** 9 | * Called when a response has been received successfully. 10 | * 11 | * @param current the number of bytes downloaded so far. 12 | * @param total the total size of the download in bytes. 13 | * @param timestamp the timestamp of when the response was received. 14 | */ 15 | void onProgressChanged(int current, int total, long timestamp); 16 | 17 | /** 18 | * Called when a response with failure has been received. 19 | * 20 | * @param e the exception. 21 | */ 22 | void onFailed(@NotNull McuMgrException e); 23 | 24 | /** 25 | * Called when the transfer is complete. 26 | */ 27 | void onCompleted(); 28 | 29 | /** 30 | * Called when the transfer has been cancelled. 31 | */ 32 | void onCanceled(); 33 | } 34 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/transfer/TransferController.java: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.transfer; 2 | 3 | public interface TransferController { 4 | 5 | /** 6 | * Pause the transfer. 7 | */ 8 | void pause(); 9 | 10 | /** 11 | * Resume a paused transfer. 12 | */ 13 | void resume(); 14 | 15 | /** 16 | * Cancel the transfer. 17 | */ 18 | void cancel(); 19 | } 20 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/transfer/Upload.java: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.transfer; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import io.runtime.mcumgr.McuMgrErrorCode; 7 | import io.runtime.mcumgr.exception.McuMgrErrorException; 8 | import io.runtime.mcumgr.exception.McuMgrException; 9 | import io.runtime.mcumgr.response.McuMgrResponse; 10 | import io.runtime.mcumgr.response.UploadResponse; 11 | 12 | @SuppressWarnings("unused") 13 | public abstract class Upload extends Transfer { 14 | 15 | private UploadCallback mCallback; 16 | 17 | protected Upload(@NotNull byte[] data) { 18 | this(data, null); 19 | } 20 | 21 | protected Upload(@NotNull byte[] data, @Nullable UploadCallback callback) { 22 | super(data); 23 | mCallback = callback; 24 | } 25 | 26 | protected abstract UploadResponse write(@NotNull byte[] data, int offset) throws McuMgrException; 27 | 28 | @Override 29 | public McuMgrResponse send(int offset) throws McuMgrException { 30 | if (mData == null) { 31 | throw new NullPointerException("Upload data cannot be null!"); 32 | } 33 | UploadResponse response = write(mData, offset); 34 | // Check for a McuManager error. 35 | if (response.rc != 0) { 36 | throw new McuMgrErrorException(McuMgrErrorCode.valueOf(response.rc)); 37 | } 38 | 39 | mOffset = response.off; 40 | 41 | return response; 42 | } 43 | 44 | @Override 45 | public void reset() { 46 | mOffset = 0; 47 | } 48 | 49 | @Override 50 | public void onProgressChanged(int current, int total, long timestamp) { 51 | if (mCallback != null) { 52 | mCallback.onUploadProgressChanged(current, total, timestamp); 53 | } 54 | } 55 | 56 | @Override 57 | public void onFailed(@NotNull McuMgrException e) { 58 | if (mCallback != null) { 59 | mCallback.onUploadFailed(e); 60 | } 61 | } 62 | 63 | @Override 64 | public void onCompleted() { 65 | if (mCallback != null) { 66 | mCallback.onUploadCompleted(); 67 | } 68 | } 69 | 70 | @Override 71 | public void onCanceled() { 72 | if (mCallback != null) { 73 | mCallback.onUploadCanceled(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/transfer/UploadCallback.java: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.transfer; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import io.runtime.mcumgr.exception.McuMgrException; 6 | 7 | public interface UploadCallback { 8 | /** 9 | * Called when a response has been received successfully. 10 | * 11 | * @param current the number of bytes sent so far. 12 | * @param total the size of the image in bytes. 13 | * @param timestamp the timestamp of when the response was received. 14 | */ 15 | void onUploadProgressChanged(int current, int total, long timestamp); 16 | 17 | /** 18 | * Called when the upload has failed. 19 | * 20 | * @param error the error. See the cause for more info. 21 | */ 22 | void onUploadFailed(@NotNull McuMgrException error); 23 | 24 | /** 25 | * Called when the upload has been canceled. 26 | */ 27 | void onUploadCanceled(); 28 | 29 | /** 30 | * Called when the upload has finished successfully. 31 | */ 32 | void onUploadCompleted(); 33 | } 34 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/transfer/UploadResult.kt: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.transfer 2 | 3 | import io.runtime.mcumgr.McuMgrErrorCode 4 | import io.runtime.mcumgr.R 5 | import io.runtime.mcumgr.response.UploadResponse 6 | 7 | class ErrorResponseException internal constructor( 8 | val code: McuMgrErrorCode 9 | ) : IllegalStateException("Request resulted in error response $code") 10 | 11 | internal sealed class UploadResult { 12 | 13 | data class Response( 14 | val body: UploadResponse, 15 | val code: McuMgrErrorCode 16 | ) : UploadResult() 17 | 18 | data class Failure( 19 | val throwable: Throwable 20 | ) : UploadResult() 21 | } 22 | 23 | internal inline fun UploadResult.onSuccess( 24 | action: (response: UploadResponse) -> Unit 25 | ): UploadResult { 26 | when (this) { 27 | is UploadResult.Response -> { 28 | if (code.isSuccess) { 29 | action(body) 30 | } 31 | } 32 | } 33 | return this 34 | } 35 | 36 | internal inline fun UploadResult.onErrorOrFailure(action: (throwable: Throwable) -> Unit): UploadResult { 37 | when (this) { 38 | is UploadResult.Response -> { 39 | if (code.isError) { 40 | action(ErrorResponseException(code)) 41 | } 42 | } 43 | is UploadResult.Failure -> action(throwable) 44 | } 45 | return this 46 | } 47 | 48 | internal val McuMgrErrorCode.isSuccess: Boolean 49 | get() = this == McuMgrErrorCode.OK 50 | 51 | internal val McuMgrErrorCode.isError: Boolean 52 | get() = this != McuMgrErrorCode.OK 53 | -------------------------------------------------------------------------------- /mcumgr-core/src/main/java/io/runtime/mcumgr/util/Endian.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2018 Runtime Inc. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.util; 8 | 9 | public enum Endian { 10 | LITTLE, 11 | BIG 12 | } 13 | -------------------------------------------------------------------------------- /mcumgr-core/src/test/java/io/runtime/mcumgr/McuMgrImageTest.kt: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr 2 | 3 | import io.runtime.mcumgr.image.McuMgrImage 4 | import org.junit.Test 5 | import java.io.ByteArrayOutputStream 6 | import java.io.InputStream 7 | 8 | class McuMgrImageTest { 9 | 10 | @Test 11 | fun `parse image without protected tlvs success`() { 12 | val inputStream = this::class.java.classLoader?.getResourceAsStream("slinky-no-prot-tlv.img")!! 13 | ?: throw IllegalStateException("input stream is null") 14 | val imageData = toByteArray(inputStream) 15 | McuMgrImage.fromBytes(imageData) 16 | } 17 | 18 | @Test 19 | fun `parse image with protected tlvs success`() { 20 | val inputStream = this::class.java.classLoader?.getResourceAsStream("slinky-prot-tlv.img") 21 | ?: throw IllegalStateException("input stream is null") 22 | val imageData = toByteArray(inputStream) 23 | McuMgrImage.fromBytes(imageData) 24 | } 25 | 26 | private fun toByteArray(inputStream: InputStream): ByteArray { 27 | val os = ByteArrayOutputStream() 28 | val buffer = ByteArray(1024) 29 | var len: Int 30 | while (inputStream.read(buffer).also { len = it } != -1) { 31 | os.write(buffer, 0, len) 32 | } 33 | return os.toByteArray() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /mcumgr-core/src/test/java/io/runtime/mcumgr/mock/McuMgrGroup.kt: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.mock 2 | 3 | // TODO pull this out of tests in major version release 4 | enum class McuMgrGroup(val value: Int) { 5 | DEFAULT(0), 6 | IMAGE(1), 7 | STATS(2), 8 | CONFIG(3), 9 | LOGS(4), 10 | CRASH(5), 11 | SPLIT(6), 12 | RUN(7), 13 | FS(8) 14 | } 15 | -------------------------------------------------------------------------------- /mcumgr-core/src/test/java/io/runtime/mcumgr/mock/McuMgrHandler.kt: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.mock 2 | 3 | import io.runtime.mcumgr.McuMgrHeader 4 | import io.runtime.mcumgr.response.McuMgrResponse 5 | 6 | interface McuMgrHandler { 7 | fun handle( 8 | header: McuMgrHeader, 9 | payload: ByteArray, 10 | responseType: Class 11 | ): T 12 | } 13 | 14 | interface OverrideHandler: McuMgrHandler { 15 | val groupId: Int 16 | val commandId: Int 17 | } 18 | -------------------------------------------------------------------------------- /mcumgr-core/src/test/java/io/runtime/mcumgr/mock/McuMgrOperation.kt: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.mock 2 | 3 | // TODO pull this out of tests in major version release 4 | enum class McuMgrOperation(val value: Int) { 5 | READ(0), 6 | READ_RESPONSE(1), 7 | WRITE(2), 8 | WRITE_RESPONSE(3) 9 | } 10 | -------------------------------------------------------------------------------- /mcumgr-core/src/test/java/io/runtime/mcumgr/mock/MockMcuMgrResponse.kt: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.mock 2 | 3 | import io.runtime.mcumgr.McuMgrErrorCode 4 | import io.runtime.mcumgr.McuMgrHeader 5 | import io.runtime.mcumgr.McuMgrScheme 6 | import io.runtime.mcumgr.response.McuMgrResponse 7 | import io.runtime.mcumgr.util.CBOR 8 | 9 | /** 10 | * Build a mock error response. 11 | */ 12 | fun buildMockErrorResponse( 13 | errorCode: McuMgrErrorCode, 14 | responseHeader: McuMgrHeader, 15 | responseType: Class, 16 | codeClass: Int = 2, 17 | codeDetail: Int = 5 18 | ): T { 19 | val responsePayload = CBOR.toBytes(McuMgrErrorResponse(errorCode)) 20 | return McuMgrResponse.buildCoapResponse( 21 | McuMgrScheme.COAP_BLE, 22 | responsePayload, 23 | responseHeader.toBytes(), 24 | responsePayload, 25 | codeClass, 26 | codeDetail, 27 | responseType 28 | ) 29 | } 30 | 31 | /** 32 | * Build a mock response. 33 | */ 34 | fun buildMockResponse( 35 | responseHeader: McuMgrHeader, 36 | responsePayload: ByteArray, 37 | responseType: Class, 38 | codeClass: Int = 2, 39 | codeDetail: Int = 5 40 | ): T = McuMgrResponse.buildCoapResponse( 41 | McuMgrScheme.COAP_BLE, 42 | responsePayload, 43 | responseHeader.toBytes(), 44 | responsePayload, 45 | codeClass, 46 | codeDetail, 47 | responseType 48 | ) 49 | 50 | /** 51 | * Helper class for building an mcumgr error response. 52 | */ 53 | class McuMgrErrorResponse(errorCode: McuMgrErrorCode): McuMgrResponse() { 54 | init { 55 | rc = errorCode.value() 56 | } 57 | } 58 | 59 | /** 60 | * Return a new mcumgr header with the operation converted to a response. 61 | */ 62 | fun McuMgrHeader.toResponse(): McuMgrHeader { 63 | val newOp = when (op) { 64 | McuMgrOperation.READ.value -> McuMgrOperation.READ_RESPONSE.value 65 | McuMgrOperation.WRITE.value -> McuMgrOperation.WRITE_RESPONSE.value 66 | else -> op 67 | } 68 | return McuMgrHeader(newOp, flags, len, groupId, sequenceNum, commandId) 69 | } 70 | -------------------------------------------------------------------------------- /mcumgr-core/src/test/resources/slinky-no-prot-tlv.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/mcumgr-core/src/test/resources/slinky-no-prot-tlv.img -------------------------------------------------------------------------------- /mcumgr-core/src/test/resources/slinky-prot-tlv.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/mcumgr-core/src/test/resources/slinky-prot-tlv.img -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | apply plugin: 'com.android.application' 8 | 9 | android { 10 | compileSdkVersion 29 11 | buildToolsVersion "29.0.3" 12 | 13 | defaultConfig { 14 | applicationId "io.runtime.mcumgr" 15 | minSdkVersion 21 16 | targetSdkVersion 29 17 | versionCode 1 18 | versionName "1.0" 19 | resConfigs "en" 20 | 21 | vectorDrawables.useSupportLibrary = true 22 | 23 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 24 | } 25 | 26 | buildTypes { 27 | release { 28 | minifyEnabled true 29 | consumerProguardFiles 'proguard-rules.pro' 30 | } 31 | } 32 | 33 | compileOptions { 34 | sourceCompatibility JavaVersion.VERSION_1_8 35 | targetCompatibility JavaVersion.VERSION_1_8 36 | } 37 | } 38 | 39 | dependencies { 40 | implementation 'androidx.appcompat:appcompat:1.3.0-alpha01' 41 | implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha04' 42 | implementation 'androidx.cardview:cardview:1.0.0' 43 | implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta7' 44 | implementation 'com.google.android.material:material:1.3.0-alpha01' 45 | 46 | // Lifecycle extensions 47 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' 48 | 49 | // Butter Knife 50 | implementation 'com.jakewharton:butterknife:10.2.1' 51 | annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1' 52 | 53 | // Dagger 2 54 | implementation 'com.google.dagger:dagger:2.27' 55 | implementation 'com.google.dagger:dagger-android:2.27' 56 | implementation 'com.google.dagger:dagger-android-support:2.27' 57 | annotationProcessor 'com.google.dagger:dagger-compiler:2.27' 58 | annotationProcessor 'com.google.dagger:dagger-android-processor:2.27' 59 | 60 | // Brings the new BluetoothLeScanner API to older platforms 61 | implementation 'no.nordicsemi.android.support.v18:scanner:1.4.3' 62 | 63 | // Timber & SLF4J 64 | implementation 'com.arcao:slf4j-timber:3.1@aar' 65 | implementation 'com.jakewharton.timber:timber:4.7.1' 66 | 67 | // Mcu Mgr 68 | implementation project(':mcumgr-ble') 69 | 70 | // Test 71 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 72 | androidTestImplementation 'androidx.test:runner:1.2.0' 73 | testImplementation 'junit:junit:4.13' 74 | } 75 | -------------------------------------------------------------------------------- /sample/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 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 11 | 14 | 15 | 22 | 23 | 24 | 33 | 36 | 37 | 38 | 39 | 40 | 41 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/adapter/DeviceDiffCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.adapter; 8 | 9 | import java.util.List; 10 | 11 | import androidx.recyclerview.widget.DiffUtil; 12 | 13 | public class DeviceDiffCallback extends DiffUtil.Callback { 14 | private final List oldList; 15 | private final List newList; 16 | 17 | DeviceDiffCallback(final List oldList, 18 | final List newList) { 19 | this.oldList = oldList; 20 | this.newList = newList; 21 | } 22 | 23 | @Override 24 | public int getOldListSize() { 25 | return oldList != null ? oldList.size() : 0; 26 | } 27 | 28 | @Override 29 | public int getNewListSize() { 30 | return newList != null ? newList.size() : 0; 31 | } 32 | 33 | @Override 34 | public boolean areItemsTheSame(final int oldItemPosition, final int newItemPosition) { 35 | return oldList.get(oldItemPosition) == newList.get(newItemPosition); 36 | } 37 | 38 | @Override 39 | public boolean areContentsTheSame(final int oldItemPosition, final int newItemPosition) { 40 | final DiscoveredBluetoothDevice device = oldList.get(oldItemPosition); 41 | return device.hasRssiLevelChanged(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/application/Dagger2Application.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.application; 8 | 9 | import android.app.Application; 10 | import android.bluetooth.BluetoothDevice; 11 | 12 | import javax.inject.Inject; 13 | 14 | import androidx.annotation.NonNull; 15 | import dagger.android.AndroidInjector; 16 | import dagger.android.DispatchingAndroidInjector; 17 | import dagger.android.HasAndroidInjector; 18 | import io.runtime.mcumgr.sample.di.AppInjector; 19 | import io.runtime.mcumgr.sample.di.component.McuMgrSubComponent; 20 | import timber.log.Timber; 21 | 22 | public class Dagger2Application extends Application implements HasAndroidInjector { 23 | 24 | @Inject 25 | DispatchingAndroidInjector dispatchingAndroidInjector; 26 | @Inject 27 | McuMgrSubComponent.Builder mBuilder; 28 | 29 | @Override 30 | public void onCreate() { 31 | super.onCreate(); 32 | 33 | // The app injector makes sure that all activities and fragments that implement Injectable 34 | // are injected in onCreate(...) or onActivityCreated(...) 35 | AppInjector.init(this); 36 | 37 | // Plant a Timber DebugTree to collect logs from sample app and McuManager 38 | Timber.plant(new Timber.DebugTree()); 39 | } 40 | 41 | @Override 42 | public AndroidInjector androidInjector() { 43 | return dispatchingAndroidInjector; 44 | } 45 | 46 | /** 47 | * Binds the target {@link BluetoothDevice} with the Dagger2 sub component. 48 | * 49 | * @param device the target device. 50 | */ 51 | public void setTarget(@NonNull final BluetoothDevice device) { 52 | mBuilder.target(device).build().update(this); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/di/Injectable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.di; 8 | 9 | /** 10 | * Marks an activity / fragment injectable. 11 | */ 12 | public interface Injectable { 13 | } 14 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/di/McuMgrScope.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.di; 8 | 9 | import java.lang.annotation.Documented; 10 | import java.lang.annotation.Retention; 11 | 12 | import javax.inject.Scope; 13 | 14 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 15 | 16 | @Scope 17 | @Documented 18 | @Retention(RUNTIME) 19 | public @interface McuMgrScope {} 20 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/di/component/ApplicationComponent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.di.component; 8 | 9 | import javax.inject.Singleton; 10 | 11 | import dagger.Component; 12 | import dagger.android.AndroidInjectionModule; 13 | import dagger.android.support.AndroidSupportInjectionModule; 14 | import io.runtime.mcumgr.sample.application.Dagger2Application; 15 | import io.runtime.mcumgr.sample.di.module.ActivitiesModule; 16 | import io.runtime.mcumgr.sample.di.module.ContextModule; 17 | import io.runtime.mcumgr.sample.di.module.McuMgrModule; 18 | import io.runtime.mcumgr.sample.di.module.ViewModelModule; 19 | 20 | /** 21 | * Check this: https://github.com/googlesamples/android-architecture-components 22 | * for more details. 23 | */ 24 | @Component(modules = { 25 | AndroidInjectionModule.class, 26 | AndroidSupportInjectionModule.class, 27 | ContextModule.class, 28 | ActivitiesModule.class, 29 | ViewModelModule.class, 30 | McuMgrModule.class 31 | }) 32 | @Singleton 33 | public interface ApplicationComponent { 34 | @Component.Builder 35 | interface Builder { 36 | Builder contextModule(final ContextModule module); 37 | 38 | ApplicationComponent build(); 39 | } 40 | 41 | void inject(final Dagger2Application application); 42 | } 43 | 44 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/di/component/McuMgrSubComponent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.di.component; 8 | 9 | import android.bluetooth.BluetoothDevice; 10 | 11 | import dagger.BindsInstance; 12 | import dagger.Subcomponent; 13 | import io.runtime.mcumgr.sample.application.Dagger2Application; 14 | import io.runtime.mcumgr.sample.di.McuMgrScope; 15 | import io.runtime.mcumgr.sample.di.module.McuMgrActivitiesModule; 16 | import io.runtime.mcumgr.sample.di.module.McuMgrFragmentBuildersModule; 17 | import io.runtime.mcumgr.sample.di.module.McuMgrManagerModule; 18 | import io.runtime.mcumgr.sample.di.module.McuMgrTransportModule; 19 | import io.runtime.mcumgr.sample.di.module.McuMgrViewModelModule; 20 | 21 | @Subcomponent(modules = { 22 | McuMgrActivitiesModule.class, 23 | McuMgrFragmentBuildersModule.class, 24 | McuMgrViewModelModule.class, 25 | McuMgrTransportModule.class, 26 | McuMgrManagerModule.class 27 | }) 28 | @McuMgrScope 29 | public interface McuMgrSubComponent { 30 | @Subcomponent.Builder 31 | interface Builder { 32 | /** 33 | * Sets the connection target. 34 | * 35 | * @param device teh target Bluetooth device. 36 | * @return The builder instance. 37 | */ 38 | @BindsInstance 39 | Builder target(final BluetoothDevice device); 40 | 41 | McuMgrSubComponent build(); 42 | } 43 | 44 | /** 45 | * Adds the {@link io.runtime.mcumgr.sample.MainActivity} to the 46 | * {@link Dagger2Application#androidInjector()}. 47 | * The {@link io.runtime.mcumgr.sample.MainActivity} requires the 48 | * {@link io.runtime.mcumgr.McuMgrTransport} to be instantiated before injecting. 49 | * 50 | * @param application the application. 51 | */ 52 | void update(final Dagger2Application application); 53 | } 54 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/di/component/McuMgrViewModelSubComponent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.di.component; 8 | 9 | import dagger.Subcomponent; 10 | import io.runtime.mcumgr.sample.viewmodel.MainViewModel; 11 | import io.runtime.mcumgr.sample.viewmodel.mcumgr.DeviceStatusViewModel; 12 | import io.runtime.mcumgr.sample.viewmodel.mcumgr.EchoViewModel; 13 | import io.runtime.mcumgr.sample.viewmodel.mcumgr.FilesDownloadViewModel; 14 | import io.runtime.mcumgr.sample.viewmodel.mcumgr.FilesUploadViewModel; 15 | import io.runtime.mcumgr.sample.viewmodel.mcumgr.ImageControlViewModel; 16 | import io.runtime.mcumgr.sample.viewmodel.mcumgr.ImageUpgradeViewModel; 17 | import io.runtime.mcumgr.sample.viewmodel.mcumgr.ImageUploadViewModel; 18 | import io.runtime.mcumgr.sample.viewmodel.mcumgr.McuMgrViewModel; 19 | import io.runtime.mcumgr.sample.viewmodel.mcumgr.McuMgrViewModelFactory; 20 | import io.runtime.mcumgr.sample.viewmodel.mcumgr.ResetViewModel; 21 | import io.runtime.mcumgr.sample.viewmodel.mcumgr.StatsViewModel; 22 | 23 | /** 24 | * A sub component to create ViewModels. It is called by the 25 | * {@link McuMgrViewModelFactory}. Using this component allows 26 | * ViewModels to define {@link javax.inject.Inject} constructors. 27 | */ 28 | @Subcomponent 29 | public interface McuMgrViewModelSubComponent { 30 | @Subcomponent.Builder 31 | interface Builder { 32 | McuMgrViewModelSubComponent build(); 33 | } 34 | 35 | MainViewModel mainViewModel(); 36 | 37 | DeviceStatusViewModel deviceStatusViewModel(); 38 | EchoViewModel echoViewModel(); 39 | ResetViewModel resetViewModel(); 40 | StatsViewModel statsViewModel(); 41 | McuMgrViewModel mcuMgrViewModel(); 42 | ImageUpgradeViewModel imageUpgradeViewModel(); 43 | ImageUploadViewModel imageUploadViewModel(); 44 | ImageControlViewModel imageControlViewModel(); 45 | FilesDownloadViewModel filesDownloadViewModel(); 46 | FilesUploadViewModel filesUploadViewModel(); 47 | } 48 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/di/component/ViewModelSubComponent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.di.component; 8 | 9 | import dagger.Subcomponent; 10 | import io.runtime.mcumgr.sample.viewmodel.ScannerViewModel; 11 | import io.runtime.mcumgr.sample.viewmodel.ViewModelFactory; 12 | 13 | /** 14 | * A sub component to create ViewModels. It is called by the 15 | * {@link ViewModelFactory}. Using this component allows 16 | * ViewModels to define {@link javax.inject.Inject} constructors. 17 | */ 18 | @Subcomponent 19 | public interface ViewModelSubComponent { 20 | @Subcomponent.Builder 21 | interface Builder { 22 | ViewModelSubComponent build(); 23 | } 24 | 25 | ScannerViewModel scannerViewModel(); 26 | } 27 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/di/module/ActivitiesModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.di.module; 8 | 9 | import dagger.Module; 10 | import dagger.android.ContributesAndroidInjector; 11 | import io.runtime.mcumgr.sample.ScannerActivity; 12 | 13 | @SuppressWarnings("unused") 14 | @Module 15 | public abstract class ActivitiesModule { 16 | @ContributesAndroidInjector 17 | abstract ScannerActivity contributeScannerActivity(); 18 | } 19 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/di/module/ContextModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.di.module; 8 | 9 | import android.app.Application; 10 | import android.content.Context; 11 | import android.content.SharedPreferences; 12 | import android.preference.PreferenceManager; 13 | 14 | import javax.inject.Singleton; 15 | 16 | import dagger.Module; 17 | import dagger.Provides; 18 | 19 | @Module 20 | public class ContextModule { 21 | private final Application mApplication; 22 | 23 | public ContextModule(final Application application) { 24 | this.mApplication = application; 25 | } 26 | 27 | @Provides 28 | @Singleton 29 | public Application provideApplication() { 30 | return mApplication; 31 | } 32 | 33 | @Provides 34 | @Singleton 35 | public Context provideApplicationContext() { 36 | return mApplication.getApplicationContext(); 37 | } 38 | 39 | @Provides 40 | @Singleton 41 | public SharedPreferences provideSharedPreferences() { 42 | return PreferenceManager.getDefaultSharedPreferences(mApplication); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/di/module/McuMgrActivitiesModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.di.module; 8 | 9 | import dagger.Module; 10 | import dagger.android.ContributesAndroidInjector; 11 | import io.runtime.mcumgr.sample.MainActivity; 12 | 13 | @SuppressWarnings("unused") 14 | @Module 15 | public abstract class McuMgrActivitiesModule { 16 | @ContributesAndroidInjector 17 | abstract MainActivity contributeMainActivity(); 18 | } 19 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/di/module/McuMgrFragmentBuildersModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.di.module; 8 | 9 | import dagger.Module; 10 | import dagger.android.ContributesAndroidInjector; 11 | import io.runtime.mcumgr.sample.dialog.PartitionDialogFragment; 12 | import io.runtime.mcumgr.sample.fragment.ImageFragment; 13 | import io.runtime.mcumgr.sample.fragment.mcumgr.DeviceStatusFragment; 14 | import io.runtime.mcumgr.sample.fragment.mcumgr.EchoFragment; 15 | import io.runtime.mcumgr.sample.fragment.mcumgr.FilesDownloadFragment; 16 | import io.runtime.mcumgr.sample.fragment.mcumgr.FilesUploadFragment; 17 | import io.runtime.mcumgr.sample.fragment.mcumgr.ImageControlFragment; 18 | import io.runtime.mcumgr.sample.fragment.mcumgr.ImageUpgradeFragment; 19 | import io.runtime.mcumgr.sample.fragment.mcumgr.ImageUploadFragment; 20 | import io.runtime.mcumgr.sample.fragment.mcumgr.ResetFragment; 21 | import io.runtime.mcumgr.sample.fragment.mcumgr.StatsFragment; 22 | 23 | @SuppressWarnings("unused") 24 | @Module 25 | public abstract class McuMgrFragmentBuildersModule { 26 | @ContributesAndroidInjector 27 | abstract DeviceStatusFragment contributeDeviceStatusFragment(); 28 | @ContributesAndroidInjector 29 | abstract EchoFragment contributeEchoFragment(); 30 | @ContributesAndroidInjector 31 | abstract ResetFragment contributeResetFragment(); 32 | @ContributesAndroidInjector 33 | abstract StatsFragment contributeStatsFragment(); 34 | @ContributesAndroidInjector 35 | abstract ImageFragment contributeImageFragment(); 36 | @ContributesAndroidInjector 37 | abstract ImageUpgradeFragment contributeImageUpgradeFragment(); 38 | @ContributesAndroidInjector 39 | abstract ImageUploadFragment contributeImageUploadFragment(); 40 | @ContributesAndroidInjector 41 | abstract ImageControlFragment contributeImageControlFragment(); 42 | @ContributesAndroidInjector 43 | abstract PartitionDialogFragment contributePartitionDialogFragment(); 44 | @ContributesAndroidInjector 45 | abstract FilesDownloadFragment contributeFileDownloadFragment(); 46 | @ContributesAndroidInjector 47 | abstract FilesUploadFragment contributeFilesUploadFragment(); 48 | } 49 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/di/module/McuMgrManagerModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.di.module; 8 | 9 | import dagger.Module; 10 | import dagger.Provides; 11 | import io.runtime.mcumgr.McuMgrTransport; 12 | import io.runtime.mcumgr.dfu.FirmwareUpgradeManager; 13 | import io.runtime.mcumgr.managers.ConfigManager; 14 | import io.runtime.mcumgr.managers.DefaultManager; 15 | import io.runtime.mcumgr.managers.FsManager; 16 | import io.runtime.mcumgr.managers.ImageManager; 17 | import io.runtime.mcumgr.managers.LogManager; 18 | import io.runtime.mcumgr.managers.StatsManager; 19 | 20 | @Module 21 | public class McuMgrManagerModule { 22 | 23 | @Provides 24 | static ConfigManager provideConfigManager(final McuMgrTransport transport) { 25 | return new ConfigManager(transport); 26 | } 27 | 28 | @Provides 29 | static DefaultManager provideDefaultManager(final McuMgrTransport transport) { 30 | return new DefaultManager(transport); 31 | } 32 | 33 | @Provides 34 | static FsManager provideFsManager(final McuMgrTransport transport) { 35 | return new FsManager(transport); 36 | } 37 | 38 | @Provides 39 | static LogManager provideLogManager(final McuMgrTransport transport) { 40 | return new LogManager(transport); 41 | } 42 | 43 | @Provides 44 | static ImageManager provideImageManager(final McuMgrTransport transport) { 45 | return new ImageManager(transport); 46 | } 47 | 48 | @Provides 49 | static StatsManager provideStatsManager(final McuMgrTransport transport) { 50 | return new StatsManager(transport); 51 | } 52 | 53 | @Provides 54 | static FirmwareUpgradeManager provideFirmwareUpgradeManager(final McuMgrTransport transport) { 55 | return new FirmwareUpgradeManager(transport); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/di/module/McuMgrModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.di.module; 8 | 9 | import android.content.SharedPreferences; 10 | 11 | import javax.inject.Singleton; 12 | 13 | import dagger.Module; 14 | import dagger.Provides; 15 | import io.runtime.mcumgr.sample.di.component.McuMgrSubComponent; 16 | import io.runtime.mcumgr.sample.utils.FsUtils; 17 | 18 | @Module(subcomponents = McuMgrSubComponent.class) 19 | public class McuMgrModule { 20 | 21 | @Provides 22 | @Singleton 23 | static FsUtils provideFsUtils(final SharedPreferences preferences) { 24 | return new FsUtils(preferences); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/di/module/McuMgrTransportModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.di.module; 8 | 9 | import android.bluetooth.BluetoothDevice; 10 | import android.content.Context; 11 | 12 | import androidx.annotation.NonNull; 13 | import androidx.lifecycle.MutableLiveData; 14 | 15 | import javax.inject.Named; 16 | 17 | import dagger.Module; 18 | import dagger.Provides; 19 | import io.runtime.mcumgr.McuMgrTransport; 20 | import io.runtime.mcumgr.sample.di.McuMgrScope; 21 | import io.runtime.mcumgr.sample.observable.ObservableMcuMgrBleTransport; 22 | 23 | @Module 24 | public class McuMgrTransportModule { 25 | 26 | @Provides 27 | @Named("busy") 28 | @McuMgrScope 29 | @NonNull 30 | static MutableLiveData provideBusyStateLiveData() { 31 | return new MutableLiveData<>(); 32 | } 33 | 34 | @Provides 35 | @McuMgrScope 36 | @NonNull 37 | static McuMgrTransport provideMcuMgrTransport(@NonNull final Context context, 38 | @NonNull final BluetoothDevice device) { 39 | return new ObservableMcuMgrBleTransport(context, device); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/di/module/McuMgrViewModelModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.di.module; 8 | 9 | import dagger.Module; 10 | import dagger.Provides; 11 | import io.runtime.mcumgr.sample.di.McuMgrScope; 12 | import io.runtime.mcumgr.sample.di.component.McuMgrViewModelSubComponent; 13 | import io.runtime.mcumgr.sample.viewmodel.mcumgr.McuMgrViewModelFactory; 14 | 15 | @Module(subcomponents = { 16 | McuMgrViewModelSubComponent.class 17 | }) 18 | public class McuMgrViewModelModule { 19 | 20 | @Provides 21 | @McuMgrScope 22 | static McuMgrViewModelFactory provideMcuMgrViewModelFactory( 23 | final McuMgrViewModelSubComponent.Builder viewModelSubComponent) { 24 | return new McuMgrViewModelFactory(viewModelSubComponent.build()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/di/module/ViewModelModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.di.module; 8 | 9 | import android.app.Application; 10 | 11 | import javax.inject.Singleton; 12 | 13 | import dagger.Module; 14 | import dagger.Provides; 15 | import io.runtime.mcumgr.sample.di.component.ViewModelSubComponent; 16 | import io.runtime.mcumgr.sample.viewmodel.ViewModelFactory; 17 | 18 | @Module(subcomponents = { 19 | ViewModelSubComponent.class 20 | }) 21 | public class ViewModelModule { 22 | 23 | @Provides 24 | @Singleton 25 | static ViewModelFactory provideViewModelFactory(final Application application, 26 | final ViewModelSubComponent.Builder viewModelSubComponent) { 27 | return new ViewModelFactory(application, viewModelSubComponent.build()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/dialog/FirmwareUpgradeModeDialogFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.dialog; 8 | 9 | import android.app.Dialog; 10 | import android.os.Bundle; 11 | 12 | import androidx.annotation.NonNull; 13 | import androidx.appcompat.app.AlertDialog; 14 | import androidx.fragment.app.DialogFragment; 15 | import io.runtime.mcumgr.dfu.FirmwareUpgradeManager; 16 | import io.runtime.mcumgr.sample.R; 17 | import io.runtime.mcumgr.sample.fragment.mcumgr.ImageUpgradeFragment; 18 | 19 | public class FirmwareUpgradeModeDialogFragment extends DialogFragment { 20 | private static final String SIS_ITEM = "item"; 21 | 22 | private int mSelectedItem; 23 | 24 | public static DialogFragment getInstance() { 25 | return new FirmwareUpgradeModeDialogFragment(); 26 | } 27 | 28 | @SuppressWarnings("ConstantConditions") 29 | @NonNull 30 | @Override 31 | public Dialog onCreateDialog(final Bundle savedInstanceState) { 32 | if (savedInstanceState != null) { 33 | mSelectedItem = savedInstanceState.getInt(SIS_ITEM); 34 | } else { 35 | mSelectedItem = 0; 36 | } 37 | 38 | return new AlertDialog.Builder(requireContext()) 39 | .setTitle(R.string.image_upgrade_mode) 40 | .setSingleChoiceItems(R.array.image_upgrade_options, mSelectedItem, 41 | (dialog, which) -> mSelectedItem = which) 42 | .setNegativeButton(android.R.string.cancel, null) 43 | .setPositiveButton(R.string.image_upgrade_action_start, (dialog, which) -> { 44 | final ImageUpgradeFragment parent = (ImageUpgradeFragment) getParentFragment(); 45 | parent.start(getMode()); 46 | }) 47 | .create(); 48 | } 49 | 50 | @Override 51 | public void onSaveInstanceState(@NonNull final Bundle outState) { 52 | super.onSaveInstanceState(outState); 53 | outState.putInt(SIS_ITEM, mSelectedItem); 54 | } 55 | 56 | private FirmwareUpgradeManager.Mode getMode() { 57 | switch (mSelectedItem) { 58 | case 2: 59 | return FirmwareUpgradeManager.Mode.CONFIRM_ONLY; 60 | case 1: 61 | return FirmwareUpgradeManager.Mode.TEST_ONLY; 62 | case 0: 63 | default: 64 | return FirmwareUpgradeManager.Mode.TEST_AND_CONFIRM; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/dialog/GenerateFileDialogFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.dialog; 8 | 9 | import android.app.Dialog; 10 | import android.content.Context; 11 | import android.content.DialogInterface; 12 | import android.os.Bundle; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.inputmethod.InputMethodManager; 16 | import android.widget.EditText; 17 | 18 | import androidx.annotation.NonNull; 19 | import androidx.annotation.Nullable; 20 | import androidx.appcompat.app.AlertDialog; 21 | import androidx.fragment.app.DialogFragment; 22 | import io.runtime.mcumgr.sample.R; 23 | import io.runtime.mcumgr.sample.fragment.mcumgr.FilesUploadFragment; 24 | 25 | public class GenerateFileDialogFragment extends DialogFragment { 26 | private InputMethodManager mImm; 27 | 28 | public static DialogFragment getInstance() { 29 | return new GenerateFileDialogFragment(); 30 | } 31 | 32 | @Override 33 | public void onCreate(@Nullable final Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | mImm = (InputMethodManager) requireContext().getSystemService(Context.INPUT_METHOD_SERVICE); 36 | } 37 | 38 | @SuppressWarnings("ConstantConditions") 39 | @NonNull 40 | @Override 41 | public Dialog onCreateDialog(final Bundle savedInstanceState) { 42 | final LayoutInflater inflater = requireActivity().getLayoutInflater(); 43 | final View view = inflater.inflate(R.layout.dialog_generate_file, null); 44 | final EditText fileSize = view.findViewById(R.id.file_size); 45 | 46 | final AlertDialog dialog = new AlertDialog.Builder(requireContext()) 47 | .setTitle(R.string.files_upload_generate_title) 48 | .setView(view) 49 | // Setting the positive button listener here would cause the dialog to dismiss. 50 | // We have to validate the value before. 51 | .setPositiveButton(R.string.files_action_generate, null) 52 | .setNegativeButton(android.R.string.cancel, null) 53 | .create(); 54 | dialog.setOnShowListener(d -> mImm.showSoftInput(fileSize, InputMethodManager.SHOW_IMPLICIT)); 55 | dialog.show(); 56 | dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> { 57 | try { 58 | final int size = Integer.parseInt(fileSize.getText().toString()); 59 | 60 | final FilesUploadFragment parent = (FilesUploadFragment) getParentFragment(); 61 | parent.onGenerateFileRequested(size); 62 | dismiss(); 63 | } catch (final NumberFormatException e) { 64 | fileSize.setError(getString(R.string.files_upload_generate_error)); 65 | } 66 | }); 67 | return dialog; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/dialog/HelpDialogFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.dialog; 8 | 9 | import android.app.Dialog; 10 | import android.os.Bundle; 11 | 12 | import androidx.annotation.NonNull; 13 | import androidx.annotation.StringRes; 14 | import androidx.appcompat.app.AlertDialog; 15 | import androidx.appcompat.app.AppCompatDialogFragment; 16 | import androidx.fragment.app.DialogFragment; 17 | import io.runtime.mcumgr.sample.R; 18 | 19 | public class HelpDialogFragment extends AppCompatDialogFragment { 20 | private static final String ARG_TITLE_RES_ID = "titleResId"; 21 | private static final String ARG_MESSAGE_RES_ID = "messageResId"; 22 | 23 | @NonNull 24 | public static DialogFragment getInstance(@StringRes final int titleResId, @StringRes final int messageResId) { 25 | final DialogFragment fragment = new HelpDialogFragment(); 26 | 27 | final Bundle args = new Bundle(); 28 | args.putInt(ARG_TITLE_RES_ID, titleResId); 29 | args.putInt(ARG_MESSAGE_RES_ID, messageResId); 30 | fragment.setArguments(args); 31 | 32 | return fragment; 33 | } 34 | 35 | @NonNull 36 | @Override 37 | public Dialog onCreateDialog(final Bundle savedInstanceState) { 38 | final Bundle args = getArguments(); 39 | if (args == null) { 40 | throw new UnsupportedOperationException("HelpDialogFragment created without arguments"); 41 | } 42 | 43 | final int titleResId = getArguments().getInt(ARG_TITLE_RES_ID); 44 | final int messageResId = getArguments().getInt(ARG_MESSAGE_RES_ID); 45 | 46 | return new AlertDialog.Builder(requireContext()) 47 | .setIcon(R.drawable.ic_help) 48 | .setTitle(titleResId) 49 | .setMessage(messageResId) 50 | .setPositiveButton(android.R.string.ok, null) 51 | .create(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/fragment/DeviceFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.fragment; 8 | 9 | import android.os.Bundle; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | 14 | import androidx.annotation.NonNull; 15 | import androidx.annotation.Nullable; 16 | import androidx.fragment.app.Fragment; 17 | import io.runtime.mcumgr.sample.R; 18 | 19 | public class DeviceFragment extends Fragment { 20 | 21 | @Nullable 22 | @Override 23 | public View onCreateView(@NonNull final LayoutInflater inflater, 24 | @Nullable final ViewGroup container, 25 | @Nullable final Bundle savedInstanceState) { 26 | return inflater.inflate(R.layout.fragment_device, container, false); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/fragment/FilesFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.fragment; 8 | 9 | import android.os.Bundle; 10 | import android.view.LayoutInflater; 11 | import android.view.Menu; 12 | import android.view.MenuInflater; 13 | import android.view.MenuItem; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | 17 | import androidx.annotation.NonNull; 18 | import androidx.annotation.Nullable; 19 | import androidx.fragment.app.DialogFragment; 20 | import androidx.fragment.app.Fragment; 21 | import io.runtime.mcumgr.sample.R; 22 | import io.runtime.mcumgr.sample.dialog.PartitionDialogFragment; 23 | 24 | public class FilesFragment extends Fragment { 25 | 26 | @Override 27 | public void onCreate(@Nullable final Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setHasOptionsMenu(true); 30 | } 31 | 32 | @Override 33 | public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { 34 | inflater.inflate(R.menu.settings, menu); 35 | } 36 | 37 | @Override 38 | public boolean onOptionsItemSelected(@NonNull final MenuItem item) { 39 | switch (item.getItemId()) { 40 | case R.id.action_settings: 41 | final DialogFragment dialog = PartitionDialogFragment.getInstance(); 42 | dialog.show(getChildFragmentManager(), null); 43 | return true; 44 | } 45 | return false; 46 | } 47 | 48 | @Nullable 49 | @Override 50 | public View onCreateView(@NonNull final LayoutInflater inflater, 51 | @Nullable final ViewGroup container, 52 | @Nullable final Bundle savedInstanceState) { 53 | return inflater.inflate(R.layout.fragment_fs, container, false); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/fragment/LogsStatsFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.fragment; 8 | 9 | import android.os.Bundle; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | 14 | import androidx.annotation.NonNull; 15 | import androidx.annotation.Nullable; 16 | import androidx.fragment.app.Fragment; 17 | import io.runtime.mcumgr.sample.R; 18 | 19 | public class LogsStatsFragment extends Fragment { 20 | 21 | @Nullable 22 | @Override 23 | public View onCreateView(@NonNull final LayoutInflater inflater, 24 | @Nullable final ViewGroup container, 25 | @Nullable final Bundle savedInstanceState) { 26 | return inflater.inflate(R.layout.fragment_logs_stats, container, false); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/fragment/mcumgr/ResetFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.fragment.mcumgr; 8 | 9 | import android.os.Bundle; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.Button; 14 | import android.widget.TextView; 15 | 16 | import javax.inject.Inject; 17 | 18 | import androidx.annotation.NonNull; 19 | import androidx.annotation.Nullable; 20 | import androidx.fragment.app.Fragment; 21 | import androidx.lifecycle.ViewModelProvider; 22 | import butterknife.BindView; 23 | import butterknife.ButterKnife; 24 | import io.runtime.mcumgr.sample.R; 25 | import io.runtime.mcumgr.sample.di.Injectable; 26 | import io.runtime.mcumgr.sample.viewmodel.mcumgr.McuMgrViewModelFactory; 27 | import io.runtime.mcumgr.sample.viewmodel.mcumgr.ResetViewModel; 28 | 29 | public class ResetFragment extends Fragment implements Injectable { 30 | 31 | @Inject 32 | McuMgrViewModelFactory mViewModelFactory; 33 | 34 | @BindView(R.id.action_reset) 35 | Button mResetAction; 36 | @BindView(R.id.reset_error) 37 | TextView mErrorView; 38 | 39 | private ResetViewModel mViewModel; 40 | 41 | @Override 42 | public void onCreate(@Nullable final Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | mViewModel = new ViewModelProvider(this, mViewModelFactory) 45 | .get(ResetViewModel.class); 46 | } 47 | 48 | @Nullable 49 | @Override 50 | public View onCreateView(@NonNull final LayoutInflater inflater, 51 | @Nullable final ViewGroup container, 52 | @Nullable final Bundle savedInstanceState) { 53 | return inflater.inflate(R.layout.fragment_card_reset, container, false); 54 | } 55 | 56 | @Override 57 | public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { 58 | super.onViewCreated(view, savedInstanceState); 59 | ButterKnife.bind(this, view); 60 | } 61 | 62 | @Override 63 | public void onActivityCreated(@Nullable final Bundle savedInstanceState) { 64 | super.onActivityCreated(savedInstanceState); 65 | 66 | mViewModel.getError().observe(getViewLifecycleOwner(), s -> mErrorView.setText(s)); 67 | mViewModel.getBusyState().observe(getViewLifecycleOwner(), busy -> mResetAction.setEnabled(!busy)); 68 | mResetAction.setOnClickListener(v -> mViewModel.reset()); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/observable/BondingState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.observable; 8 | 9 | public enum BondingState { 10 | NOT_BONDED, 11 | BONDING, 12 | BONDED 13 | } 14 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/observable/ConnectionState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.observable; 8 | 9 | public enum ConnectionState { 10 | CONNECTING, 11 | INITIALIZING, 12 | READY, 13 | DISCONNECTING, 14 | DISCONNECTED, 15 | TIMEOUT, 16 | NOT_SUPPORTED 17 | } 18 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/utils/StringUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.utils; 8 | 9 | public class StringUtils { 10 | private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); 11 | 12 | public static String toHex(final byte[] data) { 13 | if (data == null || data.length == 0) 14 | return ""; 15 | 16 | final char[] out = new char[data.length * 2]; 17 | for (int j = 0; j < data.length; j++) { 18 | int v = data[j] & 0xFF; 19 | out[j * 2] = HEX_ARRAY[v >>> 4]; 20 | out[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; 21 | } 22 | return new String(out); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/viewmodel/MainViewModel.java: -------------------------------------------------------------------------------- 1 | package io.runtime.mcumgr.sample.viewmodel; 2 | 3 | import android.app.Application; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.lifecycle.AndroidViewModel; 7 | 8 | import javax.inject.Inject; 9 | 10 | import io.runtime.mcumgr.McuMgrTransport; 11 | 12 | public class MainViewModel extends AndroidViewModel { 13 | @Inject 14 | McuMgrTransport mMcuMgrTransport; 15 | 16 | @Inject 17 | public MainViewModel(@NonNull final Application application) { 18 | super(application); 19 | } 20 | 21 | @Override 22 | protected void onCleared() { 23 | super.onCleared(); 24 | 25 | mMcuMgrTransport.release(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/viewmodel/ScannerStateLiveData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.viewmodel; 8 | 9 | import androidx.lifecycle.LiveData; 10 | 11 | /** 12 | * This class keeps the current state of the scanner. 13 | */ 14 | @SuppressWarnings("unused") 15 | public class ScannerStateLiveData extends LiveData { 16 | private boolean mScanningStarted; 17 | private boolean mHasRecords; 18 | private boolean mBluetoothEnabled; 19 | private boolean mLocationEnabled; 20 | 21 | /* package */ ScannerStateLiveData(final boolean bluetoothEnabled, final boolean locationEnabled) { 22 | mScanningStarted = false; 23 | mBluetoothEnabled = bluetoothEnabled; 24 | mLocationEnabled = locationEnabled; 25 | postValue(this); 26 | } 27 | 28 | /* package */ void refresh() { 29 | postValue(this); 30 | } 31 | 32 | /* package */ void scanningStarted() { 33 | mScanningStarted = true; 34 | postValue(this); 35 | } 36 | 37 | /* package */ void scanningStopped() { 38 | mScanningStarted = false; 39 | postValue(this); 40 | } 41 | 42 | /* package */ void bluetoothEnabled() { 43 | mBluetoothEnabled = true; 44 | postValue(this); 45 | } 46 | 47 | /* package */ 48 | synchronized void bluetoothDisabled() { 49 | mBluetoothEnabled = false; 50 | mHasRecords = false; 51 | postValue(this); 52 | } 53 | 54 | /* package */ void setLocationEnabled(final boolean enabled) { 55 | mLocationEnabled = enabled; 56 | postValue(this); 57 | } 58 | 59 | /* package */ void recordFound() { 60 | mHasRecords = true; 61 | postValue(this); 62 | } 63 | 64 | /** 65 | * Returns whether scanning is in progress. 66 | */ 67 | public boolean isScanning() { 68 | return mScanningStarted; 69 | } 70 | 71 | /** 72 | * Returns whether any records matching filter criteria has been found. 73 | */ 74 | public boolean hasRecords() { 75 | return mHasRecords; 76 | } 77 | 78 | /** 79 | * Returns whether Bluetooth adapter is enabled. 80 | */ 81 | public boolean isBluetoothEnabled() { 82 | return mBluetoothEnabled; 83 | } 84 | 85 | /** 86 | * Returns whether Location is enabled. 87 | */ 88 | public boolean isLocationEnabled() { 89 | return mLocationEnabled; 90 | } 91 | 92 | /** 93 | * Notifies the observer that scanner has no records to show. 94 | */ 95 | public void clearRecords() { 96 | mHasRecords = false; 97 | postValue(this); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/viewmodel/SingleLiveEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.viewmodel; 8 | 9 | import java.util.concurrent.atomic.AtomicBoolean; 10 | 11 | import androidx.annotation.MainThread; 12 | import androidx.annotation.NonNull; 13 | import androidx.annotation.Nullable; 14 | import androidx.lifecycle.LifecycleOwner; 15 | import androidx.lifecycle.MutableLiveData; 16 | import androidx.lifecycle.Observer; 17 | import timber.log.Timber; 18 | 19 | /** 20 | * A lifecycle-aware observable that sends only new updates after subscription, used for events like 21 | * navigation and Snackbar messages. 22 | *

23 | * This avoids a common problem with events: on configuration change (like rotation) an update 24 | * can be emitted if the observer is active. This LiveData only calls the observable if there's an 25 | * explicit call to setValue() or call(). 26 | *

27 | * Note that only one observer is going to be notified of changes. 28 | */ 29 | @SuppressWarnings("unused") 30 | public class SingleLiveEvent extends MutableLiveData { 31 | 32 | private final AtomicBoolean mPending = new AtomicBoolean(false); 33 | 34 | @MainThread 35 | public void observe(@NonNull final LifecycleOwner owner, @NonNull final Observer observer) { 36 | 37 | if (hasActiveObservers()) { 38 | Timber.w("Multiple observers registered but only one will be notified of changes."); 39 | } 40 | 41 | // Observe the internal MutableLiveData 42 | super.observe(owner, t -> { 43 | if (mPending.compareAndSet(true, false)) { 44 | observer.onChanged(t); 45 | } 46 | }); 47 | } 48 | 49 | @MainThread 50 | public void setValue(@Nullable final T t) { 51 | mPending.set(true); 52 | super.setValue(t); 53 | } 54 | 55 | /** 56 | * Used for cases where T is Void, to make calls cleaner. 57 | */ 58 | @MainThread 59 | public void call() { 60 | setValue(null); 61 | } 62 | 63 | /** 64 | * Used for cases where T is Void, to make calls cleaner. 65 | */ 66 | public void post() { 67 | postValue(null); 68 | } 69 | } -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/viewmodel/ViewModelFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.viewmodel; 8 | 9 | import android.app.Application; 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.concurrent.Callable; 14 | 15 | import javax.inject.Inject; 16 | 17 | import androidx.annotation.NonNull; 18 | import androidx.lifecycle.ViewModel; 19 | import androidx.lifecycle.ViewModelProvider; 20 | import io.runtime.mcumgr.sample.di.component.ViewModelSubComponent; 21 | 22 | public class ViewModelFactory extends ViewModelProvider.AndroidViewModelFactory { 23 | private final Map, Callable> creators; 24 | 25 | /** 26 | * Creates a {@code AndroidViewModelFactory}. 27 | * 28 | * @param application an application to pass in {@link androidx.lifecycle.AndroidViewModel}. 29 | */ 30 | @Inject 31 | public ViewModelFactory(@NonNull final Application application, 32 | @NonNull final ViewModelSubComponent viewModelSubComponent) { 33 | super(application); 34 | 35 | creators = new HashMap<>(); 36 | // we cannot inject view models directly because they won't be bound to the owner's 37 | // view model scope. 38 | creators.put(ScannerViewModel.class, viewModelSubComponent::scannerViewModel); 39 | } 40 | 41 | @SuppressWarnings("unchecked") 42 | @NonNull 43 | @Override 44 | public T create(@NonNull final Class modelClass) { 45 | Callable creator = creators.get(modelClass); 46 | if (creator == null) { 47 | for (Map.Entry, Callable> entry : creators.entrySet()) { 48 | if (modelClass.isAssignableFrom(entry.getKey())) { 49 | creator = entry.getValue(); 50 | break; 51 | } 52 | } 53 | } 54 | if (creator == null) { 55 | throw new IllegalArgumentException("unknown model class " + modelClass); 56 | } 57 | try { 58 | return (T) creator.call(); 59 | } catch (final Exception e) { 60 | throw new RuntimeException(e); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/viewmodel/mcumgr/DeviceStatusViewModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.viewmodel.mcumgr; 8 | 9 | import androidx.lifecycle.LiveData; 10 | import androidx.lifecycle.MutableLiveData; 11 | 12 | import javax.inject.Inject; 13 | import javax.inject.Named; 14 | 15 | import io.runtime.mcumgr.McuMgrTransport; 16 | import io.runtime.mcumgr.sample.observable.BondingState; 17 | import io.runtime.mcumgr.sample.observable.ConnectionState; 18 | import io.runtime.mcumgr.sample.observable.ObservableMcuMgrBleTransport; 19 | 20 | public class DeviceStatusViewModel extends McuMgrViewModel { 21 | private LiveData mConnectionStateLiveData; 22 | private LiveData mBondStateLiveData; 23 | 24 | @Inject 25 | DeviceStatusViewModel(final McuMgrTransport transport, 26 | @Named("busy") final MutableLiveData state) { 27 | super(state); 28 | 29 | if (transport instanceof ObservableMcuMgrBleTransport) { 30 | mConnectionStateLiveData = ((ObservableMcuMgrBleTransport) transport).getState(); 31 | mBondStateLiveData = ((ObservableMcuMgrBleTransport) transport).getBondingState(); 32 | } else { 33 | final MutableLiveData connectionStateLiveData = new MutableLiveData<>(); 34 | transport.addObserver(new McuMgrTransport.ConnectionObserver() { 35 | @Override 36 | public void onConnected() { 37 | connectionStateLiveData.postValue(ConnectionState.READY); 38 | } 39 | 40 | @Override 41 | public void onDisconnected() { 42 | connectionStateLiveData.postValue(ConnectionState.DISCONNECTED); 43 | } 44 | }); 45 | mConnectionStateLiveData = connectionStateLiveData; 46 | mBondStateLiveData = new MutableLiveData<>(BondingState.NOT_BONDED); 47 | } 48 | } 49 | 50 | public LiveData getConnectionState() { 51 | return mConnectionStateLiveData; 52 | } 53 | 54 | public LiveData getBondState() { 55 | return mBondStateLiveData; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/viewmodel/mcumgr/EchoViewModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.viewmodel.mcumgr; 8 | 9 | import android.content.Context; 10 | 11 | import javax.inject.Inject; 12 | import javax.inject.Named; 13 | 14 | import androidx.annotation.NonNull; 15 | import androidx.lifecycle.LiveData; 16 | import androidx.lifecycle.MutableLiveData; 17 | import io.runtime.mcumgr.McuMgrCallback; 18 | import io.runtime.mcumgr.exception.McuMgrException; 19 | import io.runtime.mcumgr.exception.McuMgrTimeoutException; 20 | import io.runtime.mcumgr.managers.DefaultManager; 21 | import io.runtime.mcumgr.response.dflt.McuMgrEchoResponse; 22 | import io.runtime.mcumgr.sample.R; 23 | 24 | public class EchoViewModel extends McuMgrViewModel { 25 | private final DefaultManager mManager; 26 | 27 | private final MutableLiveData mRequestLiveData = new MutableLiveData<>(); 28 | private final MutableLiveData mResponseLiveData = new MutableLiveData<>(); 29 | private final MutableLiveData mErrorLiveData = new MutableLiveData<>(); 30 | 31 | @Inject 32 | Context mContext; 33 | 34 | @Inject 35 | EchoViewModel(final DefaultManager manager, 36 | @Named("busy") final MutableLiveData state) { 37 | super(state); 38 | mManager = manager; 39 | } 40 | 41 | @NonNull 42 | public LiveData getRequest() { 43 | return mRequestLiveData; 44 | } 45 | 46 | @NonNull 47 | public LiveData getResponse() { 48 | return mResponseLiveData; 49 | } 50 | 51 | @NonNull 52 | public LiveData getError() { 53 | return mErrorLiveData; 54 | } 55 | 56 | public void echo(final String echo) { 57 | setBusy(); 58 | mRequestLiveData.postValue(echo); 59 | mManager.echo(echo, new McuMgrCallback() { 60 | @Override 61 | public void onResponse(@NonNull final McuMgrEchoResponse response) { 62 | mResponseLiveData.postValue(response.r); 63 | postReady(); 64 | } 65 | 66 | @Override 67 | public void onError(@NonNull final McuMgrException error) { 68 | if (error instanceof McuMgrTimeoutException) { 69 | mErrorLiveData.postValue(mContext.getString(R.string.status_connection_timeout)); 70 | } else { 71 | mErrorLiveData.postValue(error.getMessage()); 72 | } 73 | postReady(); 74 | } 75 | }); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/viewmodel/mcumgr/McuMgrViewModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.viewmodel.mcumgr; 8 | 9 | import javax.inject.Inject; 10 | import javax.inject.Named; 11 | 12 | import androidx.annotation.NonNull; 13 | import androidx.lifecycle.LiveData; 14 | import androidx.lifecycle.MutableLiveData; 15 | import androidx.lifecycle.ViewModel; 16 | 17 | public class McuMgrViewModel extends ViewModel { 18 | private final MutableLiveData mBusyStateLiveData; 19 | 20 | @Inject 21 | McuMgrViewModel(@Named("busy") final MutableLiveData state) { 22 | mBusyStateLiveData = state; 23 | } 24 | 25 | @NonNull 26 | public LiveData getBusyState() { 27 | return mBusyStateLiveData; 28 | } 29 | 30 | void setBusy() { 31 | mBusyStateLiveData.setValue(true); 32 | } 33 | 34 | void postBusy() { 35 | mBusyStateLiveData.postValue(true); 36 | } 37 | 38 | void setReady() { 39 | mBusyStateLiveData.setValue(false); 40 | } 41 | 42 | void postReady() { 43 | mBusyStateLiveData.postValue(false); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /sample/src/main/java/io/runtime/mcumgr/sample/viewmodel/mcumgr/ResetViewModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Nordic Semiconductor 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package io.runtime.mcumgr.sample.viewmodel.mcumgr; 8 | 9 | import javax.inject.Inject; 10 | import javax.inject.Named; 11 | 12 | import androidx.annotation.NonNull; 13 | import androidx.lifecycle.LiveData; 14 | import androidx.lifecycle.MutableLiveData; 15 | import io.runtime.mcumgr.McuMgrCallback; 16 | import io.runtime.mcumgr.McuMgrTransport; 17 | import io.runtime.mcumgr.exception.McuMgrException; 18 | import io.runtime.mcumgr.managers.DefaultManager; 19 | import io.runtime.mcumgr.response.McuMgrResponse; 20 | 21 | public class ResetViewModel extends McuMgrViewModel { 22 | private final DefaultManager mManager; 23 | 24 | private final MutableLiveData mErrorLiveData = new MutableLiveData<>(); 25 | 26 | @Inject 27 | ResetViewModel(final DefaultManager manager, 28 | @Named("busy") final MutableLiveData state) { 29 | super(state); 30 | mManager = manager; 31 | } 32 | 33 | @NonNull 34 | public LiveData getError() { 35 | return mErrorLiveData; 36 | } 37 | 38 | public void reset() { 39 | setBusy(); 40 | mManager.reset(new McuMgrCallback() { 41 | @Override 42 | public void onResponse(@NonNull final McuMgrResponse response) { 43 | mManager.getTransporter().addObserver(new McuMgrTransport.ConnectionObserver() { 44 | @Override 45 | public void onConnected() { 46 | // ignore 47 | } 48 | 49 | @Override 50 | public void onDisconnected() { 51 | mManager.getTransporter().removeObserver(this); 52 | postReady(); 53 | } 54 | }); 55 | } 56 | 57 | @Override 58 | public void onError(@NonNull final McuMgrException error) { 59 | mErrorLiveData.postValue(error.getMessage()); 60 | postReady(); 61 | } 62 | }); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /sample/src/main/res/color/button_destructive.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 18 | 19 | 25 | 28 | 31 | 32 | 33 | 34 | 40 | 41 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xxhdpi/ic_device_mynewt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/sample/src/main/res/drawable-xxhdpi/ic_device_mynewt.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xxhdpi/ic_device_other.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/sample/src/main/res/drawable-xxhdpi/ic_device_other.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xxhdpi/ic_device_zephyr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/sample/src/main/res/drawable-xxhdpi/ic_device_zephyr.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable/echo_error.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/echo_request.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/echo_response.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_bluetooth_disabled.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_bluetooth_searching.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_device_bg.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 9 | 10 | 11 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_filter.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_help.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_history.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_location_off.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_nav_fs.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_nav_general.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_nav_image.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_nav_stats.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_rssi_0_bar.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_rssi_1_bar.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_rssi_2_bars.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_rssi_3_bars.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_rssi_bar.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 11 | 14 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_settings.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/device_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 20 | 21 | 29 | 30 | 43 | 44 | 54 | 55 | 63 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/dialog_files_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 20 | 21 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/dialog_generate_file.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 23 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 39 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/fragment_card_reset.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 20 | 21 | 29 | 30 | 42 | 43 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/fragment_device.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 23 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/fragment_fs.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 23 | 24 | 32 | 33 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/fragment_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 24 | 25 | 33 | 34 | 42 | 43 | 51 | 52 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/fragment_logs_stats.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 23 | 24 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/info_no_bluetooth.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 25 | 26 | 33 | 34 | 40 | 41 | 49 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/info_no_devices.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 25 | 26 | 33 | 34 | 39 | 40 | 46 | 47 | 52 | 53 | 60 | 61 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/info_no_permission.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 25 | 26 | 33 | 34 | 40 | 41 | 48 | 49 | 58 | -------------------------------------------------------------------------------- /sample/src/main/res/menu/bottom_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 |

10 | 16 | 17 | 23 | 24 | 30 | 31 | 37 | -------------------------------------------------------------------------------- /sample/src/main/res/menu/filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 10 | 11 | 16 | 17 | 18 | 23 | 24 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /sample/src/main/res/menu/help.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /sample/src/main/res/menu/image_mode.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 10 | 11 | 15 | 16 | 20 | -------------------------------------------------------------------------------- /sample/src/main/res/menu/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 10 | 11 | 16 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/sample/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/sample/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/sample/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuulLabs-OSS/mcumgr-android/c05d5d111bb1aa1fb62ba8b4cfbac6f35418af07/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | @color/nordicBlue 12 | @color/white 13 | @color/nordicBlue 14 | @color/nordicBlue 15 | @color/black 16 | ?colorOnSurface 17 | #444 18 | #FFB00020 19 | @color/black 20 | @color/white 21 | #D00 22 | #8800 23 | 24 | -------------------------------------------------------------------------------- /sample/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /sample/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 64dp 10 | -------------------------------------------------------------------------------- /sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | @color/nordicBlue 12 | @color/white 13 | @color/white 14 | @color/nordicLake 15 | @color/nordicBlueDark 16 | ?colorOnSurface 17 | #DDD 18 | #FFB00020 19 | @color/lightGray 20 | @color/black 21 | #CC0000 22 | #CCAAAA 23 | 24 | 25 | -------------------------------------------------------------------------------- /sample/src/main/res/values/colors_common.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | #FFFFFFFF 28 | #FF000000 29 | #FFF5F5F5 30 | #FF3B3E4E 31 | 32 | -------------------------------------------------------------------------------- /sample/src/main/res/values/colors_nordic.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | #FF00A9CE 28 | #FF0090B0 29 | #FF0077C8 30 | #FFD9E1E2 31 | #FF333F48 32 | 33 | -------------------------------------------------------------------------------- /sample/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 16dp 10 | 16dp 11 | 36dp 12 | 8dp 13 | 8dp 14 | 32dp 15 | 50dp 16 | 32dp 17 | 18 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | Mcu Mgr Sample 10 | 11 | @string/app_name 12 | 13 | Help 14 | Settings 15 | Filter 16 | Only devices advertising SMP UUID 17 | Only nearby devices 18 | 19 | Unknown Device 20 | 21 | LOCATION PERMISSION REQUIRED 22 | From Android 6.0 Marshmallow onwards the application 23 | requires Location permission in order to scan for Bluetooth Low Energy devices.\n\nThis 24 | is because Bluetooth LE beacons, for example iBeacons or Eddystone, may be used to 25 | determine the phone\'s and user\'s location. Mcu Mgr Sample will not use this 26 | information in any way. 27 | Grant permission 28 | Settings 29 | 30 | BLUETOOTH DISABLED 31 | The Bluetooth adapter is turned off. Click the button 32 | below to enable it. 33 | Enable 34 | 35 | CAN\'T SEE YOUR DEVICE? 36 | 1. Make sure the device is turned on and is connected 37 | to a power source.\n\n2. Make sure the firmware with SMP server is flashed and the 38 | device is advertising.\n\n3. Check the filter settings. By default, the scanner displays only 39 | devices that advertise SMP service UUID (8D53DC1D–1DB7–4CD3–868B–8A527460AA84). 40 | 3. Location is turned off. Some Android phones 41 | require it enabled in order to scan for Bluetooth LE devices. If you are sure your 42 | device is advertising and it doesn\'t show up here, click the button below to 43 | enable Location. 44 | Enable 45 | 46 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings_device_status.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | Device Status 10 | Connection status: 11 | Bonding status: 12 | 13 | Not connected 14 | Connecting… 15 | Initializing… 16 | Connected 17 | Disconnecting… 18 | Disconnected 19 | Device not supported 20 | Connection failed 21 | Connection timed out 22 | 23 | Not bonded 24 | Bonding… 25 | Bonded 26 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings_echo.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | Echo 10 | Send 11 | Hello! 12 | Text 13 | Empty 14 | 15 | @color/colorPrimary 16 | @color/zephyr 17 | ?colorError 18 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings_file_loader.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | File Browser app not found. 10 | No file found. 11 | Reading file failed. 12 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings_files.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | Settings 10 | Partition 11 | Save 12 | Default 13 | Partition name cannot be empty. 14 | 15 | /%s/%s 16 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings_files_download.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | Download 10 | Download 11 | File name 12 | Recent files 13 | No recent files 14 | File name cannot be empty. 15 | 16 | File not found 17 | File empty 18 | %s (%d bytes)\n\n%s 19 | %s (%d bytes)\n 20 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings_files_upload.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | Upload 10 | File Name: 11 | Destination: 12 | File Size: 13 | %d bytes 14 | State: 15 | Upload 16 | 17 | READY 18 | UPLOADING… 19 | PAUSED 20 | UPLOAD COMPLETE 21 | 22 | Generate 23 | Select 24 | Pause 25 | Resume 26 | Cancel 27 | 28 | Generate File 29 | File size 30 | bytes 31 | Value cannot be empty. 32 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | Advanced 10 | Basic 11 | 12 | Select File 13 | Pause 14 | Resume 15 | Cancel 16 | 17 | Invalid image file. 18 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings_image_control.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | Images 10 | Tap READ button to download image slots information. 11 | Read 12 | Test 13 | Confirm 14 | Erase 15 | 16 | Split Status: %d 17 | Slot: %d\n 18 | • Version: %s\n 19 | • Hash: %s\n 20 | • Bootable: %B\n 21 | • Pending: %B\n 22 | • Confirmed: %B\n 23 | • Active: %B\n 24 | • Permanent: %B 25 | 26 | Help 27 | Images card lets you read the status 28 | of image slots on the device.\nWhen a new firmware has been sent to slot 1, 29 | the TEST, CONFIRM and ERASE buttons become available. 30 | The TEST button tells the device to run the new image on its next boot. 31 | Such firmware may automatically confirm itself when it\'s configured to do so. 32 | You may also make the image swap permanent by tapping CONFIRM button. 33 | Each action requires rebooting the target to take effect. You can do this using the 34 | Reset card below.\nERASE button erases the slot 1. 35 | 36 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings_image_upgrade.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | Firmware Upgrade 10 | File Name: 11 | File Size: 12 | %d bytes 13 | File Hash: 14 | State: 15 | Start 16 | 17 | Select Mode 18 | 19 | Test and confirm 20 | Test only 21 | Confirm only 22 | 23 | 24 | READY 25 | VALIDATING… 26 | UPLOADING… 27 | PAUSED 28 | TESTING… 29 | CONFIRMING… 30 | RESETTING… 31 | UPLOAD COMPLETE 32 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings_image_upload.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | Firmware Upload 10 | File Name: 11 | File Size: 12 | %d bytes 13 | File Hash: 14 | State: 15 | Upload 16 | 17 | READY 18 | VALIDATING… 19 | UPLOADING… 20 | PAUSED 21 | UPLOAD COMPLETE 22 | 23 | Help 24 | Firmware Update card lets you send 25 | a new firmware image onto the device to slot 1. After sending the image, 26 | use Images card to test or confirm it, and the Reset card to reboot 27 | the device. 28 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | Device 10 | Image 11 | Files 12 | Logs & Stats 13 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings_reset.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | Reset 10 | Send Reset Command 11 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings_stats.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | Stats 10 | Request 11 | Tap REFRESH button to download stats. 12 | 13 | %s: 14 | • %s: %d 15 | -------------------------------------------------------------------------------- /sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 16 | 17 | 24 | 25 | 26 | 29 | 30 | 42 | 43 | 44 |