diff --git a/foudations/mogo-live/src/main/java/com/mogo/cloud/live/socket/SocketRequestUtils.java b/foudations/mogo-live/src/main/java/com/mogo/cloud/live/socket/SocketRequestUtils.java index f759c7f..787aaf5 100644 --- a/foudations/mogo-live/src/main/java/com/mogo/cloud/live/socket/SocketRequestUtils.java +++ b/foudations/mogo-live/src/main/java/com/mogo/cloud/live/socket/SocketRequestUtils.java @@ -1,7 +1,7 @@ package com.mogo.cloud.live.socket; +import com.mogo.cloud.TelephoneUtil; import com.zhidao.ptech.shadow.server.protocol.DeviceInfo; -import com.zhidao.utils.common.TelephoneUtil; public class SocketRequestUtils { public static byte[] buildDeviceData(int c1, int c2) { diff --git a/foudations/mogo-socket/build.gradle b/foudations/mogo-socket/build.gradle index 573ef91..409427a 100644 --- a/foudations/mogo-socket/build.gradle +++ b/foudations/mogo-socket/build.gradle @@ -26,21 +26,26 @@ android { dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) - // socket-for-sdk 长链 http://wiki.zhidaohulian.com/pages/viewpage.action?pageId=48956182 外部SDK版本 - api 'com.zhidao.socket:built-in-socket:1.0.23-SNAPSHOT' - // 上报位置 http://wiki.zhidaohulian.com/pages/viewpage.action?pageId=48956200 - implementation 'com.zhidao.locupload:loc-upload-sdk:1.2.0' + implementation 'com.google.protobuf:protobuf-java:3.12.4' + + implementation 'io.netty:netty-all:4.1.8.Final' + api 'com.zhidao.ptech:connsvr-protoco:0.1.37' + api 'com.elegant.network:network:1.1.28' + api 'com.elegant.analytics:analytics:1.1.28' // socket-for-internal 长链 内部SDK版本 implementation 'com.zhidao.socketsdk:socketsdk:2.1.4' - implementation 'com.google.protobuf:protobuf-java:3.12.4' + + // 上报位置 http://wiki.zhidaohulian.com/pages/viewpage.action?pageId=48956200 + implementation 'com.zhidao.locupload:loc-upload-sdk:1.2.0' if (Boolean.valueOf(RELEASE)) { implementation "com.mogo.cloud:passport:${MOGO_PASSPORT_VERSION}" + implementation "com.mogo.cloud:utils:${MOGO_UTILS_VERSION}" } else { implementation project(path: ':foudations:mogo-passport') + implementation project(path: ':foudations:mogo-utils') } - } apply from: new File(rootProject.rootDir, "gradle/upload.gradle").toString() \ No newline at end of file diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/IMogoCloudSocketManager.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/IMogoCloudSocketManager.java index fdf3bba..705d16b 100644 --- a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/IMogoCloudSocketManager.java +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/IMogoCloudSocketManager.java @@ -3,7 +3,7 @@ package com.mogo.cloud.socket; import android.content.Context; import com.mogo.cloud.socket.entity.MsgBody; -import com.zhidao.socket.ConnectionLifecycleListener; +import com.mogo.cloud.socket.third.core.ConnectionLifecycleListener; /** diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/SocketManager.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/SocketManager.java index 7ca6d50..363e921 100644 --- a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/SocketManager.java +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/SocketManager.java @@ -12,9 +12,9 @@ import com.mogo.cloud.socket.entity.MsgBody; import com.mogo.cloud.socket.entity.SocketDownData; import com.mogo.cloud.socket.internal.InternalSocketManager; import com.mogo.cloud.socket.third.ThirdSocketManager; +import com.mogo.cloud.socket.third.core.ConnectionLifecycleListener; import com.mogo.cloud.utils.logger.Logger; import com.zhidao.ptech.connsvr.protocol.MogoConnsvr; -import com.zhidao.socket.ConnectionLifecycleListener; import java.util.ArrayList; import java.util.Iterator; diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/SocketServicesConstants.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/SocketServicesConstants.java index 847f173..f595e16 100644 --- a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/SocketServicesConstants.java +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/SocketServicesConstants.java @@ -4,12 +4,10 @@ package com.mogo.cloud.socket; import com.mogo.cloud.passport.MoGoAiCloudClient; import com.mogo.cloud.passport.MoGoAiCloudClientConfig; -import com.zhidao.socket.Environment; +import com.mogo.cloud.socket.third.core.Environment; -import static com.mogo.cloud.httpdns.MogoHttpDnsConfig.HTTP_DNS_ENV_DEMO; import static com.mogo.cloud.httpdns.MogoHttpDnsConfig.HTTP_DNS_ENV_DEV; import static com.mogo.cloud.httpdns.MogoHttpDnsConfig.HTTP_DNS_ENV_QA; -import static com.mogo.cloud.httpdns.MogoHttpDnsConfig.HTTP_DNS_ENV_RELEASE; import androidx.annotation.Keep; @@ -27,11 +25,11 @@ public class SocketServicesConstants { MoGoAiCloudClientConfig cloudClientConfig = MoGoAiCloudClient.getInstance().getAiCloudClientConfig(); switch (cloudClientConfig.getNetMode()) { case HTTP_DNS_ENV_DEV: - return com.zhidao.socket.Environment.dev; + return Environment.dev; case HTTP_DNS_ENV_QA: - return com.zhidao.socket.Environment.qa; + return Environment.qa; default: - return com.zhidao.socket.Environment.release; + return Environment.release; } } } diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/ThirdSocketManager.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/ThirdSocketManager.java index 575b3d2..a0c89e1 100644 --- a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/ThirdSocketManager.java +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/ThirdSocketManager.java @@ -6,14 +6,14 @@ import com.mogo.cloud.passport.MoGoAiCloudClient; import com.mogo.cloud.passport.MoGoAiCloudClientConfig; import com.mogo.cloud.socket.SocketManager; import com.mogo.cloud.socket.SocketServicesConstants; +import com.mogo.cloud.socket.third.core.Callback; +import com.mogo.cloud.socket.third.core.CallbackManager; +import com.mogo.cloud.socket.third.core.ConnectionLifecycleListener; +import com.mogo.cloud.socket.third.core.SocketClient; +import com.mogo.cloud.socket.third.core.SocketConfig; import com.mogo.cloud.utils.logger.Logger; import com.zhidao.locupload.Platform; import com.zhidao.ptech.connsvr.commom.protocol.MogoCommon; -import com.zhidao.socket.Callback; -import com.zhidao.socket.CallbackManager; -import com.zhidao.socket.ConnectionLifecycleListener; -import com.zhidao.socket.SocketClient; -import com.zhidao.socket.SocketConfig; import static com.mogo.cloud.socket.SocketServicesConstants.SOCKET_CHANNEL_ID; import static com.mogo.cloud.socket.SocketServicesConstants.TAG; diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/analytics/EventManager.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/analytics/EventManager.java new file mode 100644 index 0000000..523ee7a --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/analytics/EventManager.java @@ -0,0 +1,30 @@ +package com.mogo.cloud.socket.third.analytics; + +import com.elegant.analytics.Analytics; + +import org.json.JSONObject; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +public class EventManager { + + private static SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA); + + public static void trackEvent(String event, JSONObject jsonObject) { + if (jsonObject == null) { + return; + } + try { + jsonObject.put("time", getCurrentTime()); + Analytics.getInstance().track(event, jsonObject); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static String getCurrentTime() { + return format.format(new Date()); + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/analytics/EventRequest.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/analytics/EventRequest.java new file mode 100644 index 0000000..aeeb7ef --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/analytics/EventRequest.java @@ -0,0 +1,74 @@ +package com.mogo.cloud.socket.third.analytics; + +import android.text.TextUtils; + +import com.elegant.log.simplelog.Logger; +import com.elegant.utils.CheckUtils; +import com.mogo.cloud.socket.third.core.SocketConfig; + +import org.json.JSONException; +import org.json.JSONObject; + +public class EventRequest { + + //socket授权状态 + private static final String SOCKET_STATUS = "socket_status"; + //异常情况 + private static final String EXCEPTION = "exception"; + private static final String TAG = "EventRequest"; + + /** + * socket状态相关 + * + * @param status: socket 状态 + */ + public static void trackSocketStatus(String status, String desc) { + if(!SocketConfig.instance().isAnalyticsOpen()){ + Logger.d(TAG,"analytics is closed"); + return; + } + EventManager.trackEvent(SOCKET_STATUS, buildAuthStatus(status, desc)); + } + + /** + * 埋点异常情况 + * + * @param reason: 异常场景 + */ + public static void trackException(String reason) { + if(!SocketConfig.instance().isAnalyticsOpen()){ + Logger.d(TAG,"analytics is closed"); + return; + } + if (TextUtils.isEmpty(reason)) { + return; + } + EventManager.trackEvent(EXCEPTION, buildException(reason)); + } + + private static JSONObject buildAuthStatus(String status, String desc) { + try { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("socket_status", status); + jsonObject.put("desc", desc); + if(SocketConfig.instance().getAppContext() != null){ + jsonObject.put("net", CheckUtils.isNetworkConnected(SocketConfig.instance().getAppContext())); + } + return jsonObject; + } catch (JSONException e) { + e.printStackTrace(); + } + return null; + } + + private static JSONObject buildException(String reason) { + try { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("exception", reason); + return jsonObject; + } catch (JSONException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/analytics/SocketStatus.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/analytics/SocketStatus.java new file mode 100644 index 0000000..f7a0c9b --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/analytics/SocketStatus.java @@ -0,0 +1,17 @@ +package com.mogo.cloud.socket.third.analytics; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.SOURCE) +public @interface SocketStatus { + + //socket 断开连接 + String socket_disconnect = "socket_disconnect"; + + //鉴权失败 + String auth_rsp_failed = "auth_rsp_failed"; + + //发送用户信息反馈失败 + String send_userInfo_rsp_failed = "send_userInfo_rsp_failed"; +} \ No newline at end of file diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/Callback.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/Callback.java new file mode 100644 index 0000000..c5e9245 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/Callback.java @@ -0,0 +1,16 @@ +package com.mogo.cloud.socket.third.core; + +import android.support.annotation.NonNull; + +public interface Callback { + + /** + * Callback's method invoked when message is received by client + * + * @param manager callback manager. + * @param message message. + */ + void update(@NonNull CallbackManager manager, @NonNull byte[] message, String appId, long msgId); + + void onAck(@NonNull CallbackManager manager, @NonNull byte[] header, byte[] content); +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/CallbackManager.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/CallbackManager.java new file mode 100644 index 0000000..05208ab --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/CallbackManager.java @@ -0,0 +1,51 @@ +package com.mogo.cloud.socket.third.core; + +import android.support.annotation.NonNull; +import android.support.v4.util.ArraySet; + +import com.mogo.cloud.utils.logger.Logger; + +import java.util.Set; + +public final class CallbackManager { + private final Set observers = new ArraySet<>(); + + private CallbackManager() { + } + + public static CallbackManager getInstance() { + return SingletonHolder.INSTANCE; + } + + private static final class SingletonHolder { + private final static CallbackManager INSTANCE = new CallbackManager(); + } + + public synchronized void register(Callback callback) { + if (callback == null) { + Logger.i(SocketConstants.TAG, "Can not register a callback that is null."); + return; + } + observers.add(callback); + } + + public synchronized void unregister(Callback callback) { + observers.remove(callback); + } + + public synchronized void unregisterAll() { + observers.clear(); + } + + public synchronized void notify(@NonNull byte[] content, String appId, long msgId) { + for (Callback callback : observers) { + callback.update(this, content, appId, msgId); + } + } + + public synchronized void ack(@NonNull byte[] header, byte[] content) { + for (Callback callback : observers) { + callback.onAck(this, header, content); + } + } +} \ No newline at end of file diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/ConnCallbackManager.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/ConnCallbackManager.java new file mode 100644 index 0000000..b62c995 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/ConnCallbackManager.java @@ -0,0 +1,57 @@ +package com.mogo.cloud.socket.third.core; + +import android.support.v4.util.ArraySet; + +import com.mogo.cloud.utils.logger.Logger; + +import java.util.Set; + +public class ConnCallbackManager { + private ConnCallbackManager() { + } + + public static ConnCallbackManager getInstance() { + return SingletonHolder.INSTANCE; + } + + private static final class SingletonHolder { + private final static ConnCallbackManager INSTANCE = new ConnCallbackManager(); + } + + private final Set observers = new ArraySet<>(); + + public synchronized void register(ConnectionLifecycleListener callback) { + if (callback == null) { + Logger.i(SocketConstants.TAG, "Can not register a callback that is null."); + return; + } + observers.add(callback); + } + + public synchronized void unregister(ConnectionLifecycleListener callback) { + observers.remove(callback); + } + + public synchronized void unregisterAll() { + observers.clear(); + } + + public void onConnectFailure() { + for (ConnectionLifecycleListener callback : observers) { + callback.onConnectFailure(); + } + } + + public void onConnectSuccess() { + for (ConnectionLifecycleListener callback : observers) { + callback.onConnectSuccess(); + } + } + + public void onConnectLost(boolean reconnect) { + for (ConnectionLifecycleListener callback : observers) { + callback.onConnectLost(reconnect); + } + } + +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/ConnectionLifecycleListener.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/ConnectionLifecycleListener.java new file mode 100644 index 0000000..3d333dc --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/ConnectionLifecycleListener.java @@ -0,0 +1,13 @@ +package com.mogo.cloud.socket.third.core; + +public abstract class ConnectionLifecycleListener { + + public void onConnectFailure() { + } + + public void onConnectSuccess() { + } + + public void onConnectLost(boolean reconnect) { + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/Environment.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/Environment.java new file mode 100644 index 0000000..8781de3 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/Environment.java @@ -0,0 +1,27 @@ +package com.mogo.cloud.socket.third.core; + +public enum Environment { + release(3), + qa(2), + dev(1), + ; + + private int env; + + Environment(int env) { + this.env = env; + } + + public int getEnv(){ + return env; + } + + public static Environment get(int env){ + for(Environment environment : values()){ + if(environment.getEnv() == env){ + return environment; + } + } + return release; + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/SocketClient.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/SocketClient.java new file mode 100644 index 0000000..bb97c0e --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/SocketClient.java @@ -0,0 +1,215 @@ +package com.mogo.cloud.socket.third.core; + +import android.content.Context; +import android.text.TextUtils; + +import com.elegant.network.NetConfig; +import com.elegant.utils.UiThreadHandler; +import com.mogo.cloud.socket.third.core.client.ClientSocketManager; +import com.mogo.cloud.socket.third.core.network.RequestManager; +import com.mogo.cloud.socket.third.core.network.model.SocketAddressData; +import com.mogo.cloud.socket.third.utils.RequestUtil; +import com.mogo.cloud.utils.logger.Logger; +import com.zhidao.ptech.connsvr.commom.protocol.MogoCommon; +import com.zhidao.ptech.connsvr.payload.protocol.MogoPayload; +import com.zhidao.ptech.connsvr.protocol.MogoConnsvr; + +public class SocketClient { + + private static final String TAG = "SocketClient"; + + private Context mAppContext; + + private volatile boolean mHasStarted; + + private SocketClient() { + } + + public static SocketClient getInstance() { + return SingletonHolder.INSTANCE; + } + + private static final class SingletonHolder { + private static final SocketClient INSTANCE = new SocketClient(); + } + + /** + * 启动socket + * + * @param context app context + */ + public synchronized void start(Context context) { + if (mHasStarted) { + Logger.d(TAG, "socket already started"); + return; + } + if (context == null) { + throw new NullPointerException("context cannot be null"); + } + Logger.d(TAG, "start socket"); + + //ensure context be set + SocketConfig.instance().setAppContext(mAppContext = context.getApplicationContext()); + initNetConfig(); + requestSocketAddress(); + mHasStarted = true; + } + + /** + * 停止socket + */ + public synchronized void stop() { + if (!mHasStarted) { + Logger.d(TAG, "socket already stopped"); + return; + } + ClientSocketManager.getInstance().close(); + mHasStarted = false; + Logger.d(TAG, "stop"); + } + + /** + * 注册Socket Callback + * + * @param callback 用于接收socket消息的callback + */ + public void registerSocketCallback(Callback callback) { + CallbackManager.getInstance().register(callback); + } + + /** + * 注销Socket Callback + * + * @param callback 用于接收socket消息的callback + */ + public void unregisterSocketCallback(Callback callback) { + CallbackManager.getInstance().unregister(callback); + } + + /** + * 通过socket发送上行数据 + * + * @param appId 此app id可被业务定制,不用于建立长连接 + * @param productLine 产品线,参考{@link MogoCommon.Product} + * @param payload 上行数据,使用{@link MogoConnsvr.Payload}构建数据 + * @param msgType msg类型,参考{@link MogoPayload.PayloadMsgType} + * @param ack 是否需要server回执,如果为true会在{@link Callback#onAck(CallbackManager, byte[], byte[])}中收到 + * @param msgId msg id,可自定义,比如时间戳 + */ + public void sendData(String appId, + int productLine, + byte[] payload, + int msgType, + boolean ack, + long msgId) { + RequestUtil.sendPayloadData(appId, productLine, payload, msgType, ack, msgId); + } + + /** + * 注册socket连接状态监听 + * + * @param callback 用于接收socket连接状态callback + */ + public void registerSocketConnCallback(ConnectionLifecycleListener callback) { + if (callback != null) { + ConnCallbackManager.getInstance().register(callback); + } + } + + /** + * 注销socket连接状态监听 + * + * @param callback 用于接收socket连接状态callback + */ + public void unregisterSocketConnCallback(ConnectionLifecycleListener callback) { + if (callback != null) { + ConnCallbackManager.getInstance().unregister(callback); + } + } + + private void initNetConfig() { + NetConfig.instance(). + setLoggable(SocketConfig.instance().isDebug()). + setAppContext(mAppContext); + } + + private void requestSocketAddress() { + RequestManager.requestSocketAddress(mAppContext, new RequestManager.SocketAddressCallback() { + @Override + public void onGetSocketAddressSuccess(SocketAddressData addressData) { + if (addressData.result != null && checkHost(addressData.result.ip) && checkPort(addressData.result.port)) { + internalStart(addressData.result.ip, addressData.result.port); + Logger.d(TAG, "network--->ip:" + addressData.result.ip + " & port: " + addressData.result.port); + } else { + retryGetAddress(1000L); //todo 时间调整为1秒,后续在重连时增加网络状态验证,以及增加链路日志 + } + } + + @Override + public void onGetSocketAddressFailed(int code, String msg) { + Logger.d(TAG, "onFailed-->" + code + ":" + msg); + retryGetAddress(1000L); //todo 时间调整为1秒,后续在重连时增加网络状态验证,以及增加链路日志 + } + }); + } + + private final Runnable mRequestSocketAddressTask = () -> { + Logger.d(TAG, "get socket address"); + requestSocketAddress(); + }; + + private void internalStart(String host, int port) { + Logger.d(TAG, "internalStart-->host: " + host + " & port: " + port); + ClientSocketManager.getInstance().setConnectionLifecycleListener(connectionLifecycleListener); + ClientSocketManager.getInstance().start(host, port); + } + + private final ConnectionLifecycleListener connectionLifecycleListener = new ConnectionLifecycleListener() { + @Override + public void onConnectLost(boolean reconnect) { + super.onConnectLost(reconnect); + Logger.d(TAG, "---onConnectLost"); + ConnCallbackManager.getInstance().onConnectLost(reconnect); + if (reconnect) { + Logger.d(TAG, "---onConnectLost reconnect"); + retryGetAddress(1000L);//1-7s //todo 时间调整为1秒,后续在重连时增加网络状态验证,以及增加链路日志 + } + } + + @Override + public void onConnectFailure() { + super.onConnectFailure(); + Logger.d(TAG, "---onConnectFailure"); + ConnCallbackManager.getInstance().onConnectFailure(); + retryGetAddress(1000L);//3-6s //todo 时间调整为1秒,后续在重连时增加网络状态验证,以及增加链路日志 + } + + @Override + public void onConnectSuccess() { + super.onConnectSuccess(); + Logger.d(TAG, "---onConnectSuccess"); + ConnCallbackManager.getInstance().onConnectSuccess(); + } + }; + + private void retryGetAddress(long delayedInMillis) { + UiThreadHandler.removeCallbacks(mRequestSocketAddressTask); + UiThreadHandler.postDelayed(mRequestSocketAddressTask, delayedInMillis); + } + + private boolean checkPort(int port) { + if (port < 0 || port > 0xFFFF) { + Logger.e(TAG, "port out of range:" + port); + return false; + } + return true; + } + + private boolean checkHost(String hostname) { + if (TextUtils.isEmpty(hostname)) { + Logger.e(TAG, "hostname can't be null"); + return false; + } + return true; + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/SocketConfig.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/SocketConfig.java new file mode 100644 index 0000000..e8ff777 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/SocketConfig.java @@ -0,0 +1,139 @@ +package com.mogo.cloud.socket.third.core; + +import android.content.Context; +import android.text.TextUtils; + +import com.mogo.cloud.TelephoneUtil; +import com.zhidao.ptech.connsvr.commom.protocol.MogoCommon; + +public class SocketConfig { + public static final String PROTO_VERSION = "1.0.1"; + + private boolean debug = false; + + private Context appContext; + + /**Need to be registered by server before it can be applied*/ + private String channelId; + + private Environment environment; + + private MogoCommon.Client client; + + private boolean openAnalytics; + + /**Customized sn can be set*/ + private String sn; + + private String uid; + + private String token; + + private String authPubKey; + + private SocketConfig() { + } + + public static SocketConfig instance() { + return SingletonHolder.INSTANCE; + } + + private final static class SingletonHolder { + private static final SocketConfig INSTANCE = new SocketConfig(); + } + + public SocketConfig setDebug(boolean debug) { + this.debug = debug; + return this; + } + + public boolean isDebug() { + return debug; + } + + public SocketConfig setAppContext(Context appContext) { + this.appContext = appContext.getApplicationContext(); + return this; + } + + public Context getAppContext() { + return appContext; + } + + public String getChannelId() { + return channelId; + } + + public SocketConfig setChannelId(String channelId) { + this.channelId = channelId; + return this; + } + + public MogoCommon.Client getClient() { + return client == null ? MogoCommon.Client.car : client; + } + + public SocketConfig setClient(MogoCommon.Client client) { + this.client = client; + return this; + } + + public Environment getEnvironment() { + return environment == null ? Environment.release : environment; + } + + public SocketConfig setEnvironment(Environment environment) { + this.environment = environment; + return this; + } + + public boolean isAnalyticsOpen() { + return openAnalytics; + } + + public SocketConfig setOpenAnalytics(boolean openAnalytics) { + this.openAnalytics = openAnalytics; + return this; + } + + /**Retrieve sn of current device, and return customized sn first*/ + public String getSn() { + return TextUtils.isEmpty(sn) ? TelephoneUtil.getSerialNumber() : sn; + } + + /**The third-party device can set a customized sn*/ + public SocketConfig setSn(String sn) { + this.sn = sn; + return this; + } + + public String getUid() { + return uid; + } + + public SocketConfig setUid(String uid) { + this.uid = uid; + return this; + } + + public String getToken() { + return token; + } + + public SocketConfig setToken(String token) { + this.token = token; + return this; + } + + public String getAuthPubKey() { + if(TextUtils.isEmpty(authPubKey)){ + authPubKey = SocketConstants.DEFAULT_AUTH_PUB_KEY; + } + return authPubKey; + } + + public SocketConfig setAuthPubKey(String authPubKey) { + this.authPubKey = authPubKey; + return this; + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/SocketConstants.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/SocketConstants.java new file mode 100644 index 0000000..0b0f623 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/SocketConstants.java @@ -0,0 +1,22 @@ +package com.mogo.cloud.socket.third.core; + +public interface SocketConstants { + String TAG = "com.zhidao.socket--->"; + + /*unit is second*/ + int DEFAULT_READER_IDLE_TIME = 0; + /*unit is second*/ + int DEFAULT_WRITER_IDLE_TIME = 0; + /*unit is second*/ + int DEFAULT_READER_WRITER_IDLE_TIME = 60 * 3; + + /*unit is ms*/ + long ZERO = 0L; + /*unit is ms*/ + long ONE_SECOND = 1000L; + /*unit is ms*/ + long ONE_MINUTE = 60 * ONE_SECOND; + + String DEFAULT_AUTH_PUB_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCLe57ORPgOnBrcWmGac3esB4PtIyI0rAHiyk5QoCRTnTyanwWLfstu7mz2MSHD3YuveNWDiIY8vtRvFNE4M/yA7eb4Mct01VKhKFI/JqwyXgNaSh5YCfmv0vg9687c7IUjTEAg+ReC/bSLDqEB3a0vuP95Txtj9smMlx1e37XFYQIDAQAB"; + +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/client/ChannelHandlerHolder.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/client/ChannelHandlerHolder.java new file mode 100644 index 0000000..9e45413 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/client/ChannelHandlerHolder.java @@ -0,0 +1,18 @@ +package com.mogo.cloud.socket.third.core.client; + +import io.netty.channel.ChannelHandler; + +/** + * Client-side interface to collect all {@link ChannelHandler}. By doing so, we can conveniently + * use {@link ChannelHandlerHolder#handlers()} methods to reconnect to server. + */ +/*package*/ interface ChannelHandlerHolder { + + /** + * Collection of {@link ChannelHandler} used for {@link io.netty.bootstrap.Bootstrap#handler(ChannelHandler)} + * to connect server. + * + * @return Collection of {@link ChannelHandler} + */ + ChannelHandler[] handlers(); +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/client/ClientChannelReader.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/client/ClientChannelReader.java new file mode 100644 index 0000000..b04a3e6 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/client/ClientChannelReader.java @@ -0,0 +1,210 @@ +package com.mogo.cloud.socket.third.core.client; + +import android.text.TextUtils; + +import com.mogo.cloud.socket.third.analytics.EventRequest; +import com.mogo.cloud.socket.third.analytics.SocketStatus; +import com.mogo.cloud.socket.third.core.CallbackManager; +import com.mogo.cloud.socket.third.core.ConnectionLifecycleListener; +import com.mogo.cloud.socket.third.core.SocketConfig; +import com.mogo.cloud.socket.third.core.SocketConstants; +import com.mogo.cloud.socket.third.utils.LoginStatusUtil; +import com.mogo.cloud.socket.third.utils.RSAUtils; +import com.mogo.cloud.socket.third.utils.RequestUtil; +import com.mogo.cloud.utils.logger.Logger; +import com.zhidao.ptech.connsvr.protocol.MogoConnsvr; +import com.zhidao.ptech.connsvr.protocol.model.MogoConnsvrPacket; + +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.timeout.IdleStateEvent; +import io.netty.util.ReferenceCountUtil; + + +@ChannelHandler.Sharable +/*package*/ final class ClientChannelReader extends ChannelInboundHandlerAdapter { + + public ClientChannelReader() { + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt instanceof IdleStateEvent) { + + final IdleStateEvent e = (IdleStateEvent) evt; + + switch (e.state()) { + case WRITER_IDLE: + RequestUtil.sendHeartbeat(ctx.channel()); + Logger.i(SocketConstants.TAG, "client send heartbeat write-idle"); + break; + case READER_IDLE: + RequestUtil.sendHeartbeat(ctx.channel()); + Logger.i(SocketConstants.TAG, "client send heartbeat reader-idle"); + break; + default: + break; + } + } else { + super.userEventTriggered(ctx, evt); + } + } + + @Override + public void channelRead(ChannelHandlerContext channelHandlerContext, Object msg) throws Exception { + if (!(msg instanceof MogoConnsvrPacket)) { + Logger.i(SocketConstants.TAG, "msg is null or msg is not MogoConnsvrPacket type---" + (msg == null ? "" : msg.toString())); + EventRequest.trackException("msg is not instanceof MogoConnsvrPacket"); + //重新请求ip port + onRetryLogin(); + return; + } + + Logger.i(SocketConstants.TAG, "Client---client receive server---" + msg.toString()); + + final MogoConnsvrPacket mogoConnsvrPacket = (MogoConnsvrPacket) msg; + final MogoConnsvr.MsgType msgType = mogoConnsvrPacket.getMsgType(((MogoConnsvrPacket) msg).getHeaderPb()); + + Logger.i(SocketConstants.TAG, "receive server---msgType" + msgType.name()); + if (msgType == MogoConnsvr.MsgType.mogoMsgTypeConnSvrAuthMidRsp) { + //返回256位随机字符串, 加密后再次发给服务端验证 + LoginStatusUtil.setStatus(false); + if (mogoConnsvrPacket.getPayload() == null) { + Logger.i(SocketConstants.TAG, "AuthMidRsp==>Packet.getPayload is null"); + EventRequest.trackException("AuthMidRsp==>Packet.getPayload is null"); + //重新请求ip port + onRetryLogin(); + return; + } + MogoConnsvr.AuthMidRsp authMidRsp = MogoConnsvr.AuthMidRsp.parseFrom(mogoConnsvrPacket.getPayloadBytes()); + if (authMidRsp != null && !TextUtils.isEmpty(authMidRsp.getKey())) { + String key = RSAUtils.encryptByPublic(authMidRsp.getKey() + SocketConfig.instance().getSn()); + RequestUtil.checkAuth(key); + } else { + EventRequest.trackException("authMidRsp is null or getKey is null"); + //重新请求ip port + onRetryLogin(); + } + } else if (msgType == MogoConnsvr.MsgType.mogoMsgTypeConnSvrAuthRsp) { + LoginStatusUtil.setStatus(false); + if (mogoConnsvrPacket.getPayload() == null) { + EventRequest.trackException("AuthRsp==> Packet.getPayload is null"); + Logger.i(SocketConstants.TAG, "login response ---> response is null"); + //重新请求ip port + onRetryLogin(); + return; + } + MogoConnsvr.AuthRsp authRsp = MogoConnsvr.AuthRsp.parseFrom(mogoConnsvrPacket.getPayloadBytes()); + if (authRsp != null) { + MogoConnsvr.AuthCode authCode = authRsp.getAuthCode(); + if (authCode == MogoConnsvr.AuthCode.AUTH_SUCCESS) { + onAuthSuccess(channelHandlerContext.channel()); + } else { + EventRequest.trackSocketStatus(SocketStatus.auth_rsp_failed, "auth code is not success"); + onRetryLogin(); + } + } else { + EventRequest.trackException("AuthRsp==> authRsp is null"); + //重新请求ip port + onRetryLogin(); + } + } else if (msgType == MogoConnsvr.MsgType.mogoMsgTypeConnSvrAgentInfoRsp) {//用户信息 + final MogoConnsvr.ConnSvrUserInfoRsp userInfoResponse = MogoConnsvr.ConnSvrUserInfoRsp.parseFrom(mogoConnsvrPacket.getPayloadBytes()); + if (userInfoResponse.getResult()) { + Logger.i(SocketConstants.TAG, "Client receives server's user info response, result is true."); + LoginStatusUtil.setStatus(true); + } else { + EventRequest.trackSocketStatus(SocketStatus.send_userInfo_rsp_failed, "send_userInfo_rsp_failed"); + onRetryLogin(); + LoginStatusUtil.setStatus(false); + } + } else if (msgType == MogoConnsvr.MsgType.mogoMsgTypeConnSvrDisconnectRsp) {//链接断开 + LoginStatusUtil.setStatus(false); + Logger.i(SocketConstants.TAG, "mogoMsgTypeConnSvrDisconnectRsp retry login"); + EventRequest.trackSocketStatus(SocketStatus.socket_disconnect, "disconnect"); + //重新请求ip port + onRetryLogin(); + } else if (msgType == MogoConnsvr.MsgType.mogoMsgTypeAppPushKickMessageReq) { + LoginStatusUtil.setStatus(false); + Logger.i(SocketConstants.TAG, "close channel response ---> 被踢下线"); + EventRequest.trackSocketStatus(SocketStatus.socket_disconnect, "kick out, socket is close"); + ClientSocketManager.getInstance().close(); + } else if (msgType == MogoConnsvr.MsgType.mogoMsgTypeAppPushMessageReq) {//推送消息 + if (mogoConnsvrPacket.getPayload() == null) { + Logger.i(SocketConstants.TAG, "push message response is null"); + EventRequest.trackException("push payload message response is null"); + return; + } + Logger.i(SocketConstants.TAG, "push message response received"); + try { + MogoConnsvr.Header header = MogoConnsvr.Header.parseFrom(mogoConnsvrPacket.getHeaderBytes()); + if (header != null) { + //收到消息的回执 + MogoConnsvr.Header rspHeader = MogoConnsvr.Header.newBuilder(((MogoConnsvrPacket) msg).getHeaderPb()) + .setMsgType(MogoConnsvr.MsgType.mogoMsgTypeAppPushMessageRsp_VALUE) + .build(); + MogoConnsvrPacket packet = new MogoConnsvrPacket(Unpooled.wrappedBuffer(rspHeader.toByteArray())); + RequestUtil.ack(channelHandlerContext.channel(), packet.getBytes()); + + + String appId = header.getAppId(); + long msgId = header.getMsgId(); + CallbackManager.getInstance().notify(mogoConnsvrPacket.getPayloadBytes(), appId, msgId); + } + } catch (Exception e) { + e.printStackTrace(); + } + } else if (msgType == MogoConnsvr.MsgType.mogoMsgTypeAppUpstreamAck) { + Logger.i(SocketConstants.TAG, "receive message ack"); + + byte[] bytes = mogoConnsvrPacket.getHeaderBytes(); + try { + MogoConnsvr.Header header = MogoConnsvr.Header.parseFrom(bytes); + if (header != null) { + CallbackManager.getInstance().ack(bytes, mogoConnsvrPacket.getPayloadBytes()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } else if (msgType == MogoConnsvr.MsgType.mogoMsgTypeConnSvrHeartbeatReq) { + Logger.i(SocketConstants.TAG, "Client receives server's heartbeat."); + } + + ReferenceCountUtil.release(msg); + } + + private void onAuthSuccess(Channel channel) { + Logger.i(SocketConstants.TAG, "Client authority check succeeded"); + //send heartbeat. + HeartbeatAlarmManager.getInstance().send(); + //send user info + RequestUtil.sendUserInfo(channel); + } + + private synchronized void onRetryLogin() { + onConnectFailure(); + Logger.i(SocketConstants.TAG, "Client login fails, retry ing"); + } + + /** + * 重新请求ip port + */ + private void onConnectFailure() { + ConnectionLifecycleListener connectionLifecycleListener = ClientSocketManager.getInstance().getConnectionLifecycleListener(); + if (connectionLifecycleListener != null) { + //关闭原有通道,重新请求通道 + ClientSocketManager.getInstance().close(); + Logger.i(SocketConstants.TAG, "Client onConnectFailure, retry in 5s"); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + EventRequest.trackException(cause.getMessage()); + cause.printStackTrace(); + ctx.close(); + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/client/ClientSocketManager.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/client/ClientSocketManager.java new file mode 100644 index 0000000..a700cb7 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/client/ClientSocketManager.java @@ -0,0 +1,220 @@ +package com.mogo.cloud.socket.third.core.client; + +import com.mogo.cloud.socket.third.core.ConnectionLifecycleListener; +import com.mogo.cloud.socket.third.core.SocketConstants; +import com.mogo.cloud.socket.third.utils.LoginStatusUtil; +import com.mogo.cloud.socket.third.utils.RequestUtil; +import com.mogo.cloud.utils.logger.Logger; +import com.zhidao.ptech.connsvr.protocol.codec.MogoPacketDecoder; +import com.zhidao.ptech.connsvr.protocol.codec.MogoPacketFrameDecoder; +import com.zhidao.ptech.connsvr.protocol.codec.MogoPacketFrameEncoder; +import com.zhidao.ptech.connsvr.protocol.model.MogoConnsvrPacket; + +import java.util.concurrent.TimeUnit; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.AdaptiveRecvByteBufAllocator; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.timeout.IdleStateHandler; +import io.netty.util.HashedWheelTimer; + +public class ClientSocketManager { + + private final HashedWheelTimer hashedWheelTimer = new HashedWheelTimer(); + private final Bootstrap bootstrap = new Bootstrap(); + private final NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(); + + private volatile boolean canReconnect = true; + private volatile boolean isConnected = false; + + private Channel channel; + private ConnectionLifecycleListener connectionLifecycleListener; + + public static ClientSocketManager getInstance() { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder { + private static final ClientSocketManager INSTANCE = new ClientSocketManager(); + } + + private ClientSocketManager() { + initialize(); + } + + private synchronized void initialize() { + bootstrap.channel(NioSocketChannel.class); + bootstrap.option(ChannelOption.SO_KEEPALIVE, true) + .option(ChannelOption.TCP_NODELAY, true) + .option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT); + + bootstrap.group(eventLoopGroup); + + final ConnectionWatchdog watchdog = createWatchdog(); + + bootstrap.handler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel socketChannel) throws Exception { + socketChannel.pipeline().addLast(watchdog.handlers()); + } + }); + } + + public boolean isConnected() { + return isConnected; + } + + private synchronized ConnectionWatchdog createWatchdog() { + return new ConnectionWatchdog() { + @Override + public ChannelHandler[] handlers() { + return new ChannelHandler[]{ + this, + //decode + new MogoPacketFrameDecoder(MogoPacketFrameDecoder.MAX_FRAME_LENGTH, + MogoPacketFrameDecoder.LENGTH_FIELD_OFFSET, + MogoPacketFrameDecoder.LENGTH_FIELD_SIZE), + new MogoPacketDecoder(), + //encode + new MogoPacketFrameEncoder(), + //config idle state + new IdleStateHandler(SocketConstants.DEFAULT_READER_IDLE_TIME, SocketConstants.DEFAULT_READER_WRITER_IDLE_TIME, SocketConstants.DEFAULT_READER_WRITER_IDLE_TIME, TimeUnit.SECONDS), + //send and receive proto-type data. + new ClientChannelReader(), + }; + } + }; + } + + private synchronized void writeAndFlush(Object msg) { + if (channel == null) { + Logger.i(SocketConstants.TAG, "socket channel is null when write and flush"); + return; + } + + if (!(msg instanceof MogoConnsvrPacket)) { + Logger.i(SocketConstants.TAG, "msg is not MogoConnsvrPacket type when write and flush."); + return; + } + + try { + channel.writeAndFlush(msg); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public synchronized Channel getChannel() { + return channel; + } + + public synchronized void start(String host, int port) { + if (isConnected) { + Logger.i(SocketConstants.TAG, "do not need to start, channel is connected"); + return; + } + canReconnect = true; + isConnected = false; + resetInetAddress(host, port); + doConnection(SocketConstants.ONE_SECOND); + } + + public synchronized void setConnectionLifecycleListener(ConnectionLifecycleListener connectionLifecycleListener) { + this.connectionLifecycleListener = connectionLifecycleListener; + } + + public synchronized ConnectionLifecycleListener getConnectionLifecycleListener() { + return connectionLifecycleListener; + } + + private synchronized void resetInetAddress(String host, int port) { + bootstrap.remoteAddress(host, port); + } + + private synchronized void doConnection(long delayInMs) { + if (channel != null && channel.isOpen() && channel.isActive()) { + Logger.i(SocketConstants.TAG, "channel is active, no need to call doConnection(long delayInMs)"); + return; + } + + hashedWheelTimer.newTimeout(timeout -> { + if (!canReconnect) { + return; + } + final ChannelFuture future = bootstrap.connect(); + future.addListener((ChannelFutureListener) future1 -> { + if (future1.isSuccess()) { + Logger.i(SocketConstants.TAG, "Client---connecting server succeeds"); + onConnectSuccess(future1.channel()); + } else { + Logger.i(SocketConstants.TAG, "Client---connecting server fails,失败重试"); + Logger.i(SocketConstants.TAG, future1.cause().getMessage()); + onConnectFailure(); + } + }); + }, delayInMs, TimeUnit.MILLISECONDS); + } + + private synchronized void onConnectSuccess(Channel channel) { + this.isConnected = true; + this.channel = channel; + RequestUtil.login(channel); + if (connectionLifecycleListener != null) { + connectionLifecycleListener.onConnectSuccess(); + } + } + + private synchronized void onConnectFailure() { + this.isConnected = false; + close(canReconnect); + if (connectionLifecycleListener != null) { + connectionLifecycleListener.onConnectFailure(); + } + } + + synchronized void onConnectionLost() { + this.isConnected = false; + close(canReconnect); + if (connectionLifecycleListener != null) { + connectionLifecycleListener.onConnectLost(canReconnect); + } + } + + private synchronized void closeChannelSafely() { + try { + if (channel != null) { + channel.close(); + channel = null; + Logger.i(SocketConstants.TAG, "channel is null"); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public synchronized void close() { + close(false); + if (connectionLifecycleListener != null) { + connectionLifecycleListener.onConnectFailure(); + } + } + + public synchronized void close(boolean allowReconnect) { + //close channel + closeChannelSafely(); + + //reset status + isConnected = false; + canReconnect = allowReconnect; + LoginStatusUtil.setStatus(false); + HeartbeatAlarmManager.getInstance().cancel(); + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/client/ConnectionWatchdog.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/client/ConnectionWatchdog.java new file mode 100644 index 0000000..ea2ecaa --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/client/ConnectionWatchdog.java @@ -0,0 +1,32 @@ +package com.mogo.cloud.socket.third.core.client; + +import com.mogo.cloud.socket.third.analytics.EventRequest; +import com.mogo.cloud.socket.third.analytics.SocketStatus; +import com.mogo.cloud.socket.third.core.SocketConstants; +import com.mogo.cloud.utils.logger.Logger; + +import io.netty.channel.ChannelHandler.Sharable; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + + +@Sharable +/*package*/ abstract class ConnectionWatchdog extends ChannelInboundHandlerAdapter implements ChannelHandlerHolder { + + protected ConnectionWatchdog() { + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + Logger.i(SocketConstants.TAG, "channel is active"); + ctx.fireChannelActive(); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + Logger.i(SocketConstants.TAG, "channel is inactive, it depends on client's choice whether to reconnect or not."); + EventRequest.trackSocketStatus(SocketStatus.socket_disconnect, "channel is inactive"); + ClientSocketManager.getInstance().onConnectionLost(); + ctx.fireChannelInactive(); + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/client/HeartbeatAlarmManager.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/client/HeartbeatAlarmManager.java new file mode 100644 index 0000000..92defac --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/client/HeartbeatAlarmManager.java @@ -0,0 +1,133 @@ +package com.mogo.cloud.socket.third.core.client; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; + +import com.mogo.cloud.DevicesUtils; +import com.mogo.cloud.socket.third.analytics.EventRequest; +import com.mogo.cloud.socket.third.analytics.SocketStatus; +import com.mogo.cloud.socket.third.core.SocketConfig; +import com.mogo.cloud.socket.third.core.SocketConstants; +import com.mogo.cloud.socket.third.utils.LoginStatusUtil; +import com.mogo.cloud.socket.third.utils.RequestUtil; +import com.mogo.cloud.utils.logger.Logger; + +/** + * Send heartbeats according to network type to keep socket active as well as + * to stop a device falling asleep when the screen is off. + */ +/*package*/ class HeartbeatAlarmManager { + + private AlarmManager alarmManager; + private PendingIntent pendingIntent; + private static int lastNetworkType; + + private HeartbeatAlarmManager() { + } + + public static HeartbeatAlarmManager getInstance() { + return SingletonHolder.INSTANCE; + } + + private static final class SingletonHolder { + private static final HeartbeatAlarmManager INSTANCE = new HeartbeatAlarmManager(); + } + + synchronized void cancel() { + if (alarmManager != null && pendingIntent != null) { + pendingIntent.cancel(); + alarmManager.cancel(pendingIntent); + alarmManager = null; + pendingIntent = null; + + Logger.i(SocketConstants.TAG, "heartbeat alarm is cancelled"); + } + } + + synchronized void send() { + this.cancel(); + if ((lastNetworkType = DevicesUtils.getNetworkType(SocketConfig.instance().getAppContext())) == ConnectivityManager.TYPE_MOBILE) { + sendHeartbeatRepeatedly(SocketConstants.ONE_MINUTE * 2); + } else { + sendHeartbeatRepeatedly(SocketConstants.ONE_MINUTE * 2); + } + } + + /** + * Suggest that interval be 60 seconds or bigger to save power, + * and note that the interval will be set to 60 seconds when the interval is + * small than 60 seconds in or above 5.1{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}. + */ + private void sendHeartbeatRepeatedly(long interval) { + final Context appContext = SocketConfig.instance().getAppContext(); + if (appContext == null) { + Logger.i(SocketConstants.TAG, "sendHeartbeatRepeatedly---> app context is null."); + return; + } + final int requestCode = 1001; + final Intent intent = new Intent(appContext, SendHeartbeatReceiver.class); + alarmManager = (AlarmManager) appContext.getSystemService(Context.ALARM_SERVICE); + pendingIntent = PendingIntent.getBroadcast( + appContext, + requestCode, + intent, + PendingIntent.FLAG_UPDATE_CURRENT); + + if (DevicesUtils.isSdkHigherThan18()) { + alarmManager.setExact( + AlarmManager.RTC_WAKEUP, + System.currentTimeMillis() + interval, + pendingIntent); + + Logger.i(SocketConstants.TAG, "setExact(...)"); + } else { + alarmManager.setRepeating( + AlarmManager.RTC_WAKEUP, + System.currentTimeMillis() + interval, + interval, + pendingIntent); + + Logger.i(SocketConstants.TAG, "setRepeating(...)"); + } + } + + public static final class SendHeartbeatReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + if (!ClientSocketManager.getInstance().isConnected()) { + Logger.i(SocketConstants.TAG, "onReceive--->channel is not connected"); + EventRequest.trackSocketStatus(SocketStatus.socket_disconnect, "heartbeat netty socket disconnect"); + onRetryConnect(); + return; + } + + boolean result = LoginStatusUtil.isLogin(); + Logger.i(SocketConstants.TAG, "socket connected is : " + result); + //重试逻辑 + if (!LoginStatusUtil.isLogin()) { + EventRequest.trackSocketStatus(SocketStatus.socket_disconnect, "heartbeat product socket disconnect"); + onRetryConnect(); + return; + } + + Logger.i(SocketConstants.TAG, "onReceive--->send heartbeat"); + RequestUtil.sendHeartbeat(ClientSocketManager.getInstance().getChannel()); + + if (DevicesUtils.isSdkHigherThan18() || lastNetworkType != DevicesUtils.getNetworkType(SocketConfig.instance().getAppContext())) { + HeartbeatAlarmManager.getInstance().cancel(); + HeartbeatAlarmManager.getInstance().send(); + } + } + + //尝试重新连接 + private void onRetryConnect() { + //关闭原有通道,重新请求信通道 + ClientSocketManager.getInstance().close(); + } + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/ApiService.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/ApiService.java new file mode 100644 index 0000000..818e0f7 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/ApiService.java @@ -0,0 +1,16 @@ +package com.mogo.cloud.socket.third.core.network; + +import com.elegant.network.BaseResp; + +import okhttp3.RequestBody; +import retrofit2.http.Body; +import retrofit2.http.Headers; +import retrofit2.http.POST; +import rx.Observable; + +public interface ApiService { + + @Headers({"Content-Type:application/json", "Accept:application/json"}) + @POST("/connect/message/messageCallback") + Observable socketMessageCallback(@Body RequestBody requestBody); +} \ No newline at end of file diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/RequestManager.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/RequestManager.java new file mode 100644 index 0000000..8923100 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/RequestManager.java @@ -0,0 +1,115 @@ +package com.mogo.cloud.socket.third.core.network; + +import android.content.Context; + +import com.elegant.log.simplelog.Logger; +import com.elegant.network.BaseResp; +import com.elegant.network.DefSubscriberImpl; +import com.elegant.network.NetConstants; +import com.elegant.network.ParamsBuilder; +import com.elegant.network.RequestOptions; +import com.elegant.network.utils.GsonUtil; +import com.elegant.network.utils.NetworkSdkUtils; +import com.elegant.utils.CommonUtils; +import com.mogo.cloud.socket.third.core.SocketConfig; +import com.mogo.cloud.socket.third.core.network.model.MessageCallbackData; +import com.mogo.cloud.socket.third.core.network.model.SocketAddressData; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import okhttp3.RequestBody; + +public class RequestManager { + + private static final String TAG = "RequestManager"; + + public static void socketMessageCallback(long msgId, String appId) { + final List msgIds = new ArrayList<>(); + msgIds.add(msgId); + MessageCallbackData callbackData = new MessageCallbackData(appId); + callbackData.setNotifyMsgIds(msgIds); + + RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"), + GsonUtil.jsonFromObject(callbackData)); + + RequestOptions requestOptions = RequestOptions.of(SocketConfig.instance().getAppContext()); + + ServiceFactory.newApiService().socketMessageCallback(body). + compose(NetworkSdkUtils.transformer()). + subscribe(new DefSubscriberImpl(requestOptions) { + @Override + protected void onFailure(int code, String message) { + super.onFailure(code, message); + Logger.d(TAG,"socket message callback failed"); + } + + @Override + protected void onSuccess(BaseResp o) { + super.onSuccess(o); + Logger.d(TAG,"socket message callback succeeded"); + } + + @Override + protected void onNetworkLost() { + super.onNetworkLost(); + Logger.d(TAG,"request socket message callback, network is not available."); + } + }); + } + + private static volatile boolean sRequestingAddress = false; + + public static void requestSocketAddress(Context context, final SocketAddressCallback socketAddressCallback) { + if (sRequestingAddress) { + return; + } + + sRequestingAddress = true; + + final Map params = ParamsBuilder.of(false) + .append("lat", 0)//required by server + .append("lon", 0)//required by server + .append("sn", SocketConfig.instance().getSn()) + .append("versionCode", CommonUtils.getVersionCode(SocketConfig.instance().getAppContext())) + .append("versionName", CommonUtils.getVersionName(SocketConfig.instance().getAppContext())) + .build(); + + ServiceFactory.newTechApiService().requestSocketAddress(params) + .compose(NetworkSdkUtils.transformer()) + .subscribe(new DefSubscriberImpl(RequestOptions.of(context)) { + @Override + protected void onSuccess(SocketAddressData o) { + super.onSuccess(o); + sRequestingAddress = false; + if (socketAddressCallback != null) { + socketAddressCallback.onGetSocketAddressSuccess(o); + } + } + + @Override + protected void onFailure(int code, String message) { + super.onFailure(code, message); + sRequestingAddress = false; + if (socketAddressCallback != null) { + socketAddressCallback.onGetSocketAddressFailed(code, message); + } + } + + @Override + protected void onNetworkLost() { + super.onNetworkLost(); + sRequestingAddress = false; + if (socketAddressCallback != null) { + socketAddressCallback.onGetSocketAddressFailed(NetConstants.CODE_REQUEST_NO_NETWORK, "网络失败,请检查网络后重试"); + } + } + }); + } + + public interface SocketAddressCallback { + void onGetSocketAddressSuccess(SocketAddressData data); + void onGetSocketAddressFailed(int code, String msg); + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/ServiceFactory.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/ServiceFactory.java new file mode 100644 index 0000000..48af8b1 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/ServiceFactory.java @@ -0,0 +1,44 @@ +package com.mogo.cloud.socket.third.core.network; + +import com.elegant.network.SimpleServiceFactory; +import com.mogo.cloud.socket.third.core.Environment; +import com.mogo.cloud.socket.third.core.SocketConfig; + +public class ServiceFactory extends SimpleServiceFactory { + + private static final String TAG = "ServiceFactory"; + + protected ServiceFactory(){} + + public static synchronized ApiService newApiService() { + return newService(getUrl(), ApiService.class); + } + + public static synchronized TechApiService newTechApiService() { + return newService(getTechUrl(), TechApiService.class); + } + + private static String getTechUrl(){ + final Environment environment = SocketConfig.instance().getEnvironment(); + + if(environment == Environment.release){ + return "http://dzt.zhidaozhixing.com"; + }else if(environment == Environment.qa){ + return "http://dzt-test.zhidaozhixing.com"; + }else{ + return "http://dzt-dev.zhidaozhixing.com"; + } + } + + private static String getUrl(){ + final Environment environment = SocketConfig.instance().getEnvironment(); + + if(environment == Environment.release){ + return "http://api.zhidaohulian.com/"; + }else if(environment == Environment.qa){ + return "http://carlife-test.zhidaohulian.com/"; + }else{ + return "http://carlife-dev.zhidaohulian.com/"; + } + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/TechApiService.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/TechApiService.java new file mode 100644 index 0000000..792e592 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/TechApiService.java @@ -0,0 +1,15 @@ +package com.mogo.cloud.socket.third.core.network; + + +import com.mogo.cloud.socket.third.core.network.model.SocketAddressData; + +import java.util.Map; + +import retrofit2.http.GET; +import retrofit2.http.QueryMap; +import rx.Observable; + +public interface TechApiService { + @GET("/yycp-conn-admin/connsvrAddr") + Observable requestSocketAddress(@QueryMap Map params); +} \ No newline at end of file diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/model/MessageCallbackData.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/model/MessageCallbackData.java new file mode 100644 index 0000000..0447d31 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/model/MessageCallbackData.java @@ -0,0 +1,38 @@ +package com.mogo.cloud.socket.third.core.network.model; + +import java.util.List; +import java.util.Random; + +public class MessageCallbackData { + + private List notifyMsgIds; + private String reqId; + private String caller; + private long noise; + + public MessageCallbackData(String appId) { + this.reqId = getRandomStr(); + this.caller = "socketService"; + this.noise = System.currentTimeMillis(); + } + + public void setNotifyMsgIds(List notifyMsgIds) { + this.notifyMsgIds = notifyMsgIds; + } + + + private static String getRandomStr() { + char[] c = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; + int[] indexArray = new int[32]; + Random random = new Random(); + for (int i = 0; i < 32; i++) { + indexArray[i] = random.nextInt(36); + } + StringBuffer stringbuffer = new StringBuffer(); + for (int i = 0; i < indexArray.length; i++) { + stringbuffer.append(c[indexArray[i]]); + } + return stringbuffer.toString(); + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/model/SocketAddressData.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/model/SocketAddressData.java new file mode 100644 index 0000000..174416e --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/model/SocketAddressData.java @@ -0,0 +1,15 @@ +package com.mogo.cloud.socket.third.core.network.model; + +import com.elegant.network.BaseResp; + +import java.io.Serializable; + +public class SocketAddressData extends BaseResp { + public Address result; + + public static class Address implements Serializable { + public String address; + public String ip; + public int port; + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/model/SocketSendData.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/model/SocketSendData.java new file mode 100644 index 0000000..e067c85 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/core/network/model/SocketSendData.java @@ -0,0 +1,21 @@ +package com.mogo.cloud.socket.third.core.network.model; + +import com.mogo.cloud.GsonUtil; + +import org.json.JSONObject; + +import java.util.Map; + +public class SocketSendData { + public int product_line; + public byte[] payload; + public int header_type; + public String app_id; + public boolean ack; + public long msg_id; + + public static SocketSendData parse(Map map) { + JSONObject jsonObject = new JSONObject(map); + return GsonUtil.objectFromJson(jsonObject.toString(), SocketSendData.class); + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/utils/Base64Utils.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/utils/Base64Utils.java new file mode 100644 index 0000000..c334463 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/utils/Base64Utils.java @@ -0,0 +1,274 @@ +package com.mogo.cloud.socket.third.utils; + + +public final class Base64Utils { + + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static private final boolean fDebug = false; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static { + for (int i = 0; i < BASELENGTH; ++i) { + base64Alphabet[i] = -1; + } + for (int i = 'Z'; i >= 'A'; i--) { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9'; i >= '0'; i--) { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = (char) '+'; + lookUpBase64Alphabet[63] = (char) '/'; + + } + + private static boolean isWhiteSpace(char octect) { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad(char octect) { + return (octect == PAD); + } + + private static boolean isData(char octect) { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * @return Encoded Base64 array + */ + public static String encode(byte[] binaryData) { + + if (binaryData == null) { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char encodedData[] = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + if (fDebug) { + System.out.println("number of triplets = " + numberTriplets); + } + + for (int i = 0; i < numberTriplets; i++) { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + if (fDebug) { + System.out.println("b1= " + b1 + ", b2= " + b2 + ", b3= " + b3); + } + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + if (fDebug) { + System.out.println("val2 = " + val2); + System.out.println("k4 = " + (k << 4)); + System.out.println("vak = " + (val2 | (k << 4))); + } + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + if (fDebug) { + System.out.println("b1=" + b1); + System.out.println("b1<<2 = " + (b1 >> 2)); + } + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } else if (fewerThan24bits == SIXTEENBIT) { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * @return Array containind decoded data. + */ + public static byte[] decode(String encoded) { + + if (encoded == null) { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) { + return null;//should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for (; i < numberQuadruple - 1; i++) { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) + || !isData((d4 = base64Data[dataIndex++]))) { + return null; + }//if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) { + return null;//if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) {//Check if they are PAD characters + if (isPad(d3) && isPad(d4)) { + if ((b2 & 0xf) != 0)//last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } else if (!isPad(d3) && isPad(d4)) { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)//last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } else { + return null; + } + } else { //No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + + } + + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * @return the new length + */ + private static int removeWhiteSpace(char[] data) { + if (data == null) { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0; i < len; i++) { + if (!isWhiteSpace(data[i])) { + data[newSize++] = data[i]; + } + } + return newSize; + } +} \ No newline at end of file diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/utils/LoginStatusUtil.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/utils/LoginStatusUtil.java new file mode 100644 index 0000000..4c88eeb --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/utils/LoginStatusUtil.java @@ -0,0 +1,17 @@ +package com.mogo.cloud.socket.third.utils; + +public final class LoginStatusUtil { + + private static volatile boolean loginStatus = false; + + private LoginStatusUtil(){} + + public synchronized static void setStatus(boolean loginStatus){ + LoginStatusUtil.loginStatus = loginStatus; + } + + public static boolean isLogin(){ + return loginStatus; + } + +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/utils/RSAUtils.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/utils/RSAUtils.java new file mode 100644 index 0000000..1e406f4 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/utils/RSAUtils.java @@ -0,0 +1,126 @@ +package com.mogo.cloud.socket.third.utils; + +import com.mogo.cloud.socket.third.core.SocketConfig; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import javax.crypto.Cipher; + +public class RSAUtils { + + /** + * 使用私钥解密 + * + * @param content 密文 + * @param private_key 私钥 + * @param input_charset 编码 + * @return + * @throws Exception + */ + public static String decrypt(String content, String private_key, String input_charset) throws Exception { + PrivateKey prikey = getPrivateKey(private_key); + + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + cipher.init(Cipher.DECRYPT_MODE, prikey); + + InputStream ins = new ByteArrayInputStream(Base64Utils.decode(content)); + ByteArrayOutputStream writer = new ByteArrayOutputStream(); + byte[] buf = new byte[128]; + int bufl; + + while ((bufl = ins.read(buf)) != -1) { + byte[] block; + + if (buf.length == bufl) { + block = buf; + } else { + block = new byte[bufl]; + for (int i = 0; i < bufl; i++) { + block[i] = buf[i]; + } + } + + writer.write(cipher.doFinal(block)); + } + + return new String(writer.toByteArray(), input_charset); + } + + /** + * 获得私钥 + * + * @param key 私钥 + * @throws Exception + */ + public static PrivateKey getPrivateKey(String key) throws Exception { + + byte[] keyBytes; + + keyBytes = Base64Utils.decode(key); + + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + + PrivateKey privateKey = keyFactory.generatePrivate(keySpec); + + return privateKey; + } + + /** + * 得到公钥 + * + * @param bysKey + * @return + */ + private static PublicKey getPublicKeyFromX509(String bysKey) throws NoSuchAlgorithmException, Exception { + byte[] decodedKey = Base64Utils.decode(bysKey); + X509EncodedKeySpec x509 = new X509EncodedKeySpec(decodedKey); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePublic(x509); + } + + /** + * 使用公钥加密 + * + * @param content 明文 + * @return + */ + public static String encryptByPublic(String content) { + try { + PublicKey pubicKey = getPublicKeyFromX509(SocketConfig.instance().getAuthPubKey()); + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, pubicKey); + byte plaintext[] = content.getBytes("UTF-8"); + int inputLen = plaintext.length; + int offLen = 0;//偏移量 + int i = 0; + ByteArrayOutputStream bops = new ByteArrayOutputStream(); + while (inputLen - offLen > 0) { + byte[] cache; + if (inputLen - offLen > 117) { + cache = cipher.doFinal(plaintext, offLen, 117); + } else { + cache = cipher.doFinal(plaintext, offLen, inputLen - offLen); + } + bops.write(cache); + i++; + offLen = 117 * i; + } + bops.close(); + byte[] encryptedData = bops.toByteArray(); + return Base64Utils.encode(encryptedData); + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/utils/RequestUtil.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/utils/RequestUtil.java new file mode 100644 index 0000000..de12a18 --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/utils/RequestUtil.java @@ -0,0 +1,206 @@ +package com.mogo.cloud.socket.third.utils; + +import android.os.Build; +import android.text.TextUtils; + +import com.mogo.cloud.DevicesUtils; +import com.mogo.cloud.TelephoneUtil; +import com.mogo.cloud.socket.third.core.SocketConfig; +import com.mogo.cloud.socket.third.core.SocketConstants; +import com.mogo.cloud.socket.third.core.client.ClientSocketManager; +import com.mogo.cloud.utils.logger.Logger; +import com.zhidao.ptech.connsvr.commom.protocol.MogoCommon; +import com.zhidao.ptech.connsvr.protocol.MogoConnsvr; +import com.zhidao.ptech.connsvr.protocol.model.MogoConnsvrPacket; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; + +public final class RequestUtil { + + private RequestUtil() { + } + + private static boolean checkNotNull(Channel channel) { + Logger.i(SocketConstants.TAG, channel == null ? "checkNotNull(channel), channel is null." : "checkNotNull(channel), channel is not null."); + boolean isLogin = LoginStatusUtil.isLogin() && ClientSocketManager.getInstance().isConnected(); + Logger.i(SocketConstants.TAG, "socket connected is :" + isLogin); + return channel != null; + } + + public static void login(Channel channel) { + if (checkNotNull(channel)) { + channel.writeAndFlush(RequestModelUtil.buildAuthRequest()); + Logger.i(SocketConstants.TAG, "requestAuth"); + } + } + + public static void ack(Channel channel, byte[] data) { + if (checkNotNull(channel)) { + channel.writeAndFlush(data); + Logger.i(SocketConstants.TAG, "ack"); + } + } + + public static void checkAuth(String key) { + Channel channel = ClientSocketManager.getInstance().getChannel(); + if (checkNotNull(channel)) { + channel.writeAndFlush(RequestModelUtil.buildEncodeInfo(key)); + Logger.i(SocketConstants.TAG, "checkAuth"); + } + } + + public static void sendHeartbeat(Channel channel) { + if (checkNotNull(channel)) { + channel.writeAndFlush(RequestModelUtil.buildHeartbeat()); + Logger.i(SocketConstants.TAG, "send heartbeat"); + } + } + + public static void sendUserInfo(Channel channel) { + if (checkNotNull(channel)) { + channel.writeAndFlush(RequestModelUtil.buildUserInfo()); + Logger.i(SocketConstants.TAG, "send user info"); + } + } + + /** + * 此app id可被业务随意定制 + */ + public static void sendPayloadData(String appId, int productLine, byte[] payload, int headerType, boolean ack, long msgId) { + final Channel channel = ClientSocketManager.getInstance().getChannel(); + if (checkNotNull(channel)) { + channel.writeAndFlush(RequestModelUtil.buildPayloadData(productLine, appId, payload, headerType, ack, msgId)); + Logger.i(SocketConstants.TAG, "send---> appId is: " + appId + "; headerType is: " + headerType); + } + } + + public static final class RequestModelUtil { + + private RequestModelUtil() { + } + + private static MogoConnsvrPacket buildAuthRequest() { + final ByteBuf headerBuf = Unpooled.buffer(); + headerBuf.writeBytes(buildHeader(MogoConnsvr.MsgType.mogoMsgTypeConnSvrAuthReq_VALUE)); + return new MogoConnsvrPacket(headerBuf); + } + + private static MogoConnsvrPacket buildEncodeInfo(String enInfo) { + final MogoConnsvr.AuthMidReq authMidReq = MogoConnsvr.AuthMidReq.newBuilder() + .setKey(enInfo).build(); + final ByteBuf byteBuf = Unpooled.buffer(); + byteBuf.writeBytes(authMidReq.toByteArray()); + + final ByteBuf headerBuf = Unpooled.buffer(); + headerBuf.writeBytes(buildHeader(MogoConnsvr.MsgType.mogoMsgTypeConnSvrAuthMidReq_VALUE)); + + return new MogoConnsvrPacket(headerBuf, byteBuf); + } + + private static ByteBuf buildHeader(int msgType) { + String appId = SocketConfig.instance().getChannelId(); + if (TextUtils.isEmpty(appId)) { + appId = "unknown"; + } + Logger.i(SocketConstants.TAG, "socket app id is " + appId + ", msg type is " + msgType); + + return buildHeader(appId, 0, msgType, false, 0); + } + + private static ByteBuf buildHeader(String appId, int productLine, int msgType, boolean ack, long msgId) { + final String token = SocketConfig.instance().getToken(); + final String userId = SocketConfig.instance().getUid(); + final String sn = SocketConfig.instance().getSn(); + final String versionName = DevicesUtils.getVersionName(SocketConfig.instance().getAppContext()); + final String fotaVersion = TelephoneUtil.getFotaVersion(); + + long uid = 0; + try { + if (!TextUtils.isEmpty(userId)) { + uid = Long.parseLong(userId); + } + } catch (Exception e) { + e.printStackTrace(); + } + + MogoCommon.Client client = SocketConfig.instance().getClient(); + if (client == null) { + client = MogoCommon.Client.car; + } + + final MogoConnsvr.UserInfo userInfo = MogoConnsvr.UserInfo.newBuilder() + .setUid(uid) + .setToken(TextUtils.isEmpty(token) ? "" : token) + .setClient(client.getNumber()) + .setFotaVersion(TextUtils.isEmpty(fotaVersion) ? "" : fotaVersion) + .setAppVersion(TextUtils.isEmpty(versionName) ? "" : versionName) + .setAppVersionCode(DevicesUtils.getVersionCode(SocketConfig.instance().getAppContext())) + .setSn(TextUtils.isEmpty(sn) ? "" : sn).build(); + + final MogoConnsvr.Header header = MogoConnsvr.Header.newBuilder() + .setTimestamp(System.currentTimeMillis()) + .setMsgType(msgType) + .setAck(ack) + .setMsgId(msgId) + .setAppId(appId) + .setUserInfo(userInfo) + .setVersion(0) + .setProductLine(productLine).build(); + + return Unpooled.buffer().writeBytes(header.toByteArray()); + } + + private static MogoConnsvrPacket buildHeartbeat() { + return new MogoConnsvrPacket(buildHeader(MogoConnsvr.MsgType.mogoMsgTypeConnSvrHeartbeatReq_VALUE)); + } + + private static MogoConnsvrPacket buildUserInfo() { + final String versionName = DevicesUtils.getVersionName(SocketConfig.instance().getAppContext()); + final String networkTypeName = DevicesUtils.getNetworkTypeName(SocketConfig.instance().getAppContext()); + final String sn = SocketConfig.instance().getSn(); + + long uid = 0; + try { + String userId = SocketConfig.instance().getUid(); + if (!TextUtils.isEmpty(userId)) { + uid = Long.parseLong(userId); + } + } catch (Exception e) { + e.printStackTrace(); + } + + MogoCommon.Client client = SocketConfig.instance().getClient(); + if (client == null) { + client = MogoCommon.Client.car; + } + + final MogoConnsvr.ConnSvrUserInfoReq userInfoReq = MogoConnsvr.ConnSvrUserInfoReq.newBuilder() + .setUserId(uid) + .setDeviceType(MogoCommon.OS.ANDROID) + .setNettype(TextUtils.isEmpty(networkTypeName) ? "" : networkTypeName) + .setModel(Build.MODEL) + .setManufacturer(Build.MANUFACTURER) + .setBrand(Build.BRAND) + .setAppVersion(TextUtils.isEmpty(versionName) ? "" : versionName) + .setVersion(SocketConfig.PROTO_VERSION) + .setClient(client) + .setSn(TextUtils.isEmpty(sn) ? "" : sn).build(); + + final ByteBuf payloadBuf = Unpooled.buffer().writeBytes(userInfoReq.toByteArray()); + + return new MogoConnsvrPacket(buildHeader(MogoConnsvr.MsgType.mogoMsgTypeConnSvrAgentInfoReq_VALUE), payloadBuf); + } + + private static MogoConnsvrPacket buildPayloadData(int productLine, + String appId, + byte[] payload, + int headerType, + boolean ack, + long msgId) { + final ByteBuf buf = Unpooled.buffer().writeBytes(payload); + return new MogoConnsvrPacket(buildHeader(appId, productLine, headerType, ack, msgId), buf); + } + } +} diff --git a/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/utils/SignUtil.java b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/utils/SignUtil.java new file mode 100644 index 0000000..9daa26b --- /dev/null +++ b/foudations/mogo-socket/src/main/java/com/mogo/cloud/socket/third/utils/SignUtil.java @@ -0,0 +1,40 @@ +package com.mogo.cloud.socket.third.utils; + + +import java.nio.charset.StandardCharsets; + +public class SignUtil { + private static final String MD5_PREFIX = "zhidaoautosocket"; + private static final char[] DIGITS_LOWER; + + static { + DIGITS_LOWER = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + } + + public static synchronized String createSign(String token) { + String encryptString = MD5_PREFIX + token; + return encodeHexString(md5(encryptString)); + } + + private static String encodeHexString(byte[] data) { + return new String(encodeHex(data)); + } + + private static char[] encodeHex(byte[] data) { + int l = data.length; + char[] out = new char[l << 1]; + int i = 0; + + for (int var5 = 0; i < l; ++i) { + out[var5++] = DIGITS_LOWER[(240 & data[i]) >>> 4]; + out[var5++] = DIGITS_LOWER[15 & data[i]]; + } + return out; + } + + private static byte[] md5(String string) { + return string == null ? null : string.getBytes(StandardCharsets.UTF_8); + } + + +} diff --git a/foudations/mogo-utils/src/main/AndroidManifest.xml b/foudations/mogo-utils/src/main/AndroidManifest.xml index d29531b..1d29d78 100644 --- a/foudations/mogo-utils/src/main/AndroidManifest.xml +++ b/foudations/mogo-utils/src/main/AndroidManifest.xml @@ -2,4 +2,5 @@ package="com.mogo.cloud.common"> / + \ No newline at end of file diff --git a/foudations/mogo-utils/src/main/java/com/mogo/cloud/DevicesUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/cloud/DevicesUtils.java index 88c2172..14ecd25 100644 --- a/foudations/mogo-utils/src/main/java/com/mogo/cloud/DevicesUtils.java +++ b/foudations/mogo-utils/src/main/java/com/mogo/cloud/DevicesUtils.java @@ -1,5 +1,11 @@ package com.mogo.cloud; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Build; + import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -33,4 +39,69 @@ public class DevicesUtils { } return value; } + + + public static String getNetworkTypeName(Context context) { + String typeName = "unknown"; + if (context != null) { + ConnectivityManager connectivityManager = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + if (connectivityManager == null) { + return typeName; + } + NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); + if (networkInfo != null) { + typeName = networkInfo.getTypeName(); + } + } + return typeName; + } + + public static int getNetworkType(Context context) { + if (context != null) { + ConnectivityManager connectivityManager = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + if (connectivityManager != null) { + NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo(); + if (activeNetInfo != null) { + return activeNetInfo.getType(); + } + } + } + return -1; + } + + public static boolean isSdkHigherThan18() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + } + + public static String getVersionName(Context context) { + String appVersion = ""; + + try { + if (context != null) { + String packageName = context.getApplicationInfo().packageName; + appVersion = context.getPackageManager().getPackageInfo(packageName, 0).versionName; + } + } catch (Exception e) { + e.printStackTrace(); + } + + return appVersion; + } + + public static int getVersionCode(Context context) { + String pkgName = context.getPackageName(); + + try { + PackageInfo e = context.getPackageManager().getPackageInfo(pkgName, 0); + if (e != null) { + return e.versionCode; + } + } catch (Exception var2) { + var2.printStackTrace(); + } + + return 1; + } } diff --git a/foudations/mogo-utils/src/main/java/com/mogo/cloud/SingleThreadPool.java b/foudations/mogo-utils/src/main/java/com/mogo/cloud/SingleThreadPool.java new file mode 100644 index 0000000..e239cc4 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/cloud/SingleThreadPool.java @@ -0,0 +1,82 @@ +package com.mogo.cloud; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class SingleThreadPool { + + // Thread Pool + private static final int MAX_QUEUE_SIZE = 5000; + private static final int CORE_POOL_SIZE = 1; + private static final int MAX_POOL_SIZE = 1; + private static final int KEEP_ALIEVE_TIME = 5; + private static final TimeUnit UNIT = TimeUnit.SECONDS; + private static final BlockingQueue WORK_QUEUE = new LinkedBlockingQueue(MAX_QUEUE_SIZE); + private static final RejectedExecutionHandler HANDLER = new ThreadPoolExecutor.DiscardPolicy(); + + // Executor + private ExecutorService mExecutorService; + + public SingleThreadPool() { + mExecutorService = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIEVE_TIME, UNIT, WORK_QUEUE, HANDLER); + } + + @Override + protected void finalize() throws Throwable { + synchronized (SingleThreadPool.class) { + clearQueue(); + shutdown(); + } + super.finalize(); + } + + public void clearQueue() { + if (WORK_QUEUE != null) { + try { + WORK_QUEUE.clear(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public int getPoolSize() { + return WORK_QUEUE == null ? 0 : WORK_QUEUE.size(); + } + + public boolean isEmpty() { + return WORK_QUEUE == null ? true : WORK_QUEUE.size() == 0 ? true : false; + } + + public void shutdown() { + if (mExecutorService != null) { + try { + mExecutorService.shutdown(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public Future submit(Callable task) { + return mExecutorService.submit(task); + } + + public Future submit(Runnable task) { + return mExecutorService.submit(task); + } + + public Future submit(Runnable task, T result) { + return mExecutorService.submit(task, result); + } + + public void execute(Runnable command) { + mExecutorService.execute(command); + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/cloud/TelephoneUtil.java b/foudations/mogo-utils/src/main/java/com/mogo/cloud/TelephoneUtil.java new file mode 100644 index 0000000..d1aa65f --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/cloud/TelephoneUtil.java @@ -0,0 +1,171 @@ +package com.mogo.cloud; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.os.Build; +import android.telephony.TelephonyManager; +import android.text.TextUtils; + +import com.mogo.cloud.utils.logger.Logger; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + + +/** + * @author wangzhiyuan + * @since 2018/2/7 + */ + +public class TelephoneUtil { + public static String sn = ""; + public static String fotaVersion = ""; + public static String fotaDevice = ""; + public static String imei = ""; + public static String iccid = ""; + public static String vin = ""; + + public static String getFotaDevice() { + if (TextUtils.isEmpty(fotaDevice)) { + String device = ""; + try { + Class c = Class.forName("android.os.SystemProperties"); + Method get = c.getMethod("get", String.class); + device = (String) get.invoke(c, "ro.fota.device"); + if (!TextUtils.isEmpty(device)) { + device = device.trim(); + } + } catch (Exception e) { + e.printStackTrace(); + } + fotaDevice = device; + } + return fotaDevice; + } + + public static String getFotaVersion() { + if (TextUtils.isEmpty(fotaVersion)) { + String version = ""; + try { + Class c = Class.forName("android.os.SystemProperties"); + Method get = c.getMethod("get", String.class); + version = (String) get.invoke(c, "ro.fota.version"); + if (!TextUtils.isEmpty(version)) { + version = version.trim(); + } + } catch (Exception e) { + e.printStackTrace(); + } + fotaVersion = version; + } + return fotaVersion; + } + + public static boolean getMobileDataState(Context context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + // Android 5.0之前 + ConnectivityManager conMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + Class conMgrClass = null; + Object iConMgr = null; + try { + conMgrClass = Class.forName(conMgr.getClass().getName());//ConnectivityManager + Field iConMgrField = conMgrClass.getDeclaredField("mService");//IConnectivityManager + iConMgrField.setAccessible(true); + iConMgr = iConMgrField.get(conMgr);//获得ConnectivityManager的IConnectivityManager实例 + + Class iConMgrClass = Class.forName(iConMgr.getClass().getName()); + Method getMobileDataEnabledMethod = iConMgrClass.getDeclaredMethod("getMobileDataEnabled"); + if (getMobileDataEnabledMethod != null) { + getMobileDataEnabledMethod.setAccessible(true); + boolean result = (Boolean) getMobileDataEnabledMethod.invoke(iConMgr); + Logger.d("TelephoneUtil", "getMobileDataEnabled = " + result); + return result; + } + } catch (Exception e) { + e.printStackTrace(); + } + } else { + // Android 5.0之后(包含) + TelephonyManager telephonyService = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + try { + Method getDataEnabled = telephonyService.getClass().getDeclaredMethod("getDataEnabled"); + if (null != getDataEnabled) { + boolean result = (Boolean) getDataEnabled.invoke(telephonyService); + Logger.d("TelephoneUtil", "getDataEnabled = " + result); + return result; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + return false; + } + + public static void setMobileDataState(Context context, boolean enable) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + // Android 5.0之前 + ConnectivityManager conMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + Class conMgrClass = null; + Object iConMgr = null; + try { + conMgrClass = Class.forName(conMgr.getClass().getName());//ConnectivityManager + Field iConMgrField = conMgrClass.getDeclaredField("mService");//IConnectivityManager + iConMgrField.setAccessible(true); + iConMgr = iConMgrField.get(conMgr);//获得ConnectivityManager的IConnectivityManager实例 + + Class iConMgrClass = Class.forName(iConMgr.getClass().getName()); + Method setMobileDataEnabledMethod = iConMgrClass.getDeclaredMethod("setMobileDataEnabled", boolean.class); + if (setMobileDataEnabledMethod != null) { + setMobileDataEnabledMethod.setAccessible(true); + setMobileDataEnabledMethod.invoke(iConMgr, enable); + Logger.d("TelephoneUtil", "setMobileDataEnabled = " + enable); + } + } catch (Exception e) { + e.printStackTrace(); + } + } else { + // Android 5.0之后(包含) + try { + TelephonyManager telephonyService = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + Method setDataEnabledMethod = telephonyService.getClass().getDeclaredMethod("setDataEnabled", boolean.class); + if (null != setDataEnabledMethod) { + setDataEnabledMethod.invoke(telephonyService, enable); + Logger.d("TelephoneUtil", "setDataEnabled = " + enable); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public static String getSerialNumber() { + if (TextUtils.isEmpty(sn)) { + String serial = ""; + try { + Class c = Class.forName("android.os.SystemProperties"); + Method get = c.getMethod("get", String.class); + serial = (String) get.invoke(c, "zhidao.serial"); + if (TextUtils.isEmpty(serial)) { + serial = (String) get.invoke(c, "gsm.serial"); + } + if (TextUtils.isEmpty(serial)) { + serial = (String) get.invoke(c, "ro.serialno"); + } + } catch (Exception e) { + e.printStackTrace(); + } + + try { + if (serial.startsWith("ZD") || serial.startsWith("XT")) { + sn = serial; + } else { + return serial; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + return sn; + } + +} diff --git a/gradle.properties b/gradle.properties index c265fb2..9728a0e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -36,22 +36,22 @@ PASSWORD=xintai2018 RELEASE=true # AI CLOUD 云平台 # 工具类 -MOGO_UTILS_VERSION=1.1.56-live +MOGO_UTILS_VERSION=1.2.2 # 网络请求 -MOGO_NETWORK_VERSION=1.1.56-live +MOGO_NETWORK_VERSION=1.2.2 # 网络DNS -MOGO_HTTPDNS_VERSION=1.1.56-live +MOGO_HTTPDNS_VERSION=1.2.2 # 鉴权 -MOGO_PASSPORT_VERSION=1.1.56-live +MOGO_PASSPORT_VERSION=1.2.2 # 常链接 -MOGO_SOCKET_VERSION=1.1.56-live +MOGO_SOCKET_VERSION=1.2.2 # 数据采集 -MOGO_REALTIME_VERSION=1.1.56-live +MOGO_REALTIME_VERSION=1.2.2 # 探路,道路事件发布,获取 -MOGO_TANLU_VERSION=1.1.56-live +MOGO_TANLU_VERSION=1.2.2 # 直播推流 -MOGO_LIVE_VERSION=1.1.56-live +MOGO_LIVE_VERSION=1.2.2 # 直播拉流 -MOGO_TRAFFICLIVE_VERSION=1.1.56-live +MOGO_TRAFFICLIVE_VERSION=1.2.2 # 定位服务 -MOGO_LOCATION_VERSION=1.1.56-live +MOGO_LOCATION_VERSION=1.2.2