[*] android信令接入
This commit is contained in:
@@ -55,7 +55,7 @@ build/target/product/handheld_*.mk
|
||||
```
|
||||
source build/envsetup.sh
|
||||
lunch aosp_arm64-user
|
||||
make -j 8
|
||||
make -j 16
|
||||
make udpatepackage
|
||||
```
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
||||
@@ -19,6 +19,7 @@ public class BootReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.i(BootReceiver.class.getSimpleName(), "onReceive");
|
||||
final Resources resources = context.getResources();
|
||||
if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
|
||||
if(resources.getBoolean(R.bool.preview)) {
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
package com.acgist.taoyao.client;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Process;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.acgist.taoyao.client.databinding.ActivityMainBinding;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 预览界面
|
||||
*
|
||||
@@ -20,10 +24,12 @@ import com.acgist.taoyao.client.databinding.ActivityMainBinding;
|
||||
*/
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private MainHandler mainHandler;
|
||||
private ActivityMainBinding binding;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
Log.i(MainActivity.class.getSimpleName(), "onCreate");
|
||||
super.onCreate(bundle);
|
||||
// 启动点亮屏幕
|
||||
this.setTurnScreenOn(true);
|
||||
@@ -37,22 +43,27 @@ public class MainActivity extends AppCompatActivity {
|
||||
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.startActivity(settings);
|
||||
});
|
||||
this.binding.settings.setOnClickListener(this::launchSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
Log.i(MainActivity.class.getSimpleName(), "onStart");
|
||||
super.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
Log.i(MainActivity.class.getSimpleName(), "onStop");
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
Log.i(MainActivity.class.getSimpleName(), "onDestroy");
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉起媒体服务
|
||||
*/
|
||||
@@ -63,13 +74,42 @@ public class MainActivity extends AppCompatActivity {
|
||||
SystemClock.sleep(100);
|
||||
}
|
||||
if(display.STATE_ON == display.getState()) {
|
||||
// 媒体服务
|
||||
Log.i(MainActivity.class.getSimpleName(), "拉起媒体服务");
|
||||
final Intent mediaService = new Intent(this, MediaService.class);
|
||||
this.startService(mediaService);
|
||||
final Intent intent = new Intent(this, MediaService.class);
|
||||
if(this.mainHandler == null) {
|
||||
this.mainHandler = new MainHandler();
|
||||
}
|
||||
intent.putExtra("mainHandler", this.mainHandler);
|
||||
intent.setAction("connect");
|
||||
this.startService(intent);
|
||||
} else {
|
||||
Log.w(MainActivity.class.getSimpleName(), "拉起媒体服务失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉起设置页面
|
||||
*
|
||||
* @param view View
|
||||
*/
|
||||
private void launchSettings(View view) {
|
||||
final Intent intent = new Intent(this, SettingsActivity.class);
|
||||
this.startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
private static class MainHandler extends Handler implements Serializable {
|
||||
|
||||
@Override
|
||||
public void handleMessage(@NonNull Message message) {
|
||||
super.handleMessage(message);
|
||||
Log.d(MainActivity.class.getSimpleName(), "Handler消息:" + message.what + " - " + message.obj);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,18 @@
|
||||
package com.acgist.taoyao.client;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.location.LocationManager;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.acgist.taoyao.client.signal.Taoyao;
|
||||
|
||||
@@ -18,17 +27,85 @@ public class MediaService extends Service {
|
||||
System.loadLibrary("taoyao");
|
||||
}
|
||||
|
||||
public MediaService() {
|
||||
}
|
||||
private Taoyao taoyao;
|
||||
private Handler mainHandler;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
Log.i(MediaService.class.getSimpleName(), "onCreate");
|
||||
super.onCreate();
|
||||
Log.d(MediaService.class.getSimpleName(), "启动媒体服务");
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
Log.i(MediaService.class.getSimpleName(), "onBind");
|
||||
return new Binder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.i(MediaService.class.getSimpleName(), "onStartCommand");
|
||||
if("connect".equals(intent.getAction())) {
|
||||
if(this.taoyao == null) {
|
||||
Log.d(MediaService.class.getSimpleName(), "打开信令连接");
|
||||
this.mainHandler = (Handler) intent.getSerializableExtra("mainHandler");
|
||||
this.connect();
|
||||
} else {
|
||||
Log.d(MediaService.class.getSimpleName(), "信令已经连接");
|
||||
}
|
||||
} else if("reconnect".equals(intent.getAction())) {
|
||||
Log.d(MediaService.class.getSimpleName(), "重新连接信令");
|
||||
this.connect();
|
||||
} else {
|
||||
Log.w(MediaService.class.getSimpleName(), "未知动作:" + intent.getAction());
|
||||
}
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.i(MediaService.class.getSimpleName(), "onDestroy");
|
||||
super.onDestroy();
|
||||
this.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接信令
|
||||
*/
|
||||
private synchronized void connect() {
|
||||
// 加载配置
|
||||
final SharedPreferences sharedPreferences = this.getSharedPreferences("settings", Context.MODE_PRIVATE);
|
||||
final int port = sharedPreferences.getInt("settings.port", 9999);
|
||||
final String host = sharedPreferences.getString("settings.host", "192.168.1.100");
|
||||
final String name = sharedPreferences.getString("settings.name", "移动端");
|
||||
final String clientId = sharedPreferences.getString("settings.clientId", "mobile");
|
||||
final String username = sharedPreferences.getString("settings.username", "taoyao");
|
||||
final String password = sharedPreferences.getString("settings.password", "taoyao");
|
||||
final Resources resources = this.getResources();
|
||||
// 系统服务
|
||||
final Context context = this.getApplicationContext();
|
||||
final WifiManager wifiManager = context.getSystemService(WifiManager.class);
|
||||
final BatteryManager batteryManager = context.getSystemService(BatteryManager.class);
|
||||
final LocationManager locationManager = context.getSystemService(LocationManager.class);
|
||||
this.close();
|
||||
// 连接信令
|
||||
this.taoyao = new Taoyao(
|
||||
port, host, resources.getString(R.string.version),
|
||||
name, clientId, resources.getString(R.string.clientType), username, password,
|
||||
resources.getInteger(R.integer.timeout), resources.getString(R.string.encrypt), resources.getString(R.string.encryptSecret),
|
||||
this.mainHandler, context,
|
||||
wifiManager, batteryManager, locationManager
|
||||
);
|
||||
Toast.makeText(this.getApplicationContext(), "连接信令", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
private synchronized void close() {
|
||||
if(this.taoyao == null) {
|
||||
return;
|
||||
}
|
||||
Toast.makeText(this.getApplicationContext(), "关闭信令", Toast.LENGTH_SHORT).show();
|
||||
this.taoyao.close();
|
||||
this.taoyao = null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.acgist.taoyao.client;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
@@ -19,22 +22,79 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
Log.i(SettingsActivity.class.getSimpleName(), "onCreate");
|
||||
super.onCreate(savedInstanceState);
|
||||
// 布局
|
||||
this.binding = ActivitySettingsBinding.inflate(this.getLayoutInflater());
|
||||
this.setContentView(this.binding.getRoot());
|
||||
// 设置按钮
|
||||
this.binding.connect.setOnClickListener(view -> {
|
||||
final String port = this.binding.port.getText().toString();
|
||||
final String name = this.binding.name.getText().toString();
|
||||
final String host = this.binding.host.getText().toString();
|
||||
// final Taoyao taoyao = new Taoyao(
|
||||
//
|
||||
// );
|
||||
// Log.d(SettingsActivity.class.getSimpleName(), "连接信令:" + taoyao);
|
||||
final Intent main = new Intent(this, MainActivity.class);
|
||||
this.startActivity(main);
|
||||
});
|
||||
this.binding.connect.setOnClickListener(this::settingsPersistent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
Log.i(SettingsActivity.class.getSimpleName(), "onStart");
|
||||
super.onStart();
|
||||
// 回填配置
|
||||
final SharedPreferences sharedPreferences = this.getSharedPreferences("settings", Context.MODE_PRIVATE);
|
||||
this.binding.port.setText(String.valueOf(sharedPreferences.getInt("settings.port", 9999)));
|
||||
this.binding.host.setText(sharedPreferences.getString("settings.host", "192.168.1.100"));
|
||||
this.binding.name.setText(sharedPreferences.getString("settings.name", "移动端"));
|
||||
this.binding.clientId.setText(sharedPreferences.getString("settings.clientId", "mobile"));
|
||||
this.binding.username.setText(sharedPreferences.getString("settings.username", "taoyao"));
|
||||
this.binding.password.setText(sharedPreferences.getString("settings.password", "taoyao"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
Log.i(SettingsActivity.class.getSimpleName(), "onStop");
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
Log.i(SettingsActivity.class.getSimpleName(), "onDestroy");
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 持久化日志
|
||||
*
|
||||
* @param view View
|
||||
*/
|
||||
private void settingsPersistent(View view) {
|
||||
final String port = this.binding.port.getText().toString();
|
||||
final String host = this.binding.host.getText().toString();
|
||||
final String name = this.binding.name.getText().toString();
|
||||
final String clientId = this.binding.clientId.getText().toString();
|
||||
final String username = this.binding.username.getText().toString();
|
||||
final String password = this.binding.password.getText().toString();
|
||||
Log.i(SettingsActivity.class.getSimpleName(), String.format("""
|
||||
端口:%s
|
||||
地址:%s
|
||||
名称:%s
|
||||
标识:%s
|
||||
用户:%s
|
||||
密码:%s
|
||||
""", port, host, name, clientId, username, password));
|
||||
// 保存配置
|
||||
final SharedPreferences sharedPreferences = this.getSharedPreferences("settings", Context.MODE_PRIVATE);
|
||||
final SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putInt("settings.port", Integer.parseInt(port));
|
||||
editor.putString("settings.host", host);
|
||||
editor.putString("settings.name", name);
|
||||
editor.putString("settings.clientId", clientId);
|
||||
editor.putString("settings.username", username);
|
||||
editor.putString("settings.password", password);
|
||||
editor.commit();
|
||||
// 重连
|
||||
final Intent intent = new Intent(this, MediaService.class);
|
||||
intent.setAction("reconnect");
|
||||
this.startService(intent);
|
||||
// 返回预览页面
|
||||
this.startActivity(new Intent(this, MainActivity.class));
|
||||
// 结束
|
||||
this.finish();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package com.acgist.taoyao.client.media;
|
||||
|
||||
public class AudioPublisher {
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.acgist.taoyao.client.media;
|
||||
|
||||
/**
|
||||
* 终端
|
||||
*/
|
||||
public class Client {
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.acgist.taoyao.client.media;
|
||||
|
||||
public class Room {
|
||||
public class ClientManager {
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package com.acgist.taoyao.client.media;
|
||||
|
||||
public class VideoPublisher {
|
||||
}
|
||||
@@ -6,8 +6,10 @@ import android.content.pm.PackageManager;
|
||||
import android.location.Criteria;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
@@ -18,7 +20,7 @@ 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;
|
||||
import com.acgist.media.ClientRecorder;
|
||||
import com.acgist.taoyao.client.utils.IdUtils;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
@@ -52,6 +54,8 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
*/
|
||||
public final class Taoyao {
|
||||
|
||||
private static final long MAX_TIMEOUT = 60L * 1000;
|
||||
|
||||
/**
|
||||
* 端口
|
||||
*/
|
||||
@@ -87,15 +91,19 @@ public final class Taoyao {
|
||||
/**
|
||||
* 是否关闭
|
||||
*/
|
||||
private boolean close;
|
||||
private volatile boolean close;
|
||||
/**
|
||||
* 是否连接
|
||||
*/
|
||||
private boolean connect;
|
||||
private volatile boolean connect;
|
||||
/**
|
||||
* 超时时间
|
||||
*/
|
||||
private final int timeout;
|
||||
/**
|
||||
* 重试次数
|
||||
*/
|
||||
private int connectRetryTimes;
|
||||
/**
|
||||
* Socket
|
||||
*/
|
||||
@@ -116,6 +124,10 @@ public final class Taoyao {
|
||||
* 解密工具
|
||||
*/
|
||||
private final Cipher decrypt;
|
||||
/**
|
||||
* Handler
|
||||
*/
|
||||
private final Handler handler;
|
||||
/**
|
||||
* 服务上下文
|
||||
*/
|
||||
@@ -144,16 +156,13 @@ public final class Taoyao {
|
||||
* 定时任务线程池
|
||||
*/
|
||||
private final ScheduledExecutorService scheduled;
|
||||
/**
|
||||
* 全局单例
|
||||
*/
|
||||
private static Taoyao instance;
|
||||
|
||||
public Taoyao(
|
||||
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
|
||||
Handler handler, Context context,
|
||||
WifiManager wifiManager, BatteryManager batteryManager, LocationManager locationManager
|
||||
) {
|
||||
this.close = false;
|
||||
this.connect = false;
|
||||
@@ -166,9 +175,11 @@ public final class Taoyao {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.timeout = timeout;
|
||||
this.connectRetryTimes = 1;
|
||||
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.handler = handler;
|
||||
this.context = context;
|
||||
this.wifiManager = wifiManager;
|
||||
this.batteryManager = batteryManager;
|
||||
@@ -178,19 +189,8 @@ public final class Taoyao {
|
||||
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;
|
||||
this.executor.submit(this::loopMessage);
|
||||
this.scheduled.scheduleWithFixedDelay(this::heartbeat, 30, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,9 +215,9 @@ public final class Taoyao {
|
||||
/**
|
||||
* 连接信令
|
||||
*/
|
||||
public synchronized void connect() {
|
||||
public synchronized boolean connect() {
|
||||
if(this.close) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
// 释放连接
|
||||
this.disconnect();
|
||||
@@ -233,13 +233,17 @@ public final class Taoyao {
|
||||
this.output = this.socket.getOutputStream();
|
||||
this.register();
|
||||
this.connect = true;
|
||||
this.connectRetryTimes = 1;
|
||||
synchronized (this) {
|
||||
this.notifyAll();
|
||||
}
|
||||
} else {
|
||||
this.connect = false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(Taoyao.class.getSimpleName(), "连接信令异常:" + this.host + ":" + this.port, e);
|
||||
}
|
||||
return this.connect;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,7 +260,17 @@ public final class Taoyao {
|
||||
while (!this.close && !this.connect) {
|
||||
this.connect();
|
||||
synchronized (this) {
|
||||
this.wait(this.timeout);
|
||||
try {
|
||||
long timeout = this.timeout;
|
||||
if(MAX_TIMEOUT > this.timeout * this.connectRetryTimes) {
|
||||
timeout = this.timeout * this.connectRetryTimes++;
|
||||
} else {
|
||||
timeout = MAX_TIMEOUT;
|
||||
}
|
||||
this.wait(timeout);
|
||||
} catch (InterruptedException e) {
|
||||
Log.d(Taoyao.class.getSimpleName(), "信令等待异常", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 读取
|
||||
@@ -300,7 +314,9 @@ public final class Taoyao {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(Taoyao.class.getSimpleName(), "接收信令异常", e);
|
||||
this.connect();
|
||||
if(this.socket.isClosed()) {
|
||||
this.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -340,6 +356,7 @@ public final class Taoyao {
|
||||
Log.w(Taoyao.class.getSimpleName(), "通道没有打开:" + message);
|
||||
return;
|
||||
}
|
||||
Log.i(Taoyao.class.getSimpleName(), "发送信令:" + message);
|
||||
try {
|
||||
this.output.write(this.encrypt(message));
|
||||
} catch (Exception e) {
|
||||
@@ -375,7 +392,10 @@ public final class Taoyao {
|
||||
/**
|
||||
* 释放连接
|
||||
*/
|
||||
private void disconnect() {
|
||||
private synchronized void disconnect() {
|
||||
if(!this.connect) {
|
||||
return;
|
||||
}
|
||||
Log.d(Taoyao.class.getSimpleName(), "释放信令:" + this.host + ":" + this.port);
|
||||
this.connect = false;
|
||||
CloseableUtils.close(this.input);
|
||||
@@ -389,11 +409,15 @@ public final class Taoyao {
|
||||
/**
|
||||
* 关闭信令
|
||||
*/
|
||||
private void close() {
|
||||
this.disconnect();
|
||||
public synchronized void close() {
|
||||
if(this.close) {
|
||||
return;
|
||||
}
|
||||
Log.d(Taoyao.class.getSimpleName(), "关闭信令:" + this.host + ":" + this.port);
|
||||
this.close = true;
|
||||
executor.shutdownNow();
|
||||
scheduled.shutdownNow();
|
||||
this.disconnect();
|
||||
this.executor.shutdownNow();
|
||||
this.scheduled.shutdownNow();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -474,10 +498,10 @@ public final class Taoyao {
|
||||
"clientType", this.clientType,
|
||||
"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()
|
||||
"signal", this.wifiSignal(),
|
||||
"battery", this.battery(),
|
||||
"charging", this.charging(),
|
||||
"recording", ClientRecorder.getInstance().isActive()
|
||||
));
|
||||
}
|
||||
|
||||
@@ -497,18 +521,51 @@ public final class Taoyao {
|
||||
* 心跳
|
||||
*/
|
||||
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()
|
||||
));
|
||||
if(this.close || !this.connect) {
|
||||
return;
|
||||
}
|
||||
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.wifiSignal(),
|
||||
"battery", this.battery(),
|
||||
"charging", this.charging(),
|
||||
"recording", ClientRecorder.getInstance().isActive()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 电量百分比
|
||||
*/
|
||||
private int battery() {
|
||||
return this.batteryManager == null ? -1 : this.batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 充电状态
|
||||
*/
|
||||
private boolean charging() {
|
||||
return
|
||||
this.batteryManager == null ?
|
||||
false :
|
||||
this.batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS) == BatteryManager.BATTERY_STATUS_CHARGING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WIFI信号强度
|
||||
*/
|
||||
private int wifiSignal() {
|
||||
if(this.wifiManager == null) {
|
||||
return -1;
|
||||
}
|
||||
final WifiInfo wifiInfo = this.wifiManager.getConnectionInfo();
|
||||
if(wifiInfo == null) {
|
||||
return -1;
|
||||
}
|
||||
final int signal = this.wifiManager.calculateSignalLevel(wifiInfo.getRssi());
|
||||
return signal / this.wifiManager.getMaxSignalLevel() * 100;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
android:hint="@string/signal_port"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="number"
|
||||
android:text="9999"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
@@ -29,7 +28,6 @@
|
||||
android:hint="@string/signal_host"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:text="192.168.0.100"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
@@ -46,7 +44,6 @@
|
||||
android:hint="@string/signal_name"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:text="mobile"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
@@ -63,7 +60,6 @@
|
||||
android:hint="@string/signal_client_id"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:text="mobile"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
@@ -80,7 +76,6 @@
|
||||
android:hint="@string/signal_username"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:text="taoyao"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
@@ -97,7 +92,6 @@
|
||||
android:hint="@string/signal_password"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textPassword"
|
||||
android:text="taoyao"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<resources>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
<integer name="timeout">5000</integer>
|
||||
<!-- 终端类型:CAMERA|MOBILE -->
|
||||
<string name="clientType">MOBILE</string>
|
||||
<!--信令加密算法-->
|
||||
<string name="algo">DES</string>
|
||||
<!--信令加密密钥-->
|
||||
<string name="secret">DES</string>
|
||||
<!-- 信令加密策略:AES|DES -->
|
||||
<string name="encrypt">DES</string>
|
||||
<!-- 信令加密密钥 -->
|
||||
<string name="encryptSecret">2SPWy+TF1zM=</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.acgist.media;
|
||||
|
||||
public class AudioPublisher {
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
package com.acgist.taoyao.client.media;
|
||||
package com.acgist.media;
|
||||
|
||||
/**
|
||||
* 录像机
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public final class Recorder {
|
||||
public final class ClientRecorder {
|
||||
|
||||
/**
|
||||
* 是否正在录像
|
||||
*/
|
||||
private boolean active;
|
||||
|
||||
private static final Recorder INSTANCE = new Recorder();
|
||||
private static final ClientRecorder INSTANCE = new ClientRecorder();
|
||||
|
||||
public static final Recorder getInstance() {
|
||||
public static final ClientRecorder getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.acgist.media;
|
||||
|
||||
/**
|
||||
* 房间终端
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public class RoomClient {
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.acgist.media;
|
||||
|
||||
public class VideoPublisher {
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
const config = require("./Config.js");
|
||||
const process = require("child_process");
|
||||
const WebSocket = require("ws");
|
||||
const { trace } = require("console");
|
||||
|
||||
/**
|
||||
* 信令协议
|
||||
@@ -1383,7 +1384,12 @@ class Taoyao {
|
||||
});
|
||||
// await transport.enableTraceEvent([ 'probation', 'bwe' ]);
|
||||
// transport.on("trace", (trace) => {
|
||||
// console.debug("transport trace:", transport.id, trace);
|
||||
// // 网络评估
|
||||
// if (trace.type === "bwe" && trace.direction === "out") {
|
||||
// logger.debug("transport downlinkBwe:", trace);
|
||||
// } else {
|
||||
// console.debug("transport trace:", transport.id, trace);
|
||||
// }
|
||||
// });
|
||||
transport.observer.on("close", () => {
|
||||
if(room.transports.delete(transport.id)) {
|
||||
|
||||
@@ -52,7 +52,7 @@ public class SocketProperties {
|
||||
@Schema(title = "加密策略", description = "加密策略")
|
||||
private Encrypt encrypt;
|
||||
@Schema(title = "加密密钥", description = "加密密钥:为空自动生成")
|
||||
private String encryptKey;
|
||||
private String encryptSecret;
|
||||
@Schema(title = "超时时间", description = "超时时间")
|
||||
private Long timeout;
|
||||
@Schema(title = "队列长度", description = "队列长度")
|
||||
|
||||
@@ -10,7 +10,6 @@ public class TaoyaoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TaoyaoApplication.class, args);
|
||||
// System.exit(SpringApplication.exit(SpringApplication.run(TaoyaoApplication.class, args)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ taoyao:
|
||||
host: 0.0.0.0
|
||||
port: 9999
|
||||
encrypt: DES
|
||||
encrypt-key: 2SPWy+TF1zM=
|
||||
encrypt-secret: 2SPWy+TF1zM=
|
||||
timeout: ${taoyao.timeout}
|
||||
queue-size: 100000
|
||||
min-thread: 4
|
||||
|
||||
@@ -39,7 +39,7 @@ public class SocketClient extends ClientAdapter<AsynchronousSocketChannel> {
|
||||
public SocketClient(SocketProperties socketProperties, AsynchronousSocketChannel instance) {
|
||||
super(socketProperties.getTimeout(), instance);
|
||||
this.ip = this.clientIp(instance);
|
||||
this.cipher = CipherUtils.buildCipher(Cipher.ENCRYPT_MODE, socketProperties.getEncrypt(), socketProperties.getEncryptKey());
|
||||
this.cipher = CipherUtils.buildCipher(Cipher.ENCRYPT_MODE, socketProperties.getEncrypt(), socketProperties.getEncryptSecret());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -66,7 +66,7 @@ public final class SocketSignalMessageHandler implements CompletionHandler<Integ
|
||||
this.messageLength = 0;
|
||||
this.bufferSize = socketProperties.getBufferSize();
|
||||
this.maxBufferSize = socketProperties.getMaxBufferSize();
|
||||
this.cipher = CipherUtils.buildCipher(Cipher.DECRYPT_MODE, socketProperties.getEncrypt(), socketProperties.getEncryptKey());
|
||||
this.cipher = CipherUtils.buildCipher(Cipher.DECRYPT_MODE, socketProperties.getEncrypt(), socketProperties.getEncryptSecret());
|
||||
this.buffer = ByteBuffer.allocateDirect(maxBufferSize);
|
||||
this.channel = channel;
|
||||
this.clientManager = clientManager;
|
||||
|
||||
@@ -58,25 +58,25 @@ public class SocketSignalAutoConfiguration {
|
||||
*/
|
||||
private void buildSecret(SocketProperties socketProperties) {
|
||||
log.info("Socket信令加密策略:{}", socketProperties.getEncrypt());
|
||||
if(socketProperties.getEncrypt() == null || StringUtils.isNotEmpty(socketProperties.getEncryptKey())) {
|
||||
if(socketProperties.getEncrypt() == null) {
|
||||
return;
|
||||
}
|
||||
final Random random = new Random();
|
||||
switch (socketProperties.getEncrypt()) {
|
||||
case AES -> {
|
||||
final byte[] bytes = new byte[16];
|
||||
random.nextBytes(bytes);
|
||||
socketProperties.setEncryptKey(Base64.getMimeEncoder().encodeToString(bytes));
|
||||
if(StringUtils.isNotEmpty(socketProperties.getEncryptSecret())) {
|
||||
log.info("Socket信令加密密码(固定):{}", socketProperties.getEncryptSecret());
|
||||
return;
|
||||
}
|
||||
case DES -> {
|
||||
final byte[] bytes = new byte[8];
|
||||
random.nextBytes(bytes);
|
||||
socketProperties.setEncryptKey(Base64.getMimeEncoder().encodeToString(bytes));
|
||||
final byte[] bytes = switch (socketProperties.getEncrypt()) {
|
||||
case AES -> new byte[16];
|
||||
case DES -> new byte[8];
|
||||
default -> null;
|
||||
};
|
||||
if(bytes == null) {
|
||||
final Random random = new Random();
|
||||
random.nextBytes(bytes);
|
||||
socketProperties.setEncryptSecret(Base64.getMimeEncoder().encodeToString(bytes));
|
||||
log.info("Socket信令加密密码(随机):{}", socketProperties.getEncryptSecret());
|
||||
} else {
|
||||
log.warn("Socket信令加密密码算法不支持的算法:{}", socketProperties.getEncrypt());
|
||||
default -> {
|
||||
// 其他情况使用明文
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.acgist.taoyao.signal.party.p2p;
|
||||
|
||||
public class Session {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.acgist.taoyao.signal.party.p2p;
|
||||
|
||||
public class SessionManager {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.acgist.taoyao.signal.protocol.p2p;
|
||||
|
||||
public class P2PAnswerProtocol {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.acgist.taoyao.signal.protocol.p2p;
|
||||
|
||||
public class P2PCallProtocol {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.acgist.taoyao.signal.protocol.p2p;
|
||||
|
||||
public class P2PCandidateProtocol {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.acgist.taoyao.signal.protocol.p2p;
|
||||
|
||||
public class P2POfferProtocol {
|
||||
|
||||
}
|
||||
@@ -28,22 +28,22 @@ public class CipherUtils {
|
||||
/**
|
||||
* @param mode 模式
|
||||
* @param encrypt 算法
|
||||
* @param key 密钥
|
||||
* @param secret 密钥
|
||||
*
|
||||
* @return 加密工具
|
||||
*/
|
||||
public static final Cipher buildCipher(int mode, Encrypt encrypt, String key) {
|
||||
if(encrypt == null || encrypt == Encrypt.PLAINTEXT || StringUtils.isEmpty(key)) {
|
||||
public static final Cipher buildCipher(int mode, Encrypt encrypt, String secret) {
|
||||
if(encrypt == null || encrypt == Encrypt.PLAINTEXT || StringUtils.isEmpty(secret)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final String algo = encrypt.getAlgo();
|
||||
final String name = encrypt.name();
|
||||
final Cipher cipher = Cipher.getInstance(algo);
|
||||
cipher.init(mode, new SecretKeySpec(Base64.getMimeDecoder().decode(key), name));
|
||||
cipher.init(mode, new SecretKeySpec(Base64.getMimeDecoder().decode(secret), name));
|
||||
return cipher;
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
|
||||
log.error("创建加密工具异常:{} - {} - {}", mode, encrypt, key, e);
|
||||
log.error("创建加密工具异常:{} - {} - {}", mode, encrypt, secret, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user