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 0000000..523e42e Binary files /dev/null and b/taoyao-client-android/taoyao/client/src/main/res/drawable/settings.png differ 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 + + + + + + + + + + + + +