短信发送本地server模式更新 (#115)

* Update SmsForwarder.yml

* 更新:smsHub主被动模式优化

* 更新:smsHub主被动模式优化

* Update SmsForwarder.yml

Co-authored-by: pppscn <35696959@qq.com>
pull/127/head
xingxichen 2 years ago committed by GitHub
parent 5d25425781
commit 859f8eec83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,55 @@
package com.idormy.sms.forwarder.model.vo;
import lombok.Data;
@Data
public class ResVo<T> {
public static final int ERROR_CODE = 500;
public static final int Suess_CODE = 200;
private int code;
private String msg;
private String auth;
private SmsHubVo heartbeat;
private T data;
public ResVo<T> setSuessces(T data) {
this.setCode(Suess_CODE);
this.setData(data);
this.setMsg("");
return this;
}
public ResVo<T> setError(Throwable e) {
this.setCode(ERROR_CODE);
this.setMsg(e.getMessage());
return this;
}
public ResVo<T> setError(String msg) {
this.setCode(ERROR_CODE);
this.setMsg(msg);
return this;
}
public static <T> ResVo<T> suessces(T data) {
ResVo<T> resVo = new ResVo<>();
resVo.setCode(Suess_CODE);
resVo.setData(data);
resVo.setMsg("");
return resVo;
}
public static <T> ResVo<T> error(Throwable e) {
ResVo<T> resVo = new ResVo<>();
resVo.setCode(ERROR_CODE);
resVo.setMsg(e.getMessage());
return resVo;
}
public static ResVo<String> error(String msg) {
ResVo<String> resVo = new ResVo<>();
resVo.setCode(ERROR_CODE);
resVo.setMsg(msg);
return resVo;
}
}

@ -28,7 +28,7 @@ public class SmsHubVo implements Serializable {
public SmsHubVo(Type type, Integer simId, String content, String target) {
this.msgId = UUID.randomUUID().toString();
if (channel != null) {
if (simId != null) {
String simInfo = simId == 2 ? SettingUtil.getAddExtraSim2() : SettingUtil.getAddExtraSim1(); //自定义备注优先
simInfo = "SIM" + simId + ":" + simInfo;
this.channel = simInfo;
@ -37,6 +37,7 @@ public class SmsHubVo implements Serializable {
this.target = target;
this.action = Action.receive.code();
this.type = type.code;
this.ts = System.currentTimeMillis() + "";
}
//唯一id
@ -51,22 +52,25 @@ public class SmsHubVo implements Serializable {
private String errMsg;
//手机号(;分隔)或包名
private String target;
//状态或操作
//状态或操作 0:发送短信, 1:接收到的消息, 2:操作处理成功, 3:操作处理失败, -1:心跳包 (包含deviceInfo字段,children里带有两次心跳间收到的消息)
private String action;
//消息类型
//消息类型 app:通知 phone:来电, sms:短信, battery:电池信息
private String type;
//时间戳
private String ts;
//两次交互之间接收到的消息
private List<SmsHubVo> children;
public static SmsHubVo heartbeatInstance() {
public static SmsHubVo heartbeatInstance(List<SmsHubVo> data) {
SmsHubVo smsHubVo = new SmsHubVo();
HashMap<String, String> deviInfoMap = getDevInfoMap(false);
smsHubVo.setDeviceInfo(JSON.toJSONString(deviInfoMap));
smsHubVo.setChannel("SIM1:" + SimUtil.getSimInfo(1) + SettingUtil.getAddExtraSim1() + ";SIM2:" + SimUtil.getSimInfo(2) + SettingUtil.getAddExtraSim2());
smsHubVo.setTs(Long.toString(System.currentTimeMillis()));
smsHubVo.setAction(SmsHubVo.Action.heartbeat.code());
if (data != null && data.size() > 0) {
smsHubVo.setChildren(data);
}
return smsHubVo;
}

@ -5,7 +5,9 @@ import android.content.Context;
import android.util.Log;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.util.IOUtils;
import com.idormy.sms.forwarder.model.vo.ResVo;
import com.idormy.sms.forwarder.model.vo.SmsHubVo;
import com.idormy.sms.forwarder.utils.BackupDbTask;
import com.idormy.sms.forwarder.utils.SettingUtil;
@ -14,6 +16,7 @@ import com.idormy.sms.forwarder.utils.SmsHubActionHandler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.StringUtil;
import java.io.BufferedReader;
import java.io.File;
@ -43,7 +46,7 @@ public class BaseServlet extends HttpServlet {
public static final String SMSHUB_PATH = "/send_api";
private static final long serialVersionUID = 1L;
private static final String TAG = "BaseServlet";
private static final SmsHubActionHandler.SmsHubMode smsHubMode = SmsHubActionHandler.SmsHubMode.server;
public static final SmsHubActionHandler.SmsHubMode smsHubMode = SmsHubActionHandler.SmsHubMode.server;
public BaseServlet(String path, Context context) {
this.path = path;
@ -115,18 +118,6 @@ public class BaseServlet extends HttpServlet {
}
}
private void notFound(HttpServletRequest req, HttpServletResponse resp) throws IOException {
PrintWriter writer = resp.getWriter();
try {
String text = "NOT FOUND";
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
writer.println(text);
} catch (Exception e) {
e.printStackTrace();
} finally {
IOUtils.close(writer);
}
}
private void send_api(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setCharacterEncoding("utf-8");
@ -135,28 +126,31 @@ public class BaseServlet extends HttpServlet {
try {
String read = read(reader);
Log.i(TAG, "Request message:" + read);
List<SmsHubVo> smsHubVos = JSON.parseArray(read, SmsHubVo.class);
if (smsHubVos.size() == 1 && SmsHubVo.Action.heartbeat.code().equals(smsHubVos.get(0).getAction())) {
smsHubVos.clear();
SmsHubVo smsHubVo = SmsHubVo.heartbeatInstance();
smsHubVos.add(smsHubVo);
List<SmsHubVo> data = SmsHubActionHandler.getData(smsHubMode);
if (data != null && data.size() > 0) {
smsHubVo.setChildren(data);
}
} else {
for (SmsHubVo vo : smsHubVos) {
SmsHubVo smsHubVo = SmsHubVo.heartbeatInstance(SmsHubActionHandler.getData(smsHubMode));
ResVo<List<SmsHubVo>> result = ResVo.suessces(null);
if (StringUtil.isNotBlank(read)) {
ResVo<List<SmsHubVo>> obj = JSON.parseObject(read, new TypeReference<ResVo<List<SmsHubVo>>>() {
}.getType());
List<SmsHubVo> data = obj.getData();
for (SmsHubVo vo : data) {
SmsHubActionHandler.handle(TAG, vo);
}
List<SmsHubVo> data = SmsHubActionHandler.getData(smsHubMode);
if (data != null && data.size() > 0) {
SmsHubVo smsHubVo = SmsHubVo.heartbeatInstance();
smsHubVo.setChildren(data);
smsHubVos.add(smsHubVo);
}
result.setData(data);
}
result.setHeartbeat(smsHubVo);
resp.setContentType("application/json;charset=utf-8");
String text = JSON.toJSONString(smsHubVos);
StringBuilder sb = new StringBuilder();
int i = 0;
for (SmsHubVo datum : result.getData()) {
if (SmsHubVo.Action.failure.code().equals(datum.getAction())) {
sb.append(",").append("第").append(i++).append("条处理失败:").append(datum.getErrMsg());
}
i++;
}
if (sb.length() > 0) {
result.setError(sb.substring(1));
}
String text = JSON.toJSONString(result);
writer.println(text);
} catch (Exception e) {
e.printStackTrace();
@ -170,8 +164,9 @@ public class BaseServlet extends HttpServlet {
private void printErrMsg(HttpServletResponse resp, PrintWriter writer, Exception e) {
String text = "Internal server error: " + e.getMessage();
Log.e(TAG, text);
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
writer.println(text);
resp.setStatus(HttpServletResponse.SC_OK);
resp.setContentType("application/json;charset=utf-8");
writer.println(JSON.toJSONString(ResVo.error(text)));
}
//一键克隆——查询接口

@ -6,29 +6,26 @@ import android.util.Log;
import com.alibaba.fastjson.JSON;
import com.idormy.sms.forwarder.model.vo.SmsHubVo;
import com.idormy.sms.forwarder.utils.HttpUtil;
import com.idormy.sms.forwarder.utils.SettingUtil;
import com.idormy.sms.forwarder.utils.SmsHubActionHandler;
import com.idormy.sms.forwarder.utils.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.List;
import java.util.Objects;
import java.util.Timer;
import java.util.TimerTask;
/**
*
*
* @author xxc
* 2022/1/10 9:53
*/
public class SmsHubApiTask extends TimerTask {
private static Boolean hasInit = false;
public static final long DELAY_SECONDS = 30;
private static final String TAG = "SmsHubApiTask";
private static final SmsHubActionHandler.SmsHubMode smsHubMode = SmsHubActionHandler.SmsHubMode.client;
private static Boolean hasInit = false;
private static Timer sendApiTimer;
@SuppressLint("StaticFieldLeak")
private static Context context;
private static final SmsHubActionHandler.SmsHubMode smsHubMode = SmsHubActionHandler.SmsHubMode.client;
@SuppressLint("HandlerLeak")
public static void init(Context context) {
@ -42,6 +39,35 @@ public class SmsHubApiTask extends TimerTask {
}
}
@Override
public void run() {
List<SmsHubVo> data = SmsHubActionHandler.getData(smsHubMode);
SmsHubVo smsHubVo = SmsHubVo.heartbeatInstance(data);
String url = SettingUtil.getSmsHubApiUrl();
boolean asRetry = data != null && data.size() > 0;
AtomicBoolean isSusess = new AtomicBoolean(false);
Runnable runnable = () -> {
HttpUtil.asyncPostJson(TAG, url, smsHubVo, response -> {
//HttpUtil.Toast(TAG, "Response" + response.code() + "" + responseStr);
if (response.code() == 200) {
isSusess.set(true);
String responseStr = Objects.requireNonNull(response.body()).string();
List<SmsHubVo> vos = JSON.parseArray(responseStr, SmsHubVo.class);
for (SmsHubVo vo : vos) {
SmsHubActionHandler.handle(TAG, vo);
}
SmsHubActionHandler.putData(smsHubMode, vos.toArray(new SmsHubVo[0]));
}
}, null, asRetry);
};
if (asRetry) {
new Thread(runnable).start();
} else {
runnable.run();
}
}
public static void updateTimer() {
cancelTimer();
if (SettingUtil.getSwitchEnableSmsHubApi()) {
@ -69,32 +95,4 @@ public class SmsHubApiTask extends TimerTask {
sendApiTimer.schedule(new SmsHubApiTask(), 3000, seconds * 1000);
}
}
@Override
public void run() {
try {
SmsHubVo smsHubVo = SmsHubVo.heartbeatInstance();
List<SmsHubVo> data = SmsHubActionHandler.getData(smsHubMode);
if (data != null && data.size() > 0) {
smsHubVo.setChildren(data);
}
smsHubVo.setChildren(data);
String url = SettingUtil.getSmsHubApiUrl();
HttpUtil.asyncPostJson(TAG, url, smsHubVo, response -> {
//HttpUtil.Toast(TAG, "Response" + response.code() + "" + responseStr);
if (response.code() == 200) {
String responseStr = Objects.requireNonNull(response.body()).string();
List<SmsHubVo> vos = JSON.parseArray(responseStr, SmsHubVo.class);
for (SmsHubVo vo : vos) {
SmsHubActionHandler.handle(TAG, vo);
}
SmsHubActionHandler.putData(smsHubMode, vos.toArray(new SmsHubVo[0]));
}
}, null);
} catch (Exception e) {
HttpUtil.Toast(TAG, "SmsHubApiTask 执行出错,请检查问题后重新开启" + e.getMessage());
cancelTimer();
SettingUtil.switchEnableSmsHubApi(false);
}
}
}

@ -9,11 +9,13 @@ import androidx.annotation.NonNull;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.hjq.toast.ToastUtils;
import com.idormy.sms.forwarder.sender.RetryIntercepter;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
@ -25,7 +27,8 @@ import okhttp3.Response;
@SuppressWarnings("unchecked")
public class HttpUtil {
private static final OkHttpClient client = new OkHttpClient();
private static OkHttpClient client;
private static OkHttpClient retryClient;
private static final String TAG = "HttpUtil";
private static Boolean hasInit = false;
@SuppressLint("StaticFieldLeak")
@ -40,30 +43,43 @@ public class HttpUtil {
hasInit = true;
HttpUtil.context = context;
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
//设置读取超时时间
clientBuilder
.readTimeout(Define.REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.writeTimeout(Define.REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.connectTimeout(Define.REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
client = clientBuilder.build();
//设置重试拦截器
int retryTimes = SettingUtil.getRetryTimes();
int delayTime = SettingUtil.getDelayTime();
if (retryTimes > 0)
clientBuilder.addInterceptor(new RetryIntercepter.Builder().executionCount(retryTimes).retryInterval(delayTime).build());
retryClient = clientBuilder.build();
}
}
public static void asyncGet(String tag, String url, Object param, Lamda.Consumer<Response> onResponse, Lamda.Consumer<Exception> onFailure) {
public static void asyncGet(String tag, String url, Object param, Lamda.Consumer<Response> onResponse, Lamda.Consumer<Exception> onFailure, boolean doRetry) {
StringBuilder resUrl = appendQueryStr(tag, url, param);
Request request = new Request.Builder().url(resUrl.toString()).get().build();
Lamda.Func<Call, String> func = call -> {
call.enqueue(new Callback0(tag, onResponse, onFailure));
return null;
};
callAndCatch(tag, request, func);
callAndCatch(tag, request, func, doRetry);
}
public static void asyncPostJson(String tag, String url, Object param, Lamda.Consumer<Response> onResponse, Lamda.Consumer<Exception> onFailure) {
public static void asyncPostJson(String tag, String url, Object param, Lamda.Consumer<Response> onResponse, Lamda.Consumer<Exception> onFailure, boolean doRetry) {
String jsonString = JSON.toJSONString(param);
Request request = new Request.Builder().url(url).post(RequestBody.create(jsonString, MEDIA_TYPE_JSON)).build();
Lamda.Func<Call, String> func = call -> {
call.enqueue(new Callback0(tag, onResponse, onFailure));
return null;
};
callAndCatch(tag, request, func);
callAndCatch(tag, request, func, doRetry);
}
public static String postJson(String tag, String url, Object param) {
public static String postJson(String tag, String url, Object param, boolean doRetry) {
String jsonString = JSON.toJSONString(param);
Request request = new Request.Builder().url(url).post(RequestBody.create(jsonString, MEDIA_TYPE_JSON)).build();
Lamda.Func<Call, String> func = call -> {
@ -73,10 +89,10 @@ public class HttpUtil {
}
return null;
};
return callAndCatch(tag, request, func);
return callAndCatch(tag, request, func, doRetry);
}
public static String get(String tag, String url, Object param) {
public static String get(String tag, String url, Object param, boolean doRetry) {
StringBuilder resUrl = appendQueryStr(tag, url, param);
Request request = new Request.Builder().url(resUrl.toString()).get().build();
Lamda.Func<Call, String> func = call -> {
@ -86,7 +102,7 @@ public class HttpUtil {
}
return null;
};
return callAndCatch(tag, request, func);
return callAndCatch(tag, request, func, doRetry);
}
public static void Toast(String Tag, String data) {
@ -119,9 +135,9 @@ public class HttpUtil {
return resUrl;
}
public static String callAndCatch(String tag, Request request, Lamda.Func<Call, String> func) {
public static String callAndCatch(String tag, Request request, Lamda.Func<Call, String> func, boolean doRetry) {
try {
Call call = client.newCall(request);
Call call = (doRetry ? retryClient : client).newCall(request);
return func.execute(call);
} catch (Exception e) {
Toast(tag, "请求失败:" + e.getMessage());
@ -158,7 +174,7 @@ public class HttpUtil {
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
Log.d(tag, "onResponse" + response.code() + ":" + Objects.requireNonNull(response.body()).toString());
Log.d(tag, "onResponse" + response.code() + ":" + JSON.toJSONString(Objects.requireNonNull(response.body())));
if (onResponse != null)
onResponse.executeThrowRunTimeExcp(response);
}

@ -4,8 +4,10 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;
import com.alibaba.fastjson.JSON;
import com.idormy.sms.forwarder.model.LogModel;
import com.idormy.sms.forwarder.model.vo.SmsHubVo;
import com.idormy.sms.forwarder.sender.SmsHubApiTask;
import java.util.ArrayList;
import java.util.Arrays;
@ -56,8 +58,17 @@ public class SmsHubActionHandler {
}
}
private static long falg = System.currentTimeMillis();
public static synchronized void putData(SmsHubMode smsHubMode, SmsHubVo... smsHubVos) {
if (isEnable(smsHubMode)) {
if (smsHubMode == SmsHubMode.server) {
long l = falg;
falg = System.currentTimeMillis();
if (System.currentTimeMillis() - l > SmsHubApiTask.DELAY_SECONDS * 3) {
return;
}
}
Objects.requireNonNull(cache.get(smsHubMode)).addAll(Arrays.asList(smsHubVos));
}
}
@ -79,11 +90,12 @@ public class SmsHubActionHandler {
}
public static void handle(String tag, SmsHubVo vo) {
Log.i(tag, JSON.toJSONString(vo));
String action = vo.getAction();
if (SmsHubVo.Action.send.code().equals(action)) {
send(tag, vo);
} else {
String errMsg = "暂不支持的action:" + action;
String errMsg = "暂不支持的action[" + action + "]";
vo.setErrMsg(errMsg);
vo.setAction(SmsHubVo.Action.failure.code());
}
@ -97,11 +109,12 @@ public class SmsHubActionHandler {
try {
if (SmsHubVo.Action.send.code().equals(vo.getAction())) {
vo.setType(SmsHubVo.Type.sms.code());
logId = LogUtil.addLog(new LogModel(vo.getType(), vo.getTarget(), vo.getContent(), "SIM" + vo.getChannel(), RULE_ID));
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
int simId = Integer.parseInt(vo.getChannel());
vo.setChannel("SIM" + simId);
msg = SmsUtil.sendSms(SimUtil.getSubscriptionIdBySimId(simId), vo.getTarget(), vo.getContent());
int subscriptionIdBySimId = SimUtil.getSubscriptionIdBySimId(Integer.parseInt(vo.getChannel()) - 1);
msg = SmsUtil.sendSms(subscriptionIdBySimId, vo.getTarget(), vo.getContent());
String simInfo = "SIM" + (subscriptionIdBySimId + 1);
vo.setChannel(simInfo);
logId = LogUtil.addLog(new LogModel(vo.getType(), vo.getTarget(), vo.getContent(), simInfo, RULE_ID));
if (msg == null) {
failure = false;
HttpUtil.Toast(tag, "短信发送成功");

Loading…
Cancel
Save