├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── app ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── pxs │ │ └── terminal │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── cpp │ │ ├── CMakeLists.txt │ │ ├── esp-flasher │ │ │ ├── common │ │ │ │ ├── example_common.c │ │ │ │ └── example_common.h │ │ │ ├── include │ │ │ │ ├── esp_loader.h │ │ │ │ └── serial_io.h │ │ │ ├── port │ │ │ │ ├── linux_port.c │ │ │ │ └── linux_port.h │ │ │ ├── private_include │ │ │ │ ├── esp_targets.h │ │ │ │ ├── md5_hash.h │ │ │ │ ├── serial_comm.h │ │ │ │ └── serial_comm_prv.h │ │ │ └── src │ │ │ │ ├── esp_loader.c │ │ │ │ ├── esp_targets.c │ │ │ │ ├── md5_hash.c │ │ │ │ └── serial_comm.c │ │ ├── native-lib.cpp │ │ └── termux.c │ ├── java │ │ └── com │ │ │ └── pxs │ │ │ └── terminal │ │ │ ├── JNI │ │ │ └── ESP.java │ │ │ ├── LinkActivity.java │ │ │ ├── LogActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── MyApplication.java │ │ │ ├── MyUncaughtExceptionHandler.java │ │ │ ├── SerialService.java │ │ │ ├── SettingsActivity.java │ │ │ ├── ui │ │ │ ├── gallery │ │ │ │ ├── GalleryFragment.java │ │ │ │ └── GalleryViewModel.java │ │ │ ├── home │ │ │ │ ├── HomeFragment.java │ │ │ │ └── HomeViewModel.java │ │ │ └── slideshow │ │ │ │ ├── SlideshowFragment.java │ │ │ │ └── SlideshowViewModel.java │ │ │ └── util │ │ │ ├── AbstractShell.java │ │ │ ├── CustomProber.java │ │ │ ├── MyUtil.java │ │ │ ├── ProcessShell.java │ │ │ ├── ProcessUtils.java │ │ │ └── TextUtils.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ ├── ic_menu_camera.xml │ │ ├── ic_menu_gallery.xml │ │ ├── ic_menu_slideshow.xml │ │ └── side_nav_bar.xml │ │ ├── layout │ │ ├── activity_link.xml │ │ ├── activity_log.xml │ │ ├── activity_main.xml │ │ ├── app_bar_link.xml │ │ ├── content_link.xml │ │ ├── device_list_item.xml │ │ ├── fragment_gallery.xml │ │ ├── fragment_home.xml │ │ ├── fragment_slideshow.xml │ │ ├── item_bin.xml │ │ ├── nav_header_link.xml │ │ └── settings_activity.xml │ │ ├── menu │ │ ├── activity_main_drawer.xml │ │ └── link.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── esp.png │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── navigation │ │ └── mobile_navigation.xml │ │ ├── values-land │ │ └── dimens.xml │ │ ├── values-night │ │ └── themes.xml │ │ ├── values-w1240dp │ │ └── dimens.xml │ │ ├── values-w600dp │ │ └── dimens.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ └── root_preferences.xml │ └── test │ └── java │ └── com │ └── pxs │ └── terminal │ └── ExampleUnitTest.java ├── build.gradle ├── docs └── images │ └── screenshot.jpg ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | docs/images/screenshot.jpg filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle files 2 | .gradle/ 3 | build/ 4 | 5 | # Local configuration file (sdk path, etc) 6 | local.properties 7 | 8 | # Log/OS Files 9 | *.log 10 | 11 | # Android Studio generated files and folders 12 | captures/ 13 | .externalNativeBuild/ 14 | .cxx/ 15 | *.apk 16 | output.json 17 | 18 | # IntelliJ 19 | *.iml 20 | .idea/ 21 | misc.xml 22 | deploymentTargetDropDown.xml 23 | render.experimental.xml 24 | 25 | # Keystore files 26 | *.jks 27 | *.keystore 28 | 29 | # Google Services (e.g. APIs or Firebase) 30 | google-services.json 31 | 32 | # Android Profiling 33 | *.hprof 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 ailearncoder 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Move To [esptool-android](https://github.com/ailearncoder/esptool-android) 2 | 3 | ## esp-serial-flasher 4 | 5 | This is an Android application that uses the [esp-serial-flasher](https://github.com/espressif/esp-serial-flasher) C library for flashing applications to the RAM of Espressif SoCs. 6 | 7 | **Now it is recommended to use project [esptool-android](https://github.com/ailearncoder/esptool-android) with this application.** 8 | 9 | ## Installation 10 | Install the APK [esp-serial-flasher.apk](https://vip.123pan.cn/1812665715/files/esp-serial-flasher.apk). 11 | 12 | ## Screenshots 13 | ![Screenshot](https://media.githubusercontent.com/media/ailearncoder/esp-serial-flasher/refs/heads/main/docs/images/screenshot.jpg "Screenshot") 14 | 15 | ## License 16 | 17 | [The MIT License (MIT)](https://github.com/ailearncoder/esp-serial-flasher/raw/refs/heads/main/LICENSE) 18 | 19 | ## Credits 20 | 21 | This repo relies on the following third-party projects: 22 | - [esp-serial-flasher](https://github.com/espressif/esp-serial-flasher) 23 | - [usb-serial-for-android](https://github.com/mik3y/usb-serial-for-android) 24 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | compileSdk 28 7 | 8 | defaultConfig { 9 | applicationId "com.pxs.terminal" 10 | minSdk 21 11 | targetSdk 28 12 | versionCode 1 13 | versionName "1.1" 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | externalNativeBuild { 17 | cmake { 18 | cppFlags '-std=c++11' 19 | } 20 | } 21 | targetSdkVersion 30 22 | minSdkVersion 21 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled true 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | externalNativeBuild { 36 | cmake { 37 | path file('src/main/cpp/CMakeLists.txt') 38 | version '3.10.2' 39 | } 40 | } 41 | buildFeatures { 42 | viewBinding true 43 | } 44 | compileSdkVersion 30 45 | buildToolsVersion '31.0.0' 46 | 47 | lintOptions { 48 | disable 'ProtectedPermissions' 49 | } 50 | } 51 | 52 | dependencies { 53 | 54 | implementation 'com.github.mik3y:usb-serial-for-android:3.7.0' 55 | implementation 'androidx.appcompat:appcompat:1.3.1' 56 | implementation 'com.google.android.material:material:1.4.0' 57 | implementation 'androidx.constraintlayout:constraintlayout:2.1.0' 58 | implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1' 59 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' 60 | implementation 'androidx.navigation:navigation-fragment:2.3.5' 61 | implementation 'androidx.navigation:navigation-ui:2.3.5' 62 | implementation 'androidx.preference:preference:1.1.1' 63 | testImplementation 'junit:junit:4.13.2' 64 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 65 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 66 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | # 代码混淆压缩比,在0~7之间,默认为5,一般不做修改 24 | -optimizationpasses 5 25 | 26 | # 混合时不使用大小写混合,混合后的类名为小写 27 | -dontusemixedcaseclassnames 28 | 29 | # 指定不去忽略非公共库的类 30 | -dontskipnonpubliclibraryclasses 31 | 32 | # 指定不去忽略非公共库的类成员 33 | -dontskipnonpubliclibraryclassmembers 34 | 35 | # 这句话能够使我们的项目混淆后产生映射文件 36 | # 包含有类名->混淆后类名的映射关系 37 | -verbose 38 | 39 | # 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。 40 | -dontpreverify 41 | 42 | # 保留Annotation不混淆 这在JSON实体映射时非常重要,比如fastJson 43 | -keepattributes *Annotation*,InnerClasses 44 | 45 | # 避免混淆泛型 46 | -keepattributes Signature 47 | 48 | # 抛出异常时保留代码行号 49 | -keepattributes SourceFile,LineNumberTable 50 | 51 | # 指定混淆是采用的算法,后面的参数是一个过滤器 52 | # 这个过滤器是谷歌推荐的算法,一般不做更改 53 | -optimizations !code/simplification/cast,!field/*,!class/merging/* 54 | 55 | # 忽略警告 56 | -ignorewarnings 57 | 58 | # 设置是否允许改变作用域 59 | -allowaccessmodification 60 | 61 | # 把混淆类中的方法名也混淆了 62 | -useuniqueclassmembernames 63 | 64 | # apk 包内所有 class 的内部结构 65 | -dump class_files.txt 66 | 67 | # 未混淆的类和成员 68 | -printseeds seeds_txt 69 | 70 | # 列出从apk中删除的代码 71 | -printusage unused.txt 72 | 73 | # 混淆前后的映射 74 | -printmapping mapping.txt 75 | 76 | -keep public class * extends android.app.Activity 77 | -keep public class * extends android.app.Application 78 | -keep public class * extends android.app.Service 79 | -keep public class * extends android.content.BroadcastReceiver 80 | -keep public class * extends android.content.ContentProvider 81 | -keep public class * extends android.app.backup.BackupAgent 82 | -keep public class * extends android.preference.Preference 83 | -keep public class * extends android.support.v4.app.Fragment 84 | -keep public class * extends android.app.Fragment 85 | -keep public class * extends android.view.view 86 | -keep public class com.android.vending.licensing.ILicensingService 87 | 88 | -keepattributes *Annotation* 89 | 90 | -keepclassmembers enum * { 91 | public static **[] values(); 92 | public static ** valueOf(java.lang.String); 93 | } 94 | 95 | -keepclasseswithmembernames class * { 96 | native ; 97 | } 98 | 99 | -keepattributes *JavascriptInterface* 100 | 101 | -keepclassmembers class fqcn.of.javascript.interface.for.webview { 102 | public *; 103 | } 104 | 105 | -keepclassmembers class * extends android.webkit.WebViewClient { 106 | public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap); 107 | public boolean *(android.webkit.WebView, java.lang.String); 108 | } 109 | 110 | -keepclassmembers class * extends android.webkit.WebViewClient { 111 | public void *(android.webkit.WebView, java.lang.String); 112 | } 113 | 114 | -keep class * implements android.os.Parcelable { 115 | public static final android.os.Parcelable$Creator *; 116 | } 117 | 118 | -keep class * implements java.io.Serializable { 119 | public *; 120 | } 121 | 122 | -keepclassmembers class * implements java.io.Serializable { 123 | static final long serialVersionUID; 124 | private static final java.io.ObjectStreamField[] serialPersistentFields; 125 | !static !transient ; 126 | private void writeObject(java.io.ObjectOutputStream); 127 | private void readObject(java.io.ObjectInputStream); 128 | java.lang.Object writeReplace(); 129 | java.lang.Object readResolve(); 130 | } 131 | 132 | -keep class com.google.gson.** {*;} 133 | -keep class sun.misc.Unsafe {*;} 134 | -keep class com.google.gson.stream.** {*;} 135 | -keep class com.google.gson.examples.android.model.** {*;} 136 | -keep class com.google.** { 137 | ; 138 | ; 139 | } 140 | # -dontwarn class com.google.gson.** 141 | 142 | -keep class com.hoho.android.usbserial.** {*;} 143 | -keep class com.hoho.** { 144 | ; 145 | ; 146 | } 147 | 148 | -keep class com.pxs.terminal.JNI.** {*;} 149 | -keep class com.pxs.terminal.JNI.** { 150 | ; 151 | ; 152 | } 153 | 154 | -keep class com.yanzhenjie.andserver.** {*;} 155 | -keep class com.yanzhenjie.andserver.** { 156 | ; 157 | ; 158 | } 159 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/pxs/terminal/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.pxs.terminal", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 30 | 33 | 34 | 38 | 39 | 43 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | # Sets the minimum version of CMake required to build the native library. 5 | 6 | cmake_minimum_required(VERSION 3.10.2) 7 | 8 | # Declares and names the project. 9 | 10 | project("terminal") 11 | 12 | # Creates and names a library, sets it as either STATIC 13 | # or SHARED, and provides the relative paths to its source code. 14 | # You can define multiple libraries, and CMake builds them for you. 15 | # Gradle automatically packages shared libraries with your APK. 16 | 17 | add_compile_options(-DMD5_ENABLED) 18 | 19 | include_directories(esp-flasher/common esp-flasher/include esp-flasher/port esp-flasher/private_include) 20 | 21 | aux_source_directory(esp-flasher/common SRC_LIST) 22 | aux_source_directory(esp-flasher/port SRC_LIST) 23 | aux_source_directory(esp-flasher/src SRC_LIST) 24 | 25 | add_library( # Sets the name of the library. 26 | terminal 27 | 28 | # Sets the library as a shared library. 29 | SHARED 30 | 31 | # Provides a relative path to your source file(s). 32 | native-lib.cpp 33 | 34 | termux.c 35 | 36 | ${SRC_LIST}) 37 | 38 | # Searches for a specified prebuilt library and stores the path as a 39 | # variable. Because CMake includes system libraries in the search path by 40 | # default, you only need to specify the name of the public NDK library 41 | # you want to add. CMake verifies that the library exists before 42 | # completing its build. 43 | 44 | find_library( # Sets the name of the path variable. 45 | log-lib 46 | 47 | # Specifies the name of the NDK library that 48 | # you want CMake to locate. 49 | log) 50 | 51 | # Specifies libraries CMake should link to your target library. You 52 | # can link multiple libraries, such as libraries you define in this 53 | # build script, prebuilt third-party libraries, or system libraries. 54 | 55 | target_link_libraries( # Specifies the target library. 56 | terminal 57 | 58 | # Links the target library to the log library 59 | # included in the NDK. 60 | ${log-lib}) -------------------------------------------------------------------------------- /app/src/main/cpp/esp-flasher/common/example_common.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2020 Espressif Systems (Shanghai) PTE LTD 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include "serial_io.h" 20 | #include "esp_loader.h" 21 | #include "example_common.h" 22 | #include 23 | #include "linux_port.h" 24 | 25 | #include 26 | 27 | #define LOG_TAG "common" 28 | 29 | #define LOGI(...) do { __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__); } while(0) 30 | #define LOGW(...) do { __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__); } while(0) 31 | #define LOGE(...) do { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__); } while(0) 32 | 33 | #define SINGLE_TARGET_SUPPORT 34 | #ifndef SINGLE_TARGET_SUPPORT 35 | 36 | #define BOOTLOADER_ADDRESS_8266 0x1000 37 | #define BOOTLOADER_ADDRESS 0x1000 38 | #define PARTITION_ADDRESS 0x8000 39 | #define APPLICATION_ADDRESS 0x10000 40 | 41 | extern const uint8_t ESP32_bootloader_bin[]; 42 | extern const uint32_t ESP32_bootloader_bin_size; 43 | extern const uint8_t ESP32_hello_world_bin[]; 44 | extern const uint32_t ESP32_hello_world_bin_size; 45 | extern const uint8_t ESP32_partition_table_bin[]; 46 | extern const uint32_t ESP32_partition_table_bin_size; 47 | 48 | extern const uint8_t ESP32_S2_bootloader_bin[]; 49 | extern const uint32_t ESP32_S2_bootloader_bin_size; 50 | extern const uint8_t ESP32_S2_hello_world_bin[]; 51 | extern const uint32_t ESP32_S2_hello_world_bin_size; 52 | extern const uint8_t ESP32_S2_partition_table_bin[]; 53 | extern const uint32_t ESP32_S2_partition_table_bin_size; 54 | 55 | extern const uint8_t ESP8266_bootloader_bin[]; 56 | extern const uint32_t ESP8266_bootloader_bin_size; 57 | extern const uint8_t ESP8266_hello_world_bin[]; 58 | extern const uint32_t ESP8266_hello_world_bin_size; 59 | extern const uint8_t ESP8266_partition_table_bin[]; 60 | extern const uint32_t ESP8266_partition_table_bin_size; 61 | 62 | void get_example_binaries(target_chip_t target, example_binaries_t *bins) 63 | { 64 | if (target == ESP8266_CHIP) { 65 | bins->boot.data = ESP8266_bootloader_bin; 66 | bins->boot.size = ESP8266_bootloader_bin_size; 67 | bins->boot.addr = BOOTLOADER_ADDRESS_8266; 68 | bins->part.data = ESP8266_partition_table_bin; 69 | bins->part.size = ESP8266_partition_table_bin_size; 70 | bins->part.addr = PARTITION_ADDRESS; 71 | bins->app.data = ESP8266_hello_world_bin; 72 | bins->app.size = ESP8266_hello_world_bin_size; 73 | bins->app.addr = APPLICATION_ADDRESS; 74 | } else if (target == ESP32_CHIP) { 75 | bins->boot.data = ESP32_bootloader_bin; 76 | bins->boot.size = ESP32_bootloader_bin_size; 77 | bins->boot.addr = BOOTLOADER_ADDRESS; 78 | bins->part.data = ESP32_partition_table_bin; 79 | bins->part.size = ESP32_partition_table_bin_size; 80 | bins->part.addr = PARTITION_ADDRESS; 81 | bins->app.data = ESP32_hello_world_bin; 82 | bins->app.size = ESP32_hello_world_bin_size; 83 | bins->app.addr = APPLICATION_ADDRESS; 84 | } else { 85 | bins->boot.data = ESP32_S2_bootloader_bin; 86 | bins->boot.size = ESP32_S2_bootloader_bin_size; 87 | bins->boot.addr = BOOTLOADER_ADDRESS; 88 | bins->part.data = ESP32_S2_partition_table_bin; 89 | bins->part.size = ESP32_S2_partition_table_bin_size; 90 | bins->part.addr = PARTITION_ADDRESS; 91 | bins->app.data = ESP32_S2_hello_world_bin; 92 | bins->app.size = ESP32_S2_hello_world_bin_size; 93 | bins->app.addr = APPLICATION_ADDRESS; 94 | } 95 | } 96 | 97 | #endif 98 | 99 | esp_loader_error_t connect_to_target(uint32_t higher_baudrate) 100 | { 101 | esp_loader_connect_args_t connect_config = ESP_LOADER_CONNECT_DEFAULT(); 102 | 103 | esp_loader_error_t err = esp_loader_connect(&connect_config); 104 | if (err != ESP_LOADER_SUCCESS) { 105 | LOGE("Cannot connect to target. Error: %u\n", err); 106 | return err; 107 | } 108 | LOGI("Connected to target\n"); 109 | 110 | if (higher_baudrate && esp_loader_get_target() != ESP8266_CHIP) { 111 | err = esp_loader_change_baudrate(higher_baudrate); 112 | if (err == ESP_LOADER_ERROR_UNSUPPORTED_FUNC) { 113 | LOGE("ESP8266 does not support change baudrate command."); 114 | return err; 115 | } else if (err != ESP_LOADER_SUCCESS) { 116 | LOGE("Unable to change baud rate on target."); 117 | return err; 118 | } else { 119 | err = loader_port_change_baudrate(higher_baudrate); 120 | if (err != ESP_LOADER_SUCCESS) { 121 | LOGE("Unable to change baud rate."); 122 | return err; 123 | } 124 | LOGI("Baudrate changed\n"); 125 | } 126 | } 127 | 128 | return ESP_LOADER_SUCCESS; 129 | } 130 | 131 | esp_loader_error_t flash_binary(const uint8_t *bin, size_t size, size_t address) 132 | { 133 | esp_loader_error_t err; 134 | static uint8_t payload[1024]; 135 | const uint8_t *bin_addr = bin; 136 | 137 | LOGI("Erasing flash (this may take a while)...\n"); 138 | err = esp_loader_flash_start(address, size, sizeof(payload)); 139 | if (err != ESP_LOADER_SUCCESS) { 140 | LOGE("Erasing flash failed with error %d.\n", err); 141 | return err; 142 | } 143 | LOGI("Start programming\n"); 144 | 145 | size_t binary_size = size; 146 | size_t written = 0; 147 | int cur_progress = -1; 148 | 149 | while (size > 0) { 150 | size_t to_read = MIN(size, sizeof(payload)); 151 | memcpy(payload, bin_addr, to_read); 152 | 153 | err = esp_loader_flash_write(payload, to_read); 154 | if (err != ESP_LOADER_SUCCESS) { 155 | LOGE("\nPacket could not be written! Error %d.\n", err); 156 | return err; 157 | } 158 | 159 | size -= to_read; 160 | bin_addr += to_read; 161 | written += to_read; 162 | 163 | int progress = (int)(((float)written / binary_size) * 100); 164 | if(cur_progress != progress) { 165 | loader_port_callback_on_flash_process(progress); 166 | LOGI("\rProgress: %d %%", progress); 167 | fflush(stdout); 168 | cur_progress = progress; 169 | } 170 | }; 171 | 172 | LOGI("\nFinished programming\n"); 173 | 174 | #if MD5_ENABLED 175 | err = esp_loader_flash_verify(); 176 | if (err == ESP_LOADER_ERROR_UNSUPPORTED_FUNC) { 177 | printf("ESP8266 does not support flash verify command."); 178 | return ESP_LOADER_SUCCESS; 179 | } else if (err != ESP_LOADER_SUCCESS) { 180 | printf("MD5 does not match. err: %d\n", err); 181 | return err; 182 | } 183 | printf("Flash verified\n"); 184 | #endif 185 | 186 | return ESP_LOADER_SUCCESS; 187 | } -------------------------------------------------------------------------------- /app/src/main/cpp/esp-flasher/common/example_common.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020 Espressif Systems (Shanghai) PTE LTD 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #pragma once 17 | 18 | typedef struct { 19 | const uint8_t *data; 20 | uint32_t size; 21 | uint32_t addr; 22 | } partition_attr_t; 23 | 24 | typedef struct { 25 | partition_attr_t boot; 26 | partition_attr_t part; 27 | partition_attr_t app; 28 | } example_binaries_t; 29 | 30 | void get_example_binaries(target_chip_t target, example_binaries_t *binaries); 31 | esp_loader_error_t connect_to_target(uint32_t higrer_baudrate); 32 | esp_loader_error_t flash_binary(const uint8_t *bin, size_t size, size_t address); -------------------------------------------------------------------------------- /app/src/main/cpp/esp-flasher/include/esp_loader.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020 Espressif Systems (Shanghai) PTE LTD 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | /** 26 | * Macro which can be used to check the error code, 27 | * and return in case the code is not ESP_LOADER_SUCCESS. 28 | */ 29 | #define RETURN_ON_ERROR(x) do { \ 30 | esp_loader_error_t _err_ = (x); \ 31 | if (_err_ != ESP_LOADER_SUCCESS) { \ 32 | return _err_; \ 33 | } \ 34 | } while(0) 35 | 36 | /** 37 | * @brief Error codes 38 | */ 39 | typedef enum { 40 | ESP_LOADER_SUCCESS, /*!< Success */ 41 | ESP_LOADER_ERROR_FAIL, /*!< Unspecified error */ 42 | ESP_LOADER_ERROR_TIMEOUT, /*!< Timeout elapsed */ 43 | ESP_LOADER_ERROR_IMAGE_SIZE, /*!< Image size to flash is larger than flash size */ 44 | ESP_LOADER_ERROR_INVALID_MD5, /*!< Computed and received MD5 does not match */ 45 | ESP_LOADER_ERROR_INVALID_PARAM, /*!< Invalid parameter passed to function */ 46 | ESP_LOADER_ERROR_INVALID_TARGET, /*!< Connected target is invalid */ 47 | ESP_LOADER_ERROR_UNSUPPORTED_CHIP, /*!< Attached chip is not supported */ 48 | ESP_LOADER_ERROR_UNSUPPORTED_FUNC, /*!< Function is not supported on attached target */ 49 | ESP_LOADER_ERROR_INVALID_RESPONSE /*!< Internal error */ 50 | } esp_loader_error_t; 51 | 52 | /** 53 | * @brief Supported targets 54 | */ 55 | typedef enum { 56 | ESP8266_CHIP = 0, 57 | ESP32_CHIP = 1, 58 | ESP32S2_CHIP = 2, 59 | ESP32C3_CHIP = 3, 60 | ESP32S3_CHIP = 4, 61 | ESP_MAX_CHIP = 5, 62 | ESP_UNKNOWN_CHIP = 5 63 | } target_chip_t; 64 | 65 | /** 66 | * @brief SPI pin configuration arguments 67 | */ 68 | typedef union { 69 | struct { 70 | uint32_t pin_clk: 6; 71 | uint32_t pin_q: 6; 72 | uint32_t pin_d: 6; 73 | uint32_t pin_cs: 6; 74 | uint32_t pin_hd: 6; 75 | uint32_t zero: 2; 76 | }; 77 | uint32_t val; 78 | } esp_loader_spi_config_t; 79 | 80 | /** 81 | * @brief Connection arguments 82 | */ 83 | typedef struct { 84 | uint32_t sync_timeout; /*!< Maximum time to wait for response from serial interface. */ 85 | int32_t trials; /*!< Number of trials to connect to target. If greater than 1, 86 | 100 millisecond delay is inserted after each try. */ 87 | } esp_loader_connect_args_t; 88 | 89 | #define ESP_LOADER_CONNECT_DEFAULT() { \ 90 | .sync_timeout = 100, \ 91 | .trials = 30, \ 92 | } 93 | 94 | /** 95 | * @brief Connects to the target 96 | * 97 | * @param connect_args[in] Timing parameters to be used for connecting to target. 98 | * 99 | * @return 100 | * - ESP_LOADER_SUCCESS Success 101 | * - ESP_LOADER_ERROR_TIMEOUT Timeout 102 | * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error 103 | */ 104 | esp_loader_error_t esp_loader_connect(esp_loader_connect_args_t *connect_args); 105 | 106 | /** 107 | * @brief Returns attached target chip. 108 | * 109 | * @warning This function can only be called after connection with target 110 | * has been successfully established by calling esp_loader_connect(). 111 | * 112 | * @return One of target_chip_t 113 | */ 114 | target_chip_t esp_loader_get_target(void); 115 | 116 | /** 117 | * @brief Initiates flash operation 118 | * 119 | * @param offset[in] Address from which flash operation will be performed. 120 | * @param image_size[in] Size of the whole binary to be loaded into flash. 121 | * @param block_size[in] Size of buffer used in subsequent calls to esp_loader_flash_write. 122 | * 123 | * @note image_size is size of the whole image, whereas, block_size is chunk of data sent 124 | * to the target, each time esp_loader_flash_write function is called. 125 | * 126 | * @return 127 | * - ESP_LOADER_SUCCESS Success 128 | * - ESP_LOADER_ERROR_TIMEOUT Timeout 129 | * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error 130 | */ 131 | esp_loader_error_t esp_loader_flash_start(uint32_t offset, uint32_t image_size, uint32_t block_size); 132 | 133 | /** 134 | * @brief Writes supplied data to target's flash memory. 135 | * 136 | * @param payload[in] Data to be flashed into target's memory. 137 | * @param size[in] Size of payload in bytes. 138 | * 139 | * @note size must not be greater that block_size supplied to previously called 140 | * esp_loader_flash_start function. If size is less than block_size, 141 | * remaining bytes of payload buffer will be padded with 0xff. 142 | * Therefore, size of payload buffer has to be equal or greater than block_size. 143 | * 144 | * @return 145 | * - ESP_LOADER_SUCCESS Success 146 | * - ESP_LOADER_ERROR_TIMEOUT Timeout 147 | * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error 148 | */ 149 | esp_loader_error_t esp_loader_flash_write(void *payload, uint32_t size); 150 | 151 | /** 152 | * @brief Ends flash operation. 153 | * 154 | * @param reboot[in] reboot the target if true. 155 | * 156 | * @return 157 | * - ESP_LOADER_SUCCESS Success 158 | * - ESP_LOADER_ERROR_TIMEOUT Timeout 159 | * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error 160 | */ 161 | esp_loader_error_t esp_loader_flash_finish(bool reboot); 162 | 163 | /** 164 | * @brief Writes register. 165 | * 166 | * @param address[in] Address of register. 167 | * @param reg_value[in] New register value. 168 | * 169 | * @return 170 | * - ESP_LOADER_SUCCESS Success 171 | * - ESP_LOADER_ERROR_TIMEOUT Timeout 172 | * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error 173 | */ 174 | esp_loader_error_t esp_loader_write_register(uint32_t address, uint32_t reg_value); 175 | 176 | /** 177 | * @brief Reads register. 178 | * 179 | * @param address[in] Address of register. 180 | * @param reg_value[out] Register value. 181 | * 182 | * @return 183 | * - ESP_LOADER_SUCCESS Success 184 | * - ESP_LOADER_ERROR_TIMEOUT Timeout 185 | * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error 186 | */ 187 | esp_loader_error_t esp_loader_read_register(uint32_t address, uint32_t *reg_value); 188 | 189 | /** 190 | * @brief Change baud rate. 191 | * 192 | * @note Baud rate has to be also adjusted accordingly on host MCU, as 193 | * target's baud rate is changed upon return from this function. 194 | * 195 | * @param baudrate[in] new baud rate to be set. 196 | * 197 | * @return 198 | * - ESP_LOADER_SUCCESS Success 199 | * - ESP_LOADER_ERROR_TIMEOUT Timeout 200 | * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error 201 | * - ESP_LOADER_ERROR_UNSUPPORTED_FUNC Unsupported on the target 202 | */ 203 | esp_loader_error_t esp_loader_change_baudrate(uint32_t baudrate); 204 | 205 | /** 206 | * @brief Verify target's flash integrity by checking MD5. 207 | * MD5 checksum is computed from data pushed to target's memory by calling 208 | * esp_loader_flash_write() function and compared against target's MD5. 209 | * Target computes checksum based on offset and image_size passed to 210 | * esp_loader_flash_start() function. 211 | * 212 | * @note This function is only available if MD5_ENABLED is set. 213 | * 214 | * @return 215 | * - ESP_LOADER_SUCCESS Success 216 | * - ESP_LOADER_ERROR_INVALID_MD5 MD5 does not match 217 | * - ESP_LOADER_ERROR_TIMEOUT Timeout 218 | * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error 219 | * - ESP_LOADER_ERROR_UNSUPPORTED_FUNC Unsupported on the target 220 | */ 221 | #if MD5_ENABLED 222 | esp_loader_error_t esp_loader_flash_verify(void); 223 | #endif 224 | /** 225 | * @brief Toggles reset pin. 226 | */ 227 | void esp_loader_reset_target(void); 228 | 229 | esp_loader_error_t esp_loader_erase_flash(void); 230 | 231 | 232 | #ifdef __cplusplus 233 | } 234 | #endif -------------------------------------------------------------------------------- /app/src/main/cpp/esp-flasher/include/serial_io.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020 Espressif Systems (Shanghai) PTE LTD 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #pragma once 17 | 18 | #include 19 | #include "esp_loader.h" 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | /** 26 | * @brief Changes baud rate of serial peripheral. 27 | */ 28 | esp_loader_error_t loader_port_change_baudrate(uint32_t baudrate); 29 | 30 | /** 31 | * @brief Writes data to serial interface. 32 | * 33 | * @param data[in] Buffer with data to be written. 34 | * @param size[in] Size of data in bytes. 35 | * @param timeout[in] Timeout in milliseconds. 36 | * 37 | * @return 38 | * - ESP_LOADER_SUCCESS Success 39 | * - ESP_LOADER_ERROR_TIMEOUT Timeout elapsed 40 | */ 41 | esp_loader_error_t loader_port_serial_write(const uint8_t *data, uint16_t size, uint32_t timeout); 42 | 43 | /** 44 | * @brief Reads data from serial interface. 45 | * 46 | * @param data[out] Buffer into which received data will be written. 47 | * @param size[in] Number of bytes to read. 48 | * @param timeout[in] Timeout in milliseconds. 49 | * 50 | * @return 51 | * - ESP_LOADER_SUCCESS Success 52 | * - ESP_LOADER_ERROR_TIMEOUT Timeout elapsed 53 | */ 54 | esp_loader_error_t loader_port_serial_read(uint8_t *data, uint16_t size, uint32_t timeout); 55 | 56 | /** 57 | * @brief Delay in milliseconds. 58 | * 59 | * @param ms[in] Number of milliseconds. 60 | * 61 | */ 62 | void loader_port_delay_ms(uint32_t ms); 63 | 64 | /** 65 | * @brief Starts timeout timer. 66 | * 67 | * @param ms[in] Number of milliseconds. 68 | * 69 | */ 70 | void loader_port_start_timer(uint32_t ms); 71 | 72 | /** 73 | * @brief Returns remaining time since timer was started by calling esp_loader_start_timer. 74 | * 0 if timer has elapsed. 75 | * 76 | * @return Number of milliseconds. 77 | * 78 | */ 79 | uint32_t loader_port_remaining_time(void); 80 | 81 | /** 82 | * @brief Asserts bootstrap pins to enter boot mode and toggles reset pin. 83 | * 84 | * @note Reset pin should stay asserted for at least 20 milliseconds. 85 | */ 86 | void loader_port_enter_bootloader(void); 87 | 88 | /** 89 | * @brief Toggles reset pin. 90 | * 91 | * @note Reset pin should stay asserted for at least 20 milliseconds. 92 | */ 93 | void loader_port_reset_target(void); 94 | 95 | /** 96 | * @brief Function can be defined by user to print debug message. 97 | * 98 | * @note Empty weak function is used, otherwise. 99 | * 100 | */ 101 | void loader_port_debug_print(const char *str); 102 | 103 | #ifdef __cplusplus 104 | } 105 | #endif 106 | -------------------------------------------------------------------------------- /app/src/main/cpp/esp-flasher/port/linux_port.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2020 Espressif Systems (Shanghai) PTE LTD 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #include "serial_io.h" 17 | #include "serial_comm.h" 18 | #include "linux_port.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | 38 | #define LOG_TAG "linux-port" 39 | 40 | #define LOGD(...) do { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__); } while(0) 41 | #define LOGI(...) do { __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__); } while(0) 42 | #define LOGW(...) do { __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__); } while(0) 43 | #define LOGE(...) do { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__); } while(0) 44 | 45 | // #define SERIAL_DEBUG_ENABLE 46 | 47 | #ifdef SERIAL_DEBUG_ENABLE 48 | 49 | static void serial_debug_print(const uint8_t *data, uint16_t size, bool write) 50 | { 51 | static bool write_prev = false; 52 | uint8_t hex_str[3]; 53 | 54 | // if (size > 8) size = 8; // only print length 8 55 | 56 | if (write_prev != write) { 57 | write_prev = write; 58 | printf("\n--- %s ---\n", write ? "WRITE" : "READ"); 59 | } 60 | 61 | for (uint32_t i = 0; i < size; i++) { 62 | printf("%02x ", data[i]); 63 | } 64 | } 65 | 66 | #else 67 | 68 | static void serial_debug_print(const uint8_t *data, uint16_t size, bool write) {} 69 | 70 | #endif 71 | 72 | static int serial; 73 | static int64_t s_time_end; 74 | static int32_t s_reset_trigger_pin; 75 | static int32_t s_gpio0_trigger_pin; 76 | 77 | 78 | /* 79 | * This is called by the VM when the shared library is first loaded. 80 | */ 81 | 82 | typedef union { 83 | JNIEnv *env; 84 | void *venv; 85 | } UnionJNIEnvToVoid; 86 | 87 | static UnionJNIEnvToVoid uenv; 88 | static JavaVM *g_vm; 89 | 90 | jint JNI_OnLoad(JavaVM *vm, void *reserved) { 91 | uenv.venv = NULL; 92 | jint result = -1; 93 | JNIEnv *env = NULL; 94 | 95 | LOGI("JNI_OnLoad"); 96 | g_vm = vm; 97 | 98 | if ((*vm)->GetEnv(vm, &uenv.venv, JNI_VERSION_1_4) != JNI_OK) { 99 | LOGE("ERROR: GetEnv failed"); 100 | goto bail; 101 | } 102 | env = uenv.env; 103 | /* 104 | if (init_Exec(env) != JNI_TRUE) { 105 | LOGE("ERROR: init of Exec failed"); 106 | goto bail; 107 | } 108 | 109 | if (init_FileCompat(env) != JNI_TRUE) { 110 | LOGE("ERROR: init of Exec failed"); 111 | goto bail; 112 | } 113 | */ 114 | result = JNI_VERSION_1_4; 115 | 116 | bail: 117 | return result; 118 | } 119 | 120 | static int get_env(JNIEnv **env) { 121 | int attached = 0; 122 | int status = (*g_vm)->GetEnv(g_vm, (void **) env, JNI_VERSION_1_4); 123 | if (status < 0) { 124 | LOGD("callback_handler:failed to get JNI environment assuming native thread"); 125 | status = (*g_vm)->AttachCurrentThread(g_vm, env, NULL); 126 | if (status < 0) { 127 | LOGE("callback_handler: failed to attach current thread"); 128 | return 0; 129 | } 130 | attached = 1; 131 | } 132 | return attached; 133 | } 134 | 135 | esp_loader_error_t loader_port_linux_init(const loader_linux_config_t *config) { 136 | return ESP_LOADER_SUCCESS; 137 | } 138 | 139 | 140 | esp_loader_error_t loader_port_serial_write(const uint8_t *data, uint16_t size, uint32_t timeout) { 141 | JNIEnv *env = NULL; 142 | int ret = 0; 143 | int attached = get_env(&env); 144 | if (NULL == env) 145 | return ESP_LOADER_ERROR_FAIL; 146 | jclass dpclazz = (*env)->FindClass(env, "com/pxs/terminal/JNI/ESP"); 147 | if (dpclazz == 0) { 148 | LOGE("find class error %s", __FUNCTION__); 149 | return ESP_LOADER_ERROR_FAIL; 150 | } 151 | LOGD("find class %s", __FUNCTION__); 152 | //2 寻找class里面的方法 153 | jmethodID method1 = (*env)->GetStaticMethodID(env, dpclazz, "loaderPortSerialWrite", "([BII)I"); 154 | if (method1 == 0) { 155 | LOGE("find method1 error %s", __FUNCTION__); 156 | return ESP_LOADER_ERROR_FAIL; 157 | } 158 | LOGD("find method1 %s", __FUNCTION__); 159 | //3 .调用这个方法 160 | jbyteArray mdata = (*env)->NewByteArray(env, size); 161 | (*env)->SetByteArrayRegion(env, mdata, 0, size, (const jbyte *) data); 162 | jint msize = size; 163 | jint mtimeout = timeout; 164 | ret = (*env)->CallStaticIntMethod(env, dpclazz, method1, mdata, msize, mtimeout); 165 | if (attached) { 166 | (*g_vm)->DetachCurrentThread(g_vm); 167 | } 168 | return ret; 169 | } 170 | 171 | 172 | esp_loader_error_t loader_port_serial_read(uint8_t *data, uint16_t size, uint32_t timeout) { 173 | int ret = 0; 174 | JNIEnv *env = NULL; 175 | int attached = get_env(&env); 176 | if (NULL == env) 177 | return ESP_LOADER_ERROR_FAIL; 178 | jclass dpclazz = (*env)->FindClass(env, "com/pxs/terminal/JNI/ESP"); 179 | if (dpclazz == 0) { 180 | LOGE("find class error %s", __FUNCTION__); 181 | return ESP_LOADER_ERROR_FAIL; 182 | } 183 | LOGD("find class %s", __FUNCTION__); 184 | //2 寻找class里面的方法 185 | jmethodID method1 = (*env)->GetStaticMethodID(env, dpclazz, "loaderPortSerialRead", "([BII)I"); 186 | if (method1 == 0) { 187 | LOGE("find method1 error %s", __FUNCTION__); 188 | return ESP_LOADER_ERROR_FAIL; 189 | } 190 | LOGD("find method1 %s", __FUNCTION__); 191 | //3 .调用这个方法 192 | jbyteArray mdata = (*env)->NewByteArray(env, size); 193 | jint msize = size; 194 | jint mtimeout = timeout; 195 | ret = (*env)->CallStaticIntMethod(env, dpclazz, method1, mdata, msize, mtimeout); 196 | jbyte *rdata = (*env)->GetByteArrayElements(env, mdata, 0); 197 | memcpy(data, rdata, size); 198 | if (attached) { 199 | (*g_vm)->DetachCurrentThread(g_vm); 200 | } 201 | return ret; 202 | } 203 | 204 | 205 | // Set GPIO0 LOW, then assert reset pin for 50 milliseconds. 206 | void loader_port_enter_bootloader(void) { 207 | JNIEnv *env = NULL; 208 | int attached = get_env(&env); 209 | if (NULL == env) 210 | return; 211 | jclass dpclazz = (*env)->FindClass(env, "com/pxs/terminal/JNI/ESP"); 212 | if (dpclazz == 0) { 213 | LOGE("find class error %s", __FUNCTION__); 214 | return; 215 | } 216 | LOGD("find class %s", __FUNCTION__); 217 | //2 寻找class里面的方法 218 | jmethodID method1 = (*env)->GetStaticMethodID(env, dpclazz, "loaderPortEnterBootloader", "()V"); 219 | if (method1 == 0) { 220 | LOGE("find method1 error %s", __FUNCTION__); 221 | return; 222 | } 223 | LOGD("find method1 %s", __FUNCTION__); 224 | //3 .调用这个方法 225 | (*env)->CallStaticVoidMethod(env, dpclazz, method1); 226 | if (attached) { 227 | (*g_vm)->DetachCurrentThread(g_vm); 228 | } 229 | } 230 | 231 | 232 | void loader_port_reset_target(void) { 233 | JNIEnv *env = NULL; 234 | int attached = get_env(&env); 235 | if (NULL == env) 236 | return; 237 | jclass dpclazz = (*env)->FindClass(env, "com/pxs/terminal/JNI/ESP"); 238 | if (dpclazz == 0) { 239 | LOGE("find class error %s", __FUNCTION__); 240 | return; 241 | } 242 | LOGD("find class %s", __FUNCTION__); 243 | //2 寻找class里面的方法 244 | jmethodID method1 = (*env)->GetStaticMethodID(env, dpclazz, "loaderPortResetTarget", "()V"); 245 | if (method1 == 0) { 246 | LOGE("find method1 error %s", __FUNCTION__); 247 | return; 248 | } 249 | LOGD("find method1 %s", __FUNCTION__); 250 | //3 .调用这个方法 251 | (*env)->CallStaticVoidMethod(env, dpclazz, method1); 252 | if (attached) { 253 | (*g_vm)->DetachCurrentThread(g_vm); 254 | } 255 | } 256 | 257 | 258 | void loader_port_delay_ms(uint32_t ms) { 259 | usleep(ms * 1000); 260 | } 261 | 262 | 263 | void loader_port_start_timer(uint32_t ms) { 264 | s_time_end = clock() + (ms * (CLOCKS_PER_SEC / 1000)); 265 | } 266 | 267 | 268 | uint32_t loader_port_remaining_time(void) { 269 | int64_t remaining = (s_time_end - clock()) / 1000; 270 | return (remaining > 0) ? (uint32_t) remaining : 0; 271 | } 272 | 273 | 274 | void loader_port_debug_print(const char *str) { 275 | LOGI("DEBUG: %s", str); 276 | } 277 | 278 | esp_loader_error_t loader_port_change_baudrate(uint32_t baudrate) { 279 | int ret = 0; 280 | JNIEnv *env = NULL; 281 | int attached = get_env(&env); 282 | if (NULL == env) 283 | return ESP_LOADER_ERROR_FAIL; 284 | jclass dpclazz = (*env)->FindClass(env, "com/pxs/terminal/JNI/ESP"); 285 | if (dpclazz == 0) { 286 | LOGE("find class error %s", __FUNCTION__); 287 | return ESP_LOADER_ERROR_FAIL; 288 | } 289 | LOGD("find class %s", __FUNCTION__); 290 | //2 寻找class里面的方法 291 | // jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); 292 | jmethodID method1 = (*env)->GetStaticMethodID(env, dpclazz, "loaderPortChangeBaudrate", "(I)I"); 293 | if (method1 == 0) { 294 | LOGE("find method1 error %s", __FUNCTION__); 295 | return ESP_LOADER_ERROR_FAIL; 296 | } 297 | LOGD("find method1 %s", __FUNCTION__); 298 | //3 .调用这个方法 299 | // void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); 300 | ret = (*env)->CallStaticIntMethod(env, dpclazz, method1, (int) baudrate); 301 | if (attached) { 302 | (*g_vm)->DetachCurrentThread(g_vm); 303 | } 304 | return ret; 305 | } 306 | 307 | struct CallBack { 308 | jobject callbackObj; 309 | jmethodID OnFlashProcess; 310 | jmethodID OnError; 311 | }; 312 | 313 | static struct CallBack callBack = {0}; 314 | 315 | esp_loader_error_t loader_port_callback_on_flash_process(int progress) { 316 | int ret = 0; 317 | JNIEnv *env = NULL; 318 | int attached = 0; 319 | if (callBack.OnFlashProcess) { 320 | attached = get_env(&env); 321 | (*env)->CallVoidMethod(env, callBack.callbackObj, callBack.OnFlashProcess, progress); 322 | } 323 | if (attached) { 324 | (*g_vm)->DetachCurrentThread(g_vm); 325 | } 326 | return ret; 327 | } 328 | 329 | esp_loader_error_t loader_port_callback_on_error(int err_code) { 330 | int ret = 0; 331 | JNIEnv *env = NULL; 332 | int attached = 0; 333 | if (callBack.OnFlashProcess) { 334 | attached = get_env(&env); 335 | (*env)->CallVoidMethod(env, callBack.callbackObj, callBack.OnError, err_code); 336 | } 337 | if (attached) { 338 | (*g_vm)->DetachCurrentThread(g_vm); 339 | } 340 | return ret; 341 | } 342 | 343 | 344 | esp_loader_error_t loader_port_set_listener(jobject listener) { 345 | int ret = 0; 346 | JNIEnv *env = NULL; 347 | int attached = 0; 348 | attached = get_env(&env); 349 | if (NULL == env) { 350 | ret = ESP_LOADER_ERROR_FAIL; 351 | goto out; 352 | } 353 | if (NULL == listener) { 354 | ret = ESP_LOADER_ERROR_INVALID_PARAM; 355 | goto out; 356 | } 357 | jclass objClass = (*env)->GetObjectClass(env, listener); 358 | callBack.callbackObj = (*env)->NewGlobalRef(env, listener);//创建对象的本地变量 359 | // 获取方法ID 360 | callBack.OnFlashProcess = (*env)->GetMethodID(env, objClass, "OnFlashProcess", "(I)V"); 361 | callBack.OnError = (*env)->GetMethodID(env, objClass, "OnError", "(I)V"); 362 | if (NULL == callBack.OnFlashProcess || NULL == callBack.OnError) { 363 | ret = ESP_LOADER_ERROR_FAIL; 364 | goto out; 365 | } 366 | out: 367 | if (attached) { 368 | (*g_vm)->DetachCurrentThread(g_vm); 369 | } 370 | return ret; 371 | } -------------------------------------------------------------------------------- /app/src/main/cpp/esp-flasher/port/linux_port.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020 Espressif Systems (Shanghai) PTE LTD 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #pragma once 17 | 18 | #include 19 | #include "serial_io.h" 20 | #include 21 | 22 | typedef struct { 23 | const char *device; 24 | uint32_t baudrate; 25 | uint32_t reset_trigger_pin; 26 | uint32_t gpio0_trigger_pin; 27 | } loader_linux_config_t; 28 | 29 | esp_loader_error_t loader_port_linux_init(const loader_linux_config_t *config); 30 | esp_loader_error_t loader_port_callback_on_flash_process(int progress); 31 | esp_loader_error_t loader_port_callback_on_error(int err_code); 32 | esp_loader_error_t loader_port_set_listener(jobject listener); -------------------------------------------------------------------------------- /app/src/main/cpp/esp-flasher/private_include/esp_targets.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020 Espressif Systems (Shanghai) PTE LTD 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #pragma once 17 | 18 | #include 19 | #include "esp_loader.h" 20 | 21 | typedef struct { 22 | uint32_t cmd; 23 | uint32_t usr; 24 | uint32_t usr1; 25 | uint32_t usr2; 26 | uint32_t w0; 27 | uint32_t mosi_dlen; 28 | uint32_t miso_dlen; 29 | } target_registers_t; 30 | 31 | esp_loader_error_t loader_detect_chip(target_chip_t *target, const target_registers_t **regs); 32 | esp_loader_error_t loader_read_spi_config(target_chip_t target_chip, uint32_t *spi_config); -------------------------------------------------------------------------------- /app/src/main/cpp/esp-flasher/private_include/md5_hash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MD5 hash implementation and interface functions 3 | * Copyright (c) 2003-2005, Jouni Malinen 4 | * 5 | * This software may be distributed under the terms of the BSD license. 6 | * See README for more details. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | struct MD5Context { 17 | uint32_t buf[4]; 18 | uint32_t bits[2]; 19 | uint8_t in[64]; 20 | }; 21 | 22 | void MD5Init(struct MD5Context *context); 23 | void MD5Update(struct MD5Context *context, unsigned char const *buf, unsigned len); 24 | void MD5Final(unsigned char digest[16], struct MD5Context *context); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif -------------------------------------------------------------------------------- /app/src/main/cpp/esp-flasher/private_include/serial_comm.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020 Espressif Systems (Shanghai) PTE LTD 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include "esp_loader.h" 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | esp_loader_error_t loader_flash_begin_cmd(uint32_t offset, uint32_t erase_size, uint32_t block_size, uint32_t blocks_to_write, target_chip_t target); 27 | 28 | esp_loader_error_t loader_flash_data_cmd(const uint8_t *data, uint32_t size); 29 | 30 | esp_loader_error_t loader_flash_end_cmd(bool stay_in_loader); 31 | 32 | esp_loader_error_t loader_write_reg_cmd(uint32_t address, uint32_t value, uint32_t mask, uint32_t delay_us); 33 | 34 | esp_loader_error_t loader_read_reg_cmd(uint32_t address, uint32_t *reg); 35 | 36 | esp_loader_error_t loader_sync_cmd(void); 37 | 38 | esp_loader_error_t loader_spi_attach_cmd(uint32_t config); 39 | 40 | esp_loader_error_t loader_change_baudrate_cmd(uint32_t baudrate); 41 | 42 | esp_loader_error_t loader_erase_flash_cmd(void); 43 | 44 | esp_loader_error_t loader_md5_cmd(uint32_t address, uint32_t size, uint8_t *md5_out); 45 | 46 | esp_loader_error_t loader_spi_parameters(uint32_t total_size); 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif -------------------------------------------------------------------------------- /app/src/main/cpp/esp-flasher/private_include/serial_comm_prv.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020 Espressif Systems (Shanghai) PTE LTD 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | #define STATUS_FAILURE 1 26 | #define STATUS_SUCCESS 0 27 | 28 | #define READ_DIRECTION 1 29 | #define WRITE_DIRECTION 0 30 | 31 | #define MD5_SIZE 32 32 | 33 | typedef enum __attribute__((packed)) 34 | { 35 | FLASH_BEGIN = 0x02, 36 | FLASH_DATA = 0x03, 37 | FLASH_END = 0x04, 38 | MEM_BEGIN = 0x05, 39 | MEM_END = 0x06, 40 | MEM_DATA = 0x07, 41 | SYNC = 0x08, 42 | WRITE_REG = 0x09, 43 | READ_REG = 0x0a, 44 | 45 | SPI_SET_PARAMS = 0x0b, 46 | SPI_ATTACH = 0x0d, 47 | CHANGE_BAUDRATE = 0x0f, 48 | FLASH_DEFL_BEGIN = 0x10, 49 | FLASH_DEFL_DATA = 0x11, 50 | FLASH_DEFL_END = 0x12, 51 | SPI_FLASH_MD5 = 0x13, 52 | 53 | // Some commands supported by stub only 54 | ESP_ERASE_FLASH = 0xD0, 55 | ESP_ERASE_REGION = 0xD1, 56 | ESP_READ_FLASH = 0xD2, 57 | ESP_RUN_USER_CODE = 0xD3, 58 | } command_t; 59 | 60 | typedef enum __attribute__((packed)) 61 | { 62 | RESPONSE_OK = 0x00, 63 | INVALID_COMMAND = 0x05, // parameters or length field is invalid 64 | COMMAND_FAILED = 0x06, // Failed to act on received message 65 | INVALID_CRC = 0x07, // Invalid CRC in message 66 | FLASH_WRITE_ERR = 0x08, // After writing a block of data to flash, the ROM loader reads the value back and the 8-bit CRC is compared to the data read from flash. If they don't match, this error is returned. 67 | FLASH_READ_ERR = 0x09, // SPI read failed 68 | READ_LENGTH_ERR = 0x0a, // SPI read request length is too long 69 | DEFLATE_ERROR = 0x0b, // ESP32 compressed uploads only 70 | } error_code_t; 71 | 72 | typedef struct __attribute__((packed)) 73 | { 74 | uint8_t direction; 75 | uint8_t command; // One of command_t 76 | uint16_t size; 77 | uint32_t checksum; 78 | } command_common_t; 79 | 80 | typedef struct __attribute__((packed)) 81 | { 82 | command_common_t common; 83 | uint32_t erase_size; 84 | uint32_t packet_count; 85 | uint32_t packet_size; 86 | uint32_t offset; 87 | uint32_t encrypted; 88 | } begin_command_t; 89 | 90 | typedef struct __attribute__((packed)) 91 | { 92 | command_common_t common; 93 | uint32_t data_size; 94 | uint32_t sequence_number; 95 | uint32_t zero_0; 96 | uint32_t zero_1; 97 | } data_command_t; 98 | 99 | typedef struct __attribute__((packed)) 100 | { 101 | command_common_t common; 102 | uint32_t stay_in_loader; 103 | } flash_end_command_t; 104 | 105 | typedef struct __attribute__((packed)) 106 | { 107 | command_common_t common; 108 | uint32_t stay_in_loader; 109 | uint32_t entry_point_address; 110 | } mem_end_command_t; 111 | 112 | typedef struct __attribute__((packed)) 113 | { 114 | command_common_t common; 115 | uint8_t sync_sequence[36]; 116 | } sync_command_t; 117 | 118 | typedef struct __attribute__((packed)) 119 | { 120 | command_common_t common; 121 | uint32_t address; 122 | uint32_t value; 123 | uint32_t mask; 124 | uint32_t delay_us; 125 | } write_reg_command_t; 126 | 127 | typedef struct __attribute__((packed)) 128 | { 129 | command_common_t common; 130 | uint32_t address; 131 | } read_reg_command_t; 132 | 133 | typedef struct __attribute__((packed)) 134 | { 135 | command_common_t common; 136 | uint32_t configuration; 137 | uint32_t zero; // ESP32 ROM only 138 | } spi_attach_command_t; 139 | 140 | typedef struct __attribute__((packed)) 141 | { 142 | command_common_t common; 143 | uint32_t new_baudrate; 144 | uint32_t old_baudrate; 145 | } change_baudrate_command_t; 146 | 147 | typedef struct __attribute__((packed)) 148 | { 149 | command_common_t common; 150 | } erase_flash_command_t; 151 | 152 | typedef struct __attribute__((packed)) 153 | { 154 | command_common_t common; 155 | uint32_t address; 156 | uint32_t size; 157 | uint32_t reserved_0; 158 | uint32_t reserved_1; 159 | } spi_flash_md5_command_t; 160 | 161 | typedef struct __attribute__((packed)) 162 | { 163 | uint8_t direction; 164 | uint8_t command; // One of command_t 165 | uint16_t size; 166 | uint32_t value; 167 | } common_response_t; 168 | 169 | typedef struct __attribute__((packed)) 170 | { 171 | uint8_t failed; 172 | uint8_t error; 173 | } response_status_t; 174 | 175 | typedef struct __attribute__((packed)) 176 | { 177 | common_response_t common; 178 | response_status_t status; 179 | } response_t; 180 | 181 | typedef struct __attribute__((packed)) 182 | { 183 | common_response_t common; 184 | uint8_t md5[MD5_SIZE]; // ROM only 185 | response_status_t status; 186 | } rom_md5_response_t; 187 | 188 | typedef struct __attribute__((packed)) 189 | { 190 | command_common_t common; 191 | uint32_t id; 192 | uint32_t total_size; 193 | uint32_t block_size; 194 | uint32_t sector_size; 195 | uint32_t page_size; 196 | uint32_t status_mask; 197 | } write_spi_command_t; 198 | 199 | 200 | #ifdef __cplusplus 201 | } 202 | #endif -------------------------------------------------------------------------------- /app/src/main/cpp/esp-flasher/src/esp_loader.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2020 Espressif Systems (Shanghai) PTE LTD 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #include "serial_comm_prv.h" 17 | #include "serial_comm.h" 18 | #include "serial_io.h" 19 | #include "esp_loader.h" 20 | #include "esp_targets.h" 21 | #include "md5_hash.h" 22 | #include 23 | #include 24 | #include 25 | 26 | #ifndef MAX 27 | #define MAX(a, b) ((a) > (b)) ? (a) : (b) 28 | #endif 29 | 30 | #ifndef MIN 31 | #define MIN(a, b) ((a) < (b)) ? (a) : (b) 32 | #endif 33 | 34 | static const uint32_t DEFAULT_TIMEOUT = 1000; 35 | static const uint32_t DEFAULT_FLASH_TIMEOUT = 3000; // timeout for most flash operations 36 | static const uint32_t ERASE_REGION_TIMEOUT_PER_MB = 10000; // timeout (per megabyte) for erasing a region 37 | static const uint8_t PADDING_PATTERN = 0xFF; 38 | 39 | typedef enum { 40 | SPI_FLASH_READ_ID = 0x9F 41 | } spi_flash_cmd_t; 42 | 43 | static uint32_t s_flash_write_size = 0; 44 | static const target_registers_t *s_reg = NULL; 45 | static target_chip_t s_target = ESP_UNKNOWN_CHIP; 46 | 47 | #if MD5_ENABLED 48 | 49 | static const uint32_t MD5_TIMEOUT_PER_MB = 800; 50 | static struct MD5Context s_md5_context; 51 | static uint32_t s_start_address; 52 | static uint32_t s_image_size; 53 | 54 | static inline void init_md5(uint32_t address, uint32_t size) 55 | { 56 | s_start_address = address; 57 | s_image_size = size; 58 | MD5Init(&s_md5_context); 59 | } 60 | 61 | static inline void md5_update(const uint8_t *data, uint32_t size) 62 | { 63 | MD5Update(&s_md5_context, data, size); 64 | } 65 | 66 | static inline void md5_final(uint8_t digets[16]) 67 | { 68 | MD5Final(digets, &s_md5_context); 69 | } 70 | 71 | #else 72 | 73 | static inline void init_md5(uint32_t address, uint32_t size) { } 74 | static inline void md5_update(const uint8_t *data, uint32_t size) { } 75 | static inline void md5_final(uint8_t digets[16]) { } 76 | 77 | #endif 78 | 79 | 80 | static uint32_t timeout_per_mb(uint32_t size_bytes, uint32_t time_per_mb) 81 | { 82 | uint32_t timeout = time_per_mb * (size_bytes / 1e6); 83 | return MAX(timeout, DEFAULT_FLASH_TIMEOUT); 84 | } 85 | 86 | esp_loader_error_t esp_loader_connect(esp_loader_connect_args_t *connect_args) 87 | { 88 | uint32_t spi_config; 89 | esp_loader_error_t err; 90 | int32_t trials = connect_args->trials; 91 | 92 | loader_port_enter_bootloader(); 93 | 94 | printf("wait\n"); 95 | do { 96 | printf("."); 97 | fflush(stdout); 98 | loader_port_start_timer(connect_args->sync_timeout); 99 | err = loader_sync_cmd(); 100 | if (err == ESP_LOADER_ERROR_TIMEOUT) { 101 | if (--trials == 0) { 102 | return ESP_LOADER_ERROR_TIMEOUT; 103 | } 104 | loader_port_delay_ms(100); 105 | } else if (err != ESP_LOADER_SUCCESS) { 106 | return err; 107 | } 108 | } while (err != ESP_LOADER_SUCCESS); 109 | printf("\n"); 110 | 111 | RETURN_ON_ERROR( loader_detect_chip(&s_target, &s_reg) ); 112 | 113 | if (s_target == ESP8266_CHIP) { 114 | err = loader_flash_begin_cmd(0, 0, 0, 0, s_target); 115 | } else { 116 | RETURN_ON_ERROR( loader_read_spi_config(s_target, &spi_config) ); 117 | loader_port_start_timer(DEFAULT_TIMEOUT); 118 | err = loader_spi_attach_cmd(spi_config); 119 | } 120 | 121 | return err; 122 | } 123 | 124 | target_chip_t esp_loader_get_target(void) 125 | { 126 | return s_target; 127 | } 128 | 129 | static esp_loader_error_t spi_set_data_lengths(size_t mosi_bits, size_t miso_bits) 130 | { 131 | if (mosi_bits > 0) { 132 | RETURN_ON_ERROR( esp_loader_write_register(s_reg->mosi_dlen, mosi_bits - 1) ); 133 | } 134 | if (miso_bits > 0) { 135 | RETURN_ON_ERROR( esp_loader_write_register(s_reg->miso_dlen, miso_bits - 1) ); 136 | } 137 | 138 | return ESP_LOADER_SUCCESS; 139 | } 140 | 141 | static esp_loader_error_t spi_set_data_lengths_8266(size_t mosi_bits, size_t miso_bits) 142 | { 143 | uint32_t mosi_mask = (mosi_bits == 0) ? 0 : mosi_bits - 1; 144 | uint32_t miso_mask = (miso_bits == 0) ? 0 : miso_bits - 1; 145 | return esp_loader_write_register(s_reg->usr1, (miso_mask << 8) | (mosi_mask << 17)); 146 | } 147 | 148 | static esp_loader_error_t spi_flash_command(spi_flash_cmd_t cmd, void *data_tx, size_t tx_size, void *data_rx, size_t rx_size) 149 | { 150 | assert(rx_size <= 32); // Reading more than 32 bits back from a SPI flash operation is unsupported 151 | assert(tx_size <= 64); // Writing more than 64 bytes of data with one SPI command is unsupported 152 | 153 | uint32_t SPI_USR_CMD = (1 << 31); 154 | uint32_t SPI_USR_MISO = (1 << 28); 155 | uint32_t SPI_USR_MOSI = (1 << 27); 156 | uint32_t SPI_CMD_USR = (1 << 18); 157 | uint32_t CMD_LEN_SHIFT = 28; 158 | 159 | // Save SPI configuration 160 | uint32_t old_spi_usr; 161 | uint32_t old_spi_usr2; 162 | RETURN_ON_ERROR( esp_loader_read_register(s_reg->usr, &old_spi_usr) ); 163 | RETURN_ON_ERROR( esp_loader_read_register(s_reg->usr2, &old_spi_usr2) ); 164 | 165 | if (s_target == ESP8266_CHIP) { 166 | RETURN_ON_ERROR( spi_set_data_lengths_8266(tx_size, rx_size) ); 167 | } else { 168 | RETURN_ON_ERROR( spi_set_data_lengths(tx_size, rx_size) ); 169 | } 170 | 171 | uint32_t usr_reg_2 = (7 << CMD_LEN_SHIFT) | cmd; 172 | uint32_t usr_reg = SPI_USR_CMD; 173 | if (rx_size > 0) { 174 | usr_reg |= SPI_USR_MISO; 175 | } 176 | if (tx_size > 0) { 177 | usr_reg |= SPI_USR_MOSI; 178 | } 179 | 180 | RETURN_ON_ERROR( esp_loader_write_register(s_reg->usr, usr_reg) ); 181 | RETURN_ON_ERROR( esp_loader_write_register(s_reg->usr2, usr_reg_2 ) ); 182 | 183 | if (tx_size == 0) { 184 | // clear data register before we read it 185 | RETURN_ON_ERROR( esp_loader_write_register(s_reg->w0, 0) ); 186 | } else { 187 | uint32_t *data = (uint32_t *)data_tx; 188 | uint32_t words_to_write = (tx_size + 31) / (8 * 4); 189 | uint32_t data_reg_addr = s_reg->w0; 190 | 191 | while (words_to_write--) { 192 | uint32_t word = *data++; 193 | RETURN_ON_ERROR( esp_loader_write_register(data_reg_addr, word) ); 194 | data_reg_addr += 4; 195 | } 196 | } 197 | 198 | RETURN_ON_ERROR( esp_loader_write_register(s_reg->cmd, SPI_CMD_USR) ); 199 | 200 | uint32_t trials = 10; 201 | while (trials--) { 202 | uint32_t cmd_reg; 203 | RETURN_ON_ERROR( esp_loader_read_register(s_reg->cmd, &cmd_reg) ); 204 | if ((cmd_reg & SPI_CMD_USR) == 0) { 205 | break; 206 | } 207 | } 208 | 209 | if (trials == 0) { 210 | return ESP_LOADER_ERROR_TIMEOUT; 211 | } 212 | 213 | RETURN_ON_ERROR( esp_loader_read_register(s_reg->w0, data_rx) ); 214 | 215 | // Restore SPI configuration 216 | RETURN_ON_ERROR( esp_loader_write_register(s_reg->usr, old_spi_usr) ); 217 | RETURN_ON_ERROR( esp_loader_write_register(s_reg->usr2, old_spi_usr2) ); 218 | 219 | return ESP_LOADER_SUCCESS; 220 | } 221 | 222 | static esp_loader_error_t detect_flash_size(size_t *flash_size) 223 | { 224 | uint32_t flash_id = 0; 225 | 226 | RETURN_ON_ERROR( spi_flash_command(SPI_FLASH_READ_ID, NULL, 0, &flash_id, 24) ); 227 | uint32_t size_id = flash_id >> 16; 228 | 229 | if (size_id < 0x12 || size_id > 0x18) { 230 | return ESP_LOADER_ERROR_UNSUPPORTED_CHIP; 231 | } 232 | 233 | *flash_size = 1 << size_id; 234 | 235 | return ESP_LOADER_SUCCESS; 236 | } 237 | 238 | esp_loader_error_t esp_loader_flash_start(uint32_t offset, uint32_t image_size, uint32_t block_size) 239 | { 240 | uint32_t blocks_to_write = (image_size + block_size - 1) / block_size; 241 | uint32_t erase_size = block_size * blocks_to_write; 242 | s_flash_write_size = block_size; 243 | 244 | size_t flash_size = 0; 245 | if (detect_flash_size(&flash_size) == ESP_LOADER_SUCCESS) { 246 | if (image_size > flash_size) { 247 | return ESP_LOADER_ERROR_IMAGE_SIZE; 248 | } 249 | loader_port_start_timer(DEFAULT_TIMEOUT); 250 | RETURN_ON_ERROR( loader_spi_parameters(flash_size) ); 251 | } else { 252 | loader_port_debug_print("Flash size detection failed, falling back to default"); 253 | } 254 | 255 | init_md5(offset, image_size); 256 | 257 | loader_port_start_timer(timeout_per_mb(erase_size, ERASE_REGION_TIMEOUT_PER_MB)); 258 | return loader_flash_begin_cmd(offset, erase_size, block_size, blocks_to_write, s_target); 259 | } 260 | 261 | 262 | esp_loader_error_t esp_loader_flash_write(void *payload, uint32_t size) 263 | { 264 | uint32_t padding_bytes = s_flash_write_size - size; 265 | uint8_t *data = (uint8_t *)payload; 266 | uint32_t padding_index = size; 267 | 268 | while (padding_bytes--) { 269 | data[padding_index++] = PADDING_PATTERN; 270 | } 271 | 272 | md5_update(payload, (size + 3) & ~3); 273 | 274 | loader_port_start_timer(DEFAULT_TIMEOUT); 275 | 276 | return loader_flash_data_cmd(data, s_flash_write_size); 277 | } 278 | 279 | 280 | esp_loader_error_t esp_loader_flash_finish(bool reboot) 281 | { 282 | loader_port_start_timer(DEFAULT_TIMEOUT); 283 | 284 | return loader_flash_end_cmd(!reboot); 285 | } 286 | 287 | 288 | esp_loader_error_t esp_loader_read_register(uint32_t address, uint32_t *reg_value) 289 | { 290 | loader_port_start_timer(DEFAULT_TIMEOUT); 291 | 292 | return loader_read_reg_cmd(address, reg_value); 293 | } 294 | 295 | 296 | esp_loader_error_t esp_loader_write_register(uint32_t address, uint32_t reg_value) 297 | { 298 | loader_port_start_timer(DEFAULT_TIMEOUT); 299 | 300 | return loader_write_reg_cmd(address, reg_value, 0xFFFFFFFF, 0); 301 | } 302 | 303 | esp_loader_error_t esp_loader_change_baudrate(uint32_t baudrate) 304 | { 305 | if (s_target == ESP8266_CHIP) { 306 | return ESP_LOADER_ERROR_UNSUPPORTED_FUNC; 307 | } 308 | 309 | loader_port_start_timer(DEFAULT_TIMEOUT); 310 | 311 | return loader_change_baudrate_cmd(baudrate); 312 | } 313 | 314 | #if MD5_ENABLED 315 | 316 | static void hexify(const uint8_t raw_md5[16], uint8_t hex_md5_out[32]) 317 | { 318 | static const uint8_t dec_to_hex[] = { 319 | '0', '1', '2', '3', '4', '5', '6', '7', 320 | '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 321 | }; 322 | for (int i = 0; i < 16; i++) { 323 | *hex_md5_out++ = dec_to_hex[raw_md5[i] >> 4]; 324 | *hex_md5_out++ = dec_to_hex[raw_md5[i] & 0xF]; 325 | } 326 | } 327 | 328 | 329 | esp_loader_error_t esp_loader_flash_verify(void) 330 | { 331 | if (s_target == ESP8266_CHIP) { 332 | return ESP_LOADER_ERROR_UNSUPPORTED_FUNC; 333 | } 334 | 335 | uint8_t raw_md5[16]; 336 | uint8_t hex_md5[MD5_SIZE + 1]; 337 | uint8_t received_md5[MD5_SIZE + 1]; 338 | 339 | md5_final(raw_md5); 340 | hexify(raw_md5, hex_md5); 341 | 342 | loader_port_start_timer(timeout_per_mb(s_image_size, MD5_TIMEOUT_PER_MB)); 343 | 344 | RETURN_ON_ERROR( loader_md5_cmd(s_start_address, s_image_size, received_md5) ); 345 | 346 | bool md5_match = memcmp(hex_md5, received_md5, MD5_SIZE) == 0; 347 | 348 | if (!md5_match) { 349 | hex_md5[MD5_SIZE] = '\n'; 350 | received_md5[MD5_SIZE] = '\n'; 351 | 352 | loader_port_debug_print("Error: MD5 checksum does not match:\n"); 353 | loader_port_debug_print("Expected:\n"); 354 | loader_port_debug_print((char *)received_md5); 355 | loader_port_debug_print("Actual:\n"); 356 | loader_port_debug_print((char *)hex_md5); 357 | 358 | return ESP_LOADER_ERROR_INVALID_MD5; 359 | } 360 | 361 | return ESP_LOADER_SUCCESS; 362 | } 363 | 364 | #endif 365 | 366 | void esp_loader_reset_target(void) 367 | { 368 | loader_port_reset_target(); 369 | } 370 | 371 | esp_loader_error_t esp_loader_erase_flash(void) 372 | { 373 | loader_port_start_timer(ERASE_REGION_TIMEOUT_PER_MB); 374 | return loader_erase_flash_cmd(); 375 | } 376 | -------------------------------------------------------------------------------- /app/src/main/cpp/esp-flasher/src/esp_targets.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2020 Espressif Systems (Shanghai) PTE LTD 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #include "esp_targets.h" 17 | #include 18 | #include 19 | 20 | typedef esp_loader_error_t (*read_spi_config_t)(uint32_t efuse_base, uint32_t *spi_config); 21 | 22 | typedef struct { 23 | target_registers_t regs; 24 | uint32_t efuse_base; 25 | uint32_t chip_magic_value; 26 | read_spi_config_t read_spi_config; 27 | } esp_target_t; 28 | 29 | // This ROM address has a different value on each chip model 30 | #define CHIP_DETECT_MAGIC_REG_ADDR 0x40001000 31 | 32 | #define ESP8266_SPI_REG_BASE 0x60000200 33 | #define ESP32S2_SPI_REG_BASE 0x3f402000 34 | #define ESP32C3_SPI_REG_BASE 0x60002000 35 | #define ESP32S3_SPI_REG_BASE 0x60002000 36 | #define ESP32_SPI_REG_BASE 0x3ff42000 37 | 38 | static esp_loader_error_t spi_config_esp32(uint32_t efuse_base, uint32_t *spi_config); 39 | static esp_loader_error_t spi_config_esp32xx(uint32_t efuse_base, uint32_t *spi_config); 40 | 41 | static const esp_target_t esp_target[ESP_MAX_CHIP] = { 42 | 43 | // ESP8266 44 | { 45 | .regs = { 46 | .cmd = ESP8266_SPI_REG_BASE + 0x00, 47 | .usr = ESP8266_SPI_REG_BASE + 0x1c, 48 | .usr1 = ESP8266_SPI_REG_BASE + 0x20, 49 | .usr2 = ESP8266_SPI_REG_BASE + 0x24, 50 | .w0 = ESP8266_SPI_REG_BASE + 0x40, 51 | .mosi_dlen = 0, 52 | .miso_dlen = 0, 53 | }, 54 | .efuse_base = 0, // Not used 55 | .chip_magic_value = 0xfff0c101, 56 | .read_spi_config = NULL, // Not used 57 | }, 58 | 59 | // ESP32 60 | { 61 | .regs = { 62 | .cmd = ESP32_SPI_REG_BASE + 0x00, 63 | .usr = ESP32_SPI_REG_BASE + 0x1c, 64 | .usr1 = ESP32_SPI_REG_BASE + 0x20, 65 | .usr2 = ESP32_SPI_REG_BASE + 0x24, 66 | .w0 = ESP32_SPI_REG_BASE + 0x80, 67 | .mosi_dlen = ESP32_SPI_REG_BASE + 0x28, 68 | .miso_dlen = ESP32_SPI_REG_BASE + 0x2c, 69 | }, 70 | .efuse_base = 0x3ff5A000, 71 | .chip_magic_value = 0x00f01d83, 72 | .read_spi_config = spi_config_esp32, 73 | }, 74 | 75 | // ESP32S2 76 | { 77 | .regs = { 78 | .cmd = ESP32S2_SPI_REG_BASE + 0x00, 79 | .usr = ESP32S2_SPI_REG_BASE + 0x18, 80 | .usr1 = ESP32S2_SPI_REG_BASE + 0x1c, 81 | .usr2 = ESP32S2_SPI_REG_BASE + 0x20, 82 | .w0 = ESP32S2_SPI_REG_BASE + 0x58, 83 | .mosi_dlen = ESP32S2_SPI_REG_BASE + 0x24, 84 | .miso_dlen = ESP32S2_SPI_REG_BASE + 0x28, 85 | }, 86 | .efuse_base = 0x3f41A000, 87 | .chip_magic_value = 0x000007c6, 88 | .read_spi_config = spi_config_esp32xx, 89 | }, 90 | 91 | // ESP32C3 92 | { 93 | .regs = { 94 | .cmd = ESP32C3_SPI_REG_BASE + 0x00, 95 | .usr = ESP32C3_SPI_REG_BASE + 0x18, 96 | .usr1 = ESP32C3_SPI_REG_BASE + 0x1c, 97 | .usr2 = ESP32C3_SPI_REG_BASE + 0x20, 98 | .w0 = ESP32C3_SPI_REG_BASE + 0x58, 99 | .mosi_dlen = ESP32C3_SPI_REG_BASE + 0x24, 100 | .miso_dlen = ESP32C3_SPI_REG_BASE + 0x28, 101 | }, 102 | .efuse_base = 0x60008800, 103 | .chip_magic_value = 0x6921506f, 104 | .read_spi_config = spi_config_esp32xx, 105 | }, 106 | 107 | // ESP32S3 108 | { 109 | .regs = { 110 | .cmd = ESP32C3_SPI_REG_BASE + 0x00, 111 | .usr = ESP32C3_SPI_REG_BASE + 0x18, 112 | .usr1 = ESP32C3_SPI_REG_BASE + 0x1c, 113 | .usr2 = ESP32C3_SPI_REG_BASE + 0x20, 114 | .w0 = ESP32C3_SPI_REG_BASE + 0x58, 115 | .mosi_dlen = ESP32C3_SPI_REG_BASE + 0x24, 116 | .miso_dlen = ESP32C3_SPI_REG_BASE + 0x28, 117 | }, 118 | .efuse_base = 0x60007000, 119 | .chip_magic_value = 0x00000009, 120 | .read_spi_config = spi_config_esp32xx, // ! 121 | }, 122 | }; 123 | 124 | const target_registers_t *get_esp_target_data(target_chip_t chip) 125 | { 126 | return (target_registers_t *)&esp_target[chip]; 127 | } 128 | 129 | esp_loader_error_t loader_detect_chip(target_chip_t *target_chip, const target_registers_t **target_data) 130 | { 131 | uint32_t magic_value; 132 | RETURN_ON_ERROR( esp_loader_read_register(CHIP_DETECT_MAGIC_REG_ADDR, &magic_value) ); 133 | 134 | for (int chip = 0; chip < ESP_MAX_CHIP; chip++) { 135 | if (magic_value == esp_target[chip].chip_magic_value) { 136 | *target_chip = (target_chip_t)chip; 137 | *target_data = (target_registers_t *)&esp_target[chip]; 138 | return ESP_LOADER_SUCCESS; 139 | } 140 | } 141 | 142 | return ESP_LOADER_ERROR_INVALID_TARGET; 143 | } 144 | 145 | esp_loader_error_t loader_read_spi_config(target_chip_t target_chip, uint32_t *spi_config) 146 | { 147 | const esp_target_t *target = &esp_target[target_chip]; 148 | return target->read_spi_config(target->efuse_base, spi_config); 149 | } 150 | 151 | static inline uint32_t efuse_word_addr(uint32_t efuse_base, uint32_t n) 152 | { 153 | return efuse_base + (n * 4); 154 | } 155 | 156 | // 30->GPIO32 | 31->GPIO33 157 | static inline uint8_t adjust_pin_number(uint8_t num) 158 | { 159 | return (num >= 30) ? num + 2 : num; 160 | } 161 | 162 | 163 | static esp_loader_error_t spi_config_esp32(uint32_t efuse_base, uint32_t *spi_config) 164 | { 165 | *spi_config = 0; 166 | 167 | uint32_t reg5, reg3; 168 | RETURN_ON_ERROR( esp_loader_read_register(efuse_word_addr(efuse_base, 5), ®5) ); 169 | RETURN_ON_ERROR( esp_loader_read_register(efuse_word_addr(efuse_base, 3), ®3) ); 170 | 171 | uint32_t pins = reg5 & 0xfffff; 172 | 173 | if (pins == 0 || pins == 0xfffff) { 174 | return ESP_LOADER_SUCCESS; 175 | } 176 | 177 | uint8_t clk = adjust_pin_number( (pins >> 0) & 0x1f ); 178 | uint8_t q = adjust_pin_number( (pins >> 5) & 0x1f ); 179 | uint8_t d = adjust_pin_number( (pins >> 10) & 0x1f ); 180 | uint8_t cs = adjust_pin_number( (pins >> 15) & 0x1f ); 181 | uint8_t hd = adjust_pin_number( (reg3 >> 4) & 0x1f ); 182 | 183 | if (clk == cs || clk == d || clk == q || q == cs || q == d || q == d) { 184 | return ESP_LOADER_SUCCESS; 185 | } 186 | 187 | *spi_config = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk; 188 | 189 | return ESP_LOADER_SUCCESS; 190 | } 191 | 192 | // Applies for esp32s2, esp32c3 and esp32c3 193 | static esp_loader_error_t spi_config_esp32xx(uint32_t efuse_base, uint32_t *spi_config) 194 | { 195 | *spi_config = 0; 196 | 197 | uint32_t reg1, reg2; 198 | RETURN_ON_ERROR( esp_loader_read_register(efuse_word_addr(efuse_base, 18), ®1) ); 199 | RETURN_ON_ERROR( esp_loader_read_register(efuse_word_addr(efuse_base, 19), ®2) ); 200 | 201 | uint32_t pins = ((reg1 >> 16) | ((reg2 & 0xfffff) << 16)) & 0x3fffffff; 202 | 203 | if (pins == 0 || pins == 0xffffffff) { 204 | return ESP_LOADER_SUCCESS; 205 | } 206 | 207 | *spi_config = pins; 208 | return ESP_LOADER_SUCCESS; 209 | } -------------------------------------------------------------------------------- /app/src/main/cpp/esp-flasher/src/md5_hash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MD5 hash implementation and interface functions 3 | * Copyright (c) 2003-2005, Jouni Malinen 4 | * 5 | * This software may be distributed under the terms of the BSD license. 6 | * See README for more details. 7 | */ 8 | 9 | 10 | #include "md5_hash.h" 11 | #include 12 | #include 13 | 14 | 15 | static void MD5Transform(uint32_t buf[4], uint32_t const in[16]); 16 | 17 | 18 | /* ===== start - public domain MD5 implementation ===== */ 19 | /* 20 | * This code implements the MD5 message-digest algorithm. 21 | * The algorithm is due to Ron Rivest. This code was 22 | * written by Colin Plumb in 1993, no copyright is claimed. 23 | * This code is in the public domain; do with it what you wish. 24 | * 25 | * Equivalent code is available from RSA Data Security, Inc. 26 | * This code has been tested against that, and is equivalent, 27 | * except that you don't need to include two pages of legalese 28 | * with every copy. 29 | * 30 | * To compute the message digest of a chunk of bytes, declare an 31 | * MD5Context structure, pass it to MD5Init, call MD5Update as 32 | * needed on buffers full of bytes, and then call MD5Final, which 33 | * will fill a supplied 16-byte array with the digest. 34 | */ 35 | 36 | #ifndef WORDS_BIGENDIAN 37 | #define byteReverse(buf, len) /* Nothing */ 38 | #else 39 | /* 40 | * Note: this code is harmless on little-endian machines. 41 | */ 42 | static void byteReverse(unsigned char *buf, unsigned longs) 43 | { 44 | uint32_t t; 45 | do { 46 | t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | 47 | ((unsigned) buf[1] << 8 | buf[0]); 48 | *(uint32_t *) buf = t; 49 | buf += 4; 50 | } while (--longs); 51 | } 52 | #endif 53 | 54 | /* 55 | * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious 56 | * initialization constants. 57 | */ 58 | void MD5Init(struct MD5Context *ctx) 59 | { 60 | ctx->buf[0] = 0x67452301; 61 | ctx->buf[1] = 0xefcdab89; 62 | ctx->buf[2] = 0x98badcfe; 63 | ctx->buf[3] = 0x10325476; 64 | 65 | ctx->bits[0] = 0; 66 | ctx->bits[1] = 0; 67 | } 68 | 69 | /* 70 | * Update context to reflect the concatenation of another buffer full 71 | * of bytes. 72 | */ 73 | void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) 74 | { 75 | uint32_t t; 76 | 77 | /* Update bitcount */ 78 | 79 | t = ctx->bits[0]; 80 | if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) { 81 | ctx->bits[1]++; /* Carry from low to high */ 82 | } 83 | ctx->bits[1] += len >> 29; 84 | 85 | t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ 86 | 87 | /* Handle any leading odd-sized chunks */ 88 | 89 | if (t) { 90 | unsigned char *p = (unsigned char *) ctx->in + t; 91 | 92 | t = 64 - t; 93 | if (len < t) { 94 | memcpy(p, buf, len); 95 | return; 96 | } 97 | memcpy(p, buf, t); 98 | byteReverse(ctx->in, 16); 99 | MD5Transform((uint32_t *)ctx->buf, (uint32_t *) ctx->in); 100 | buf += t; 101 | len -= t; 102 | } 103 | /* Process data in 64-byte chunks */ 104 | 105 | while (len >= 64) { 106 | memcpy(ctx->in, buf, 64); 107 | byteReverse(ctx->in, 16); 108 | MD5Transform((uint32_t *)ctx->buf, (uint32_t *) ctx->in); 109 | buf += 64; 110 | len -= 64; 111 | } 112 | 113 | /* Handle any remaining bytes of data. */ 114 | 115 | memcpy(ctx->in, buf, len); 116 | } 117 | 118 | /* 119 | * Final wrapup - pad to 64-byte boundary with the bit pattern 120 | * 1 0* (64-bit count of bits processed, MSB-first) 121 | */ 122 | void MD5Final(unsigned char digest[16], struct MD5Context *ctx) 123 | { 124 | unsigned count; 125 | unsigned char *p; 126 | 127 | /* Compute number of bytes mod 64 */ 128 | count = (ctx->bits[0] >> 3) & 0x3F; 129 | 130 | /* Set the first char of padding to 0x80. This is safe since there is 131 | always at least one byte free */ 132 | p = ctx->in + count; 133 | *p++ = 0x80; 134 | 135 | /* Bytes of padding needed to make 64 bytes */ 136 | count = 64 - 1 - count; 137 | 138 | /* Pad out to 56 mod 64 */ 139 | if (count < 8) { 140 | /* Two lots of padding: Pad the first block to 64 bytes */ 141 | memset(p, 0, count); 142 | byteReverse(ctx->in, 16); 143 | MD5Transform((uint32_t *)ctx->buf, (uint32_t *) ctx->in); 144 | 145 | /* Now fill the next block with 56 bytes */ 146 | memset(ctx->in, 0, 56); 147 | } else { 148 | /* Pad block to 56 bytes */ 149 | memset(p, 0, count - 8); 150 | } 151 | byteReverse(ctx->in, 14); 152 | 153 | /* Append length in bits and transform */ 154 | ((uint32_t *) ctx->in)[14] = ctx->bits[0]; 155 | ((uint32_t *) ctx->in)[15] = ctx->bits[1]; 156 | 157 | MD5Transform((uint32_t *)ctx->buf, (uint32_t *) ctx->in); 158 | byteReverse((unsigned char *) ctx->buf, 4); 159 | memcpy(digest, ctx->buf, 16); 160 | memset(ctx, 0, sizeof(struct MD5Context)); /* In case it's sensitive */ 161 | } 162 | 163 | /* The four core functions - F1 is optimized somewhat */ 164 | 165 | /* #define F1(x, y, z) (x & y | ~x & z) */ 166 | #define F1(x, y, z) (z ^ (x & (y ^ z))) 167 | #define F2(x, y, z) F1(z, x, y) 168 | #define F3(x, y, z) (x ^ y ^ z) 169 | #define F4(x, y, z) (y ^ (x | ~z)) 170 | 171 | /* This is the central step in the MD5 algorithm. */ 172 | #define MD5STEP(f, w, x, y, z, data, s) \ 173 | ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) 174 | 175 | /* 176 | * The core of the MD5 algorithm, this alters an existing MD5 hash to 177 | * reflect the addition of 16 longwords of new data. MD5Update blocks 178 | * the data and converts bytes into longwords for this routine. 179 | */ 180 | static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) 181 | { 182 | register uint32_t a, b, c, d; 183 | 184 | a = buf[0]; 185 | b = buf[1]; 186 | c = buf[2]; 187 | d = buf[3]; 188 | 189 | MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); 190 | MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); 191 | MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); 192 | MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); 193 | MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); 194 | MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); 195 | MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); 196 | MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); 197 | MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); 198 | MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); 199 | MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); 200 | MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); 201 | MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); 202 | MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); 203 | MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); 204 | MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); 205 | 206 | MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); 207 | MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); 208 | MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); 209 | MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); 210 | MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); 211 | MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); 212 | MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); 213 | MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); 214 | MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); 215 | MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); 216 | MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); 217 | MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); 218 | MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); 219 | MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); 220 | MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); 221 | MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); 222 | 223 | MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); 224 | MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); 225 | MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); 226 | MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); 227 | MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); 228 | MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); 229 | MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); 230 | MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); 231 | MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); 232 | MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); 233 | MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); 234 | MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); 235 | MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); 236 | MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); 237 | MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); 238 | MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); 239 | 240 | MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); 241 | MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); 242 | MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); 243 | MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); 244 | MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); 245 | MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); 246 | MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); 247 | MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); 248 | MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); 249 | MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); 250 | MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); 251 | MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); 252 | MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); 253 | MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); 254 | MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); 255 | MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); 256 | 257 | buf[0] += a; 258 | buf[1] += b; 259 | buf[2] += c; 260 | buf[3] += d; 261 | } 262 | /* ===== end - public domain MD5 implementation ===== */ 263 | -------------------------------------------------------------------------------- /app/src/main/cpp/native-lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern "C" JNIEXPORT jstring JNICALL 5 | Java_com_pxs_terminal_MainActivity_stringFromJNI( 6 | JNIEnv* env, 7 | jobject /* this */) { 8 | std::string hello = "Hello from C++"; 9 | return env->NewStringUTF(hello.c_str()); 10 | } -------------------------------------------------------------------------------- /app/src/main/cpp/termux.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define TERMUX_UNUSED(x) x __attribute__((__unused__)) 14 | #ifdef __APPLE__ 15 | # define LACKS_PTSNAME_R 16 | #endif 17 | 18 | static int throw_runtime_exception(JNIEnv *env, char const *message) { 19 | jclass exClass = (*env)->FindClass(env, "java/lang/RuntimeException"); 20 | (*env)->ThrowNew(env, exClass, message); 21 | return -1; 22 | } 23 | 24 | static int create_subprocess(JNIEnv *env, 25 | char const *cmd, 26 | char const *cwd, 27 | char *const argv[], 28 | char **envp, 29 | int *pProcessId, 30 | jint rows, 31 | jint columns) { 32 | int ptm = open("/dev/ptmx", O_RDWR | O_CLOEXEC); 33 | if (ptm < 0) return throw_runtime_exception(env, "Cannot open /dev/ptmx"); 34 | 35 | #ifdef LACKS_PTSNAME_R 36 | char* devname; 37 | #else 38 | char devname[64]; 39 | #endif 40 | if (grantpt(ptm) || unlockpt(ptm) || 41 | #ifdef LACKS_PTSNAME_R 42 | (devname = ptsname(ptm)) == NULL 43 | #else 44 | ptsname_r(ptm, devname, sizeof(devname)) 45 | #endif 46 | ) { 47 | return throw_runtime_exception(env, "Cannot grantpt()/unlockpt()/ptsname_r() on /dev/ptmx"); 48 | } 49 | 50 | // Enable UTF-8 mode and disable flow control to prevent Ctrl+S from locking up the display. 51 | struct termios tios; 52 | tcgetattr(ptm, &tios); 53 | tios.c_iflag |= IUTF8; 54 | tios.c_iflag &= ~(IXON | IXOFF); 55 | tcsetattr(ptm, TCSANOW, &tios); 56 | 57 | /** Set initial winsize. */ 58 | struct winsize sz = {.ws_row = (unsigned short) rows, .ws_col = (unsigned short) columns}; 59 | ioctl(ptm, TIOCSWINSZ, &sz); 60 | 61 | pid_t pid = fork(); 62 | if (pid < 0) { 63 | return throw_runtime_exception(env, "Fork failed"); 64 | } else if (pid > 0) { 65 | *pProcessId = (int) pid; 66 | return ptm; 67 | } else { 68 | // Clear signals which the Android java process may have blocked: 69 | sigset_t signals_to_unblock; 70 | sigfillset(&signals_to_unblock); 71 | sigprocmask(SIG_UNBLOCK, &signals_to_unblock, 0); 72 | 73 | close(ptm); 74 | setsid(); 75 | 76 | int pts = open(devname, O_RDWR); 77 | if (pts < 0) exit(-1); 78 | 79 | dup2(pts, 0); 80 | dup2(pts, 1); 81 | dup2(pts, 2); 82 | 83 | DIR *self_dir = opendir("/proc/self/fd"); 84 | if (self_dir != NULL) { 85 | int self_dir_fd = dirfd(self_dir); 86 | struct dirent *entry; 87 | while ((entry = readdir(self_dir)) != NULL) { 88 | int fd = atoi(entry->d_name); 89 | if (fd > 2 && fd != self_dir_fd) close(fd); 90 | } 91 | closedir(self_dir); 92 | } 93 | 94 | clearenv(); 95 | if (envp) for (; *envp; ++envp) putenv(*envp); 96 | 97 | if (chdir(cwd) != 0) { 98 | char *error_message; 99 | // No need to free asprintf()-allocated memory since doing execvp() or exit() below. 100 | if (asprintf(&error_message, "chdir(\"%s\")", cwd) == -1) error_message = "chdir()"; 101 | perror(error_message); 102 | fflush(stderr); 103 | } 104 | execvp(cmd, argv); 105 | // Show terminal output about failing exec() call: 106 | char *error_message; 107 | if (asprintf(&error_message, "exec(\"%s\")", cmd) == -1) error_message = "exec()"; 108 | perror(error_message); 109 | _exit(1); 110 | } 111 | } 112 | 113 | JNIEXPORT jint 114 | JNICALL Java_com_pxs_terminal_MainActivity_createSubprocess( 115 | JNIEnv *env, 116 | jclass TERMUX_UNUSED(clazz), 117 | jstring cmd, 118 | jstring cwd, 119 | jobjectArray args, 120 | jobjectArray envVars, 121 | jintArray processIdArray, 122 | jint rows, 123 | jint columns) { 124 | jsize size = args ? (*env)->GetArrayLength(env, args) : 0; 125 | char **argv = NULL; 126 | if (size > 0) { 127 | argv = (char **) malloc((size + 1) * sizeof(char *)); 128 | if (!argv) return throw_runtime_exception(env, "Couldn't allocate argv array"); 129 | for (int i = 0; i < size; ++i) { 130 | jstring arg_java_string = (jstring) (*env)->GetObjectArrayElement(env, args, i); 131 | char const *arg_utf8 = (*env)->GetStringUTFChars(env, arg_java_string, NULL); 132 | if (!arg_utf8) 133 | return throw_runtime_exception(env, "GetStringUTFChars() failed for argv"); 134 | argv[i] = strdup(arg_utf8); 135 | (*env)->ReleaseStringUTFChars(env, arg_java_string, arg_utf8); 136 | } 137 | argv[size] = NULL; 138 | } 139 | 140 | size = envVars ? (*env)->GetArrayLength(env, envVars) : 0; 141 | char **envp = NULL; 142 | if (size > 0) { 143 | envp = (char **) malloc((size + 1) * sizeof(char *)); 144 | if (!envp) return throw_runtime_exception(env, "malloc() for envp array failed"); 145 | for (int i = 0; i < size; ++i) { 146 | jstring env_java_string = (jstring) (*env)->GetObjectArrayElement(env, envVars, i); 147 | char const *env_utf8 = (*env)->GetStringUTFChars(env, env_java_string, 0); 148 | if (!env_utf8) 149 | return throw_runtime_exception(env, "GetStringUTFChars() failed for env"); 150 | envp[i] = strdup(env_utf8); 151 | (*env)->ReleaseStringUTFChars(env, env_java_string, env_utf8); 152 | } 153 | envp[size] = NULL; 154 | } 155 | 156 | int procId = 0; 157 | char const *cmd_cwd = (*env)->GetStringUTFChars(env, cwd, NULL); 158 | char const *cmd_utf8 = (*env)->GetStringUTFChars(env, cmd, NULL); 159 | int ptm = create_subprocess(env, cmd_utf8, cmd_cwd, argv, envp, &procId, rows, columns); 160 | (*env)->ReleaseStringUTFChars(env, cmd, cmd_utf8); 161 | (*env)->ReleaseStringUTFChars(env, cmd, cmd_cwd); 162 | 163 | if (argv) { 164 | for (char **tmp = argv; *tmp; ++tmp) free(*tmp); 165 | free(argv); 166 | } 167 | if (envp) { 168 | for (char **tmp = envp; *tmp; ++tmp) free(*tmp); 169 | free(envp); 170 | } 171 | 172 | int *pProcId = (int *) (*env)->GetPrimitiveArrayCritical(env, processIdArray, NULL); 173 | if (!pProcId) 174 | return throw_runtime_exception(env, 175 | "JNI call GetPrimitiveArrayCritical(processIdArray, &isCopy) failed"); 176 | 177 | *pProcId = procId; 178 | (*env)->ReleasePrimitiveArrayCritical(env, processIdArray, pProcId, 0); 179 | 180 | return ptm; 181 | } 182 | 183 | JNIEXPORT void JNICALL 184 | Java_com_pxs_terminal_MainActivity_setPtyWindowSize(JNIEnv *TERMUX_UNUSED(env), 185 | jclass TERMUX_UNUSED( 186 | clazz), jint 187 | fd, jint rows, jint 188 | cols) { 189 | struct winsize sz = {.ws_row = (unsigned short) rows, .ws_col = (unsigned short) cols}; 190 | ioctl(fd, TIOCSWINSZ, &sz); 191 | } 192 | 193 | JNIEXPORT void JNICALL 194 | Java_com_pxs_terminal_MainActivity_setPtyUTF8Mode(JNIEnv 195 | *TERMUX_UNUSED(env), 196 | jclass TERMUX_UNUSED(clazz), jint 197 | fd) { 198 | struct termios tios; 199 | tcgetattr(fd, 200 | &tios); 201 | if ((tios.c_iflag & IUTF8) == 0) { 202 | tios.c_iflag |= 203 | IUTF8; 204 | tcsetattr(fd, TCSANOW, 205 | &tios); 206 | } 207 | } 208 | 209 | JNIEXPORT jint 210 | 211 | JNICALL 212 | Java_com_pxs_terminal_MainActivity_waitFor(JNIEnv *TERMUX_UNUSED(env), jclass TERMUX_UNUSED(clazz), 213 | jint pid) { 214 | int status; 215 | waitpid(pid, &status, 0); 216 | if (WIFEXITED(status)) { 217 | return WEXITSTATUS(status); 218 | } else if (WIFSIGNALED(status)) { 219 | return -WTERMSIG(status); 220 | } else { 221 | // Should never happen - waitpid(2) says "One of the first three macros will evaluate to a non-zero (true) value". 222 | return 0; 223 | } 224 | } 225 | 226 | JNIEXPORT void JNICALL 227 | Java_com_pxs_terminal_JNI_ESP_close(JNIEnv 228 | *TERMUX_UNUSED(env), 229 | jclass TERMUX_UNUSED(clazz), jint 230 | fileDescriptor) { 231 | close(fileDescriptor); 232 | } 233 | 234 | #include "esp_loader.h" 235 | #include "example_common.h" 236 | #include "linux_port.h" 237 | #include "serial_comm.h" 238 | 239 | JNIEXPORT jint 240 | 241 | JNICALL 242 | Java_com_pxs_terminal_JNI_ESP_LoaderPortLinuxInit(JNIEnv *TERMUX_UNUSED(env), 243 | jclass TERMUX_UNUSED(clazz), 244 | jstring device, 245 | jint baudrate) { 246 | loader_linux_config_t config = {0}; 247 | config.device = (*env)->GetStringUTFChars(env, device, NULL);; 248 | config.baudrate = (uint32_t) baudrate; 249 | return (int) loader_port_linux_init(&config); 250 | } 251 | 252 | JNIEXPORT jint 253 | 254 | JNICALL 255 | Java_com_pxs_terminal_JNI_ESP_ConnectToTarget(JNIEnv *TERMUX_UNUSED(env), 256 | jclass TERMUX_UNUSED(clazz), 257 | jint higrerBaudrate) { 258 | return (int) connect_to_target((uint32_t) higrerBaudrate); 259 | } 260 | 261 | JNIEXPORT void 262 | 263 | JNICALL 264 | Java_com_pxs_terminal_JNI_ESP_ResetTarget(JNIEnv *TERMUX_UNUSED(env), 265 | jclass TERMUX_UNUSED(clazz)) { 266 | loader_port_reset_target(); 267 | } 268 | 269 | JNIEXPORT jint 270 | 271 | JNICALL 272 | Java_com_pxs_terminal_JNI_ESP_EraseFlash(JNIEnv *TERMUX_UNUSED(env), 273 | jclass TERMUX_UNUSED(clazz)) { 274 | return (int) esp_loader_erase_flash(); 275 | } 276 | 277 | JNIEXPORT jint 278 | 279 | JNICALL 280 | Java_com_pxs_terminal_JNI_ESP_FlashBinary(JNIEnv *TERMUX_UNUSED(env), 281 | jclass TERMUX_UNUSED(clazz), 282 | jbyteArray bin, jint address, jobject listener) { 283 | jbyte *cBuf = (*env)->GetByteArrayElements(env, bin, NULL); 284 | jsize len = (*env)->GetArrayLength(env, bin); 285 | loader_port_set_listener(listener); 286 | return (int) flash_binary((const uint8_t *) cBuf, (size_t) len, (size_t) address); 287 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/JNI/ESP.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal.JNI; 2 | 3 | import android.util.Log; 4 | 5 | import com.pxs.terminal.LinkActivity; 6 | 7 | public class ESP { 8 | public static LinkActivity linkActivity = null; 9 | // Used to load the 'terminal' library on application startup. 10 | static { 11 | System.loadLibrary("terminal"); 12 | } 13 | 14 | private static final String TAG = "ESP"; 15 | public enum EspLoaderErr { 16 | ESP_LOADER_SUCCESS, /*!< Success */ 17 | ESP_LOADER_ERROR_FAIL, /*!< Unspecified error */ 18 | ESP_LOADER_ERROR_TIMEOUT, /*!< Timeout elapsed */ 19 | ESP_LOADER_ERROR_IMAGE_SIZE, /*!< Image size to flash is larger than flash size */ 20 | ESP_LOADER_ERROR_INVALID_MD5, /*!< Computed and received MD5 does not match */ 21 | ESP_LOADER_ERROR_INVALID_PARAM, /*!< Invalid parameter passed to function */ 22 | ESP_LOADER_ERROR_INVALID_TARGET, /*!< Connected target is invalid */ 23 | ESP_LOADER_ERROR_UNSUPPORTED_CHIP, /*!< Attached chip is not supported */ 24 | ESP_LOADER_ERROR_UNSUPPORTED_FUNC, /*!< Function is not supported on attached target */ 25 | ESP_LOADER_ERROR_INVALID_RESPONSE /*!< Internal error */ 26 | } 27 | 28 | public interface OnFlashListener { 29 | void OnFlashProcess(int percent); 30 | void OnError(int errCode); 31 | } 32 | 33 | // esp_loader_error_t loader_port_change_baudrate(uint32_t baudrate) 34 | public static int loaderPortChangeBaudrate(int baudrate) { 35 | Log.i(TAG, "loaderPortChangeBaudrate: "); 36 | if(null == linkActivity){ 37 | Log.e(TAG, "loaderPortSerialRead: null activity"); 38 | } 39 | return linkActivity.loaderPortChangeBaudrate(baudrate); 40 | } 41 | 42 | public static void loaderPortResetTarget() { 43 | Log.i(TAG, "loaderPortResetTarget: "); 44 | if(null == linkActivity){ 45 | Log.e(TAG, "loaderPortSerialRead: null activity"); 46 | } 47 | linkActivity.loaderPortResetTarget(); 48 | } 49 | 50 | public static void loaderPortEnterBootloader() { 51 | Log.i(TAG, "loaderPortEnterBootloader: "); 52 | if(null == linkActivity){ 53 | Log.e(TAG, "loaderPortSerialRead: null activity"); 54 | } 55 | linkActivity.loaderPortEnterBootloader(); 56 | } 57 | 58 | public static int loaderPortSerialRead(byte[] data, int size, int timeout) { 59 | Log.d(TAG, "loaderPortSerialRead: "); 60 | if(null == linkActivity){ 61 | Log.e(TAG, "loaderPortSerialRead: null activity"); 62 | return 1; 63 | } 64 | return linkActivity.loaderPortSerialRead(data, size, timeout); 65 | } 66 | 67 | public static int loaderPortSerialWrite(byte[] data, int size, int timeout) { 68 | Log.d(TAG, "loaderPortSerialWrite: "); 69 | if(null == linkActivity){ 70 | Log.e(TAG, "loaderPortSerialRead: null activity"); 71 | return 1; 72 | } 73 | return linkActivity.loaderPortSerialWrite(data, size, timeout); 74 | } 75 | 76 | public static native int LoaderPortLinuxInit(String device, int baudrate); 77 | public static native int ConnectToTarget(int higrerBaudrate); 78 | public static native void ResetTarget(); 79 | public static native int FlashBinary(byte[] bin, int address, OnFlashListener listener); 80 | public static native int EraseFlash(); 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/LogActivity.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.os.Bundle; 6 | import android.widget.TextView; 7 | 8 | public class LogActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_log); 14 | String stack = getIntent().getStringExtra("stack"); 15 | if (stack != null) { 16 | ((TextView) findViewById(R.id.text_log)).setText(stack); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.content.Context; 6 | import android.hardware.usb.UsbDeviceConnection; 7 | import android.hardware.usb.UsbManager; 8 | import android.os.Bundle; 9 | import android.util.Log; 10 | import android.widget.TextView; 11 | 12 | import com.hoho.android.usbserial.driver.UsbSerialPort; 13 | import com.pxs.terminal.databinding.ActivityMainBinding; 14 | import com.pxs.terminal.util.ProcessShell; 15 | 16 | import java.io.BufferedReader; 17 | import java.io.DataInputStream; 18 | import java.io.DataOutputStream; 19 | import java.io.File; 20 | import java.io.FileDescriptor; 21 | import java.io.FileInputStream; 22 | import java.io.FileOutputStream; 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | import java.io.InputStreamReader; 26 | import java.lang.reflect.Field; 27 | import java.nio.charset.StandardCharsets; 28 | import java.util.List; 29 | 30 | import com.hoho.android.usbserial.driver.UsbSerialDriver; 31 | import com.hoho.android.usbserial.driver.UsbSerialProber; 32 | 33 | public class MainActivity extends AppCompatActivity { 34 | 35 | private static final String TAG = "MainActivity"; 36 | 37 | // Used to load the 'terminal' library on application startup. 38 | static { 39 | System.loadLibrary("terminal"); 40 | } 41 | 42 | private ActivityMainBinding binding; 43 | 44 | @Override 45 | protected void onCreate(Bundle savedInstanceState) { 46 | super.onCreate(savedInstanceState); 47 | 48 | binding = ActivityMainBinding.inflate(getLayoutInflater()); 49 | setContentView(binding.getRoot()); 50 | 51 | // Example of a call to a native method 52 | TextView tv = binding.sampleText; 53 | tv.setText(stringFromJNI()); 54 | /* 55 | new File(getFilesDir(), "home").mkdirs(); 56 | runCommand("cp /sdcard/busybox /data/data/com.pxs.terminal/files/home/busybox"); 57 | runCommand("chmod 755 /data/data/com.pxs.terminal/files/home/busybox"); 58 | runCommand("/data/data/com.pxs.terminal/files/home/busybox"); 59 | new Thread(() -> { 60 | runCommand("cd /data/data/com.pxs.terminal/files/home;./busybox telnetd -l /system/bin/sh -p 2121 -F"); 61 | // ProcessShell.exec("cd /data/data/com.pxs.terminal/files/home;./busybox telnetd -l /system/bin/sh -p 2121 -F", false); 62 | runCommand("ps -ef"); 63 | }).start(); 64 | */ 65 | // ProcessShell.exec("cd /data/data/com.pxs.terminal;pwd;./busybox", false); 66 | // createSubprocess(); 67 | /* 68 | new File(getFilesDir(), "home").mkdirs(); 69 | File file = new File("/data/data/com.pxs.terminal/files/home/busybox"); 70 | file.setReadable(true); 71 | file.setWritable(true); 72 | file.setExecutable(true); 73 | */ 74 | // test(); 75 | } 76 | 77 | private void serialStart() 78 | { 79 | } 80 | 81 | // "/data/data/com.termux/files/usr/bin/login", TermuxConstants.TERMUX_HOME_DIR_PATH, new String[]{"-login"}, environment, this 82 | private String mShellPath = "/system/bin/sh"; 83 | private String mCwd = "/data/data/com.pxs.terminal/files/home"; 84 | private String[] mArgs = new String[]{"-"}; 85 | private String[] mEnv = new String[3]; 86 | 87 | private static FileDescriptor wrapFileDescriptor(int fileDescriptor) { 88 | FileDescriptor result = new FileDescriptor(); 89 | try { 90 | Field descriptorField; 91 | try { 92 | descriptorField = FileDescriptor.class.getDeclaredField("descriptor"); 93 | } catch (NoSuchFieldException e) { 94 | // For desktop java: 95 | descriptorField = FileDescriptor.class.getDeclaredField("fd"); 96 | } 97 | descriptorField.setAccessible(true); 98 | descriptorField.set(result, fileDescriptor); 99 | } catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException e) { 100 | Log.e(TAG, "Error accessing FileDescriptor#descriptor private field", e); 101 | System.exit(1); 102 | } 103 | return result; 104 | } 105 | 106 | void sleep(int time) { 107 | try { 108 | Thread.sleep(time); 109 | } catch (InterruptedException e) { 110 | e.printStackTrace(); 111 | } 112 | } 113 | 114 | void test() { 115 | 116 | String path = System.getenv("PATH"); 117 | mEnv[0] = "TERM=" + "screen"; 118 | mEnv[1] = "PATH=" + path; 119 | mEnv[2] = "HOME=" + mCwd; 120 | 121 | int columns = 80; 122 | int rows = 24; 123 | int[] processId = new int[1]; 124 | int mTerminalFileDescriptor = createSubprocess(mShellPath, mCwd, mArgs, mEnv, processId, rows, columns); 125 | int mShellPid = processId[0]; 126 | final FileDescriptor terminalFileDescriptorWrapped = wrapFileDescriptor(mTerminalFileDescriptor); 127 | 128 | new Thread("TermSessionInputReader[pid=" + mShellPid + "]") { 129 | @Override 130 | public void run() { 131 | try (InputStream termIn = new FileInputStream(terminalFileDescriptorWrapped)) { 132 | final byte[] buffer = new byte[4096]; 133 | while (true) { 134 | int read = termIn.read(buffer); 135 | if (read == -1) return; 136 | Log.i(TAG, "run: read:" + new String(buffer, 0, read)); 137 | } 138 | } catch (Exception e) { 139 | // Ignore, just shutting down. 140 | } 141 | } 142 | }.start(); 143 | 144 | new Thread("TermSessionOutputWriter[pid=" + mShellPid + "]") { 145 | @Override 146 | public void run() { 147 | try (FileOutputStream termOut = new FileOutputStream(terminalFileDescriptorWrapped)) { 148 | termOut.write("./busybox telnetd -l /system/bin/sh -p 2121 -F\r".getBytes(StandardCharsets.UTF_8)); 149 | while (true) { 150 | termOut.write("ps -ef\r".getBytes(StandardCharsets.UTF_8)); 151 | MainActivity.this.sleep(3000); 152 | } 153 | } catch (IOException e) { 154 | // Ignore. 155 | } 156 | } 157 | }.start(); 158 | 159 | new Thread("TermSessionWaiter[pid=" + mShellPid + "]") { 160 | @Override 161 | public void run() { 162 | int processExitCode = waitFor(mShellPid); 163 | Log.w(TAG, "run: exit " + processExitCode); 164 | } 165 | }.start(); 166 | } 167 | 168 | /** 169 | * A native method that is implemented by the 'terminal' native library, 170 | * which is packaged with this application. 171 | */ 172 | public native String stringFromJNI(); 173 | 174 | /** 175 | * Create a subprocess. Differs from {@link ProcessBuilder} in that a pseudoterminal is used to communicate with the 176 | * subprocess. 177 | *

178 | * Callers are responsible for calling {@link #close(int)} on the returned file descriptor. 179 | * 180 | * @param cmd The command to execute 181 | * @param cwd The current working directory for the executed command 182 | * @param args An array of arguments to the command 183 | * @param envVars An array of strings of the form "VAR=value" to be added to the environment of the process 184 | * @param processId A one-element array to which the process ID of the started process will be written. 185 | * @return the file descriptor resulting from opening /dev/ptmx master device. The sub process will have opened the 186 | * slave device counterpart (/dev/pts/$N) and have it as stdint, stdout and stderr. 187 | */ 188 | public native int createSubprocess(String cmd, String cwd, String[] args, String[] envVars, int[] processId, int rows, int columns); 189 | 190 | /** 191 | * Set the window size for a given pty, which allows connected programs to learn how large their screen is. 192 | */ 193 | public native void setPtyWindowSize(int fd, int rows, int cols); 194 | 195 | /** 196 | * Causes the calling thread to wait for the process associated with the receiver to finish executing. 197 | * 198 | * @return if >= 0, the exit status of the process. If < 0, the signal causing the process to stop negated. 199 | */ 200 | public native int waitFor(int processId); 201 | 202 | /** 203 | * Close a file descriptor through the close(2) system call. 204 | */ 205 | public native void close(int fileDescriptor); 206 | 207 | /* 208 | public native int LoaderPortLinuxInit(String device, int baudrate); 209 | public native int ConnectToTarget(int higrerBaudrate); 210 | public native void ResetTarget(); 211 | public native int FlashBinary(byte[] bin, int address); 212 | */ 213 | 214 | public static void runCommand(String command) { 215 | Process process = null; 216 | DataOutputStream dataOutputStream = null; 217 | DataInputStream dataInputStream = null; 218 | DataInputStream dataErrStream = null; 219 | StringBuffer wifiConf = new StringBuffer(); 220 | try { 221 | process = Runtime.getRuntime().exec("sh -"); 222 | dataOutputStream = new DataOutputStream(process.getOutputStream()); 223 | dataInputStream = new DataInputStream(process.getInputStream()); 224 | dataErrStream = new DataInputStream(process.getErrorStream()); 225 | dataOutputStream 226 | .writeBytes(command + "\n"); 227 | dataOutputStream.flush(); 228 | dataOutputStream.writeBytes("exit\n"); 229 | dataOutputStream.flush(); 230 | InputStreamReader inputStreamReader = new InputStreamReader( 231 | dataInputStream, "UTF-8"); 232 | BufferedReader bufferedReader = new BufferedReader( 233 | inputStreamReader); 234 | String line = null; 235 | while ((line = bufferedReader.readLine()) != null) { 236 | wifiConf.append(line); 237 | } 238 | bufferedReader.close(); 239 | inputStreamReader.close(); 240 | bufferedReader = null; 241 | inputStreamReader = null; 242 | inputStreamReader = new InputStreamReader( 243 | dataErrStream, "UTF-8"); 244 | bufferedReader = new BufferedReader( 245 | inputStreamReader); 246 | line = null; 247 | while ((line = bufferedReader.readLine()) != null) { 248 | wifiConf.append(line + "\n"); 249 | } 250 | bufferedReader.close(); 251 | inputStreamReader.close(); 252 | process.waitFor(); 253 | Log.d("shell命令执行结果:", wifiConf.toString() + " " + process.exitValue() + ""); 254 | } catch (Exception e) { 255 | e.printStackTrace(); 256 | } finally { 257 | try { 258 | if (dataOutputStream != null) { 259 | dataOutputStream.close(); 260 | } 261 | if (dataInputStream != null) { 262 | dataInputStream.close(); 263 | } 264 | process.destroy(); 265 | } catch (Exception e) { 266 | e.printStackTrace(); 267 | } 268 | } 269 | } 270 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal; 2 | 3 | import android.app.Application; 4 | 5 | public class MyApplication extends Application { 6 | @Override 7 | public void onCreate() { 8 | super.onCreate(); 9 | Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler(getApplicationContext())); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/MyUncaughtExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal; 2 | 3 | import android.app.AlarmManager; 4 | import android.app.PendingIntent; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.system.Os; 8 | 9 | import androidx.annotation.NonNull; 10 | 11 | import java.io.PrintWriter; 12 | import java.io.StringWriter; 13 | import java.io.Writer; 14 | 15 | public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { 16 | private Context context; 17 | 18 | public MyUncaughtExceptionHandler(Context context) { 19 | this.context = context; 20 | } 21 | 22 | @Override 23 | public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) { 24 | final Writer result = new StringWriter(); 25 | final PrintWriter printWriter = new PrintWriter(result); 26 | //如果异常时在AsyncTask里面的后台线程抛出的 27 | //那么实际的异常仍然可以通过getCause获得 28 | Throwable cause = throwable; 29 | while (null != cause) { 30 | cause.printStackTrace(printWriter); 31 | cause = cause.getCause(); 32 | } 33 | //stacktraceAsString就是获取的carsh堆栈信息 34 | final String stacktraceAsString = result.toString(); 35 | printWriter.close(); 36 | 37 | Intent intent = new Intent(context, LogActivity.class); 38 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 39 | intent.putExtra("stack", stacktraceAsString); 40 | PendingIntent pi = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); 41 | AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 42 | manager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 3 * 1000, pi); 43 | android.os.Process.killProcess(android.os.Process.myPid()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal; 2 | 3 | import android.os.Bundle; 4 | import android.view.MenuItem; 5 | 6 | import androidx.appcompat.app.ActionBar; 7 | import androidx.appcompat.app.AppCompatActivity; 8 | import androidx.preference.PreferenceFragmentCompat; 9 | 10 | public class SettingsActivity extends AppCompatActivity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.settings_activity); 16 | if (savedInstanceState == null) { 17 | getSupportFragmentManager() 18 | .beginTransaction() 19 | .replace(R.id.settings, new SettingsFragment()) 20 | .commit(); 21 | } 22 | ActionBar actionBar = getSupportActionBar(); 23 | if (actionBar != null) { 24 | actionBar.setDisplayHomeAsUpEnabled(true); 25 | actionBar.setHomeButtonEnabled(true); 26 | } 27 | } 28 | 29 | public static class SettingsFragment extends PreferenceFragmentCompat { 30 | @Override 31 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 32 | setPreferencesFromResource(R.xml.root_preferences, rootKey); 33 | } 34 | } 35 | 36 | public boolean onOptionsItemSelected(MenuItem item) 37 | { 38 | // TODO Auto-generated method stub 39 | //android.R.id.home对应应用程序图标的id 40 | if(item.getItemId() == android.R.id.home) 41 | { 42 | finish(); 43 | return true; 44 | } 45 | return super.onOptionsItemSelected(item); 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/ui/gallery/GalleryFragment.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal.ui.gallery; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.AdapterView; 9 | import android.widget.ArrayAdapter; 10 | import android.widget.ListView; 11 | import android.widget.RadioButton; 12 | import android.widget.TextView; 13 | import android.widget.Toast; 14 | 15 | import androidx.annotation.NonNull; 16 | import androidx.annotation.Nullable; 17 | import androidx.fragment.app.Fragment; 18 | import androidx.lifecycle.Observer; 19 | import androidx.lifecycle.ViewModelProvider; 20 | 21 | import com.pxs.terminal.LinkActivity; 22 | import com.pxs.terminal.R; 23 | import com.pxs.terminal.SerialService; 24 | import com.pxs.terminal.databinding.FragmentGalleryBinding; 25 | import com.pxs.terminal.ui.home.HomeFragment; 26 | import com.pxs.terminal.util.MyUtil; 27 | 28 | import java.util.ArrayList; 29 | import java.util.Locale; 30 | 31 | public class GalleryFragment extends Fragment { 32 | 33 | private GalleryViewModel galleryViewModel; 34 | private FragmentGalleryBinding binding; 35 | private ListView listView; 36 | private ArrayAdapter listAdapter; 37 | 38 | public View onCreateView(@NonNull LayoutInflater inflater, 39 | ViewGroup container, Bundle savedInstanceState) { 40 | galleryViewModel = 41 | new ViewModelProvider(this).get(GalleryViewModel.class); 42 | 43 | binding = FragmentGalleryBinding.inflate(inflater, container, false); 44 | View root = binding.getRoot(); 45 | 46 | final TextView textView = binding.textGallery; 47 | galleryViewModel.getText().observe(getViewLifecycleOwner(), new Observer() { 48 | @Override 49 | public void onChanged(@Nullable String s) { 50 | textView.setText(s); 51 | } 52 | }); 53 | listView = binding.listDevices; 54 | listAdapter = new ArrayAdapter(getActivity(), 0, galleryViewModel.getListItems().getValue()) { 55 | @NonNull 56 | @Override 57 | public View getView(int position, View view, @NonNull ViewGroup parent) { 58 | GalleryViewModel.ListItem item = galleryViewModel.getListItems().getValue().get(position); 59 | if (view == null) 60 | view = getActivity().getLayoutInflater().inflate(R.layout.device_list_item, parent, false); 61 | TextView text1 = view.findViewById(R.id.text1); 62 | TextView text2 = view.findViewById(R.id.text2); 63 | if (item.driver == null) 64 | text1.setText(""); 65 | else if (item.driver.getPorts().size() == 1) 66 | text1.setText(item.driver.getClass().getSimpleName().replace("SerialDriver", "")); 67 | else 68 | text1.setText(item.driver.getClass().getSimpleName().replace("SerialDriver", "") + ", Port " + item.port); 69 | text1.append(" " + SerialService.getInstance().getSerialInfo(item.device.getDeviceId())); 70 | text2.setText(String.format(Locale.US, "Vendor %04X, Product %04X", item.device.getVendorId(), item.device.getProductId())); 71 | if (galleryViewModel.getChoosePosition() == position) { 72 | ((RadioButton) view.findViewById(R.id.radioButton)).setChecked(true); 73 | } else { 74 | ((RadioButton) view.findViewById(R.id.radioButton)).setChecked(false); 75 | } 76 | return view; 77 | } 78 | }; 79 | listView.setAdapter(listAdapter); 80 | galleryViewModel.getListItems().observe(getViewLifecycleOwner(), listItems -> { 81 | if (listItems.size() == 0) { 82 | textView.setVisibility(View.VISIBLE); 83 | } else { 84 | textView.setVisibility(View.INVISIBLE); 85 | } 86 | listAdapter.notifyDataSetChanged(); 87 | }); 88 | galleryViewModel.refresh(getActivity()); 89 | listView.setOnItemClickListener((adapterView, view, i, l) -> { 90 | galleryViewModel.setChoosePosition(i); 91 | GalleryViewModel.ListItem item = galleryViewModel.getListItems().getValue().get(i); 92 | // ((LinkActivity)getActivity()).deviceId = item.device.getDeviceId(); 93 | // ((LinkActivity)getActivity()).port = item.port; 94 | connect(i, item.device.getDeviceId(), item.port); 95 | }); 96 | int nettype = MyUtil.getNetWorkState(getContext()); 97 | switch (nettype) { 98 | case -1: 99 | binding.textIp.append("无网络连接\n本地IP:127.0.0.1"); 100 | break; 101 | case 0: 102 | binding.textIp.append("移动网络连接\n本地IP:127.0.0.1\n移动IP:" + MyUtil.getNetWorkState(getContext())); 103 | break; 104 | case 1: 105 | binding.textIp.append("WIFI网络连接\n本地IP:127.0.0.1\n局域网IP:" + MyUtil.getLocalIPAddress(getContext())); 106 | break; 107 | default: 108 | break; 109 | } 110 | return root; 111 | } 112 | 113 | private void connect(final int index, int deviceId, int port) { 114 | ((LinkActivity) getActivity()).listener = new HomeFragment.OnDeivceLinkListener() { 115 | @Override 116 | public void connected() { 117 | SerialService.getInstance().startServer(((LinkActivity) getActivity()).getSerial(), 10010 + index); 118 | ((LinkActivity) getActivity()).runOnUiThread(() -> listAdapter.notifyDataSetChanged()); 119 | ((LinkActivity) getActivity()).listener = null; 120 | } 121 | 122 | @Override 123 | public void disConnected() { 124 | 125 | } 126 | }; 127 | if (!SerialService.getInstance().isSerialStart(deviceId)) { 128 | ((LinkActivity) getActivity()).connect(deviceId, port, 115200); 129 | } else { 130 | SerialService.getInstance().stopServer(deviceId); 131 | Toast.makeText(getContext(), "服务停止", Toast.LENGTH_SHORT).show(); 132 | } 133 | } 134 | 135 | @Override 136 | public void onDestroyView() { 137 | super.onDestroyView(); 138 | binding = null; 139 | } 140 | 141 | private Handler handler = new Handler(); 142 | private Runnable deviceRunnable = null; 143 | 144 | @Override 145 | public void onResume() { 146 | super.onResume(); 147 | if (deviceRunnable == null) { 148 | deviceRunnable = () -> { 149 | galleryViewModel.refresh(getActivity()); 150 | handler.postDelayed(deviceRunnable, 500); 151 | }; 152 | } 153 | handler.postDelayed(deviceRunnable, 500); 154 | SerialService.getInstance().setStateChanged(() -> getActivity().runOnUiThread(() -> listAdapter.notifyDataSetChanged())); 155 | } 156 | 157 | @Override 158 | public void onPause() { 159 | handler.removeCallbacks(deviceRunnable); 160 | SerialService.getInstance().setStateChanged(null); 161 | super.onPause(); 162 | } 163 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/ui/gallery/GalleryViewModel.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal.ui.gallery; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.hardware.usb.UsbDevice; 6 | import android.hardware.usb.UsbManager; 7 | 8 | import androidx.lifecycle.LiveData; 9 | import androidx.lifecycle.MutableLiveData; 10 | import androidx.lifecycle.ViewModel; 11 | 12 | import com.hoho.android.usbserial.driver.UsbSerialDriver; 13 | import com.hoho.android.usbserial.driver.UsbSerialProber; 14 | import com.pxs.terminal.util.CustomProber; 15 | 16 | import java.util.ArrayList; 17 | 18 | public class GalleryViewModel extends ViewModel { 19 | 20 | private MutableLiveData mText; 21 | private final MutableLiveData> listItems = new MutableLiveData<>(); 22 | private int choosePosition = 0; 23 | 24 | static class ListItem { 25 | UsbDevice device; 26 | int port; 27 | UsbSerialDriver driver; 28 | 29 | ListItem(UsbDevice device, int port, UsbSerialDriver driver) { 30 | this.device = device; 31 | this.port = port; 32 | this.driver = driver; 33 | } 34 | } 35 | 36 | public GalleryViewModel() { 37 | mText = new MutableLiveData<>(); 38 | mText.setValue("设备列表为空"); 39 | listItems.setValue(new ArrayList<>()); 40 | } 41 | 42 | void refresh(Context activity) { 43 | if (null == activity) { 44 | return; 45 | } 46 | UsbManager usbManager = (UsbManager) activity.getSystemService(Context.USB_SERVICE); 47 | UsbSerialProber usbDefaultProber = UsbSerialProber.getDefaultProber(); 48 | UsbSerialProber usbCustomProber = CustomProber.getCustomProber(); 49 | listItems.getValue().clear(); 50 | for (UsbDevice device : usbManager.getDeviceList().values()) { 51 | UsbSerialDriver driver = usbDefaultProber.probeDevice(device); 52 | if (driver == null) { 53 | driver = usbCustomProber.probeDevice(device); 54 | } 55 | if (driver != null) { 56 | for (int port = 0; port < driver.getPorts().size(); port++) 57 | listItems.getValue().add(new ListItem(device, port, driver)); 58 | } else { 59 | listItems.getValue().add(new ListItem(device, 0, null)); 60 | } 61 | } 62 | listItems.postValue(listItems.getValue()); 63 | } 64 | 65 | public LiveData getText() { 66 | return mText; 67 | } 68 | 69 | public MutableLiveData> getListItems() { 70 | return listItems; 71 | } 72 | 73 | public int getChoosePosition() { 74 | return choosePosition; 75 | } 76 | 77 | public void setChoosePosition(int choosePosition) { 78 | this.choosePosition = choosePosition; 79 | } 80 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/ui/home/HomeViewModel.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal.ui.home; 2 | 3 | import androidx.lifecycle.LiveData; 4 | import androidx.lifecycle.MutableLiveData; 5 | import androidx.lifecycle.ViewModel; 6 | 7 | public class HomeViewModel extends ViewModel { 8 | 9 | private MutableLiveData mText; 10 | 11 | public HomeViewModel() { 12 | mText = new MutableLiveData<>(); 13 | mText.setValue("This is home fragment"); 14 | } 15 | 16 | public LiveData getText() { 17 | return mText; 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/ui/slideshow/SlideshowFragment.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal.ui.slideshow; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.TextView; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.annotation.Nullable; 11 | import androidx.fragment.app.Fragment; 12 | import androidx.lifecycle.Observer; 13 | import androidx.lifecycle.ViewModelProvider; 14 | 15 | import com.pxs.terminal.R; 16 | import com.pxs.terminal.databinding.FragmentSlideshowBinding; 17 | 18 | public class SlideshowFragment extends Fragment { 19 | 20 | private SlideshowViewModel slideshowViewModel; 21 | private FragmentSlideshowBinding binding; 22 | 23 | public View onCreateView(@NonNull LayoutInflater inflater, 24 | ViewGroup container, Bundle savedInstanceState) { 25 | slideshowViewModel = 26 | new ViewModelProvider(this).get(SlideshowViewModel.class); 27 | 28 | binding = FragmentSlideshowBinding.inflate(inflater, container, false); 29 | View root = binding.getRoot(); 30 | 31 | final TextView textView = binding.textSlideshow; 32 | slideshowViewModel.getText().observe(getViewLifecycleOwner(), new Observer() { 33 | @Override 34 | public void onChanged(@Nullable String s) { 35 | textView.setText(s); 36 | } 37 | }); 38 | return root; 39 | } 40 | 41 | @Override 42 | public void onDestroyView() { 43 | super.onDestroyView(); 44 | binding = null; 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/ui/slideshow/SlideshowViewModel.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal.ui.slideshow; 2 | 3 | import androidx.lifecycle.LiveData; 4 | import androidx.lifecycle.MutableLiveData; 5 | import androidx.lifecycle.ViewModel; 6 | 7 | public class SlideshowViewModel extends ViewModel { 8 | 9 | private MutableLiveData mText; 10 | 11 | public SlideshowViewModel() { 12 | mText = new MutableLiveData<>(); 13 | mText.setValue("开发中..."); 14 | } 15 | 16 | public LiveData getText() { 17 | return mText; 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/util/AbstractShell.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal.util; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * Created by Stardust on 2017/4/24. 7 | */ 8 | 9 | public abstract class AbstractShell { 10 | 11 | public static class Result { 12 | public int code = -1; 13 | public String error; 14 | public String result; 15 | 16 | @Override 17 | public String toString() { 18 | return "ShellResult{" + 19 | "code=" + code + 20 | ", error='" + error + '\'' + 21 | ", result='" + result + '\'' + 22 | '}'; 23 | } 24 | } 25 | 26 | protected static final String COMMAND_SU = "su"; 27 | protected static final String COMMAND_SH = "sh"; 28 | protected static final String COMMAND_EXIT = "exit\n"; 29 | protected static final String COMMAND_LINE_END = "\n"; 30 | 31 | 32 | private int mTouchDevice = -1; 33 | // private ScreenMetrics mScreenMetrics; 34 | 35 | private boolean mRoot; 36 | protected Context mContext; 37 | 38 | public AbstractShell() { 39 | this(false); 40 | } 41 | 42 | public AbstractShell(boolean root) { 43 | this(null, root); 44 | } 45 | 46 | public AbstractShell(Context context, boolean root) { 47 | mContext = context; 48 | mRoot = root; 49 | /* 50 | if (context != null) 51 | mTouchDevice = RootAutomatorEngine.getTouchDevice(context); 52 | */ 53 | init(root ? COMMAND_SU : COMMAND_SH); 54 | } 55 | 56 | public boolean isRoot() { 57 | return mRoot; 58 | } 59 | 60 | protected abstract void init(String initialCommand); 61 | 62 | public abstract void exec(String command); 63 | 64 | public abstract void exit(); 65 | 66 | public void SetTouchDevice(int touchDevice) { 67 | if (mTouchDevice > 0) 68 | return; 69 | mTouchDevice = touchDevice; 70 | } 71 | 72 | public void SendEvent(int type, int code, int value) { 73 | SendEvent(mTouchDevice, type, code, value); 74 | } 75 | 76 | public void SendEvent(int device, int type, int code, int value) { 77 | exec(TextUtils.join("", new Object[]{"sendevent /dev/input/event", device, " ", type, " ", code, " ", value})); 78 | } 79 | 80 | public void SetScreenMetrics(int width, int height) { 81 | /* 82 | if (mScreenMetrics == null) { 83 | mScreenMetrics = new ScreenMetrics(); 84 | } 85 | mScreenMetrics.setScreenMetrics(width, height); 86 | */ 87 | } 88 | 89 | /* 90 | public void SetScreenMetrics(ScreenMetrics screenMetrics) { 91 | mScreenMetrics = screenMetrics; 92 | } 93 | 94 | 95 | public void Touch(int x, int y) { 96 | TouchX(x); 97 | TouchY(y); 98 | } 99 | 100 | public void TouchX(int x) { 101 | SendEvent(mTouchDevice, 3, 53, scaleX(x)); 102 | } 103 | */ 104 | /* 105 | private int scaleX(int x) { 106 | if (mScreenMetrics == null) 107 | return x; 108 | return mScreenMetrics.scaleX(x); 109 | } 110 | 111 | 112 | public void TouchY(int y) { 113 | SendEvent(mTouchDevice, 3, 54, scaleY(y)); 114 | } 115 | 116 | private int scaleY(int y) { 117 | if (mScreenMetrics == null) 118 | return y; 119 | return mScreenMetrics.scaleY(y); 120 | } 121 | 122 | public void Tap(int x, int y) { 123 | exec("input tap " + scaleX(x) + " " + scaleY(y)); 124 | } 125 | 126 | public void Swipe(int x1, int y1, int x2, int y2) { 127 | exec(TextUtils.join(" ", "input", "swipe", scaleX(x1), scaleY(y1), scaleX(x2), scaleY(y2))); 128 | } 129 | 130 | public void Swipe(int x1, int y1, int x2, int y2, int time) { 131 | exec(TextUtils.join(" ", "input", "swipe", scaleX(x1), scaleY(y1), scaleX(x2), scaleY(y2), time)); 132 | } 133 | */ 134 | public void KeyCode(int keyCode) { 135 | exec("input keyevent " + keyCode); 136 | } 137 | 138 | public void KeyCode(String keyCode) { 139 | exec("input keyevent " + keyCode); 140 | } 141 | 142 | public void Home() { 143 | KeyCode(3); 144 | } 145 | 146 | public void Back() { 147 | KeyCode(4); 148 | } 149 | 150 | public void Power() { 151 | KeyCode(26); 152 | } 153 | 154 | public void Up() { 155 | KeyCode(19); 156 | } 157 | 158 | public void Down() { 159 | KeyCode(20); 160 | } 161 | 162 | public void Left() { 163 | KeyCode(21); 164 | } 165 | 166 | public void Right() { 167 | KeyCode(22); 168 | } 169 | 170 | public void OK() { 171 | KeyCode(23); 172 | } 173 | 174 | public void VolumeUp() { 175 | KeyCode(24); 176 | } 177 | 178 | public void VolumeDown() { 179 | KeyCode(25); 180 | } 181 | 182 | public void Menu() { 183 | KeyCode(1); 184 | } 185 | 186 | public void Camera() { 187 | KeyCode(27); 188 | } 189 | 190 | public void Input(String text) { 191 | exec("input text " + text); 192 | } 193 | 194 | public void Screencap(String path) { 195 | exec("screencap -p " + path); 196 | } 197 | 198 | public void Text(String text) { 199 | Input(text); 200 | } 201 | 202 | public abstract void exitAndWaitFor(); 203 | 204 | public void sleep(long i) { 205 | exec("sleep " + i); 206 | } 207 | 208 | public void usleep(long l) { 209 | exec("usleep " + l); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/util/CustomProber.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal.util; 2 | 3 | import com.hoho.android.usbserial.driver.CdcAcmSerialDriver; 4 | import com.hoho.android.usbserial.driver.ProbeTable; 5 | import com.hoho.android.usbserial.driver.UsbSerialProber; 6 | 7 | /** 8 | * add devices here, that are not known to DefaultProber 9 | * 10 | * if the App should auto start for these devices, also 11 | * add IDs to app/src/main/res/xml/device_filter.xml 12 | */ 13 | public class CustomProber { 14 | 15 | public static UsbSerialProber getCustomProber() { 16 | ProbeTable customTable = new ProbeTable(); 17 | customTable.addProduct(0x16d0, 0x087e, CdcAcmSerialDriver.class); // e.g. Digispark CDC 18 | return new UsbSerialProber(customTable); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/util/MyUtil.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal.util; 2 | 3 | import android.content.Context; 4 | import android.net.ConnectivityManager; 5 | import android.net.NetworkInfo; 6 | import android.net.wifi.WifiInfo; 7 | import android.net.wifi.WifiManager; 8 | 9 | import java.net.Inet4Address; 10 | import java.net.InetAddress; 11 | import java.net.NetworkInterface; 12 | import java.net.SocketException; 13 | import java.util.Enumeration; 14 | 15 | public class MyUtil { 16 | /** 17 | * @param context 18 | * @return int 19 | * @Title: getNetWorkState 20 | * @Description: 获取当前网络状态 21 | */ 22 | public static int getNetWorkState(Context context) { 23 | final int network_none = -1;// 没有连接网络 24 | 25 | final int network_mobile = 0;// 移动网络 26 | 27 | final int network_wifi = 1;// 无线网络 28 | 29 | // 得到连接管理器对象 30 | ConnectivityManager connectivityManager = (ConnectivityManager) context 31 | .getSystemService(Context.CONNECTIVITY_SERVICE); 32 | 33 | NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); 34 | if (activeNetworkInfo != null && activeNetworkInfo.isConnected()) { 35 | 36 | if (activeNetworkInfo.getType() == (ConnectivityManager.TYPE_WIFI)) { 37 | return network_wifi; 38 | } else if (activeNetworkInfo.getType() == (ConnectivityManager.TYPE_MOBILE)) { 39 | return network_mobile; 40 | } 41 | } else { 42 | return network_none; 43 | } 44 | return network_none; 45 | } 46 | 47 | /** 48 | * @return String 49 | * @Title: getIpAddress 50 | * @Description: 获取设备ip地址 51 | */ 52 | public static String getIpAddress() { 53 | try { 54 | for (Enumeration enNetI = NetworkInterface.getNetworkInterfaces(); enNetI 55 | .hasMoreElements(); ) { 56 | NetworkInterface netI = enNetI.nextElement(); 57 | for (Enumeration enumIpAddr = netI.getInetAddresses(); enumIpAddr.hasMoreElements(); ) { 58 | InetAddress inetAddress = enumIpAddr.nextElement(); 59 | if (inetAddress instanceof Inet4Address && !inetAddress.isLoopbackAddress()) { 60 | return inetAddress.getHostAddress(); 61 | } 62 | } 63 | } 64 | } catch (SocketException e) { 65 | e.printStackTrace(); 66 | } 67 | return ""; 68 | } 69 | 70 | 71 | //将获取的int转为真正的ip地址,参考的网上的,修改了下 72 | private static String intToInet(int i) { 73 | return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF) + "." + ((i >> 24) & 0xFF); 74 | } 75 | 76 | // wifi下获取本地网络IP地址(局域网地址) 77 | public static String getLocalIPAddress(Context context) { 78 | WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 79 | if (wifiManager != null) { 80 | WifiInfo wifiInfo = wifiManager.getConnectionInfo(); 81 | String ipAddress = intToInet(wifiInfo.getIpAddress()); 82 | return ipAddress; 83 | } 84 | return ""; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/util/ProcessShell.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal.util; 2 | 3 | import android.util.Log; 4 | import java.io.BufferedReader; 5 | import java.io.DataOutputStream; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.InputStreamReader; 9 | import java.io.UncheckedIOException; 10 | 11 | /** 12 | * Created by Stardust on 2017/1/20. 13 | *

14 | * 来自网络~~ 15 | */ 16 | 17 | public class ProcessShell extends AbstractShell { 18 | 19 | private static final String TAG = "ProcessShell"; 20 | 21 | private Process mProcess; 22 | private DataOutputStream mCommandOutputStream; 23 | private BufferedReader mSucceedReader; 24 | private BufferedReader mErrorReader; 25 | 26 | private StringBuilder mSucceedOutput = new StringBuilder(); 27 | private StringBuilder mErrorOutput = new StringBuilder(); 28 | 29 | public ProcessShell() { 30 | 31 | } 32 | 33 | public ProcessShell(boolean root) { 34 | super(root); 35 | } 36 | 37 | @Override 38 | protected void init(String initialCommand) { 39 | try { 40 | mProcess = new ProcessBuilder(initialCommand).redirectErrorStream(true).start(); 41 | mCommandOutputStream = new DataOutputStream(mProcess.getOutputStream()); 42 | mSucceedReader = new BufferedReader(new InputStreamReader(mProcess.getInputStream())); 43 | mErrorReader = new BufferedReader(new InputStreamReader(mProcess.getErrorStream())); 44 | } catch (IOException e) { 45 | e.printStackTrace(); 46 | } 47 | } 48 | 49 | @Override 50 | public void exec(String command) { 51 | try { 52 | mCommandOutputStream.writeBytes(command); 53 | if (!command.endsWith(COMMAND_LINE_END)) { 54 | mCommandOutputStream.writeBytes(COMMAND_LINE_END); 55 | } 56 | mCommandOutputStream.flush(); 57 | } catch (IOException e) { 58 | throw new RuntimeException(e); 59 | } 60 | } 61 | 62 | @Override 63 | public void exit() { 64 | if (mProcess != null) { 65 | Log.d(TAG, "exit: pid = " + ProcessUtils.getProcessPid(mProcess)); 66 | mProcess.destroy(); 67 | mProcess = null; 68 | } 69 | if (mSucceedReader != null) { 70 | try { 71 | mSucceedReader.close(); 72 | } catch (IOException ignored) { 73 | 74 | } 75 | mSucceedReader = null; 76 | } 77 | if (mErrorReader != null) { 78 | try { 79 | mErrorReader.close(); 80 | } catch (IOException ignored) { 81 | 82 | } 83 | mErrorReader = null; 84 | } 85 | 86 | } 87 | 88 | @Override 89 | public void exitAndWaitFor() { 90 | exec(COMMAND_EXIT); 91 | waitFor(); 92 | exit(); 93 | } 94 | 95 | public int waitFor() { 96 | try { 97 | return mProcess.waitFor(); 98 | } catch (InterruptedException e) { 99 | throw new RuntimeException(e); 100 | } 101 | } 102 | 103 | public ProcessShell readAll() { 104 | return readSucceedOutput().readErrorOutput(); 105 | } 106 | 107 | public ProcessShell readSucceedOutput() { 108 | read(mSucceedReader, mSucceedOutput); 109 | return this; 110 | } 111 | 112 | private void read(BufferedReader reader, StringBuilder sb) { 113 | try { 114 | String line; 115 | while ((line = reader.readLine()) != null) { 116 | sb.append(line).append("\n"); 117 | } 118 | } catch (IOException e) { 119 | e.printStackTrace(); 120 | } 121 | } 122 | 123 | public ProcessShell readErrorOutput() { 124 | read(mErrorReader, mErrorOutput); 125 | return this; 126 | } 127 | 128 | public StringBuilder getSucceedOutput() { 129 | return mSucceedOutput; 130 | } 131 | 132 | public StringBuilder getErrorOutput() { 133 | return mErrorOutput; 134 | } 135 | 136 | public Process getProcess() { 137 | return mProcess; 138 | } 139 | 140 | public BufferedReader getSucceedReader() { 141 | return mSucceedReader; 142 | } 143 | 144 | public BufferedReader getErrorReader() { 145 | return mErrorReader; 146 | } 147 | 148 | public static Result exec(String command, boolean isRoot) { 149 | String[] commands = command.split("\n"); 150 | return exec(commands, isRoot); 151 | } 152 | 153 | public static Result exec(String[] commands, boolean isRoot) { 154 | ProcessShell shell = null; 155 | try { 156 | shell = new ProcessShell(isRoot); 157 | for (String command : commands) { 158 | shell.exec(command); 159 | } 160 | shell.exec(COMMAND_EXIT); 161 | Result result = new Result(); 162 | result.code = shell.waitFor(); 163 | shell.readAll(); 164 | result.error = shell.getErrorOutput().toString(); 165 | result.result = shell.getSucceedOutput().toString(); 166 | shell.exit(); 167 | return result; 168 | } finally { 169 | if (shell != null) { 170 | shell.exit(); 171 | } 172 | } 173 | } 174 | 175 | public static Result execCommand(String[] commands, boolean isRoot) { 176 | Result commandResult = new Result(); 177 | if (commands == null || commands.length == 0) 178 | throw new IllegalArgumentException("command is empty"); 179 | Process process = null; 180 | DataOutputStream os = null; 181 | try { 182 | process = Runtime.getRuntime().exec(isRoot ? COMMAND_SU : COMMAND_SH); 183 | os = new DataOutputStream(process.getOutputStream()); 184 | for (String command : commands) { 185 | if (command != null) { 186 | os.write(command.getBytes()); 187 | os.writeBytes(COMMAND_LINE_END); 188 | os.flush(); 189 | } 190 | } 191 | os.writeBytes(COMMAND_EXIT); 192 | os.flush(); 193 | Log.d(TAG, "pid = " + ProcessUtils.getProcessPid(process)); 194 | commandResult.code = process.waitFor(); 195 | commandResult.result = readAll(process.getInputStream()); 196 | commandResult.error = readAll(process.getErrorStream()); 197 | Log.d(TAG, commandResult.toString()); 198 | } catch (Exception e) { 199 | e.printStackTrace(); 200 | } finally { 201 | try { 202 | if (os != null) os.close(); 203 | if (process != null) { 204 | process.getInputStream().close(); 205 | process.getOutputStream().close(); 206 | } 207 | } catch (IOException ignored) { 208 | 209 | } 210 | if (process != null) { 211 | process.destroy(); 212 | } 213 | } 214 | return commandResult; 215 | } 216 | 217 | private static String readAll(InputStream inputStream) throws IOException { 218 | String line; 219 | StringBuilder builder = new StringBuilder(); 220 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); 221 | while ((line = reader.readLine()) != null) { 222 | builder.append(line).append('\n'); 223 | } 224 | return builder.toString(); 225 | } 226 | 227 | public static Result execCommand(String command, boolean isRoot) { 228 | String[] commands = command.split("\n"); 229 | return execCommand(commands, isRoot); 230 | } 231 | 232 | 233 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/util/ProcessUtils.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal.util; 2 | 3 | import android.util.Log; 4 | import java.lang.reflect.Field; 5 | 6 | /** 7 | * Created by Stardust on 2017/8/3. 8 | */ 9 | 10 | public class ProcessUtils { 11 | 12 | 13 | private static final String LOG_TAG = "ProcessUtils"; 14 | 15 | public static int getProcessPid(Process process) { 16 | try { 17 | Field pid = process.getClass().getDeclaredField("pid"); 18 | pid.setAccessible(true); 19 | return (int) pid.get(process); 20 | } catch (Exception e) { 21 | e.printStackTrace(); 22 | return -1; 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/pxs/terminal/util/TextUtils.java: -------------------------------------------------------------------------------- 1 | package com.pxs.terminal.util; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | /** 6 | * Created by Stardust on 2017/5/3. 7 | */ 8 | 9 | public class TextUtils { 10 | 11 | public static String join(CharSequence delimiter, Object... tokens) { 12 | return android.text.TextUtils.join(delimiter, tokens); 13 | } 14 | 15 | 16 | @NonNull 17 | public static String toEmptyIfNull(String message) { 18 | if (message == null) 19 | return ""; 20 | return message; 21 | } 22 | 23 | public static int lastIndexOf(CharSequence text, char ch, int fromIndex) { 24 | if (text instanceof String) { 25 | return ((String) text).lastIndexOf(ch, fromIndex); 26 | } 27 | int i = Math.min(fromIndex, text.length() - 1); 28 | for (; i >= 0; i--) { 29 | if (text.charAt(i) == ch) { 30 | return i; 31 | } 32 | } 33 | return -1; 34 | 35 | } 36 | 37 | public static int indexOf(CharSequence text, char ch, int fromIndex) { 38 | if (text instanceof String) { 39 | return ((String) text).indexOf(ch, fromIndex); 40 | } 41 | final int max = text.length(); 42 | if (fromIndex < 0) { 43 | fromIndex = 0; 44 | } else if (fromIndex >= max) { 45 | // Note: fromIndex might be near -1>>>1. 46 | return -1; 47 | } 48 | 49 | // handle most cases here (ch is a BMP code point or a 50 | // negative value (invalid code point)) 51 | for (int i = fromIndex; i < max; i++) { 52 | if (text.charAt(i) == ch) { 53 | return i; 54 | } 55 | } 56 | return -1; 57 | } 58 | 59 | public static int indexOf(CharSequence source, 60 | CharSequence target, 61 | int fromIndex) { 62 | if (source instanceof String && target instanceof String) { 63 | return ((String) source).indexOf((String) target); 64 | } 65 | if (fromIndex >= source.length()) { 66 | return (target.length() == 0 ? source.length() : -1); 67 | } 68 | if (fromIndex < 0) { 69 | fromIndex = 0; 70 | } 71 | if (target.length() == 0) { 72 | return fromIndex; 73 | } 74 | 75 | char first = target.charAt(0); 76 | int max = (source.length() - target.length()); 77 | 78 | for (int i = fromIndex; i <= max; i++) { 79 | /* Look for first character. */ 80 | if (source.charAt(i) != first) { 81 | while (++i <= max && source.charAt(i) != first) ; 82 | } 83 | 84 | /* Found first character, now look at the rest of v2 */ 85 | if (i <= max) { 86 | int j = i + 1; 87 | int end = j + target.length() - 1; 88 | for (int k = 1; j < end && source.charAt(j) 89 | == target.charAt(k); j++, k++) 90 | ; 91 | 92 | if (j == end) { 93 | /* Found whole string. */ 94 | return i; 95 | } 96 | } 97 | } 98 | return -1; 99 | } 100 | 101 | public static int lastIndexOf(CharSequence source, 102 | CharSequence target, 103 | int fromIndex) { 104 | if (source instanceof String && target instanceof String) { 105 | return ((String) source).lastIndexOf((String) target); 106 | } 107 | /* 108 | * Check arguments; return immediately where possible. For 109 | * consistency, don't check for null str. 110 | */ 111 | int rightIndex = source.length() - target.length(); 112 | if (fromIndex < 0) { 113 | return -1; 114 | } 115 | if (fromIndex > rightIndex) { 116 | fromIndex = rightIndex; 117 | } 118 | /* Empty string always matches. */ 119 | if (target.length() == 0) { 120 | return fromIndex; 121 | } 122 | 123 | int strLastIndex = target.length() - 1; 124 | char strLastChar = target.charAt(strLastIndex); 125 | int min = target.length() - 1; 126 | int i = min + fromIndex; 127 | 128 | startSearchForLastChar: 129 | while (true) { 130 | while (i >= min && source.charAt(i) != strLastChar) { 131 | i--; 132 | } 133 | if (i < min) { 134 | return -1; 135 | } 136 | int j = i - 1; 137 | int start = j - (target.length() - 1); 138 | int k = strLastIndex - 1; 139 | 140 | while (j > start) { 141 | if (source.charAt(j--) != target.charAt(k--)) { 142 | i--; 143 | continue startSearchForLastChar; 144 | } 145 | } 146 | return start + 1; 147 | } 148 | } 149 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_camera.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_gallery.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_slideshow.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/side_nav_bar.xml: -------------------------------------------------------------------------------- 1 | 3 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_link.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_log.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 20 | 21 | 25 | 26 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/app_bar_link.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 20 | 21 | 22 | 23 | 26 | 27 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_link.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 20 | 21 |