From 4b706478d1c168d5cc20fb441892699644c49228 Mon Sep 17 00:00:00 2001 From: acgist <289547414@qq.com> Date: Sun, 26 Mar 2023 12:42:29 +0800 Subject: [PATCH] =?UTF-8?q?[*]=20=E4=BF=A1=E4=BB=A4+=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 +- README.md | 1 + docs/AOSP.md | 72 +++++ docs/Deploy.md | 84 ----- docs/WebRTC.md | 90 ++++++ taoyao-client-android/README.md | 10 +- .../client/src/main/AndroidManifest.xml | 32 +- .../acgist/taoyao/client/BootReceiver.java | 23 ++ .../acgist/taoyao/client/MainActivity.java | 38 ++- .../acgist/taoyao/client/MediaService.java | 12 + .../taoyao/client/SettingsActivity.java | 26 +- .../acgist/taoyao/client/signal/Taoyao.java | 291 ++++++++++++++---- .../client/src/main/res/drawable/settings.png | Bin 0 -> 6468 bytes .../src/main/res/layout/activity_main.xml | 14 +- .../src/main/res/layout/activity_settings.xml | 120 +++++++- .../src/main/res/values-night/themes.xml | 12 - .../client/src/main/res/values/arrays.xml | 11 - .../client/src/main/res/values/colors.xml | 5 - .../client/src/main/res/values/settings.xml | 10 + .../client/src/main/res/values/strings.xml | 22 +- .../client/src/main/res/values/themes.xml | 12 - .../client/src/main/res/xml/backup_rules.xml | 1 - .../main/res/xml/data_extraction_rules.xml | 1 - .../taoyao/media/CMakeLists.txt | 6 +- .../taoyao/media/src/main/cpp/main.cpp | 19 -- .../cpp/{media/MediaPublisher.cpp => rtp.cpp} | 0 .../taoyao/media/src/main/cpp/rtp.hpp | 1 + .../taoyao/media/src/main/cpp/webrtc.cpp | 1 + .../src/main/cpp/{main.hpp => webrtc.hpp} | 0 .../cpp/{media => webrtc}/AudioPublisher.cpp | 0 .../src/main/cpp/webrtc/MediaPublisher.cpp | 0 .../cpp/{media => webrtc}/MediaRecorder.cpp | 0 .../cpp/{media => webrtc}/VideoPublisher.cpp | 0 .../taoyao/signal/client/ClientType.java | 24 +- .../protocol/room/RoomCreateProtocol.java | 2 +- 35 files changed, 656 insertions(+), 289 deletions(-) create mode 100644 docs/AOSP.md create mode 100644 docs/WebRTC.md create mode 100644 taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/BootReceiver.java create mode 100644 taoyao-client-android/taoyao/client/src/main/res/drawable/settings.png delete mode 100644 taoyao-client-android/taoyao/client/src/main/res/values-night/themes.xml delete mode 100644 taoyao-client-android/taoyao/client/src/main/res/values/arrays.xml create mode 100644 taoyao-client-android/taoyao/client/src/main/res/values/settings.xml delete mode 100644 taoyao-client-android/taoyao/client/src/main/res/values/themes.xml delete mode 100644 taoyao-client-android/taoyao/media/src/main/cpp/main.cpp rename taoyao-client-android/taoyao/media/src/main/cpp/{media/MediaPublisher.cpp => rtp.cpp} (100%) create mode 100644 taoyao-client-android/taoyao/media/src/main/cpp/rtp.hpp create mode 100644 taoyao-client-android/taoyao/media/src/main/cpp/webrtc.cpp rename taoyao-client-android/taoyao/media/src/main/cpp/{main.hpp => webrtc.hpp} (100%) rename taoyao-client-android/taoyao/media/src/main/cpp/{media => webrtc}/AudioPublisher.cpp (100%) create mode 100644 taoyao-client-android/taoyao/media/src/main/cpp/webrtc/MediaPublisher.cpp rename taoyao-client-android/taoyao/media/src/main/cpp/{media => webrtc}/MediaRecorder.cpp (100%) rename taoyao-client-android/taoyao/media/src/main/cpp/{media => webrtc}/VideoPublisher.cpp (100%) diff --git a/.gitignore b/.gitignore index fbd6a97..f1c2c72 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,11 @@ *.settings *.classpath *.factorypath + +.vscode + package-lock.json logs target -node_modules \ No newline at end of file +node_modules diff --git a/README.md b/README.md index 7b3d0d1..5a4f363 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ Android还在学习之中... ## TODO * P2P +* RTP * 标识 -> ID * 所有字段获取 -> get * 优化JS错误回调 -> platform::error diff --git a/docs/AOSP.md b/docs/AOSP.md new file mode 100644 index 0000000..329e480 --- /dev/null +++ b/docs/AOSP.md @@ -0,0 +1,72 @@ +# AOSP + +本文档内容旨在独立定制编译`AOSP`系统,非必需使用。 + +## 参考文档 + +* https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/ +* https://source.android.google.cn/source/initializing?hl=zh-cn +* https://source.android.google.cn/docs/setup/about/build-numbers?hl=zh-cn +* https://developers.google.cn/android/drivers#sargosp2a.220505.008 + +## 机器配置 + +* 内存`32G` +* 十六核`CPU` +* 硬盘`300G` +* 系统`Ubuntu 18.xx` +* 公司网络`100Mbps/s` +* 整个下载过程大概需要四到五个小时 +* 整个编译过程大概需要半到一个小时 + +## 源码 + +``` +# 工具 +mkdir /data/android +cd /data/android +curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo -o repo +chmod a+x repo +export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo' + +git config --global user.email "taoyao@acgist.com" +git config --global user.name "acgist" + +# 源码 +./repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-12.1.0_r27 +./repo sync +``` + +ROOT权限 + +## 裁剪 + +``` +# 应用 + +# 驱动 + +# root +userdebug + +# framework +``` + +## 编译 + +``` +lunch aosp_arm64-user +make -j 8 +``` + +## 刷机 + +``` +adb reboot bootloader +fastboot flashall +``` + + +https://blog.csdn.net/u012932409/article/details/106792906 +https://blog.csdn.net/qq_33240707/article/details/123704679 +https://blog.csdn.net/weixin_42929891/article/details/122667831 \ No newline at end of file diff --git a/docs/Deploy.md b/docs/Deploy.md index 4d77bff..b4e6aa5 100644 --- a/docs/Deploy.md +++ b/docs/Deploy.md @@ -278,90 +278,6 @@ SELINUX=disabled --- ``` -## libwebrtc(可选) - -* https://webrtc.github.io/webrtc-org/native-code/android/ -* https://webrtc.github.io/webrtc-org/native-code/development/ -* https://webrtc.github.io/webrtc-org/native-code/development/prerequisite-sw/ -* https://www.chromium.org/developers/how-tos/install-depot-tools/ - -建议直接购买国外的按需使用的主机,用完直接释放,配置建议: - -* 内存`8G` -* 四核`CPU` -* 硬盘`100G` -* 系统`Ubuntu 20.xx` -* 宽带按需`100Mbps/s`(不要固定宽带) -* 整个编译过程大概需要两到三个小时(不会下载回来很慢) - -``` -# 编译工具 -mkdir -p /data -git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git - -# 源码 -mkdir -p /data/webrtc -cd /data/webrtc -fetch --nohooks webrtc_android -/data/depot_tools/gclient sync - -# 分支 -cd src -git checkout -b m94 branch-heads/4606 -/data/depot_tools/gclient sync - -# 编译依赖 -./build/install-build-deps.sh -./build/install-build-deps-android.sh -source ./build/android/envsetup.sh - ---- -'target_os': 'android', -'is_clang': True, -'is_debug': False, -'use_rtti': True, -'rtc_use_h264': True, -'use_custom_libcxx': False, -'rtc_include_tests': False, -'is_component_build': False, -'treat_warnings_as_errors': False, -'use_goma': use_goma, -'target_cpu': _GetTargetCpu(arch) ---- - -# 编译.so -./tools_webrtc/android/build_aar.py --build-dir ./out/release-build/ -# 指定CPU架构:--arch x86 x86_64 arm64-v8a armeabi-v7a - -# 编译.a -/data/depot_tools/autoninja -C ./out/release-build/x86 webrtc && -/data/depot_tools/autoninja -C ./out/release-build/x86_64 webrtc && -/data/depot_tools/autoninja -C ./out/release-build/arm64-v8a webrtc && -/data/depot_tools/autoninja -C ./out/release-build/armeabi-v7a webrtc - -# 依赖打包 -zip -r webrtc.zip out libwebrtc.aar -``` - -[WebRTC](https://pan.baidu.com/s/1E_DXv32D9ODyj5J-o-ji_g?pwd=hudc) - -## libmediasoupclient(可选) - -https://mediasoup.org/documentation/v3/libmediasoupclient/installation/ - -``` -# 编译 -cmake . -B build \ --DCMAKE_BUILD_TYPE=Debug | Release \ --DMEDIASOUPCLIENT_LOG_DEV=OFF \ --DMEDIASOUPCLIENT_LOG_TRACE=OFF \ --DMEDIASOUPCLIENT_BUILD_TESTS=OFF \ --DLIBWEBRTC_INCLUDE_PATH:PATH=PATH_TO_LIBWEBRTC_SOURCES \ --DLIBWEBRTC_BINARY_PATH:PATH=PATH_TO_LIBWEBRTC_BINARY -make -C build -make install -C build -``` - ## 下载源码 ``` diff --git a/docs/WebRTC.md b/docs/WebRTC.md new file mode 100644 index 0000000..d56d721 --- /dev/null +++ b/docs/WebRTC.md @@ -0,0 +1,90 @@ +# WebRTC + +本文档内容旨在独立编译`WebRTC`项目,非必需使用。 + +## libwebrtc + +* https://webrtc.github.io/webrtc-org/native-code/android/ +* https://webrtc.github.io/webrtc-org/native-code/development/ +* https://webrtc.github.io/webrtc-org/native-code/development/prerequisite-sw/ +* https://www.chromium.org/developers/how-tos/install-depot-tools/ + +国内镜像需要配置比较麻烦,建议直接按需购买能够访问外网的主机,用完直接释放,配置建议: + +* 内存`8G` +* 四核`CPU` +* 硬盘`100G` +* 系统`Ubuntu 20.xx` +* 宽带按需`100Mbps/s`(不要固定宽带) +* 整个下载过程大概需要半到一个小时 +* 整个编译过程大概需要一到两个小时 +* 最痛苦的就是下载回来速度很慢 + +``` +# 编译工具 +mkdir -p /data +git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git + +# 源码 +mkdir -p /data/webrtc +cd /data/webrtc +fetch --nohooks webrtc_android +/data/depot_tools/gclient sync + +# 分支 +cd src +git checkout -b m94 branch-heads/4606 +/data/depot_tools/gclient sync + +# 编译依赖 +./build/install-build-deps.sh +./build/install-build-deps-android.sh +source ./build/android/envsetup.sh + +# 编译配置:./tools_webrtc/android/build_aar.py +--- +'target_os': 'android', +'is_clang': True, +'is_debug': False, +'use_rtti': True, +'rtc_use_h264': True, +'use_custom_libcxx': False, +'rtc_include_tests': False, +'is_component_build': False, +'treat_warnings_as_errors': False, +'use_goma': use_goma, +'target_cpu': _GetTargetCpu(arch) +--- + +# 编译项目 +./tools_webrtc/android/build_aar.py --build-dir ./out/release-build/ +# 指定CPU架构:--arch x86 x86_64 arm64-v8a armeabi-v7a + +# 生成静态库 +/data/depot_tools/autoninja -C ./out/release-build/x86 webrtc +/data/depot_tools/autoninja -C ./out/release-build/x86_64 webrtc +/data/depot_tools/autoninja -C ./out/release-build/arm64-v8a webrtc +/data/depot_tools/autoninja -C ./out/release-build/armeabi-v7a webrtc + +# 打包 +zip -r webrtc.zip out libwebrtc.aar +``` + +[WebRTC](https://pan.baidu.com/s/1E_DXv32D9ODyj5J-o-ji_g?pwd=hudc) + +## libmediasoupclient + +https://mediasoup.org/documentation/v3/libmediasoupclient/installation/ + +``` +# 编译 +cmake . -B build \ +-DCMAKE_BUILD_TYPE=Debug | Release \ +-DMEDIASOUPCLIENT_LOG_DEV=OFF \ +-DMEDIASOUPCLIENT_LOG_TRACE=OFF \ +-DMEDIASOUPCLIENT_BUILD_TESTS=OFF \ +-DLIBWEBRTC_INCLUDE_PATH:PATH=PATH_TO_LIBWEBRTC_SOURCES \ +-DLIBWEBRTC_BINARY_PATH:PATH=PATH_TO_LIBWEBRTC_BINARY +make -C build +make install -C build +``` diff --git a/taoyao-client-android/README.md b/taoyao-client-android/README.md index 3762511..0810594 100644 --- a/taoyao-client-android/README.md +++ b/taoyao-client-android/README.md @@ -10,8 +10,7 @@ ## 项目配置 -可以自己编译`WebRTC`依赖或者下载已有依赖, -项目导入以后拷贝`libmediasoupclient`源码还有`WebRTC`头文件和二进制文件到`deps`目录。 +可以自己编译`WebRTC`依赖或者下载已有依赖,项目导入以后拷贝`libmediasoupclient`源码还有`WebRTC`头文件和二进制文件到`deps`目录。 [WebRTC](https://pan.baidu.com/s/1E_DXv32D9ODyj5J-o-ji_g?pwd=hudc) @@ -22,7 +21,7 @@ * https://developer.android.google.cn/docs?hl=zh-cn * https://developer.android.google.cn/guide?hl=zh-cn -## 依赖编译 +## 依赖编译(可选) * https://webrtc.github.io/webrtc-org/native-code/android/ * https://webrtc.github.io/webrtc-org/native-code/development/ @@ -30,6 +29,11 @@ * https://www.chromium.org/developers/how-tos/install-depot-tools/ * https://mediasoup.org/documentation/v3/libmediasoupclient/installation/ +## 安卓编译(可选) + +* https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/ +* https://source.android.google.cn/source/initializing?hl=zh-cn + ## 参考项目 * https://github.com/haiyangwu/webrtc-android-build diff --git a/taoyao-client-android/taoyao/client/src/main/AndroidManifest.xml b/taoyao-client-android/taoyao/client/src/main/AndroidManifest.xml index 8862696..5d1fd29 100644 --- a/taoyao-client-android/taoyao/client/src/main/AndroidManifest.xml +++ b/taoyao-client-android/taoyao/client/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ + - - + android:supportsRtl="true"> + android:exported="true" + android:label="@string/title_activity_main"> + + + + + + + + \ No newline at end of file diff --git a/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/BootReceiver.java b/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/BootReceiver.java new file mode 100644 index 0000000..6e45f68 --- /dev/null +++ b/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/BootReceiver.java @@ -0,0 +1,23 @@ +package com.acgist.taoyao.client; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +/** + * 开机启动 + * + * @author acgist + */ +public class BootReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) { + final Intent mainActivity = new Intent(context, MainActivity.class); + mainActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(mainActivity); + } + } + +} diff --git a/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/MainActivity.java b/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/MainActivity.java index efc2428..01ca806 100644 --- a/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/MainActivity.java +++ b/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/MainActivity.java @@ -1,32 +1,38 @@ package com.acgist.taoyao.client; +import android.content.Intent; +import android.os.Bundle; +import android.util.AttributeSet; +import android.widget.TextView; + import androidx.appcompat.app.AppCompatActivity; -import android.os.Bundle; -import android.widget.TextView; - import com.acgist.taoyao.client.databinding.ActivityMainBinding; +import com.google.android.material.floatingactionbutton.FloatingActionButton; -import java.util.List; -import java.util.Map; - +/** + * 预览界面 + * + * @author acgist + */ public class MainActivity extends AppCompatActivity { - static { - System.loadLibrary("taoyao"); - } - private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - binding = ActivityMainBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - TextView tv = binding.sampleText; - tv.setText(stringFromJNI()); + // 媒体服务 + final Intent mediaService = new Intent(this, MediaService.class); + this.startService(mediaService); + // 布局 + this.binding = ActivityMainBinding.inflate(this.getLayoutInflater()); + this.setContentView(this.binding.getRoot()); + // 设置按钮 + this.binding.settings.setOnClickListener(view -> { + final Intent settings = new Intent(this, SettingsActivity.class); + this.startService(settings); + }); } - public native String stringFromJNI(); - } \ No newline at end of file diff --git a/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/MediaService.java b/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/MediaService.java index 5d46401..6760feb 100644 --- a/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/MediaService.java +++ b/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/MediaService.java @@ -4,7 +4,19 @@ import android.app.Service; import android.content.Intent; import android.os.IBinder; +import com.acgist.taoyao.client.signal.Taoyao; + +/** + * 媒体服务 + * + * @author acgist + */ public class MediaService extends Service { + + static { + System.loadLibrary("taoyao"); + } + public MediaService() { } diff --git a/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/SettingsActivity.java b/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/SettingsActivity.java index d4ffa0a..76ca352 100644 --- a/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/SettingsActivity.java +++ b/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/SettingsActivity.java @@ -1,18 +1,38 @@ package com.acgist.taoyao.client; +import android.content.Intent; import android.os.Bundle; +import android.util.Log; -import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; -import com.acgist.taoyao.client.R; +import com.acgist.taoyao.client.databinding.ActivitySettingsBinding; +import com.acgist.taoyao.client.signal.Taoyao; +/** + * 设置界面 + * + * @author acgist + */ public class SettingsActivity extends AppCompatActivity { + private ActivitySettingsBinding binding; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_settings); + // 布局 + this.binding = ActivitySettingsBinding.inflate(this.getLayoutInflater()); + this.setContentView(this.binding.getRoot()); + // 设置按钮 + this.binding.connect.setOnClickListener(view -> { +// final Taoyao taoyao = new Taoyao( +// +// ); +// Log.d(SettingsActivity.class.getSimpleName(), "连接信令:" + taoyao); + final Intent main = new Intent(this, MainActivity.class); + this.startService(main); + }); } } \ No newline at end of file diff --git a/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/signal/Taoyao.java b/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/signal/Taoyao.java index 5fbe6cb..944060f 100644 --- a/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/signal/Taoyao.java +++ b/taoyao-client-android/taoyao/client/src/main/java/com/acgist/taoyao/client/signal/Taoyao.java @@ -1,5 +1,8 @@ package com.acgist.taoyao.client.signal; +import android.Manifest; +import android.content.Context; +import android.content.pm.PackageManager; import android.location.Criteria; import android.location.Location; import android.location.LocationManager; @@ -7,8 +10,12 @@ import android.net.wifi.WifiManager; import android.os.BatteryManager; import android.util.Log; +import androidx.core.app.ActivityCompat; + import com.acgist.taoyao.boot.model.Header; import com.acgist.taoyao.boot.model.Message; +import com.acgist.taoyao.boot.model.MessageCode; +import com.acgist.taoyao.boot.model.MessageCodeException; import com.acgist.taoyao.boot.utils.CloseableUtils; import com.acgist.taoyao.boot.utils.JSONUtils; import com.acgist.taoyao.client.media.Recorder; @@ -26,8 +33,11 @@ import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -51,14 +61,21 @@ public final class Taoyao { */ private final String host; /** - * 终端ID + * 信令版本 */ - private final String clientId; - private final String clientType = "camera"; + private final String version; /** * 终端名称 */ private final String name; + /** + * 终端ID + */ + private final String clientId; + /** + * 终端类型 + */ + private final String clientType; /** * 桃夭帐号 */ @@ -67,55 +84,122 @@ public final class Taoyao { * 桃夭密码 */ private final String password; - private String version; /** - * Socket + * 是否关闭 */ - private Socket socket; - private InputStream input; - private OutputStream output; private boolean close; /** * 是否连接 */ private boolean connect; + /** + * 超时时间 + */ + private final int timeout; + /** + * Socket + */ + private Socket socket; + /** + * 信令输入 + */ + private InputStream input; + /** + * 信令输出 + */ + private OutputStream output; + /** + * 加密工具 + */ private final Cipher encrypt; + /** + * 解密工具 + */ private final Cipher decrypt; + /** + * 服务上下文 + */ + private final Context context; + /** + * Wifi管理器 + */ private final WifiManager wifiManager; + /** + * 电池管理器 + */ private final BatteryManager batteryManager; + /** + * 位置管理器 + */ private final LocationManager locationManager; - // 线程池 - private final ExecutorService executor = Executors.newFixedThreadPool(8); - // 定时任务线程池 - private final ExecutorService scheduled = Executors.newScheduledThreadPool(2); + /** + * 请求消息:同步消息 + */ + private final Map requestMessage; + /** + * 线程池 + */ + private final ExecutorService executor; + /** + * 定时任务线程池 + */ + private final ScheduledExecutorService scheduled; + /** + * 全局单例 + */ + private static Taoyao instance; public Taoyao( - int port, String host, String algo, String secret, String clientId, String name, String username, String password, - WifiManager wifiManager, BatteryManager batteryManager, LocationManager locationManager + int port, String host, String version, + String name, String clientId, String clientType, String username, String password, + int timeout, String algo, String secret, + Context context, WifiManager wifiManager, BatteryManager batteryManager, LocationManager locationManager ) { - this.port = port; - this.host = host; this.close = false; this.connect = false; - if (algo == null || algo.isEmpty() || algo.equals("PLAINTEXT")) { - // 明文 - this.encrypt = null; - this.decrypt = null; - } else { - this.encrypt = this.buildCipher(Cipher.ENCRYPT_MODE, algo, secret); - this.decrypt = this.buildCipher(Cipher.DECRYPT_MODE, algo, secret); - } - this.clientId = clientId; + this.port = port; + this.host = host; + this.version = version; this.name = name; + this.clientId = clientId; + this.clientType = clientType; this.username = username; this.password = password; + this.timeout = timeout; + final boolean plaintext = algo == null || algo.isEmpty() || algo.equals("PLAINTEXT"); + this.encrypt = plaintext ? null : this.buildCipher(Cipher.ENCRYPT_MODE, algo, secret); + this.decrypt = plaintext ? null : this.buildCipher(Cipher.DECRYPT_MODE, algo, secret); + this.context = context; this.wifiManager = wifiManager; this.batteryManager = batteryManager; this.locationManager = locationManager; - executor.submit(this::read); - scheduled.submit(this::heartbeat); + this.requestMessage = new ConcurrentHashMap<>(); + // 读取线程 + 两条处理线程 + this.executor = Executors.newFixedThreadPool(3); + // 心跳线程 + this.scheduled = Executors.newScheduledThreadPool(1); + executor.submit(this::loopMessage); + scheduled.scheduleWithFixedDelay(this::heartbeat, 30, 30, TimeUnit.SECONDS); + if(Taoyao.instance != null) { + Taoyao.instance.close(); + } + Taoyao.instance = this; } + /** + * @return 信令 + */ + public static final Taoyao getInstance() { + return Taoyao.instance; + } + + /** + * @param mode 加密/解密 + * @param name 算法名称 + * @param secret 密钥 + * + * @return 加解密工具 + */ private Cipher buildCipher(int mode, String name, String secret) { try { final String algo = name.equals("DES") ? "DES/ECB/PKCS5Padding" : "AES/ECB/PKCS5Padding"; @@ -123,7 +207,7 @@ public final class Taoyao { cipher.init(mode, new SecretKeySpec(Base64.getMimeDecoder().decode(secret), name)); return cipher; } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) { - // TODO:日志 + Log.e(Taoyao.class.getSimpleName(), "创建加解密工具异常", e); } return null; } @@ -131,44 +215,51 @@ public final class Taoyao { /** * 连接信令 */ - public void connect() { - this.close(); -// HiLog.debug(this.label, "连接信令:%s:%d", this.host, this.port); + public synchronized void connect() { + if(this.close) { + return; + } + // 释放连接 + this.disconnect(); + // 开始连接 + Log.d(Taoyao.class.getSimpleName(), "连接信令:" + this.host + ":" + this.port); this.socket = new Socket(); try { -// socket.setSoTimeout(5000); - this.socket.connect(new InetSocketAddress(this.host, this.port), 5000); + // 设置读取超时时间:不要设置一直阻塞 +// socket.setSoTimeout(this.timeout); + this.socket.connect(new InetSocketAddress(this.host, this.port), this.timeout); if (this.socket.isConnected()) { this.input = this.socket.getInputStream(); this.output = this.socket.getOutputStream(); this.register(); + this.connect = true; synchronized (this) { this.notifyAll(); } } } catch (Exception e) { - e.printStackTrace(); -// HiLog.error(this.label, "连接信令异常:%s:%d", this.host, this.port); + Log.e(Taoyao.class.getSimpleName(), "连接信令异常:" + this.host + ":" + this.port, e); } } - private void heartbeat() { - - } - - private void read() { + /** + * 循环读取信令消息 + */ + private void loopMessage() { int length = 0; short messageLength = 0; final byte[] bytes = new byte[1024]; final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024); while (!this.close) { try { - while (this.input == null) { + // 重连 + while (!this.close && !this.connect) { this.connect(); synchronized (this) { - this.wait(5000); + this.wait(this.timeout); } } + // 读取 while ((length = this.input.read(bytes)) >= 0) { buffer.put(bytes, 0, length); while (buffer.position() > 0) { @@ -195,6 +286,7 @@ public final class Taoyao { buffer.get(message); buffer.compact(); final String content = new String(this.decrypt.doFinal(message)); + Log.d(Taoyao.class.getSimpleName(), "处理信令:" + content); executor.submit(() -> { try { Taoyao.this.on(content); @@ -207,16 +299,15 @@ public final class Taoyao { } } } catch (Exception e) { - e.printStackTrace(); + Log.e(Taoyao.class.getSimpleName(), "接收信令异常", e); this.connect(); - // TODO:日志 -// log.error("读取异常", e); } } } /** - * @param message 消息 + * @param message 原始消息 + * * @return 加密消息 */ private byte[] encrypt(Message message) { @@ -225,41 +316,67 @@ public final class Taoyao { try { // 加密 final byte[] encryptBytes = this.encrypt.doFinal(bytes); - // 发送 + // 编码 final ByteBuffer buffer = ByteBuffer.allocateDirect(Short.BYTES + encryptBytes.length); buffer.putShort((short) encryptBytes.length); buffer.put(encryptBytes); buffer.flip(); - final byte[] sendBytes = new byte[buffer.capacity()]; - buffer.get(sendBytes); - return sendBytes; + // 编码 + final byte[] encodingBytes = new byte[buffer.capacity()]; + buffer.get(encodingBytes); + return encodingBytes; } catch (IllegalBlockSizeException | BadPaddingException e) { - e.printStackTrace(); -// log.error("加密异常:{}", message); + Log.e(Taoyao.class.getSimpleName(), "加密异常:" + message, e); } } return bytes; } + /** + * @param message 信令消息 + */ public void push(Message message) { if (this.output == null) { + Log.w(Taoyao.class.getSimpleName(), "通道没有打开:" + message); return; } try { this.output.write(this.encrypt(message)); } catch (Exception e) { - e.printStackTrace(); + Log.e(Taoyao.class.getSimpleName(), "请求信令异常:" + message, e); } } + /** + * @param request 信令请求消息 + * + * @return 信令响应消息 + */ public Message request(Message request) { - return null; + final Header header = request.getHeader(); + final Long id = header.getId(); + this.requestMessage.put(id, request); + synchronized (request) { + this.push(request); + try { + request.wait(this.timeout); + } catch (InterruptedException e) { + Log.e(Taoyao.class.getSimpleName(), "请求信令等待异常:" + request, e); + } + } + final Message response = this.requestMessage.remove(id); + if (response == null || request.equals(response)) { + Log.w(Taoyao.class.getSimpleName(), "请求信令没有响应:" + request); + throw MessageCodeException.of(MessageCode.CODE_2001, "请求信令没有响应"); + } + return response; } /** * 释放连接 */ - private void close() { + private void disconnect() { + Log.d(Taoyao.class.getSimpleName(), "释放信令:" + this.host + ":" + this.port); this.connect = false; CloseableUtils.close(this.input); CloseableUtils.close(this.output); @@ -272,8 +389,8 @@ public final class Taoyao { /** * 关闭信令 */ - private void shutdown() { - this.close(); + private void close() { + this.disconnect(); this.close = true; executor.shutdownNow(); scheduled.shutdownNow(); @@ -285,7 +402,7 @@ public final class Taoyao { * * @return 消息 */ - public Message buildMessage(String signal, Object ... args) { + public Message buildMessage(String signal, Object... args) { final Map map = new HashMap<>(); if (ArrayUtils.isNotEmpty(args)) { for (int index = 0; index < args.length; index += 2) { @@ -325,10 +442,21 @@ public final class Taoyao { if (header == null) { return; } - final Map body = message.body(); - switch (header.getSignal()) { - case "client::register" -> this.register(message, body); - default -> Log.i(Taoyao.class.getSimpleName(), "没有适配信令:" + content); + final Long id = header.getId(); + final Message request = this.requestMessage.get(id); + if (request != null) { + // 同步处理:重新设置响应消息 + this.requestMessage.put(id, message); + // 唤醒等待线程 + synchronized (request) { + request.notifyAll(); + } + } else { + final Map body = message.body(); + switch (header.getSignal()) { + case "client::register" -> this.register(message, body); + default -> Log.i(Taoyao.class.getSimpleName(), "没有适配信令:" + content); + } } } @@ -359,18 +487,45 @@ public final class Taoyao { */ private void register(Message message, Map body) { final Integer index = (Integer) body.get("index"); + if (index == null) { + return; + } IdUtils.setClientIndex(index); } + /** + * 心跳 + */ + private void heartbeat() { + while(!this.close) { + final Location location = this.location(); + this.push(this.buildMessage( + "client::heartbeat", + "latitude", location == null ? -1 : location.getLatitude(), + "longitude", location == null ? -1 : location.getLongitude(), + "signal", this.wifiManager == null ? -1 : this.wifiManager.getMaxSignalLevel(), + "batter", this.batteryManager == null ? -1 : this.batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY), + "charging", this.batteryManager == null ? -1 : this.batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS), + "recording", Recorder.getInstance().isActive() + )); + } + } + /** * @return 位置 */ private Location location() { - if(this.locationManager == null) { + if (this.locationManager == null) { + return null; + } + if ( + ActivityCompat.checkSelfPermission(this.context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(this.context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED + ) { return null; } final Criteria criteria = new Criteria(); - // 耗电 + // 功耗 criteria.setCostAllowed(false); // 不要海拔 criteria.setAltitudeRequired(false); @@ -378,9 +533,13 @@ public final class Taoyao { criteria.setBearingRequired(false); // 精度 criteria.setAccuracy(Criteria.ACCURACY_FINE); - // 低功耗 + // 功耗 criteria.setPowerRequirement(Criteria.POWER_LOW); - final String provider = locationManager.getBestProvider(criteria, true); + // 最佳提供者 + final String provider = this.locationManager.getBestProvider(criteria, true); + if (provider == null) { + return null; + } return this.locationManager.getLastKnownLocation(provider); } diff --git a/taoyao-client-android/taoyao/client/src/main/res/drawable/settings.png b/taoyao-client-android/taoyao/client/src/main/res/drawable/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..523e42e3bbdb3c3cf7f70cc5fe66bd1a2b3eab93 GIT binary patch literal 6468 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 literal 0 HcmV?d00001 diff --git a/taoyao-client-android/taoyao/client/src/main/res/layout/activity_main.xml b/taoyao-client-android/taoyao/client/src/main/res/layout/activity_main.xml index 6cd7e12..0ff6a1b 100644 --- a/taoyao-client-android/taoyao/client/src/main/res/layout/activity_main.xml +++ b/taoyao-client-android/taoyao/client/src/main/res/layout/activity_main.xml @@ -2,18 +2,22 @@ - \ No newline at end of file diff --git a/taoyao-client-android/taoyao/client/src/main/res/layout/activity_settings.xml b/taoyao-client-android/taoyao/client/src/main/res/layout/activity_settings.xml index 7cbb9ea..8ef7165 100644 --- a/taoyao-client-android/taoyao/client/src/main/res/layout/activity_settings.xml +++ b/taoyao-client-android/taoyao/client/src/main/res/layout/activity_settings.xml @@ -1,5 +1,121 @@ - + - \ No newline at end of file + + + + + + + + + + + + +