2021-11-15 1.增加认证和加密通讯,未调通
 
	
	
	
	
	
	
	
	
	
	
	
	
	
 |  |  | 
 |  |  | <?xml version="1.0" encoding="UTF-8"?>
 | 
 |  |  | <project version="4">
 | 
 |  |  |   <component name="CompilerConfiguration">
 | 
 |  |  |     <bytecodeTargetLevel target="11" />
 | 
 |  |  |     <bytecodeTargetLevel target="1.8" />
 | 
 |  |  |   </component>
 | 
 |  |  | </project> | 
 
 |  |  | 
 |  |  |       </map>
 | 
 |  |  |     </option>
 | 
 |  |  |   </component>
 | 
 |  |  |   <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
 | 
 |  |  |   <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
 | 
 |  |  |     <output url="file://$PROJECT_DIR$/build/classes" />
 | 
 |  |  |   </component>
 | 
 |  |  |   <component name="ProjectType">
 | 
 
 |  |  | 
 |  |  | import android.Manifest; | 
 |  |  | import android.app.Instrumentation; | 
 |  |  | import android.os.Bundle; | 
 |  |  | import android.util.Log; | 
 |  |  | import android.view.View; | 
 |  |  | import android.widget.TextView; | 
 |  |  |  | 
 |  |  | import com.chad.library.adapter.base.BaseQuickAdapter; | 
 |  |  | import com.chad.library.adapter.base.listener.OnItemClickListener; | 
 |  |  | import com.google.gson.JsonObject; | 
 |  |  | import com.hdl.sdk.common.config.AuthenticateConfig; | 
 |  |  | import com.hdl.sdk.common.config.TopicConstant; | 
 |  |  | import com.hdl.sdk.common.utils.IdUtils; | 
 |  |  | import com.hdl.sdk.common.utils.IpUtils; | 
 |  |  | import com.hdl.sdk.connect.HDLAuthSocket; | 
 |  |  | import com.hdl.sdk.connect.HDLSocket; | 
 |  |  | import com.hdl.sdk.connect.bean.DeviceControlRequest; | 
 |  |  | import com.hdl.sdk.connect.bean.GatewaySearchBean; | 
 |  |  | import com.hdl.sdk.connect.bean.LinkRequest; | 
 |  |  | import com.hdl.sdk.connect.protocol.LinkMessageDecoder; | 
 |  |  | import com.hdl.sdk.connect.protocol.LinkMessageEncoder; | 
 |  |  | 
 |  |  |     private RecyclerView rv; | 
 |  |  |     private TextView tv; | 
 |  |  |     private TextView responseTv; | 
 |  |  |  | 
 |  |  |     boolean isOn; | 
 |  |  |  | 
 |  |  |  | 
 |  |  |     @Override | 
 |  |  | 
 |  |  |         tv = findViewById(R.id.state_tv); | 
 |  |  |         rv = findViewById(R.id.rv); | 
 |  |  |         rv.setLayoutManager(new LinearLayoutManager(this)); | 
 |  |  |         AuthenticateConfig.getInstance().setLocalEncrypt(true); | 
 |  |  |         AuthenticateConfig.getInstance().setLocalSecret("5648d9b9cafd30dd"); | 
 |  |  |  | 
 |  |  |         ActivityResultLauncher<String[]> launcher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() { | 
 |  |  |             @Override | 
 |  |  | 
 |  |  |         beans.add(new DemoBean("设备控制")); | 
 |  |  |         beans.add(new DemoBean("状态上报")); | 
 |  |  |         beans.add(new DemoBean("读取状态")); | 
 |  |  |         beans.add(new DemoBean("入网认证")); | 
 |  |  |         demoAdapter = new DemoAdapter(beans); | 
 |  |  |         rv.setAdapter(demoAdapter); | 
 |  |  |  | 
 |  |  | 
 |  |  |                         break; | 
 |  |  |                     case 3: | 
 |  |  |                         //设备控制 | 
 |  |  |                          // HDLSocket.getInstance().propertyDown(); | 
 |  |  |                         isOn = !isOn; | 
 |  |  |                         List<DeviceControlRequest> requestList = new ArrayList<>(); | 
 |  |  |                         DeviceControlRequest request = new DeviceControlRequest(); | 
 |  |  |                         request.setSid("000101B847C71B02020100010101"); | 
 |  |  |                         List<DeviceControlRequest.StatusBean>  statusBeanList= new ArrayList<>(); | 
 |  |  |                         DeviceControlRequest.StatusBean bean =  new DeviceControlRequest.StatusBean(); | 
 |  |  |                         bean.setKey("on_off"); | 
 |  |  |                         bean.setValue(isOn ? "on" : "off"); | 
 |  |  |                         statusBeanList.add(bean); | 
 |  |  |                         request.setStatus(statusBeanList); | 
 |  |  |                         requestList.add(request); | 
 |  |  |                         HDLAuthSocket.getInstance().propertyDown(requestList,null); | 
 |  |  |  | 
 |  |  |  | 
 |  |  |                         break; | 
 |  |  |                     case 4: | 
 |  |  |                         //状态上报 | 
 |  |  | 
 |  |  |                         //读取状态 | 
 |  |  |                         // HDLSocket.getInstance().propertyRead(); | 
 |  |  |                         break; | 
 |  |  |                     case 6: | 
 |  |  |                         //入网认证 | 
 |  |  |                         HDLAuthSocket.getInstance().sendAuthenticateRequest(new HDLAuthSocket.CallBack() { | 
 |  |  |                             @Override | 
 |  |  |                             public void onError(String error) { | 
 |  |  |                                 Log.i("TAG", "onError: 认证失败"); | 
 |  |  | //                                tv.setText("认证失败"); | 
 |  |  | //                                responseTv.setText(error); | 
 |  |  |                             } | 
 |  |  |  | 
 |  |  |                             @Override | 
 |  |  |                             public void onSuccess(String data) { | 
 |  |  |                                 tv.setText("认证成功"); | 
 |  |  |                                 responseTv.setText(data); | 
 |  |  |                             } | 
 |  |  |                         }); | 
 |  |  |  | 
 |  |  | //                        HDLAuthSocket.getInstance().searchGateway("1406844230123372545", new HDLAuthSocket.SearchGatewayCallBack() { | 
 |  |  | //                            @Override | 
 |  |  | //                            public void onEnd(String error) { | 
 |  |  | // | 
 |  |  | //                            } | 
 |  |  | // | 
 |  |  | //                            @Override | 
 |  |  | //                            public void onSuccess(GatewaySearchBean gatewaySearchBean) { | 
 |  |  | //                                Log.i("TAG", "onSuccess: 搜索成功:"+gatewaySearchBean.getGatewayId()); | 
 |  |  | //                            } | 
 |  |  | //                        }); | 
 |  |  |  | 
 |  |  |  | 
 |  |  |                         break; | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         }); | 
 
| New file | 
 |  |  | 
 |  |  | package com.hdl.sdk.common.config; | 
 |  |  |  | 
 |  |  | import android.text.TextUtils; | 
 |  |  |  | 
 |  |  | import com.hdl.sdk.common.utils.SPUtils; | 
 |  |  |  | 
 |  |  | import static com.hdl.sdk.common.config.TopicConstant.DEIVCE_AUTH_REQUEST; | 
 |  |  | import static com.hdl.sdk.common.config.TopicConstant.GATEWAY_SEARCH; | 
 |  |  |  | 
 |  |  | /** | 
 |  |  |  * Created by jlchen on 11/11/21. | 
 |  |  |  * | 
 |  |  |  * @Description : AuthenticateConfig | 
 |  |  |  */ | 
 |  |  | public class AuthenticateConfig { | 
 |  |  |     private static final String Authenticate_LS_KEY = "auth_ls_key"; | 
 |  |  |     private static final String AUTHENTICATE_GATEWAYID_KEY = "auth_gatewayid_key"; | 
 |  |  |     private static final String AUTHENTICATE_IPADDRESS_KEY = "auth_ipaddress_key"; | 
 |  |  |  | 
 |  |  |     private String localSecret;//本地加密密钥 | 
 |  |  |     private String gatewayId; | 
 |  |  |     private String ipAddress; | 
 |  |  |     private boolean isLocalEncrypt;//网关是否需要加密通讯 | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * instance | 
 |  |  |      */ | 
 |  |  |     private volatile static AuthenticateConfig instance; | 
 |  |  |     /** | 
 |  |  |      * getInstance | 
 |  |  |      * | 
 |  |  |      * @return AuthenticateConfig | 
 |  |  |      */ | 
 |  |  |     public static synchronized AuthenticateConfig getInstance() { | 
 |  |  |         if (instance == null) { | 
 |  |  |             synchronized (AuthenticateConfig.class) { | 
 |  |  |                 if (instance == null) { | 
 |  |  |                     instance = new AuthenticateConfig(); | 
 |  |  |                     instance.loadConfig(); | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |         return instance; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 清空缓存 | 
 |  |  |      */ | 
 |  |  |     public void clearConfig() { | 
 |  |  |         this.gatewayId = ""; | 
 |  |  |         this.ipAddress = ""; | 
 |  |  |         this.localSecret = ""; | 
 |  |  |         SPUtils.remove(Authenticate_LS_KEY); | 
 |  |  |         SPUtils.remove(AUTHENTICATE_GATEWAYID_KEY); | 
 |  |  |         SPUtils.remove(AUTHENTICATE_IPADDRESS_KEY); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 加载缓存 | 
 |  |  |      */ | 
 |  |  |     void loadConfig(){ | 
 |  |  |         localSecret = SPUtils.getString(Authenticate_LS_KEY, ""); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 保存配置 | 
 |  |  |      * @param localSecret | 
 |  |  |      * @param gatewayId | 
 |  |  |      * @param ipAddress | 
 |  |  |      */ | 
 |  |  |     public void saveConfig(String localSecret, String gatewayId, String ipAddress) { | 
 |  |  |         this.localSecret = localSecret; | 
 |  |  |         this.gatewayId = gatewayId; | 
 |  |  |         this.ipAddress = ipAddress; | 
 |  |  |         SPUtils.put(Authenticate_LS_KEY, localSecret); | 
 |  |  |         SPUtils.put(AUTHENTICATE_GATEWAYID_KEY, gatewayId); | 
 |  |  |         SPUtils.put(AUTHENTICATE_IPADDRESS_KEY, ipAddress); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 检测是否已经认证过 | 
 |  |  |      * @return | 
 |  |  |      */ | 
 |  |  |     public boolean checkIfCertified(){ | 
 |  |  |         //localSecret不为空并且长度等于16 | 
 |  |  |         return !TextUtils.isEmpty(localSecret) && localSecret.length() == 16; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public void setLocalSecret(String localSecret) { | 
 |  |  |         this.localSecret = localSecret; | 
 |  |  |         SPUtils.put(Authenticate_LS_KEY, localSecret); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public String getLocalSecret() { | 
 |  |  |         return localSecret; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public String getGatewayId() { | 
 |  |  |         return gatewayId; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public String getIpAddress() { | 
 |  |  |         return ipAddress; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public boolean isLocalEncrypt() { | 
 |  |  |         return isLocalEncrypt; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public void setLocalEncrypt(boolean localEncrypt) { | 
 |  |  |         isLocalEncrypt = localEncrypt; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 判断当前主题数据是否需要加密 | 
 |  |  |      * @param topicStr 当前主题 | 
 |  |  |      * @return | 
 |  |  |      */ | 
 |  |  |     public boolean ifNeedEncrypt(String topicStr){ | 
 |  |  |         //过滤相关需要加密的主题 | 
 |  |  |         return (!topicStr.contains(DEIVCE_AUTH_REQUEST) //入网认证 | 
 |  |  |                 && !topicStr.contains(GATEWAY_SEARCH) //搜索网关主题 | 
 |  |  |                 && isLocalEncrypt//启用加密标志 | 
 |  |  |         ); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |  | 
 |  |  |  | 
 |  |  | } | 
 
 |  |  | 
 |  |  |  */
 | 
 |  |  | public class TopicConstant {
 | 
 |  |  | 
 | 
 |  |  |     //网关广播入网指令
 | 
 |  |  |     public static final String GATEWAY_AUTH_BROADCAST = "/user/all/custom/device/network_access/broadcast";
 | 
 |  |  | 
 | 
 |  |  |     //设备入网和认证
 | 
 |  |  |     public static final String DEIVCE_AUTH_REQUEST = "/user/all/custom/device/network_access/request";
 | 
 |  |  | 
 | 
 |  |  |     //设备入网和认证响应
 | 
 |  |  |     public static final String DEIVCE_AUTH_REQUEST_REPLY = "/user/all/custom/device/network_access/request_reply";
 | 
 |  |  | 
 | 
 |  |  |     //搜索网关
 | 
 |  |  |     public static final String GATEWAY_SEARCH = "/user/all/custom/gateway/search";
 | 
 |  |  | 
 | 
 
 |  |  | 
 |  |  | 
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public static void e(String msg, Throwable tr) {
 | 
 |  |  | 
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public static void w(String tag, String msg) {
 | 
 |  |  | 
 | 
 |  |  |     }
 | 
 
 |  |  | 
 |  |  |     api project(path: ':hdl-socket') | 
 |  |  |     implementation 'androidx.appcompat:appcompat:1.2.0' | 
 |  |  |     implementation 'com.google.android.material:material:1.3.0' | 
 |  |  |  | 
 |  |  |     //加密 | 
 |  |  |     implementation 'cn.hutool:hutool-all:5.6.5' | 
 |  |  |     implementation 'org.bouncycastle:bcprov-jdk15on:1.60' | 
 |  |  | } | 
 
| New file | 
 |  |  | 
 |  |  | package com.hdl.sdk.connect; | 
 |  |  |  | 
 |  |  | import android.text.TextUtils; | 
 |  |  | import android.util.Log; | 
 |  |  |  | 
 |  |  | import com.google.gson.Gson; | 
 |  |  | import com.google.gson.JsonObject; | 
 |  |  | import com.google.gson.reflect.TypeToken; | 
 |  |  | import com.hdl.sdk.common.config.AuthenticateConfig; | 
 |  |  | import com.hdl.sdk.common.config.TopicConstant; | 
 |  |  | import com.hdl.sdk.common.utils.IdUtils; | 
 |  |  | import com.hdl.sdk.common.utils.IpUtils; | 
 |  |  | import com.hdl.sdk.common.utils.gson.GsonConvert; | 
 |  |  | import com.hdl.sdk.connect.bean.AuthenticateRequest; | 
 |  |  | import com.hdl.sdk.connect.bean.AuthenticateRequest.AuthenticateDeviceInfoBean; | 
 |  |  | import com.hdl.sdk.connect.bean.BaseLocalResponse; | 
 |  |  | import com.hdl.sdk.connect.bean.DeviceControlRequest; | 
 |  |  | import com.hdl.sdk.connect.bean.GatewaySearchBean; | 
 |  |  | import com.hdl.sdk.connect.bean.LinkRequest; | 
 |  |  | import com.hdl.sdk.connect.bean.LinkResponse; | 
 |  |  | import com.hdl.sdk.connect.protocol.LinkMessageDecoder; | 
 |  |  | import com.hdl.sdk.connect.protocol.LinkMessageEncoder; | 
 |  |  | import com.hdl.sdk.socket.SocketBoot; | 
 |  |  | import com.hdl.sdk.socket.SocketOptions; | 
 |  |  | import com.hdl.sdk.socket.client.UdpClient; | 
 |  |  | import com.hdl.sdk.socket.codec.MessagePipeLine; | 
 |  |  |  | 
 |  |  | import java.io.UnsupportedEncodingException; | 
 |  |  | import java.security.MessageDigest; | 
 |  |  | import java.security.NoSuchAlgorithmException; | 
 |  |  | import java.util.List; | 
 |  |  |  | 
 |  |  | import static com.hdl.sdk.common.config.TopicConstant.DEIVCE_AUTH_REQUEST; | 
 |  |  |  | 
 |  |  | /** | 
 |  |  |  * Created by jlchen on 11/11/21. | 
 |  |  |  * | 
 |  |  |  * @Description : HDLAuthSocket | 
 |  |  |  */ | 
 |  |  | public class HDLAuthSocket { | 
 |  |  |     /** | 
 |  |  |      * udp默认端口 | 
 |  |  |      */ | 
 |  |  |     private static final int UDP_PORT = 8585; | 
 |  |  |     private static SocketBoot updBoot; | 
 |  |  |     /** | 
 |  |  |      * instance | 
 |  |  |      */ | 
 |  |  |     private volatile static HDLAuthSocket instance; | 
 |  |  |  | 
 |  |  |     public interface CallBack { | 
 |  |  |  | 
 |  |  |         void onError(String error); | 
 |  |  |  | 
 |  |  |         void onSuccess(String data); | 
 |  |  |  | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * getInstance | 
 |  |  |      * | 
 |  |  |      * @return HDLAuthSocket | 
 |  |  |      */ | 
 |  |  |     public static synchronized HDLAuthSocket getInstance() { | 
 |  |  |         if (instance == null) { | 
 |  |  |             synchronized (AuthenticateConfig.class) { | 
 |  |  |                 if (instance == null) { | 
 |  |  |                     instance = new HDLAuthSocket(); | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |         return instance; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     private SocketOptions getUdpOptions() { | 
 |  |  |         final SocketOptions options = new SocketOptions(); | 
 |  |  |         final MessagePipeLine pipeLine = new MessagePipeLine(); | 
 |  |  |         pipeLine.add(new LinkMessageDecoder()); | 
 |  |  |         pipeLine.add(new LinkMessageEncoder()); | 
 |  |  |         options.setHandleMessage(pipeLine); | 
 |  |  |         options.setEnabledHeartbeat(false); | 
 |  |  |         return options; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     private SocketBoot getUdpBoot(String ip) { | 
 |  |  |         if (updBoot == null) { | 
 |  |  |             updBoot = UdpClient.init(ip, UDP_PORT, getUdpOptions()); | 
 |  |  |             updBoot.connect(); | 
 |  |  |         } | 
 |  |  |         return updBoot; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     private String getOid() { | 
 |  |  |         return "000101EA9B8C1C22"; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     private String getSid() { | 
 |  |  |         return "000101EA9B8C1C2202010001021A"; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 发送入网及认证请求 | 
 |  |  |      * | 
 |  |  |      * @param ip       网关IP | 
 |  |  |      * @param request  认证请求信息 | 
 |  |  |      * @param callBack 结果回调 | 
 |  |  |      */ | 
 |  |  |     public void sendAuthenticateRequest(String ip, AuthenticateRequest request, CallBack callBack) { | 
 |  |  |         if (request == null) { | 
 |  |  |             callBack.onError("request null"); | 
 |  |  |         } | 
 |  |  |         String topic = DEIVCE_AUTH_REQUEST; | 
 |  |  |         Gson gs = new Gson(); | 
 |  |  |         String requestStr = gs.toJson(request); | 
 |  |  |         LinkRequest message = new LinkRequest(topic, requestStr); | 
 |  |  |  | 
 |  |  |         HdlSocketHelper.send(getUdpBoot(ip), message, new HdlSocketHelper.HdlSocketListener() { | 
 |  |  |             @Override | 
 |  |  |             public void onSucceed(Object msg) { | 
 |  |  |                 callBack.onSuccess(msg.toString()); | 
 |  |  |             } | 
 |  |  |  | 
 |  |  |             @Override | 
 |  |  |             public void onFailure() { | 
 |  |  |                 Log.i("TAG", "onFailure: "); | 
 |  |  |                 callBack.onError("超时"); | 
 |  |  |             } | 
 |  |  |         }); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 发送入网及认证请求 | 
 |  |  |      * | 
 |  |  |      * @param callBack 结果回调 | 
 |  |  |      */ | 
 |  |  |     public void sendAuthenticateRequest(CallBack callBack) { | 
 |  |  |         String macStr = "AA000000000000BB"; | 
 |  |  |         String secret = "87ae414b7a853f65"; | 
 |  |  |         String mac_key = stringToMD5(stringToMD5(macStr + secret)); | 
 |  |  |  | 
 |  |  |         String versionString = "HDL_V1.0.1"; | 
 |  |  |         String time = String.valueOf(System.currentTimeMillis()); | 
 |  |  |  | 
 |  |  |         //1.设置认证信息 | 
 |  |  |         AuthenticateRequest.RequestBean requestBean = new AuthenticateRequest.RequestBean(); | 
 |  |  |         requestBean.setMAC(macStr); | 
 |  |  |         requestBean.setSupplier("HDL"); | 
 |  |  |         requestBean.setFirmwareVersion(versionString); | 
 |  |  |         requestBean.setHardwareModel("1956F"); | 
 |  |  |         AuthenticateRequest.AuthBean authbean = new AuthenticateRequest.AuthBean(); | 
 |  |  |         authbean.setSpk("ir.module"); | 
 |  |  |         authbean.setMACKey(mac_key); | 
 |  |  |         authbean.setRequest(requestBean); | 
 |  |  |  | 
 |  |  |  | 
 |  |  |         //2.设置设备信息 | 
 |  |  |         AuthenticateDeviceInfoBean infoBean = new AuthenticateDeviceInfoBean(); | 
 |  |  |         infoBean.setDeviceMAC(macStr); | 
 |  |  |         infoBean.setIPMAC(macStr); | 
 |  |  |         infoBean.setDeviceName("HDL面板"); | 
 |  |  |         infoBean.setAccessMode("HDL"); | 
 |  |  |         infoBean.setOID(getOid()); | 
 |  |  |         infoBean.setSid(getSid()); | 
 |  |  | //        infoBean.set | 
 |  |  |         AuthenticateRequest.VersionBean[] versionBeans = new AuthenticateRequest.VersionBean[]{new AuthenticateRequest.VersionBean("FW", versionString), new AuthenticateRequest.VersionBean("HW", "1956F")}; | 
 |  |  |         infoBean.setVersions(versionBeans); | 
 |  |  |         AuthenticateRequest request = new AuthenticateRequest(IdUtils.getUUId(), time, infoBean, authbean); | 
 |  |  |  | 
 |  |  |         String ip = IpUtils.getBroadcastAddress(); | 
 |  |  |         ip = "192.168.10.102"; | 
 |  |  |         sendAuthenticateRequest(ip, request, callBack); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public interface SearchGatewayCallBack { | 
 |  |  |         /** | 
 |  |  |          * 搜索结束 | 
 |  |  |          * | 
 |  |  |          * @param error | 
 |  |  |          */ | 
 |  |  |         void onEnd(String error); | 
 |  |  |  | 
 |  |  |         /** | 
 |  |  |          * 搜索网关成功 | 
 |  |  |          * | 
 |  |  |          * @param gatewaySearchBean | 
 |  |  |          */ | 
 |  |  |         void onSuccess(GatewaySearchBean gatewaySearchBean); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 开始搜索所有网关,有网关回复就回调,上层自己做去重判断 | 
 |  |  |      * | 
 |  |  |      * @param callBack 回调 | 
 |  |  |      */ | 
 |  |  |     public void startSearchAllGateway(SearchGatewayCallBack callBack) { | 
 |  |  |  | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 暂停搜索网关 | 
 |  |  |      */ | 
 |  |  |     public void endSearchAllGateway() { | 
 |  |  |  | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 搜索指定网关是否在线,搜索到则返回指定的网关对象 | 
 |  |  |      * | 
 |  |  |      * @param gatewayId 网关id | 
 |  |  |      * @param callBack  回调 | 
 |  |  |      */ | 
 |  |  |     public void searchGateway(String gatewayId, SearchGatewayCallBack callBack) { | 
 |  |  |         String time = String.valueOf(System.currentTimeMillis()); | 
 |  |  |         JsonObject jsonObject = new JsonObject(); | 
 |  |  |         jsonObject.addProperty("id", IdUtils.getUUId()); | 
 |  |  |         jsonObject.addProperty("time_stamp", time); | 
 |  |  |         String ip = IpUtils.getBroadcastAddress(); | 
 |  |  |         LinkRequest message = new LinkRequest(TopicConstant.GATEWAY_SEARCH, | 
 |  |  |                 jsonObject.toString()); | 
 |  |  |  | 
 |  |  |         HdlSocketHelper.send(getUdpBoot(ip), message, new HdlSocketHelper.HdlSocketListener() { | 
 |  |  |             @Override | 
 |  |  |             public void onSucceed(Object msg) { | 
 |  |  |                 GatewaySearchBean searchBean = getGatewaySearchBean(msg); | 
 |  |  |                 if (searchBean != null && searchBean.getGatewayId() == gatewayId) { | 
 |  |  |                     Log.i("TAG", "onSuccess: "); | 
 |  |  |                     callBack.onSuccess(searchBean); | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |  | 
 |  |  |             @Override | 
 |  |  |             public void onFailure() { | 
 |  |  |                 Log.i("TAG", "onFailure: "); | 
 |  |  |                 callBack.onEnd("超时"); | 
 |  |  |             } | 
 |  |  |         }); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     private GatewaySearchBean getGatewaySearchBean(Object msg) { | 
 |  |  |         GatewaySearchBean searchBean = null; | 
 |  |  |         if (msg instanceof LinkResponse) { | 
 |  |  |             LinkResponse linkResponse = (LinkResponse) msg; | 
 |  |  |             String data = linkResponse.getData(); | 
 |  |  |             if (!TextUtils.isEmpty(data)) { | 
 |  |  |                 final BaseLocalResponse<GatewaySearchBean> response = GsonConvert.getGson().fromJson(data, new TypeToken<BaseLocalResponse<GatewaySearchBean>>() { | 
 |  |  |                 }.getType()); | 
 |  |  |                 searchBean = response.getObjects(); | 
 |  |  |             } | 
 |  |  |  | 
 |  |  |         } | 
 |  |  |         return searchBean; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 设备控制 | 
 |  |  |      */ | 
 |  |  |     public void propertyDown(List<DeviceControlRequest> request, HDLSocket.CallBack callBack) { | 
 |  |  |  | 
 |  |  | //        if (!TextUtils.isEmpty(getGatewayId()) && !TextUtils.isEmpty(getTcpIp())) { | 
 |  |  |         String time = String.valueOf(System.currentTimeMillis()); | 
 |  |  |  | 
 |  |  |         final BaseLocalResponse<List<DeviceControlRequest>> data = new BaseLocalResponse<>(); | 
 |  |  |         data.setId(IdUtils.getUUId()); | 
 |  |  |         data.setTime_stamp(time); | 
 |  |  |         data.setObjects(request); | 
 |  |  |  | 
 |  |  |  | 
 |  |  |         String topic = String.format(TopicConstant.PROPERTY_DOWN, "1406844230123372545"); | 
 |  |  |         LinkRequest message = new LinkRequest(topic, | 
 |  |  |                 GsonConvert.getGson().toJson(request)); | 
 |  |  |  | 
 |  |  |         Log.i("TAG", "propertyDown: " + message.getData()); | 
 |  |  |  | 
 |  |  |         String ip = IpUtils.getBroadcastAddress(); | 
 |  |  |         HdlSocketHelper.send(getUdpBoot(ip), message, null); | 
 |  |  |  | 
 |  |  | //        } | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     String stringToMD5(String text) { | 
 |  |  |         byte[] hash; | 
 |  |  |         try { | 
 |  |  |             hash = MessageDigest.getInstance("MD5").digest(text.getBytes("UTF-8")); | 
 |  |  |         } catch (NoSuchAlgorithmException e) { | 
 |  |  |             e.printStackTrace(); | 
 |  |  |             return null; | 
 |  |  |         } catch (UnsupportedEncodingException e) { | 
 |  |  |             e.printStackTrace(); | 
 |  |  |             return null; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         StringBuilder hex = new StringBuilder(hash.length * 2); | 
 |  |  |         for (byte b : hash) { | 
 |  |  |             if ((b & 0xFF) < 0x10) | 
 |  |  |                 hex.append("0"); | 
 |  |  |             hex.append(Integer.toHexString(b & 0xFF)); | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         return hex.toString(); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  | } | 
 
 |  |  | 
 |  |  | import com.hdl.sdk.common.utils.SPUtils;
 | 
 |  |  | import com.hdl.sdk.common.utils.ThreadToolUtils;
 | 
 |  |  | import com.hdl.sdk.common.utils.gson.GsonConvert;
 | 
 |  |  | import com.hdl.sdk.connect.bean.AuthenticateRequest;
 | 
 |  |  | import com.hdl.sdk.connect.bean.BaseLocalRequest;
 | 
 |  |  | import com.hdl.sdk.connect.bean.BaseLocalResponse;
 | 
 |  |  | import com.hdl.sdk.connect.bean.DeviceControlRequest;
 | 
 |  |  | 
 |  |  |      * 组播搜索
 | 
 |  |  |      */
 | 
 |  |  |     public void searchGateway(CallBack callBack) {
 | 
 |  |  |         gatewayId = "";//重置网关ID
 | 
 |  |  |         this.searchCallBack = callBack;
 | 
 |  |  | 
 | 
 |  |  |         if (searchGatewayThread != null) {
 | 
 |  |  | 
 |  |  | 
 | 
 |  |  |             String replyTopic = String.format(TopicConstant.PROPERTY_DOWN_REPLY, getGatewayId());
 | 
 |  |  |             try {
 | 
 |  |  |                 sendMsg(message.toString().getBytes("utf-8"), replyTopic, callBack, new SendListener() {
 | 
 |  |  |                 sendMsg(message.getSendBytes(), replyTopic, callBack, new SendListener() {
 | 
 |  |  |                     @Override
 | 
 |  |  |                     public void onSucceed() {
 | 
 |  |  | 
 | 
 |  |  | 
 |  |  |                         }
 | 
 |  |  |                     }
 | 
 |  |  |                 });
 | 
 |  |  |             } catch (UnsupportedEncodingException e) {
 | 
 |  |  |             } catch (Exception e) {
 | 
 |  |  |                 if (callBack != null) {
 | 
 |  |  |                     callBack.onError("控制指令发送失败");
 | 
 |  |  |                 }
 | 
 
| New file | 
 |  |  | 
 |  |  | package com.hdl.sdk.connect;
 | 
 |  |  | 
 | 
 |  |  | import android.text.TextUtils;
 | 
 |  |  | 
 | 
 |  |  | import com.hdl.sdk.common.event.EventDispatcher;
 | 
 |  |  | import com.hdl.sdk.common.event.EventListener;
 | 
 |  |  | import com.hdl.sdk.common.utils.ThreadToolUtils;
 | 
 |  |  | import com.hdl.sdk.connect.bean.LinkRequest;
 | 
 |  |  | import com.hdl.sdk.socket.SocketBoot;
 | 
 |  |  | 
 | 
 |  |  | import java.util.concurrent.ScheduledExecutorService;
 | 
 |  |  | import java.util.concurrent.TimeUnit;
 | 
 |  |  | import java.util.concurrent.atomic.AtomicBoolean;
 | 
 |  |  | import java.util.concurrent.atomic.AtomicInteger;
 | 
 |  |  | 
 | 
 |  |  | /**
 | 
 |  |  |  * Created by Tong on 2021/11/11.
 | 
 |  |  |  */
 | 
 |  |  | public class HdlSocketHelper {
 | 
 |  |  | 
 | 
 |  |  |     private static final Long DEF_SEND_TIMEOUT = 1000L;
 | 
 |  |  |     private static final int DEF_MAX_RETRY = 4;
 | 
 |  |  |     private static final int DEF_SEND_ONE = 1;
 | 
 |  |  | 
 | 
 |  |  |     private final Long sendAwaitTime;
 | 
 |  |  |     private final int maxRetry;
 | 
 |  |  | 
 | 
 |  |  |     private final SocketBoot boot;
 | 
 |  |  |     private final LinkRequest linkRequest;
 | 
 |  |  |     private final EventListener eventListener;
 | 
 |  |  | 
 | 
 |  |  |     private final AtomicInteger sendNumber;
 | 
 |  |  | 
 | 
 |  |  |     private final AtomicBoolean isSend = new AtomicBoolean();
 | 
 |  |  | 
 | 
 |  |  |     private final HdlSocketListener listener;
 | 
 |  |  | 
 | 
 |  |  |     private ScheduledExecutorService sendThread;
 | 
 |  |  | 
 | 
 |  |  |     public interface HdlSocketListener {
 | 
 |  |  |         void onSucceed(Object msg);
 | 
 |  |  | 
 | 
 |  |  |         void onFailure();
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     private HdlSocketHelper(Long sendAwaitTime, int maxRetry, SocketBoot boot, LinkRequest linkRequest, String observeTopic, HdlSocketListener listener) {
 | 
 |  |  |         this.sendAwaitTime = sendAwaitTime;
 | 
 |  |  |         this.maxRetry = maxRetry;
 | 
 |  |  |         this.boot = boot;
 | 
 |  |  |         this.linkRequest = linkRequest;
 | 
 |  |  |         this.listener = listener;
 | 
 |  |  |         this.sendNumber = new AtomicInteger(0);
 | 
 |  |  |         eventListener = new EventListener() {
 | 
 |  |  |             @Override
 | 
 |  |  |             public void onMessage(Object msg) {
 | 
 |  |  |                 isSend.set(true);
 | 
 |  |  |                 if (listener != null) {
 | 
 |  |  |                     listener.onSucceed(msg);
 | 
 |  |  |                 }
 | 
 |  |  |                 if (sendThread != null) {
 | 
 |  |  |                     sendThread.shutdownNow();
 | 
 |  |  |                 }
 | 
 |  |  |                 EventDispatcher.getInstance().remove(eventListener);
 | 
 |  |  |             }
 | 
 |  |  |         };
 | 
 |  |  |         EventDispatcher.getInstance().register(observeTopic, eventListener);
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public static void send(SocketBoot boot, LinkRequest linkRequest, String observeTopic, HdlSocketListener listener, Long sendAwaitTime, int maxRetry) {
 | 
 |  |  |         if (TextUtils.isEmpty(observeTopic)) {
 | 
 |  |  |             observeTopic = linkRequest.getTopic() + "_reply";
 | 
 |  |  |         }
 | 
 |  |  |         HdlSocketHelper socketHelper = new HdlSocketHelper(sendAwaitTime, maxRetry, boot, linkRequest, observeTopic, listener);
 | 
 |  |  |         socketHelper.resend();
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public static void send(SocketBoot boot, LinkRequest linkRequest, String observeTopic, HdlSocketListener listener) {
 | 
 |  |  |         send(boot, linkRequest, observeTopic, listener, DEF_SEND_TIMEOUT, DEF_MAX_RETRY);
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  | 
 | 
 |  |  |     public static void send(SocketBoot boot, LinkRequest linkRequest, HdlSocketListener listener) {
 | 
 |  |  |         send(boot, linkRequest, "", listener, DEF_SEND_TIMEOUT, DEF_MAX_RETRY);
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public static void sendOne(SocketBoot boot, LinkRequest linkRequest, HdlSocketListener listener) {
 | 
 |  |  |         send(boot, linkRequest, "", listener, DEF_SEND_TIMEOUT, DEF_SEND_ONE);
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     private void resend() {
 | 
 |  |  |         getSendThread().scheduleWithFixedDelay(new Runnable() {
 | 
 |  |  |             @Override
 | 
 |  |  |             public void run() {
 | 
 |  |  |                 if ((sendNumber.get() < maxRetry + 2) || !isSend.get()) {
 | 
 |  |  |                     try {
 | 
 |  |  |                         if (sendNumber.get() < maxRetry + 1) {
 | 
 |  |  |                             if (boot != null) {
 | 
 |  |  |                                 boot.sendMsg(linkRequest.getSendBytes());
 | 
 |  |  |                             }
 | 
 |  |  |                             sendNumber.set(sendNumber.get() + 1);
 | 
 |  |  |                         } else {
 | 
 |  |  |                             notifyFailure();
 | 
 |  |  |                         }
 | 
 |  |  |                     } catch (Exception e) {
 | 
 |  |  |                         e.printStackTrace();
 | 
 |  |  |                         sendNumber.set(sendNumber.get() + 1);
 | 
 |  |  |                     } finally {
 | 
 |  |  |                         if (sendNumber.get() > maxRetry + 1 && !isSend.get()) {
 | 
 |  |  |                             notifyFailure();
 | 
 |  |  |                         }
 | 
 |  |  |                     }
 | 
 |  |  |                 }
 | 
 |  |  |             }
 | 
 |  |  |         }, sendAwaitTime, sendAwaitTime, TimeUnit.MILLISECONDS);
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public ScheduledExecutorService getSendThread() {
 | 
 |  |  |         if (sendThread == null) {
 | 
 |  |  |             sendThread = ThreadToolUtils.getInstance().newScheduledThreadPool(1);
 | 
 |  |  |         }
 | 
 |  |  |         return sendThread;
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  | 
 | 
 |  |  |     private void notifyFailure() {
 | 
 |  |  |         if (sendThread != null) {
 | 
 |  |  |             sendThread.shutdownNow();
 | 
 |  |  |         }
 | 
 |  |  |         if (listener != null) {
 | 
 |  |  |             listener.onFailure();
 | 
 |  |  |         }
 | 
 |  |  |     }
 | 
 |  |  | }
 | 
 
| New file | 
 |  |  | 
 |  |  | package com.hdl.sdk.connect.bean; | 
 |  |  |  | 
 |  |  | import java.io.Serializable; | 
 |  |  |  | 
 |  |  | /** | 
 |  |  |  * Created by jlchen on 11/11/21. | 
 |  |  |  * | 
 |  |  |  * @Description : AuthenticateRequest | 
 |  |  |  * 三方网络设备入网及认证 请求参数 | 
 |  |  |  * 请求topic:/user/all/custom/deivce/network_access/request | 
 |  |  |  */ | 
 |  |  | public class AuthenticateRequest implements Serializable { | 
 |  |  |     private String id; | 
 |  |  |     private String time_stamp; | 
 |  |  |     private AuthenticateDeviceInfoBean objects;//当前设备基本信息 | 
 |  |  |     private AuthBean auth;//认证信息 | 
 |  |  |  | 
 |  |  |     public String getID() { return id; } | 
 |  |  |     public void setID(String value) { this.id = value; } | 
 |  |  |  | 
 |  |  |     public String getTimeStamp() { return time_stamp; } | 
 |  |  |     public void setTimeStamp(String value) { this.time_stamp = value; } | 
 |  |  |  | 
 |  |  |     public AuthenticateDeviceInfoBean getObjects() { return objects; } | 
 |  |  |     public void setObjects(AuthenticateDeviceInfoBean value) { this.objects = value; } | 
 |  |  |  | 
 |  |  |     public AuthBean getAuth() { return auth; } | 
 |  |  |     public void setAuth(AuthBean value) { this.auth = value; } | 
 |  |  |  | 
 |  |  |     public AuthenticateRequest(String id, String time_stamp, AuthenticateDeviceInfoBean objects, AuthBean auth) { | 
 |  |  |         this.id = id; | 
 |  |  |         this.time_stamp = time_stamp; | 
 |  |  |         this.objects = objects; | 
 |  |  |         this.auth = auth; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public static class AuthBean implements Serializable { | 
 |  |  |         private String mac_key;//(mac+secret)的两次md5值 | 
 |  |  |         private String spk;//(mac+secret)的两次md5值 | 
 |  |  |         private RequestBean request;//设备相关信息 | 
 |  |  |  | 
 |  |  |         public String getMACKey() { return mac_key; } | 
 |  |  |         public void setMACKey(String value) { this.mac_key = value; } | 
 |  |  |  | 
 |  |  |         public String getSpk() { return spk; } | 
 |  |  |         public void setSpk(String value) { this.spk = value; } | 
 |  |  |  | 
 |  |  |         public RequestBean getRequest() { return request; } | 
 |  |  |         public void setRequest(RequestBean value) { this.request = value; } | 
 |  |  |  | 
 |  |  |         public AuthBean() { | 
 |  |  |  | 
 |  |  |         } | 
 |  |  |         public AuthBean(String mac_key, String spk, RequestBean request) { | 
 |  |  |             this.mac_key = mac_key; | 
 |  |  |             this.spk = spk; | 
 |  |  |             this.request = request; | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public static class RequestBean implements Serializable{ | 
 |  |  |         private String mac;//设备mac | 
 |  |  |         private String supplier;//厂家 | 
 |  |  |         private String hardware_model;//设备型号 | 
 |  |  |         private String firmware_version;//程序版本 | 
 |  |  |         public RequestBean(){}; | 
 |  |  |         public RequestBean(String mac, String supplier, String hardware_model, String firmware_version) { | 
 |  |  |             this.mac = mac; | 
 |  |  |             this.supplier = supplier; | 
 |  |  |             this.hardware_model = hardware_model; | 
 |  |  |             this.firmware_version = firmware_version; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         public String getMAC() { return mac; } | 
 |  |  |         public void setMAC(String value) { this.mac = value; } | 
 |  |  |  | 
 |  |  |         public String getSupplier() { return supplier; } | 
 |  |  |         public void setSupplier(String value) { this.supplier = value; } | 
 |  |  |  | 
 |  |  |         public String getHardwareModel() { return hardware_model; } | 
 |  |  |         public void setHardwareModel(String value) { this.hardware_model = value; } | 
 |  |  |  | 
 |  |  |         public String getFirmwareVersion() { return firmware_version; } | 
 |  |  |         public void setFirmwareVersion(String value) { this.firmware_version = value; } | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public static class AuthenticateDeviceInfoBean implements Serializable{ | 
 |  |  |         private String oid;//设备的Oid | 
 |  |  |         private String device_mac;//设备的Mac | 
 |  |  |         private String device_name;// | 
 |  |  |         private String device_model; | 
 |  |  |         private String access_mode = "WIFI"; | 
 |  |  |         private String sid; | 
 |  |  |         private String ip_mac; | 
 |  |  |         private String ip_address = "192.16.10.104"; | 
 |  |  |         private String netmask = "255.255.255.0"; | 
 |  |  |         private String ip_gateway = "192.16.10.1"; | 
 |  |  |         private String dns1 = "114.114.114.114"; | 
 |  |  |         private String dns2 = "8.8.8.8"; | 
 |  |  |         private VersionBean[] versions; | 
 |  |  |  | 
 |  |  |         public String getOID() { return oid; } | 
 |  |  |         public void setOID(String value) { this.oid = value; } | 
 |  |  |  | 
 |  |  |         public String getDeviceMAC() { return device_mac; } | 
 |  |  |         public void setDeviceMAC(String value) { this.device_mac = value; } | 
 |  |  |  | 
 |  |  |         public String getDeviceName() { return device_name; } | 
 |  |  |         public void setDeviceName(String value) { this.device_name = value; } | 
 |  |  |  | 
 |  |  |         public String getDeviceModel() { return device_model; } | 
 |  |  |         public void setDeviceModel(String value) { this.device_model = value; } | 
 |  |  |  | 
 |  |  |         public String getAccessMode() { return access_mode; } | 
 |  |  |         public void setAccessMode(String value) { this.access_mode = value; } | 
 |  |  |  | 
 |  |  |         public String getSid() { return sid; } | 
 |  |  |         public void setSid(String value) { this.sid = value; } | 
 |  |  |  | 
 |  |  |         public String getIPMAC() { return ip_mac; } | 
 |  |  |         public void setIPMAC(String value) { this.ip_mac = value; } | 
 |  |  |  | 
 |  |  |         public String getIPAddress() { return ip_address; } | 
 |  |  |         public void setIPAddress(String value) { this.ip_address = value; } | 
 |  |  |  | 
 |  |  |         public String getNetmask() { return netmask; } | 
 |  |  |         public void setNetmask(String value) { this.netmask = value; } | 
 |  |  |  | 
 |  |  |         public String getIPGateway() { return ip_gateway; } | 
 |  |  |         public void setIPGateway(String value) { this.ip_gateway = value; } | 
 |  |  |  | 
 |  |  |         public String getDns1() { return dns1; } | 
 |  |  |         public void setDns1(String value) { this.dns1 = value; } | 
 |  |  |  | 
 |  |  |         public String getDns2() { return dns2; } | 
 |  |  |         public void setDns2(String value) { this.dns2 = value; } | 
 |  |  |  | 
 |  |  |         public VersionBean[] getVersions() { return versions; } | 
 |  |  |         public void setVersions(VersionBean[] value) { this.versions = value; } | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public static class VersionBean implements Serializable{ | 
 |  |  |         private String module; | 
 |  |  |         private String version; | 
 |  |  |  | 
 |  |  |         public VersionBean(String module, String version) { | 
 |  |  |             this.module = module; | 
 |  |  |             this.version = version; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         public String getModule() { | 
 |  |  |             return module; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         public void setModule(String value) { | 
 |  |  |             this.module = value; | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  | } | 
 
| New file | 
 |  |  | 
 |  |  | package com.hdl.sdk.connect.bean; | 
 |  |  |  | 
 |  |  | import java.io.Serializable; | 
 |  |  |  | 
 |  |  | /** | 
 |  |  |  * Created by jlchen on 11/12/21. | 
 |  |  |  * | 
 |  |  |  * @Description : AuthenticateResponseBean | 
 |  |  |  */ | 
 |  |  | public class AuthenticateResponseBean implements Serializable { | 
 |  |  |     private String id; | 
 |  |  |     private String timeStamp; | 
 |  |  |     private String code; | 
 |  |  |     private GatewayObjects objects; | 
 |  |  |     private Auth auth; | 
 |  |  |  | 
 |  |  |     public String getID() { return id; } | 
 |  |  |     public void setID(String value) { this.id = value; } | 
 |  |  |  | 
 |  |  |     public String getTimeStamp() { return timeStamp; } | 
 |  |  |     public void setTimeStamp(String value) { this.timeStamp = value; } | 
 |  |  |  | 
 |  |  |     public String getCode() { return code; } | 
 |  |  |     public void setCode(String value) { this.code = value; } | 
 |  |  |  | 
 |  |  |     public GatewayObjects getObjects() { return objects; } | 
 |  |  |     public void setObjects(GatewayObjects value) { this.objects = value; } | 
 |  |  |  | 
 |  |  |     public Auth getAuth() { return auth; } | 
 |  |  |     public void setAuth(Auth value) { this.auth = value; } | 
 |  |  |  | 
 |  |  |     public class Auth implements Serializable { | 
 |  |  |         private String localSecret; | 
 |  |  |  | 
 |  |  |         public String getLocalSecret() { return localSecret; } | 
 |  |  |         public void setLocalSecret(String value) { this.localSecret = value; } | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public class GatewayObjects implements Serializable  { | 
 |  |  |         private String ip_address; | 
 |  |  |         private String gatewayId; | 
 |  |  |         private String oid; | 
 |  |  |  | 
 |  |  |         public String getIPAddress() { return ip_address; } | 
 |  |  |         public void setIPAddress(String value) { this.ip_address = value; } | 
 |  |  |  | 
 |  |  |         public String getGatewayID() { return gatewayId; } | 
 |  |  |         public void setGatewayID(String value) { this.gatewayId = value; } | 
 |  |  |  | 
 |  |  |         public String getOID() { return oid; } | 
 |  |  |         public void setOID(String value) { this.oid = value; } | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |  | 
 |  |  | } | 
 
 |  |  | 
 |  |  |     private String ip_address;
 | 
 |  |  |     private String access_mode;
 | 
 |  |  |     private String master;
 | 
 |  |  |     private boolean isLocalEncrypt;//2021-11-11 新增
 | 
 |  |  | 
 | 
 |  |  |     public String getDevice_model() {
 | 
 |  |  |         return device_model;
 | 
 |  |  | 
 |  |  |     public void setMaster(String master) {
 | 
 |  |  |         this.master = master;
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public boolean isLocalEncrypt() {
 | 
 |  |  |         return isLocalEncrypt;
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public void setLocalEncrypt(boolean localEncrypt) {
 | 
 |  |  |         isLocalEncrypt = localEncrypt;
 | 
 |  |  |     }
 | 
 |  |  | }
 | 
 
 |  |  | 
 |  |  | package com.hdl.sdk.connect.bean;
 | 
 |  |  | 
 | 
 |  |  | import android.text.TextUtils;
 | 
 |  |  | import android.util.Log;
 | 
 |  |  | 
 | 
 |  |  | import androidx.annotation.NonNull;
 | 
 |  |  | 
 | 
 |  |  | import com.hdl.sdk.common.config.AuthenticateConfig;
 | 
 |  |  | import com.hdl.sdk.connect.utils.AESUtils;
 | 
 |  |  | import com.hdl.sdk.connect.utils.AesUtil;
 | 
 |  |  | 
 | 
 |  |  | import java.io.UnsupportedEncodingException;
 | 
 |  |  | 
 | 
 |  |  | import static com.hdl.sdk.common.config.TopicConstant.DEIVCE_AUTH_REQUEST;
 | 
 |  |  | import static com.hdl.sdk.common.config.TopicConstant.GATEWAY_SEARCH;
 | 
 |  |  | 
 | 
 |  |  | /**
 | 
 |  |  |  * Created by Tong on 2021/9/29.
 | 
 |  |  | 
 |  |  | public class LinkRequest {
 | 
 |  |  |     private String topic;
 | 
 |  |  |     private String data;
 | 
 |  |  | 
 | 
 |  |  |     private int length;
 | 
 |  |  | 
 | 
 |  |  |     public LinkRequest() {
 | 
 |  |  | 
 |  |  |     public void setData(String data) {
 | 
 |  |  |         this.data = data;
 | 
 |  |  |         if (!TextUtils.isEmpty(data)) {
 | 
 |  |  |             setLength(data.length());
 | 
 |  |  |             setLength(getBytesLength(data));
 | 
 |  |  | //            setLength(data.length());
 | 
 |  |  |         } else {
 | 
 |  |  |             setLength(0);
 | 
 |  |  |         }
 | 
 |  |  | 
 |  |  |                 "\r\n\r\n" +
 | 
 |  |  |                 getData();
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     private static byte[] stringToBytes(String str) {
 | 
 |  |  |         try {
 | 
 |  |  |             // 使用指定的字符集将此字符串编码为byte序列并存到一个byte数组中
 | 
 |  |  |             return str.getBytes("utf-8");
 | 
 |  |  |         } catch (UnsupportedEncodingException e) {
 | 
 |  |  |             e.printStackTrace();
 | 
 |  |  |         }
 | 
 |  |  |         return new byte[]{};
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     private int getBytesLength(String str){
 | 
 |  |  |         return stringToBytes(str).length;
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     /**
 | 
 |  |  |      * 合并数组
 | 
 |  |  |      * @param bt1
 | 
 |  |  |      * @param bt2
 | 
 |  |  |      * @return
 | 
 |  |  |      */
 | 
 |  |  |     public static byte[] byteMerger(byte[] bt1, byte[] bt2){
 | 
 |  |  |         byte[] bt3 = new byte[bt1.length + bt2.length];
 | 
 |  |  |         System.arraycopy(bt1, 0, bt3, 0, bt1.length);
 | 
 |  |  |         System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);
 | 
 |  |  |         return bt3;
 | 
 |  |  |     }
 | 
 |  |  |     /**
 | 
 |  |  |      * 获取发送数据byte
 | 
 |  |  |      *
 | 
 |  |  |      * @return
 | 
 |  |  |      */
 | 
 |  |  |     public byte[] getSendBytes() {
 | 
 |  |  |         try {
 | 
 |  |  |             //判断是否需要加密
 | 
 |  |  |             if (AuthenticateConfig.getInstance().ifNeedEncrypt(topic)) {
 | 
 |  |  |                 //需要加密
 | 
 |  |  |                 byte[] dataBytes = AesUtil.aesEncrypt(stringToBytes(data), AuthenticateConfig.getInstance().getLocalSecret());
 | 
 |  |  |                 String headString = "Topic:" + getTopic() + "\r\n" + "Length:" + dataBytes.length + "\r\n" + "\r\n";
 | 
 |  |  | //                byte[] headBytes = headString.getBytes("utf-8");
 | 
 |  |  |                 byte[] headBytes = headString.getBytes();
 | 
 |  |  |                 byte[] sendBytes = byteMerger(headBytes, dataBytes);
 | 
 |  |  |                 return sendBytes;
 | 
 |  |  |             } else {
 | 
 |  |  |                 return this.toString().getBytes("utf-8");
 | 
 |  |  |             }
 | 
 |  |  | 
 | 
 |  |  |         } catch (Exception e) {
 | 
 |  |  |             return new byte[]{};
 | 
 |  |  |         }
 | 
 |  |  |     }
 | 
 |  |  | }
 | 
 
 |  |  | 
 |  |  | package com.hdl.sdk.connect.protocol;
 | 
 |  |  | 
 | 
 |  |  | 
 | 
 |  |  | import android.util.Log;
 | 
 |  |  | 
 | 
 |  |  | import com.hdl.sdk.common.config.AuthenticateConfig;
 | 
 |  |  | import com.hdl.sdk.common.event.EventDispatcher;
 | 
 |  |  | import com.hdl.sdk.common.utils.ByteUtils;
 | 
 |  |  | import com.hdl.sdk.connect.bean.LinkResponse;
 | 
 |  |  | import com.hdl.sdk.connect.utils.AesUtil;
 | 
 |  |  | import com.hdl.sdk.connect.utils.ProtocolParse;
 | 
 |  |  | import com.hdl.sdk.socket.codec.ByteToMessageDecoder;
 | 
 |  |  | 
 | 
 |  |  | 
 |  |  |             if (bodyLength > 0) {
 | 
 |  |  |                 if (byteArray.length >= bodyLength + bodyStartIndex) {
 | 
 |  |  |                     byte[] body = ByteUtils.getRangeBytes(bytes, bodyStartIndex, bodyStartIndex + bodyLength);
 | 
 |  |  |                     response.setData(new String(body, "utf-8"));
 | 
 |  |  | 
 | 
 |  |  |                     if(AuthenticateConfig.getInstance().ifNeedEncrypt(response.getTopic())){
 | 
 |  |  |                         //需要解密
 | 
 |  |  |                         byte[] bodyBytes = AesUtil.aesDecrypt(body,AuthenticateConfig.getInstance().getLocalSecret());
 | 
 |  |  |                         response.setData(new String(bodyBytes, "utf-8"));
 | 
 |  |  |                         Log.i("TAG", "decoder: ");
 | 
 |  |  |                     }else{
 | 
 |  |  |                         response.setData(new String(body, "utf-8"));
 | 
 |  |  |                     }
 | 
 |  |  | 
 | 
 |  |  | 
 | 
 |  |  | 
 | 
 |  |  |                     if (byteArray.length >= bodyLength + bodyStartIndex) {
 | 
 |  |  |                         //保存余留
 | 
 
 |  |  | 
 |  |  |     protected byte[] encode(byte[] data) throws Exception {
 | 
 |  |  |         return data;
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  | }
 | 
 
| New file | 
 |  |  | 
 |  |  | package com.hdl.sdk.connect.utils;
 | 
 |  |  | 
 | 
 |  |  | import android.util.Base64;
 | 
 |  |  | 
 | 
 |  |  | import java.io.UnsupportedEncodingException;
 | 
 |  |  | import java.security.InvalidAlgorithmParameterException;
 | 
 |  |  | import java.security.InvalidKeyException;
 | 
 |  |  | import java.security.NoSuchAlgorithmException;
 | 
 |  |  | 
 | 
 |  |  | import javax.crypto.BadPaddingException;
 | 
 |  |  | import javax.crypto.Cipher;
 | 
 |  |  | import javax.crypto.IllegalBlockSizeException;
 | 
 |  |  | import javax.crypto.NoSuchPaddingException;
 | 
 |  |  | import javax.crypto.spec.IvParameterSpec;
 | 
 |  |  | import javax.crypto.spec.SecretKeySpec;
 | 
 |  |  | 
 | 
 |  |  | /**
 | 
 |  |  |  * Created by Tong on 2021/11/3.
 | 
 |  |  |  */
 | 
 |  |  | public class AESUtils {
 | 
 |  |  | 
 | 
 |  |  |     public static String encryptAES(String content, String key) throws NoSuchPaddingException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidKeyException {
 | 
 |  |  |         return encryptAES(content, key, "");
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public static String encryptAES(String content, String key, String ivString)
 | 
 |  |  |             throws InvalidKeyException, NoSuchAlgorithmException,
 | 
 |  |  |             NoSuchPaddingException, UnsupportedEncodingException,
 | 
 |  |  |             InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
 | 
 |  |  |         byte[] byteContent = content.getBytes("UTF-8");
 | 
 |  |  |         byte[] encryptedBytes = encryptAES(byteContent,key,ivString);
 | 
 |  |  |         return Base64.encodeToString(encryptedBytes, Base64.NO_WRAP);
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public static byte[] encryptAES(byte[] byteContent, String key) throws NoSuchPaddingException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidKeyException {
 | 
 |  |  |         return encryptAES(byteContent, key, key);
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public static byte[] encryptAES(byte[] byteContent, String key, String ivString)
 | 
 |  |  |             throws InvalidKeyException, NoSuchAlgorithmException,
 | 
 |  |  |             NoSuchPaddingException, UnsupportedEncodingException,
 | 
 |  |  |             InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
 | 
 |  |  | 
 | 
 |  |  |         byte[] enCodeFormat = key.getBytes();
 | 
 |  |  |         SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
 | 
 |  |  | 
 | 
 |  |  |         byte[] initParam = ivString.getBytes();
 | 
 |  |  |         IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
 | 
 |  |  | 
 | 
 |  |  |         // 指定加密的算法、工作模式和填充方式
 | 
 |  |  |         Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
 | 
 |  |  |         cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
 | 
 |  |  | 
 | 
 |  |  |         byte[] encryptedBytes = cipher.doFinal(byteContent);
 | 
 |  |  |         return encryptedBytes;
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public static String decryptAES(String content, String key) throws NoSuchPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
 | 
 |  |  |         return decryptAES(content, key, "");
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public static String decryptAES(String content, String key, String ivString)
 | 
 |  |  |             throws InvalidKeyException, NoSuchAlgorithmException,
 | 
 |  |  |             NoSuchPaddingException, InvalidAlgorithmParameterException,
 | 
 |  |  |             IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
 | 
 |  |  | 
 | 
 |  |  | 
 | 
 |  |  |         // base64 解码
 | 
 |  |  |         byte[] encryptedBytes = Base64.decode(content, Base64.NO_WRAP);
 | 
 |  |  | 
 | 
 |  |  |         byte[] enCodeFormat = key.getBytes();
 | 
 |  |  |         SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, "AES");
 | 
 |  |  | 
 | 
 |  |  |         byte[] initParam = ivString.getBytes();
 | 
 |  |  |         IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
 | 
 |  |  | 
 | 
 |  |  |         Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
 | 
 |  |  |         cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
 | 
 |  |  | 
 | 
 |  |  |         byte[] result = cipher.doFinal(encryptedBytes);
 | 
 |  |  | 
 | 
 |  |  |         return new String(result, "UTF-8");
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 
| New file | 
 |  |  | 
 |  |  | package com.hdl.sdk.connect.utils; | 
 |  |  |  | 
 |  |  |  | 
 |  |  | import com.hdl.sdk.common.utils.LogUtils; | 
 |  |  |  | 
 |  |  | import org.bouncycastle.jce.provider.BouncyCastleProvider; | 
 |  |  |  | 
 |  |  | import java.security.InvalidAlgorithmParameterException; | 
 |  |  | import java.security.InvalidKeyException; | 
 |  |  | import java.security.Key; | 
 |  |  | import java.security.NoSuchAlgorithmException; | 
 |  |  | import java.security.Security; | 
 |  |  |  | 
 |  |  | import javax.crypto.BadPaddingException; | 
 |  |  | import javax.crypto.Cipher; | 
 |  |  | import javax.crypto.IllegalBlockSizeException; | 
 |  |  | import javax.crypto.NoSuchPaddingException; | 
 |  |  | import javax.crypto.spec.IvParameterSpec; | 
 |  |  | import javax.crypto.spec.SecretKeySpec; | 
 |  |  |  | 
 |  |  | import cn.hutool.core.util.HexUtil; | 
 |  |  |  | 
 |  |  | /** | 
 |  |  |  * Aes 加解密工具类 | 
 |  |  |  * | 
 |  |  |  * @author yangtao | 
 |  |  |  * 2020年10月29日 | 
 |  |  |  */ | 
 |  |  | public class AesUtil { | 
 |  |  |  | 
 |  |  |     private static final String CIPHER_INSTANCE = "AES/ECB/PKCS5Padding"; | 
 |  |  |     private static final String AES_ALGORITHM = "AES"; | 
 |  |  |  | 
 |  |  |     static { | 
 |  |  |         //AES/CBC/PKCS7Padding 依赖 | 
 |  |  |         Security.addProvider(new BouncyCastleProvider()); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * AES加密 | 
 |  |  |      * | 
 |  |  |      * @param key | 
 |  |  |      * @return | 
 |  |  |      * @throws Exception | 
 |  |  |      */ | 
 |  |  |     public static byte[] aesEncrypt(byte[] content, String key) { | 
 |  |  |         return encrypt(content, key.getBytes(), "AES/CBC/PKCS7Padding", true, null); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * AES解密 | 
 |  |  |      * | 
 |  |  |      * @param key | 
 |  |  |      * @return | 
 |  |  |      * @throws Exception | 
 |  |  |      */ | 
 |  |  |     public static byte[] aesDecrypt(byte[] content, String key) { | 
 |  |  |         return decrypt(content, key.getBytes(), "AES/CBC/PKCS7Padding", true, null); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 加密 | 
 |  |  |      * | 
 |  |  |      * @param content 待加密字符串 | 
 |  |  |      * @param keyHex  aesKye的hexStr | 
 |  |  |      * @return | 
 |  |  |      */ | 
 |  |  |     public static String encrypt(String content, String keyHex) { | 
 |  |  |         return HexUtil.encodeHexStr(encrypt(content.getBytes(), HexUtil.decodeHex(keyHex), CIPHER_INSTANCE, false, null)); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 加密 | 
 |  |  |      * | 
 |  |  |      * @param content 待加密字符串 | 
 |  |  |      * @param key     密钥 | 
 |  |  |      * @return | 
 |  |  |      */ | 
 |  |  |     public static String encrypt(String content, byte[] key) { | 
 |  |  |         return HexUtil.encodeHexStr(encrypt(content.getBytes(), key, CIPHER_INSTANCE, false, null)); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 加密 | 
 |  |  |      * | 
 |  |  |      * @param content 待加密字符串 | 
 |  |  |      * @param keyHex  aesKye的hexStr | 
 |  |  |      * @return | 
 |  |  |      */ | 
 |  |  |     public static byte[] encryptReturnByte(String content, String keyHex) { | 
 |  |  |         return encrypt(content.getBytes(), HexUtil.decodeHex(keyHex), CIPHER_INSTANCE, false, null); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 加密 | 
 |  |  |      * | 
 |  |  |      * @param content 待加密字符串 | 
 |  |  |      * @param key     密钥 | 
 |  |  |      * @return | 
 |  |  |      */ | 
 |  |  |     public static byte[] encryptReturnByte(String content, byte[] key) { | 
 |  |  |         return encrypt(content.getBytes(), key, CIPHER_INSTANCE, false, null); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 加密 | 
 |  |  |      * | 
 |  |  |      * @param content        待加密字符串 | 
 |  |  |      * @param keyByte        aesKye的hexStr | 
 |  |  |      * @param cipherInstance AES算法填充方式 | 
 |  |  |      * @param isIv           是否使用偏移量 ECB模式不可用 | 
 |  |  |      * @param iv             偏移量 不传默认密钥 | 
 |  |  |      * @return | 
 |  |  |      */ | 
 |  |  |     public static byte[] encrypt(byte[] content, byte[] keyByte, String cipherInstance, Boolean isIv, byte[] iv) { | 
 |  |  |         try { | 
 |  |  |             //KEY转换 | 
 |  |  |             Key key = new SecretKeySpec(keyByte, AES_ALGORITHM); | 
 |  |  |             //加密 | 
 |  |  |             Cipher cipher = Cipher.getInstance(cipherInstance); | 
 |  |  |             if (isIv != null && isIv) { | 
 |  |  |                 //偏移量 不传默认密钥 | 
 |  |  |                 if (iv == null) { | 
 |  |  |                     iv = keyByte; | 
 |  |  |                 } | 
 |  |  |                 IvParameterSpec ivps = new IvParameterSpec(iv); | 
 |  |  |                 cipher.init(Cipher.ENCRYPT_MODE, key, ivps); | 
 |  |  |             } else { | 
 |  |  |                 cipher.init(Cipher.ENCRYPT_MODE, key); | 
 |  |  |             } | 
 |  |  |             byte[] result = cipher.doFinal(content); | 
 |  |  |             return result; | 
 |  |  |         } catch (NoSuchAlgorithmException e) { | 
 |  |  |             LogUtils.e(e.getMessage(), e); | 
 |  |  |         } catch (InvalidKeyException e) { | 
 |  |  |             LogUtils.e(e.getMessage(), e); | 
 |  |  |         } catch (NoSuchPaddingException e) { | 
 |  |  |             LogUtils.e(e.getMessage(), e); | 
 |  |  |         } catch (BadPaddingException e) { | 
 |  |  |             LogUtils.e(e.getMessage(), e); | 
 |  |  |         } catch (IllegalBlockSizeException e) { | 
 |  |  |             LogUtils.e(e.getMessage(), e); | 
 |  |  |         } catch (InvalidAlgorithmParameterException e) { | 
 |  |  |             LogUtils.e(e.getMessage(), e); | 
 |  |  |         } | 
 |  |  |         return null; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 解密 | 
 |  |  |      * | 
 |  |  |      * @param contentHex 待解密待字符串hexStr | 
 |  |  |      * @param keyHex     desKye的hexStr | 
 |  |  |      * @return | 
 |  |  |      */ | 
 |  |  |     public static String decrypt(String contentHex, String keyHex) { | 
 |  |  |         return new String(decrypt(HexUtil.decodeHex(contentHex), HexUtil.decodeHex(keyHex), CIPHER_INSTANCE, false, null)); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 解密 | 
 |  |  |      * | 
 |  |  |      * @param contentHex 待解密待字符串hexStr | 
 |  |  |      * @param key        密钥 | 
 |  |  |      * @return | 
 |  |  |      */ | 
 |  |  |     public static String decrypt(String contentHex, byte[] key) { | 
 |  |  |         return new String(decrypt(HexUtil.decodeHex(contentHex), key, CIPHER_INSTANCE, false, null)); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 解密 | 
 |  |  |      * | 
 |  |  |      * @param content 待解密待内容 | 
 |  |  |      * @param keyHex  密钥 | 
 |  |  |      * @return | 
 |  |  |      */ | 
 |  |  |     public static String decrypt(byte[] content, String keyHex) { | 
 |  |  |         return new String(decrypt(content, HexUtil.decodeHex(keyHex), CIPHER_INSTANCE, false, null)); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 解密 | 
 |  |  |      * | 
 |  |  |      * @param content 待解密待内容 | 
 |  |  |      * @param key     密钥 | 
 |  |  |      * @return | 
 |  |  |      */ | 
 |  |  |     public static String decrypt(byte[] content, byte[] key) { | 
 |  |  |         return new String(decrypt(content, key, CIPHER_INSTANCE, false, null)); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 解密 | 
 |  |  |      * | 
 |  |  |      * @param contentByte    待解密待字符串hexStr | 
 |  |  |      * @param contentByte    密钥 | 
 |  |  |      * @param cipherInstance AES算法填充方式 | 
 |  |  |      * @param isIv           是否使用偏移量 ECB模式不可用 | 
 |  |  |      * @param iv             偏移量 不传默认密钥 | 
 |  |  |      * @return | 
 |  |  |      */ | 
 |  |  |     public static byte[] decrypt(byte[] contentByte, byte[] keyByte, String cipherInstance, Boolean isIv, byte[] iv) { | 
 |  |  |         try { | 
 |  |  |             //KEY转换 | 
 |  |  |             Key key = new SecretKeySpec(keyByte, AES_ALGORITHM); | 
 |  |  |             //解密 | 
 |  |  |             Cipher cipher = Cipher.getInstance(cipherInstance); | 
 |  |  |             if (isIv != null && isIv) { | 
 |  |  |                 //偏移量 不传默认密钥 | 
 |  |  |                 if (iv == null) { | 
 |  |  |                     iv = keyByte; | 
 |  |  |                 } | 
 |  |  |                 IvParameterSpec ivps = new IvParameterSpec(iv); | 
 |  |  |                 cipher.init(Cipher.DECRYPT_MODE, key, ivps); | 
 |  |  |             } else { | 
 |  |  |                 cipher.init(Cipher.DECRYPT_MODE, key); | 
 |  |  |             } | 
 |  |  |             byte[] result = cipher.doFinal(contentByte); | 
 |  |  |             return result; | 
 |  |  |         } catch (NoSuchAlgorithmException e) { | 
 |  |  |             LogUtils.e(e.getMessage(), e); | 
 |  |  |         } catch (InvalidKeyException e) { | 
 |  |  |             LogUtils.e(e.getMessage(), e); | 
 |  |  |         } catch (NoSuchPaddingException e) { | 
 |  |  |             LogUtils.e(e.getMessage(), e); | 
 |  |  |         } catch (BadPaddingException e) { | 
 |  |  |             LogUtils.e(e.getMessage(), e); | 
 |  |  |         } catch (IllegalBlockSizeException e) { | 
 |  |  |             LogUtils.e(e.getMessage(), e); | 
 |  |  |         } catch (InvalidAlgorithmParameterException e) { | 
 |  |  |             LogUtils.e(e.getMessage(), e); | 
 |  |  |         } | 
 |  |  |         return null; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |  | 
 |  |  |     public static void main(String[] args) { | 
 |  |  | //        jiemi(); | 
 |  |  |         //jiami(); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public static void jiami() { | 
 |  |  |         String key = "1f6714fc-fb9b-4a"; | 
 |  |  |         String content = "{\"id\":\"123\",\"objects\":[{\"sid\":\"0001011565879801020200010101\",\"type\":\"PWD\",\"local_id\":\"2\"}],\"time_stamp\":\"1603281282000\"}"; | 
 |  |  |         byte[] res = aesEncrypt(content.getBytes(), key); | 
 |  |  |         System.out.println(HexUtil.encodeHexStr(res)); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  | //    /** | 
 |  |  | //     * | 
 |  |  | //     * @param str | 
 |  |  | //     * @param houseId | 
 |  |  | //     * @return | 
 |  |  | //     */ | 
 |  |  | //    public static String jiemi(byte[] str, String houseId) { | 
 |  |  | //        byte[] rs = aesDecrypt(str, HouseIdSecretUtil.getSecret(houseId)); | 
 |  |  | //        String content = new String(rs); | 
 |  |  | //        return content; | 
 |  |  | //    } | 
 |  |  |  | 
 |  |  | //    public static void main(String[] args) { | 
 |  |  | //        try { | 
 |  |  | //            //第一步: 生成KEY | 
 |  |  | //            //KeyGenerator keyGenerator = KeyGenerator.getInstance(AES_ALGORITHM); | 
 |  |  | //            //keyGenerator.init(256); | 
 |  |  | //            //第二步: 产生密钥 | 
 |  |  | //            //SecretKey secretKey = keyGenerator.generateKey(); | 
 |  |  | //            //第三步: 获取密钥 | 
 |  |  | //            //byte[] keyBytes = secretKey.getEncoded(); | 
 |  |  | //            //System.out.println(byteArrayToHexStr(keyBytes)); | 
 |  |  | //            String src = "aaaaaaaaaVVVVaaaAAAA无"; | 
 |  |  | //            String pwd = "41151AF257BFDB7859EEC62FB341EE95EE42E648FE24E1F8CE8DADE287CC1E5C"; | 
 |  |  | //            String mw = encrypt(src,pwd); | 
 |  |  | //            System.out.println("mw:"+mw); | 
 |  |  | //            String jm = decrypt(mw,pwd); | 
 |  |  | //            System.out.println("jm:"+jm); | 
 |  |  | // | 
 |  |  | //        }catch (Exception e){ | 
 |  |  | // | 
 |  |  | //        } | 
 |  |  | // | 
 |  |  | //    } | 
 |  |  | } | 
 
 |  |  | 
 |  |  |                 while (isRun.get()) {
 | 
 |  |  |                     if (client.isConnect()) {
 | 
 |  |  |                         Log.d("=====", "==发送数据==");
 | 
 |  |  | 
 | 
 |  |  |                         try {
 | 
 |  |  |                             SocketRequest socketRequest = mMessageQueue.take();
 | 
 |  |  |                             final String sendStr = new String(socketRequest.getData(), 0, socketRequest.getData().length);
 | 
 |  |  |                             Log.d("=====", "==发送数据==:"+sendStr);
 | 
 |  |  |                             final String action = socketRequest.getAction();
 | 
 |  |  |                             try {
 | 
 |  |  |                                 client.sendMsg(socketRequest.getData());
 | 
 
| New file | 
 |  |  | 
 |  |  | package com.hdl.sdk.socket;
 | 
 |  |  | 
 | 
 |  |  | import com.hdl.sdk.socket.client.IClient;
 | 
 |  |  | 
 | 
 |  |  | import java.net.DatagramSocket;
 | 
 |  |  | import java.net.InetSocketAddress;
 | 
 |  |  | import java.net.SocketException;
 | 
 |  |  | import java.util.concurrent.ConcurrentHashMap;
 | 
 |  |  | 
 | 
 |  |  | /**
 | 
 |  |  |  * Created by Tong on 2021/10/19.
 | 
 |  |  |  * 计划参考generic-pool、Commons Pool进行完善
 | 
 |  |  |  */
 | 
 |  |  | public class SocketPool {
 | 
 |  |  | 
 | 
 |  |  |     private final ConcurrentHashMap<String, IClient> mPool;
 | 
 |  |  |     private final ConcurrentHashMap<String, DatagramSocket> mUdpClientPool;
 | 
 |  |  | 
 | 
 |  |  |     private SocketPool() {
 | 
 |  |  |         mPool = new ConcurrentHashMap<>();
 | 
 |  |  |         mUdpClientPool = new ConcurrentHashMap<>();
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  | 
 | 
 |  |  |     private static class SingletonInstance {
 | 
 |  |  |         private static final SocketPool INSTANCE = new SocketPool();
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public static SocketPool getInstance() {
 | 
 |  |  |         return SingletonInstance.INSTANCE;
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public void clear() {
 | 
 |  |  |         mPool.clear();
 | 
 |  |  |         mUdpClientPool.clear();
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     public synchronized DatagramSocket getUdpSocket(InetSocketAddress address) throws SocketException {
 | 
 |  |  |         DatagramSocket socket;
 | 
 |  |  |         final String key = address.toString();
 | 
 |  |  |         if (mUdpClientPool.containsKey(key)) {
 | 
 |  |  |             socket = mUdpClientPool.get(key);
 | 
 |  |  |             if (socket != null && !socket.isClosed()) {
 | 
 |  |  |                 return socket;
 | 
 |  |  |             } else {
 | 
 |  |  |                 socket = new DatagramSocket(address);
 | 
 |  |  |             }
 | 
 |  |  |         } else {
 | 
 |  |  |             socket = new DatagramSocket(address);
 | 
 |  |  |         }
 | 
 |  |  |         mUdpClientPool.put(address.toString(), socket);
 | 
 |  |  |         return socket;
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  | }
 | 
 
 |  |  | 
 |  |  | import com.hdl.sdk.common.utils.ThreadToolUtils;
 | 
 |  |  | import com.hdl.sdk.socket.SocketBoot;
 | 
 |  |  | import com.hdl.sdk.socket.SocketOptions;
 | 
 |  |  | import com.hdl.sdk.socket.SocketPool;
 | 
 |  |  | import com.hdl.sdk.socket.annotation.ConnectStatus;
 | 
 |  |  | import com.hdl.sdk.socket.codec.IHandleMessage;
 | 
 |  |  | import com.hdl.sdk.socket.listener.ConnectStatusListener;
 | 
 |  |  | 
 |  |  | import java.net.DatagramPacket;
 | 
 |  |  | import java.net.DatagramSocket;
 | 
 |  |  | import java.net.InetAddress;
 | 
 |  |  | import java.net.InetSocketAddress;
 | 
 |  |  | import java.util.List;
 | 
 |  |  | import java.util.concurrent.atomic.AtomicBoolean;
 | 
 |  |  | 
 | 
 |  |  | 
 |  |  | 
 | 
 |  |  |     @Override
 | 
 |  |  |     public void connect() throws Exception {
 | 
 |  |  | 
 | 
 |  |  |         try {
 | 
 |  |  |             mSocket = ClientPool.getInstance().getUdpSocket(ip, monitorPort);
 | 
 |  |  |             mSocket = SocketPool.getInstance().getUdpSocket(new InetSocketAddress(monitorPort));
 | 
 |  |  | 
 | 
 |  |  |             mSocket.setBroadcast(true);
 | 
 |  |  |             mSocket.setReuseAddress(true);
 | 
 |  |  | 
 | 
 |  |  |             isConnect.set(true);
 | 
 |  |  |             if (receivePacket == null) {
 | 
 |  |  |                 receivePacket = new DatagramPacket(receiveByte, BUFFER);
 |